diff --git a/.editorconfig b/.editorconfig index 9a3e06f4..dc610a65 100755 --- a/.editorconfig +++ b/.editorconfig @@ -1,7 +1,7 @@ root = true -[{src, translations}/*] -end_of_line = crlf +[*] +end_of_line = lf insert_final_newline = true indent_style = space indent_size = 4 diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index d3510226..00000000 --- a/.eslintignore +++ /dev/null @@ -1,11 +0,0 @@ -artwork/* -build/* -electron/* -gulp/* -node_modules/* -res/* -res_built/* -res_raw/* -tmp_standalone_files/* -tools/* -translations/* diff --git a/.eslintrc.yml b/.eslintrc.yml deleted file mode 100644 index 60ddb5cd..00000000 --- a/.eslintrc.yml +++ /dev/null @@ -1,26 +0,0 @@ -env: - browser: true - es6: true -extends: - - "eslint:recommended" - - "plugin:@typescript-eslint/eslint-recommended" - - "prettier" -globals: - Atomics: readonly - SharedArrayBuffer: readonly -parser: "@typescript-eslint/parser" -parserOptions: - ecmaVersion: 6 - sourceType: "module" - ecmaFeatures: - - modules: true -plugins: - - "@typescript-eslint" - - "prettier" -rules: - prettier/prettier: error - no-undef: off - no-unused-vars: off - no-unreachable: off - no-prototype-builtins: off - linebreak-style: off diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 78a05301..96ed4382 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,42 +10,6 @@ on: - master jobs: - setup: - name: CI - - runs-on: ubuntu-latest - - steps: - - name: Install Dependencies - run: | - sudo apt-get update - sudo apt-get install ffmpeg - - - name: Setup Node - uses: actions/setup-node@v2-beta - with: - node-version: 16.x - - - name: Checkout repo - uses: actions/checkout@v2 - - - name: Install Yarn Dependencies - run: | - yarn - cd gulp/ - yarn - cd .. - - name: Lint - run: | - yarn lint - - name: TSLint - run: | - cd gulp - yarn gulp translations.fullBuild - yarn gulp localConfig.findOrCreate - cd .. - yarn tslint - yaml-lint: name: yaml-lint runs-on: ubuntu-latest diff --git a/.gitignore b/.gitignore index 0e74d8cd..a72a2050 100644 --- a/.gitignore +++ b/.gitignore @@ -2,8 +2,6 @@ logs *.log npm-debug.log* -yarn-debug.log* -yarn-error.log* lerna-debug.log* # Diagnostic reports (https://nodejs.org/api/report.html) @@ -27,15 +25,9 @@ node_modules/ # Optional npm cache directory .npm -# Optional eslint cache -.eslintcache - # Optional REPL history .node_repl_history -# Yarn Integrity file -.yarn-integrity - # dotenv environment variables file .env .env.test @@ -46,8 +38,6 @@ res_built gulp/runnable-texturepacker.jar tmp_standalone_files -tmp_standalone_files_china -tmp_standalone_files_wegame # Local config config.local.js @@ -56,9 +46,12 @@ config.local.js # Editor artifacts *.*.swp *.*.swo -app.vdf -steamtmp build_output -built_vdfs tmp + +src/js/built-temp +translations/tmp +gulp/additional_build_files + +electron/dist diff --git a/.gitpod.Dockerfile b/.gitpod.Dockerfile deleted file mode 100644 index 41956947..00000000 --- a/.gitpod.Dockerfile +++ /dev/null @@ -1,4 +0,0 @@ -FROM gitpod/workspace-full - -RUN sudo apt-get update \ - && sudo apt install ffmpeg -yq diff --git a/.gitpod.yml b/.gitpod.yml deleted file mode 100644 index 18373c95..00000000 --- a/.gitpod.yml +++ /dev/null @@ -1,10 +0,0 @@ -image: - file: .gitpod.Dockerfile -tasks: - - init: yarn && gp sync-done boot - - before: cd gulp - init: gp sync-await boot && yarn - command: yarn gulp -ports: - - port: 3005 - onOpen: open-preview diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..43d24495 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,7 @@ +*.gif +*.jpg +*.mp3 +*.png +*.wav +*.webm +*.woff2 diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..ec6ce6df --- /dev/null +++ b/.prettierrc @@ -0,0 +1,9 @@ +{ + "arrowParens": "avoid", + "bracketSpacing": true, + "printWidth": 110, + "quoteProps": "consistent", + "semi": true, + "singleQuote": false, + "trailingComma": "es5" +} diff --git a/.prettierrc.yaml b/.prettierrc.yaml deleted file mode 100644 index 0b5b2dba..00000000 --- a/.prettierrc.yaml +++ /dev/null @@ -1,11 +0,0 @@ -# .prettierrc or .prettierrc.yaml -trailingComma: "es5" -tabWidth: 4 -semi: true -singleQuote: false -printWidth: 110 -useTabs: false -quoteProps: "consistent" -bracketSpacing: true -arrowParens: avoid -endOfLine: auto diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 6b8b5e97..00000000 --- a/.travis.yml +++ /dev/null @@ -1,166 +0,0 @@ -# validate config at https://config.travis-ci.com/explore -os: linux -dist: xenial -language: node_js -node_js: - - "12" -cache: yarn - -# platform specific configuration -jobs: - # jobs which have to succeed - include: - # OS: MAC - ## -> build darwin - # - name: "Standalone MacOS on MacOS" - # os: osx - # osx_image: xcode11.3 - # before_install: - # - HOMEBREW_NO_AUTO_UPDATE=1 brew install git-lfs - # - HOMEBREW_NO_AUTO_UPDATE=1 brew install ffmpeg - # script: - # - cd gulp - # - yarn gulp build.standalone-prod || travis_terminate 1 - # - yarn gulp standalone.prepare - # - yarn gulp standalone.package.prod.darwin64 - # - cd .. - - ## -> build win - # - name: "Standalone Windows on MacOS" - # os: osx - # osx_image: xcode11.3 - # before_install: - # - HOMEBREW_NO_AUTO_UPDATE=1 brew install git-lfs - # - HOMEBREW_NO_AUTO_UPDATE=1 brew install ffmpeg - # - HOMEBREW_NO_AUTO_UPDATE=1 brew cask install wine-stable - # # prevent Wine popup dialogs about installing additional packages - # - export WINEDLLOVERRIDES="mscoree,mshtml=" - # - export WINEDEBUG="-all" - # script: - # - cd gulp - # - yarn gulp build.standalone-prod || travis_terminate 1 - # - yarn gulp standalone.prepare - # - yarn gulp standalone.package.prod.win64 - # - yarn gulp standalone.package.prod.win32 - # - cd .. - - ## -> build linux - # - name: "Standalone Linux on MacOS" - # os: osx - # osx_image: xcode11.3 - # before_install: - # - HOMEBREW_NO_AUTO_UPDATE=1 brew install git-lfs - # - HOMEBREW_NO_AUTO_UPDATE=1 brew install ffmpeg - # script: - # - cd gulp - # - yarn gulp build.standalone-prod || travis_terminate 1 - # - yarn gulp standalone.prepare - # - yarn gulp standalone.package.prod.linux64 - # - yarn gulp standalone.package.prod.linux32 - # - cd .. - - # OS: LINUX - ## -> build darwin - ## not possible - - ## -> build win - # - name: "Standalone Windows on Linux" - # os: linux - # addons: - # apt: - # packages: - # - libavformat-dev - # - libavfilter-dev - # - libavdevice-dev - # - ffmpeg - # - wine - # script: - # - cd gulp - # - yarn gulp build.standalone-prod || travis_terminate 1 - # - yarn gulp standalone.prepare - # - yarn gulp standalone.package.prod.win64 - # - yarn gulp standalone.package.prod.win32 - # - cd .. - - ## -> build linux - # - name: "Standalone Linux on Linux" - # os: linux - # addons: - # apt: - # packages: - # - libavformat-dev - # - libavfilter-dev - # - libavdevice-dev - # - ffmpeg - # script: - # - cd gulp - # - yarn gulp build.standalone-prod || travis_terminate 1 - # - yarn gulp standalone.prepare - # - yarn gulp standalone.package.prod.linux64 - # - yarn gulp standalone.package.prod.linux32 - # - cd .. - - # OS: WINDOWS - ## -> build darwin - ## not possible - - ## -> build linux - # - name: "Standalone Linux on Windows" - # os: windows - # env: YARN_GPG=no - # before_install: - # - choco install git-lfs -y -f || echo "0" # choco fails but git-lfs is still installed - # - choco install ffmpeg --version=4.2.3 - # - export PATH=/C/ProgramData/chocolatey/lib/ffmpeg/tools/ffmpeg/bin:$PATH - # - wget https://github.com/moiamond/docker-ffmpeg-base-windowsservercore/raw/master/System32/avicap32.dll -P /C/Windows/System32/ - # - wget https://github.com/moiamond/docker-ffmpeg-base-windowsservercore/raw/master/System32/msvfw32.dll -P /C/Windows/System32/ - # script: - # - cd gulp - # - yarn gulp build.standalone-prod || travis_terminate 1 - # - yarn gulp standalone.prepare - # - yarn gulp standalone.package.prod.linux64 - # - yarn gulp standalone.package.prod.linux32 - # - cd .. - - ## -> build win - - name: "Standalone Windows on Windows" - os: windows - env: YARN_GPG=no - before_install: - - choco install git-lfs -y -f || echo "0" # choco fails but git-lfs is still installed - - choco install ffmpeg --version=4.2.3 - - choco install wget - - export PATH=/C/ProgramData/chocolatey/lib/ffmpeg/tools/ffmpeg/bin:$PATH - - wget https://github.com/moiamond/docker-ffmpeg-base-windowsservercore/raw/master/System32/avicap32.dll -P /C/Windows/System32/ - - wget https://github.com/moiamond/docker-ffmpeg-base-windowsservercore/raw/master/System32/msvfw32.dll -P /C/Windows/System32/ - script: - - cd gulp - - yarn gulp build.standalone-prod || travis_terminate 1 - - yarn gulp standalone.prepare - - yarn gulp standalone.package.prod.win64 - - yarn gulp standalone.package.prod.win32 - - cd .. - - # mark build as finished even if "allow_failures" are still running - fast_finish: true - - # optional jobs which may fail - #allow_failures: - # - name: "" - -# shared -install: - - git lfs install - - git lfs pull - - - yarn - - # electron dependencies - - cd electron - - yarn - - cd .. - - # gulp dependendencies - - cd gulp - - yarn - - cd .. diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 84a7327f..ce27cbd6 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,9 +1,3 @@ { - // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. - // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp - - // List of extensions which should be recommended for users of this workspace. - "recommendations": ["esbenp.prettier-vscode"], - // List of extensions recommended by VS Code that should not be recommended for users of this workspace. - "unwantedRecommendations": [] + "recommendations": ["esbenp.prettier-vscode", "EditorConfig.EditorConfig", "dbaeumer.vscode-eslint"] } diff --git a/.vscode/settings.json b/.vscode/settings.json index e47654f3..a23a2942 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,13 @@ { "editor.defaultFormatter": "esbenp.prettier-vscode", "files.trimTrailingWhitespace": true, - "editor.formatOnSave": true -} \ No newline at end of file + "editor.formatOnSave": true, + "files.exclude": { + "**/.git": true, + "**/.DS_Store": true, + "**/Thumbs.db": true, + "node_modules/": true, + "build/": true, + "build_output/": true + } +} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 6055ac1c..00000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,33 +0,0 @@ -Contributor license agreement (CLA) - -1. Preamble - Thank you for your interest in shapez by tobspr IT Solutions (the "Company"). In order to clarify the intellectual property license granted with Contributions from any person or entity, the Company must have a Contributor License Agreement ("CLA") on file that has been signed by each Contributor, indicating agreement to the license terms below. This license is for your protection as a Contributor as well as the protection of the Company and its users; it does not change your rights to use your own Contributions for any other purpose. - -2. General - You accept and agree to the following terms and conditions for Your present and future Contributions submitted to the Company. In return, the Company shall not use Your Contributions in a way that is contrary to the public benefit or inconsistent with its bylaws in effect at the time of the Contribution. Except for the license granted herein to the Company and recipients of software distributed by the Company, You reserve all right, title, and interest in and to Your Contributions. - You represent that you have the full authority to enter into this agreement. - -3. Definitions - "You" (or "Your") "You" (or "Your") shall mean the copyright owner or legal entity authorized by the copyright owner that is making this Agreement with the Company. For legal entities, the entity making a Contribution and all other entities that control, are controlled by, or are under common control with that entity are considered to be a single Contributor. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - - "Contribution" "Contribution" shall mean any original work of authorship, including any modifications or additions to an existing work, that is intentionally submitted by You to the Company for inclusion in, or documentation of, any of the products owned or managed by the Company (the "Work"). For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Company or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Company for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution." - -4. Grant of Copyright License - Subject to the terms and conditions of this Agreement, You hereby grant to the Company and to recipients of software distributed by the Company a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your Contributions and such derivative works. - You agree that your changes/additions are incorporated into the source code under a GPL-3 license. - You agree that the Company is free to use its code without a GPL-3 license as closed source in any context, including for commercial purposes, without any license whatsoever - -5. Grant of Patent License - Subject to the terms and conditions of this Agreement, You hereby grant to the Company and to recipients of software distributed by the Company a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by You that are necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with the Work to which such Contribution(s) was submitted. If any entity institutes patent litigation against You or any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that your Contribution, or the Work to which you have contributed, constitutes direct or contributory patent infringement, then any patent licenses granted to that entity under this Agreement for that Contribution or Work shall terminate as of the date such litigation is filed. - -6. Liability / Obligations - You represent that you are legally entitled to grant the above license. If your employer(s) has rights to intellectual property that you create that includes your Contributions, you represent that you have received permission to make Contributions on behalf of that employer, that your employer has waived such rights for your Contributions to the Company, or that your employer has executed a separate Corporate CLA with the Company. - You represent that each of Your Contributions is Your original creation (see section 7 for submissions on behalf of others). You represent that Your Contribution submissions include complete details of any third-party license or other restriction (including, but not limited to, related patents and trademarks) of which you are personally aware and which are associated with any part of Your Contributions. - If you make changes or additions to the code, you assume full liability for this and assure that the changes/additions do not infringe the rights of third parties (e.g. copyrights). - You are not expected to provide support for Your Contributions, except to the extent You desire to provide support. You may provide support for free, for a fee, or not at all. Unless required by applicable law or agreed to in writing, You provide Your Contributions on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON- INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - Should You wish to submit work that is not Your original creation, You may submit it to the Company separately from any Contribution, identifying the complete details of its source and of any license or other restriction (including, but not limited to, related patents, trademarks, and license agreements) of which you are personally aware, and conspicuously marking the work as "Submitted on behalf of a third-party: [named here]". - You agree to notify the Company of any facts or circumstances of which you become aware that would make these representations inaccurate in any respect. - -7. Final provisions - The law of the Federal Republic of Germany applies to this agreement. - The contract remains binding in its remaining parts even if individual points are legally ineffective. In place of the ineffective points, the statutory provisions, if any, apply. Insofar as this would represent unreasonable hardship for one of the contracting parties, the contract as a whole will become ineffective. diff --git a/CONTRIBUTORS b/CONTRIBUTORS deleted file mode 100644 index d6233b1c..00000000 --- a/CONTRIBUTORS +++ /dev/null @@ -1,5 +0,0 @@ -dengr1065 -- Contributed various fixes to the dark mode - -thelockj -- Contributed the design and initial implementation for the storage building \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 0fc1601a..453cb893 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,7 +7,7 @@ WORKDIR /shapez.io RUN apt-get update && apt-get install -y --no-install-recommends \ ffmpeg default-jre \ && apt-get clean \ - && rm -rf /var/lib/apt/lists/* + && rm -rf /var/lib/apt/lists/* COPY package.json yarn.lock ./ RUN yarn @@ -20,7 +20,6 @@ WORKDIR /shapez.io COPY res ./res COPY src/html ./src/html COPY src/css ./src/css -COPY version ./version COPY sync-translations.js ./ COPY translations ./translations COPY src/js ./src/js diff --git a/README.md b/README.md index 0c34ed22..e5109242 100644 --- a/README.md +++ b/README.md @@ -1,95 +1,84 @@ -## NEW: Shapez 2! +# shapez Community Edition shapez Logo -We are currently working on a successor to shapez, with 3D Graphics, Exploration, Layers, Mass transport, New Shape Mechanics, Research and a lot more! Be sure to check it out: +**shapez Community Edition** (abbreviated as **CE**) is a community-maintained version of [shapez](https://store.steampowered.com/app/1318690/shapez/)! - - shapez 2 Announcement - +CE was created as the tobspr Games team moved away from shapez to work full-time on the upcoming [Shapez 2](https://store.steampowered.com/app/2162800/shapez_2/). +CE aims to: -
+- Continue the development of shapez as guided by the community. +- Allow contributors to continue submitting new features and improvements to the game. +- Provide an experimental and forgiving environment for faster development. -# shapez +> [!IMPORTANT] +> CE is different from the official game published on Steam and other platforms. +> CE was forked off of the official shapez, which has [its own repository](https://github.com/tobspr-games/shapez.io). +> No plans exist to merge the two versions of shapez. - - shapez Logo - - -
-This is the source code for shapez, an open source base building game inspired by Factorio. -Your goal is to produce shapes by cutting, rotating, merging and painting parts of shapes. - -- [Play on Steam](https://get.shapez.io/ghr) -- [Online Demo](https://shapez.io) -- [Official Discord](https://discord.com/invite/HN7EVzV) <- _Highly recommended to join!_ -- [Trello Board & Roadmap](https://trello.com/b/ISQncpJP/shapezio) - -## Reporting issues, suggestions, feedback, bugs - -1. Ask in `#bugs` / `#feedback` / `#questions` on the [Official Discord](https://discord.com/invite/HN7EVzV) if you are not entirely sure if it's a bug -2. Check out the trello board: https://trello.com/b/ISQncpJP/shapezio -3. See if it's already there - If so, vote for it, done. I will see it. (You have to be signed in on trello) -4. If not, check if it's already reported here: https://github.com/tobspr-games/shapez.io/issues -5. If not, file a new issue here: https://github.com/tobspr-games/shapez.io/issues/new -6. I will then have a look (This can take days or weeks) and convert it to trello, and comment with the link. You can then vote there ;) - -## Building - -- Make sure `ffmpeg` is on your path -- Install Node.js 16 and Yarn -- Install Java (required for texture packer) -- Run `yarn` in the root folder -- `cd` into `gulp` folder -- Run `yarn` and then `yarn gulp` - it should now open in your browser - -**Notice**: This will produce a debug build with several debugging flags enabled. If you want to disable them, modify [`src/js/core/config.js`](src/js/core/config.js). - -## Creating Mods - -Mods can be found [here](https://shapez.mod.io). The documentation for creating mods can be found [here](mod_examples/), including a bunch of sample mods. - -## Build Online with one-click setup - -You can use [Gitpod](https://www.gitpod.io/) (an Online Open Source VS Code-like IDE which is free for Open Source) for working on issues and making PRs to this project. With a single click it will start a workspace and automatically: - -- clone the `shapez.io` repo. -- install all of the dependencies. -- start `gulp` in `gulp/` directory. - -[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/tobspr-games/shapez.io) - -## Helping translate - -Please checkout the [Translations readme](translations/). +As of now, CE must be built from source and supports only a standalone build, +with no plans for re-supporting a web version. +In the future, builds of CE may provided for owners of the full version of shapez. ## Contributing -I will only accept pull requests which add a benefit to a large portion of the player base. If the feature is useful but only to a fraction of players, or is controversial, I recommend making a mod instead. +We communicate on the [official shapez Discord server](https://discord.com/invite/HN7EVzV). +For historical reasons, we have communicated in a private channel, +but we are moving to the public `#contributing` channel. +If you would like to contribute to CE, feel free to share your ideas, plans, etc. there. -If you want to add a new feature or in generally contribute I recommend to get in touch on Discord in advance, which largely increases the chance of the PR to get merged: +In our current workflow, we (the "collaborators" of the repository) create internal branches and corresponding pull requests to work on a feature, refactor, etc. +We discuss changes in the Discord, and when 2 collaborators (including the PR creator) approve of a change, it can be merged. +See our existing [pull requests](https://github.com/tobspr-games/shapez-community-edition/pulls?q=) for examples. - -discord logo - +If you are not a collaborator and want to submit a change, +you can fork our repo and make a pull request. +Note that because of plans to overhaul many parts of the game, +unless you are improving translations, you should probably communicate with us on Discord! + +> [!TIP] +> Be aware that [pull requests to the official shapez repository](https://github.com/tobspr-games/shapez.io/pulls) are unlikely to get merged in the near future. Instead, submit them to CE! +> In fact, because the game is licensed under the [GNU GPL v3.0](https://www.gnu.org/licenses/gpl-3.0.html), +> existing pull requests can be resubmitted to CE even if you aren't the author! **This is not legal advice.** ### Code -The game is based on a custom engine which itself is based on the YORG.io 3 game engine (Actually it shares almost the same core). +The game uses a custom engine originally based on the YORG.io 3 game engine. The code within the engine is relatively clean with some code for the actual game on top being hacky. -This project is based on ES5 (If I would develop it again, I would definitely use TypeScript). Some ES2015 features are used but most of them are too slow, especially when polyfilled. For example, `Array.prototype.forEach` is only used within non-critical loops since its slower than a plain for loop. +We are in the process of migrating to TypeScript and JSX/TSX. +New changes should be implemented in TypeScript if possible, +but because we are planning on overhauling many parts of the game, +there is no need to convert existing code to TypeScript. -### Assets +This project is fine with using cutting-edge and bleeding-edge features +and does not intend to provide compatibility for older clients. -You can find most assets here. +## Building -All assets will be automatically rebuilt into the atlas once changed (Thanks to dengr1065!) +### Prerequisites -shapez Screenshot +- [Node.js](https://nodejs.org) +- [ffmpeg](https://www.ffmpeg.org/download.html) for audio transcoding +- [Java](https://www.oracle.com/java/technologies/downloads/) (or [OpenJDK](https://openjdk.org/)) to run the texture packer -
+### Development -## Check out our other games! +- Run `npm i` in the root folder and in `electron/`. +- Run `npm run gulp` in the root folder to build and serve files. + If a new browser tab opens, ignore it. +- Open a new terminal and run `npm start` in `electron/` to open an Electron window. + - Use `npm start -- --dev` to run in development mode. + - Tip: If you open the Electron window too early, you can reload it when focused on DevTools. - -tobspr Games - +### Release + +- Run `npm i` in the root folder and in `electron/`. +- In the root folder, run `npm run package-$PLATFORM-$ARCH` where: + - `$PLATFORM` is `win32`, `linux` or `darwin` depending on your system. + - `$ARCH` is the target system architecture (`x64` or `arm64`) +- The build will be found under `build_output/standalone` as `shapez-...`. + +## Credits + +Thanks to [tobspr](https://tobspr.io) for creating this project! + +[tobspr Games](https://tobspr.io) diff --git a/electron/.gitignore b/electron/.gitignore deleted file mode 100644 index 0cdb30f4..00000000 --- a/electron/.gitignore +++ /dev/null @@ -1 +0,0 @@ -mods/*.js \ No newline at end of file diff --git a/electron/electron.code-workspace b/electron/electron.code-workspace deleted file mode 100644 index fc9ab864..00000000 --- a/electron/electron.code-workspace +++ /dev/null @@ -1,13 +0,0 @@ -{ - "folders": [ - { - "path": "." - } - ], - "settings": { - "files.exclude": { - "**/node_modules": true, - "**/typedefs_gen": true - } - } -} \ No newline at end of file diff --git a/electron/index.js b/electron/index.js deleted file mode 100644 index 31b4bca2..00000000 --- a/electron/index.js +++ /dev/null @@ -1,389 +0,0 @@ -/* eslint-disable quotes,no-undef */ - -const { app, BrowserWindow, Menu, MenuItem, ipcMain, shell, dialog, session } = require("electron"); -const path = require("path"); -const url = require("url"); -const fs = require("fs"); -const steam = require("./steam"); -const asyncLock = require("async-lock"); -const windowStateKeeper = require("electron-window-state"); - -// Disable hardware key handling, i.e. being able to pause/resume the game music -// with hardware keys -app.commandLine.appendSwitch("disable-features", "HardwareMediaKeyHandling"); - -const isDev = app.commandLine.hasSwitch("dev"); -const isLocal = app.commandLine.hasSwitch("local"); -const safeMode = app.commandLine.hasSwitch("safe-mode"); -const externalMod = app.commandLine.getSwitchValue("load-mod"); - -const roamingFolder = - process.env.APPDATA || - (process.platform == "darwin" - ? process.env.HOME + "/Library/Preferences" - : process.env.HOME + "/.local/share"); - -let storePath = path.join(roamingFolder, "shapez.io", "saves"); -let modsPath = path.join(roamingFolder, "shapez.io", "mods"); - -if (!fs.existsSync(storePath)) { - // No try-catch by design - fs.mkdirSync(storePath, { recursive: true }); -} - -if (!fs.existsSync(modsPath)) { - fs.mkdirSync(modsPath, { recursive: true }); -} - -/** @type {BrowserWindow} */ -let win = null; -let menu = null; - -function createWindow() { - let faviconExtension = ".png"; - if (process.platform === "win32") { - faviconExtension = ".ico"; - } - - const mainWindowState = windowStateKeeper({ - defaultWidth: 1000, - defaultHeight: 800, - }); - - win = new BrowserWindow({ - x: mainWindowState.x, - y: mainWindowState.y, - width: mainWindowState.width, - height: mainWindowState.height, - show: false, - backgroundColor: "#222428", - useContentSize: false, - minWidth: 800, - minHeight: 600, - title: "shapez", - transparent: false, - icon: path.join(__dirname, "favicon" + faviconExtension), - // fullscreen: true, - autoHideMenuBar: !isDev, - webPreferences: { - nodeIntegration: false, - nodeIntegrationInWorker: false, - nodeIntegrationInSubFrames: false, - contextIsolation: true, - enableRemoteModule: false, - disableBlinkFeatures: "Auxclick", - - webSecurity: true, - sandbox: true, - preload: path.join(__dirname, "preload.js"), - experimentalFeatures: false, - }, - allowRunningInsecureContent: false, - }); - - mainWindowState.manage(win); - - if (isLocal) { - win.loadURL("http://localhost:3005"); - } else { - win.loadURL( - url.format({ - pathname: path.join(__dirname, "index.html"), - protocol: "file:", - slashes: true, - }) - ); - } - win.webContents.session.clearCache(); - win.webContents.session.clearStorageData(); - - ////// SECURITY - - // Disable permission requests - win.webContents.session.setPermissionRequestHandler((webContents, permission, callback) => { - callback(false); - }); - session.fromPartition("default").setPermissionRequestHandler((webContents, permission, callback) => { - callback(false); - }); - - app.on("web-contents-created", (event, contents) => { - // Disable vewbiew - contents.on("will-attach-webview", (event, webPreferences, params) => { - event.preventDefault(); - }); - // Disable navigation - contents.on("will-navigate", (event, navigationUrl) => { - event.preventDefault(); - }); - }); - - win.webContents.on("will-redirect", (contentsEvent, navigationUrl) => { - // Log and prevent the app from redirecting to a new page - console.error( - `The application tried to redirect to the following address: '${navigationUrl}'. This attempt was blocked.` - ); - contentsEvent.preventDefault(); - }); - - // Filter loading any module via remote; - // you shouldn't be using remote at all, though - // https://electronjs.org/docs/tutorial/security#16-filter-the-remote-module - app.on("remote-require", (event, webContents, moduleName) => { - event.preventDefault(); - }); - - // built-ins are modules such as "app" - app.on("remote-get-builtin", (event, webContents, moduleName) => { - event.preventDefault(); - }); - - app.on("remote-get-global", (event, webContents, globalName) => { - event.preventDefault(); - }); - - app.on("remote-get-current-window", (event, webContents) => { - event.preventDefault(); - }); - - app.on("remote-get-current-web-contents", (event, webContents) => { - event.preventDefault(); - }); - - //// END SECURITY - - win.webContents.on("new-window", (event, pth) => { - event.preventDefault(); - - if (pth.startsWith("https://") || pth.startsWith("steam://")) { - shell.openExternal(pth); - } - }); - - win.on("closed", () => { - console.log("Window closed"); - win = null; - }); - - if (isDev) { - menu = new Menu(); - - win.webContents.toggleDevTools(); - - const mainItem = new MenuItem({ - label: "Toggle Dev Tools", - click: () => win.webContents.toggleDevTools(), - accelerator: "F12", - }); - menu.append(mainItem); - - const reloadItem = new MenuItem({ - label: "Reload", - click: () => win.reload(), - accelerator: "F5", - }); - menu.append(reloadItem); - - const fullscreenItem = new MenuItem({ - label: "Fullscreen", - click: () => win.setFullScreen(!win.isFullScreen()), - accelerator: "F11", - }); - menu.append(fullscreenItem); - - const mainMenu = new Menu(); - mainMenu.append( - new MenuItem({ - label: "shapez.io", - submenu: menu, - }) - ); - - Menu.setApplicationMenu(mainMenu); - } else { - Menu.setApplicationMenu(null); - } - - win.once("ready-to-show", () => { - win.show(); - win.focus(); - }); -} - -if (!app.requestSingleInstanceLock()) { - app.exit(0); -} else { - app.on("second-instance", () => { - // Someone tried to run a second instance, we should focus - if (win) { - if (win.isMinimized()) { - win.restore(); - } - win.focus(); - } - }); -} - -app.on("ready", createWindow); - -app.on("window-all-closed", () => { - console.log("All windows closed"); - app.quit(); -}); - -ipcMain.on("set-fullscreen", (event, flag) => { - win.setFullScreen(flag); -}); - -ipcMain.on("exit-app", () => { - win.close(); - app.quit(); -}); - -let renameCounter = 1; - -const fileLock = new asyncLock({ - timeout: 30000, - maxPending: 1000, -}); - -function niceFileName(filename) { - return filename.replace(storePath, "@"); -} - -async function writeFileSafe(filename, contents) { - ++renameCounter; - const prefix = "[ " + renameCounter + ":" + niceFileName(filename) + " ] "; - const transactionId = String(new Date().getTime()) + "." + renameCounter; - - if (fileLock.isBusy()) { - console.warn(prefix, "Concurrent write process on", filename); - } - - fileLock.acquire(filename, async () => { - console.log(prefix, "Starting write on", niceFileName(filename), "in transaction", transactionId); - - if (!fs.existsSync(filename)) { - // this one is easy - console.log(prefix, "Writing file instantly because it does not exist:", niceFileName(filename)); - await fs.promises.writeFile(filename, contents, "utf8"); - return; - } - - // first, write a temporary file (.tmp-XXX) - const tempName = filename + ".tmp-" + transactionId; - console.log(prefix, "Writing temporary file", niceFileName(tempName)); - await fs.promises.writeFile(tempName, contents, "utf8"); - - // now, rename the original file to (.backup-XXX) - const oldTemporaryName = filename + ".backup-" + transactionId; - console.log( - prefix, - "Renaming old file", - niceFileName(filename), - "to", - niceFileName(oldTemporaryName) - ); - await fs.promises.rename(filename, oldTemporaryName); - - // now, rename the temporary file (.tmp-XXX) to the target - console.log( - prefix, - "Renaming the temporary file", - niceFileName(tempName), - "to the original", - niceFileName(filename) - ); - await fs.promises.rename(tempName, filename); - - // we are done now, try to create a backup, but don't fail if the backup fails - try { - // check if there is an old backup file - const backupFileName = filename + ".backup"; - if (fs.existsSync(backupFileName)) { - console.log(prefix, "Deleting old backup file", niceFileName(backupFileName)); - // delete the old backup - await fs.promises.unlink(backupFileName); - } - - // rename the old file to the new backup file - console.log(prefix, "Moving", niceFileName(oldTemporaryName), "to the backup file location"); - await fs.promises.rename(oldTemporaryName, backupFileName); - } catch (ex) { - console.error(prefix, "Failed to switch backup files:", ex); - } - }); -} - -ipcMain.handle("fs-job", async (event, job) => { - const filenameSafe = job.filename.replace(/[^a-z\.\-_0-9]/gi, "_"); - const fname = path.join(storePath, filenameSafe); - switch (job.type) { - case "read": { - if (!fs.existsSync(fname)) { - // Special FILE_NOT_FOUND error code - return { error: "file_not_found" }; - } - return await fs.promises.readFile(fname, "utf8"); - } - case "write": { - await writeFileSafe(fname, job.contents); - return job.contents; - } - - case "delete": { - await fs.promises.unlink(fname); - return; - } - - default: - throw new Error("Unknown fs job: " + job.type); - } -}); - -ipcMain.handle("open-mods-folder", async () => { - shell.openPath(modsPath); -}); - -console.log("Loading mods ..."); - -function loadMods() { - if (safeMode) { - console.log("Safe Mode enabled for mods, skipping mod search"); - } - console.log("Loading mods from", modsPath); - let modFiles = safeMode - ? [] - : fs - .readdirSync(modsPath) - .filter(filename => filename.endsWith(".js")) - .map(filename => path.join(modsPath, filename)); - - if (externalMod) { - console.log("Adding external mod source:", externalMod); - const externalModPaths = externalMod.split(","); - modFiles = modFiles.concat(externalModPaths); - } - - return modFiles.map(filename => fs.readFileSync(filename, "utf8")); -} - -let mods = []; -try { - mods = loadMods(); - console.log("Loaded", mods.length, "mods"); -} catch (ex) { - console.error("Failed to load mods"); - dialog.showErrorBox("Failed to load mods:", ex); -} - -ipcMain.handle("get-mods", async () => { - return mods; -}); - -steam.init(isDev); - -// Only allow achievements and puzzle DLC if no mods are loaded -if (mods.length === 0) { - steam.listen(); -} diff --git a/electron/mods/README.txt b/electron/mods/README.txt deleted file mode 100644 index 666cc18f..00000000 --- a/electron/mods/README.txt +++ /dev/null @@ -1,6 +0,0 @@ -Here you can place mods. Every mod should be a single file ending with ".js". - ---- WARNING --- -Mods can potentially access to your filesystem. -Please only install mods from trusted sources and developers. ---- WARNING --- diff --git a/electron/package-lock.json b/electron/package-lock.json new file mode 100644 index 00000000..0ddfd19d --- /dev/null +++ b/electron/package-lock.json @@ -0,0 +1,935 @@ +{ + "name": "electron", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "electron", + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "chokidar": "^4.0.3", + "semver": "^7.7.1", + "zod": "^3.24.2" + }, + "devDependencies": { + "@types/semver": "^7.7.0", + "electron": "^37.2.3", + "typescript": "^5.8.2" + } + }, + "node_modules/@electron/get": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-2.0.3.tgz", + "integrity": "sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "env-paths": "^2.2.0", + "fs-extra": "^8.1.0", + "got": "^11.8.5", + "progress": "^2.0.3", + "semver": "^6.2.0", + "sumchecker": "^3.0.1" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "global-agent": "^3.0.0" + } + }, + "node_modules/@electron/get/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "dev": true, + "license": "MIT", + "dependencies": { + "defer-to-connect": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@types/cacheable-request": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", + "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-cache-semantics": "*", + "@types/keyv": "^3.1.4", + "@types/node": "*", + "@types/responselike": "^1.0.0" + } + }, + "node_modules/@types/http-cache-semantics": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", + "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/keyv": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", + "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "22.16.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.16.4.tgz", + "integrity": "sha512-PYRhNtZdm2wH/NT2k/oAJ6/f2VD2N2Dag0lGlx2vWgMSJXGNmlce5MiTQzoWAiIJtso30mjnfQCOKVH+kAQC/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/responselike": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", + "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/semver": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.0.tgz", + "integrity": "sha512-k107IF4+Xr7UHjwDc7Cfd6PRQfbdkiRabXGRjo07b4WyPahFBZCZ1sE+BNxYIJPPg73UkfOsVOLwqVc/6ETrIA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/yauzl": { + "version": "2.10.3", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", + "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/boolean": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", + "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.6.0" + } + }, + "node_modules/cacheable-request": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", + "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/clone-response": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/electron": { + "version": "37.2.3", + "resolved": "https://registry.npmjs.org/electron/-/electron-37.2.3.tgz", + "integrity": "sha512-JRKKn8cRDXDfkC+oWISbYs+c+L6RA776JM0NiB9bn2yV8H/LnBUlVPzKKfsXgrUIokN4YcbCw694vfAdEJwtGw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@electron/get": "^2.0.0", + "@types/node": "^22.7.7", + "extract-zip": "^2.0.1" + }, + "bin": { + "electron": "cli.js" + }, + "engines": { + "node": ">= 12.20.55" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true, + "license": "MIT", + "optional": true + }, + "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, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/global-agent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", + "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "boolean": "^3.0.1", + "es6-error": "^4.1.1", + "matcher": "^3.0.0", + "roarr": "^2.15.3", + "semver": "^7.3.2", + "serialize-error": "^7.0.1" + }, + "engines": { + "node": ">=10.0" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/got": { + "version": "11.8.6", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", + "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=10.19.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "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, + "license": "MIT" + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true, + "license": "ISC", + "optional": true + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "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, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/matcher": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", + "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "escape-string-regexp": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "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, + "license": "MIT" + }, + "node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.4" + } + }, + "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, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true, + "license": "MIT" + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/pump": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", + "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/responselike": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lowercase-keys": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/roarr": { + "version": "2.15.4", + "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", + "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "boolean": "^3.0.1", + "detect-node": "^2.0.4", + "globalthis": "^1.0.1", + "json-stringify-safe": "^5.0.1", + "semver-compare": "^1.0.0", + "sprintf-js": "^1.1.2" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/serialize-error": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", + "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "type-fest": "^0.13.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true + }, + "node_modules/sumchecker": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", + "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "debug": "^4.1.0" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.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, + "license": "ISC" + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/zod": { + "version": "3.24.2", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.2.tgz", + "integrity": "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/electron/package.json b/electron/package.json index 4ae171f8..5c17a55d 100644 --- a/electron/package.json +++ b/electron/package.json @@ -1,21 +1,21 @@ -{ - "name": "electron", - "version": "1.0.0", - "main": "index.js", - "license": "MIT", - "private": true, - "scripts": { - "startDev": "electron --disable-direct-composition --in-process-gpu . --dev --local", - "startDevGpu": "electron --enable-gpu-rasterization --enable-accelerated-2d-canvas --num-raster-threads=8 --enable-zero-copy . --dev --local", - "start": "electron --disable-direct-composition --in-process-gpu ." - }, - "devDependencies": {}, - "optionalDependencies": { - "shapez.io-private-artifacts": "github:tobspr/shapez.io-private-artifacts#abi-v99" - }, - "dependencies": { - "async-lock": "^1.2.8", - "electron": "16.2.8", - "electron-window-state": "^5.0.3" - } -} +{ + "name": "electron", + "version": "1.0.0", + "license": "MIT", + "type": "module", + "main": "dist/index.js", + "private": true, + "scripts": { + "start": "tsc && electron ." + }, + "dependencies": { + "chokidar": "^4.0.3", + "semver": "^7.7.1", + "zod": "^3.24.2" + }, + "devDependencies": { + "@types/semver": "^7.7.0", + "electron": "^37.2.3", + "typescript": "^5.8.2" + } +} diff --git a/electron/preload.js b/electron/preload.cjs similarity index 100% rename from electron/preload.js rename to electron/preload.cjs diff --git a/electron/src/config.ts b/electron/src/config.ts new file mode 100644 index 00000000..aae058e9 --- /dev/null +++ b/electron/src/config.ts @@ -0,0 +1,22 @@ +import { app } from "electron"; +import path from "node:path"; + +const disabledFeatures = ["HardwareMediaKeyHandling"]; +app.commandLine.appendSwitch("disable-features", disabledFeatures.join(",")); + +export const defaultWindowTitle = "shapez CE"; +app.setName("shapez-ce"); + +// This variable should be used to avoid situations where the app name +// wasn't set yet. +export const userData = app.getPath("userData"); +export const executableDir = path.dirname(app.getPath("exe")); + +export const pageUrl = app.isPackaged + ? new URL("../index.html", import.meta.url).href + : "http://localhost:3005/"; + +export const switches = { + dev: app.commandLine.hasSwitch("dev"), + safeMode: app.commandLine.hasSwitch("safe-mode"), +}; diff --git a/electron/src/fsjob.ts b/electron/src/fsjob.ts new file mode 100644 index 00000000..fb759ac2 --- /dev/null +++ b/electron/src/fsjob.ts @@ -0,0 +1,132 @@ +import { BrowserWindow, dialog, FileFilter } from "electron"; +import fs from "fs/promises"; +import path from "path"; +import { userData } from "./config.js"; + +interface GenericFsJob { + id: string; +} + +export type InitializeFsJob = GenericFsJob & { type: "initialize" }; +type ListFsJob = GenericFsJob & { type: "list"; filename: string }; +type ReadFsJob = GenericFsJob & { type: "read"; filename: string }; +type WriteFsJob = GenericFsJob & { type: "write"; filename: string; contents: Uint8Array }; +type DeleteFsJob = GenericFsJob & { type: "delete"; filename: string }; + +type OpenExternalFsJob = GenericFsJob & { type: "open-external"; extension: string }; +type SaveExternalFsJob = GenericFsJob & { type: "save-external"; filename: string; contents: Uint8Array }; + +export type FsJob = + | InitializeFsJob + | ListFsJob + | ReadFsJob + | WriteFsJob + | DeleteFsJob + | OpenExternalFsJob + | SaveExternalFsJob; +type FsJobResult = Uint8Array | string[] | void; + +export class FsJobHandler { + readonly rootDir: string; + private initialized = false; + + constructor(subDir: string) { + this.rootDir = path.join(userData, subDir); + } + + async initialize(): Promise { + if (this.initialized) { + return; + } + + // Create the directory so that users know where to put files + await fs.mkdir(this.rootDir, { recursive: true }); + this.initialized = true; + } + + handleJob(job: FsJob): Promise { + switch (job.type) { + case "initialize": + return this.initialize(); + case "open-external": + return this.openExternal(job.extension); + case "save-external": + return this.saveExternal(job.filename, job.contents); + } + + const filename = this.safeFileName(job.filename); + + switch (job.type) { + case "list": + return this.list(filename); + case "read": + return fs.readFile(filename); + case "write": + return this.write(filename, job.contents); + case "delete": + return fs.unlink(filename); + } + + // @ts-expect-error this method can actually receive garbage + throw new Error(`Unknown FS job type: ${job.type}`); + } + + private async openExternal(extension: string): Promise { + const filters = this.getFileDialogFilters(extension === "*" ? undefined : extension); + const window = BrowserWindow.getAllWindows()[0]!; + + const result = await dialog.showOpenDialog(window, { filters, properties: ["openFile"] }); + if (result.canceled) { + return undefined; + } + + return await fs.readFile(result.filePaths[0]); + } + + private async saveExternal(filename: string, contents: Uint8Array): Promise { + // Try to guess extension + const ext = filename.indexOf(".") < 1 ? filename.split(".").at(-1)! : undefined; + const filters = this.getFileDialogFilters(ext); + const window = BrowserWindow.getAllWindows()[0]!; + + const result = await dialog.showSaveDialog(window, { defaultPath: filename, filters }); + if (result.canceled) { + return; + } + + return await fs.writeFile(result.filePath, contents); + } + + private getFileDialogFilters(extension?: string): FileFilter[] { + const filters: FileFilter[] = [{ name: "All files", extensions: ["*"] }]; + + if (extension !== undefined) { + filters.unshift({ + name: `${extension.toUpperCase()} files`, + extensions: [extension], + }); + } + + return filters; + } + + private list(subdir: string): Promise { + // Bare-bones implementation + return fs.readdir(subdir); + } + + private async write(file: string, contents: Uint8Array): Promise { + // The target directory might not exist, ensure it does + const parentDir = path.dirname(file); + await fs.mkdir(parentDir, { recursive: true }); + + await fs.writeFile(file, contents); + } + + private safeFileName(name: string) { + // TODO: Rather than restricting file names, attempt to resolve everything + // relative to the data directory (i.e. normalize the file path, then join) + const relative = name.replace(/[^a-z.0-9_-]/gi, "_"); + return path.join(this.rootDir, relative); + } +} diff --git a/electron/src/index.ts b/electron/src/index.ts new file mode 100644 index 00000000..396d9cc9 --- /dev/null +++ b/electron/src/index.ts @@ -0,0 +1,95 @@ +import { BrowserWindow, app, shell } from "electron"; +import path from "path"; +import { defaultWindowTitle, pageUrl, switches } from "./config.js"; +import { IpcHandler } from "./ipc.js"; +import { ModLoader } from "./mods/loader.js"; +import { ModProtocolHandler } from "./mods/protocol_handler.js"; + +let win: BrowserWindow | null = null; + +if (!app.requestSingleInstanceLock()) { + app.quit(); +} else { + app.on("second-instance", () => { + if (win?.isMinimized()) { + win.restore(); + } + + win?.focus(); + }); +} + +const modLoader = new ModLoader(); +const modProtocol = new ModProtocolHandler(modLoader); +const ipc = new IpcHandler(modLoader); + +function createWindow() { + // The protocol can only be handled after "ready" event + modProtocol.install(); + + const window = new BrowserWindow({ + minWidth: 800, + minHeight: 600, + useContentSize: true, + autoHideMenuBar: !switches.dev, + show: false, + title: defaultWindowTitle, + webPreferences: { + preload: path.join(import.meta.dirname, "../preload.cjs"), + }, + }); + + win = window; + + if (!switches.dev) { + window.removeMenu(); + } + + window.on("ready-to-show", () => { + window.show(); + }); + + ipc.install(window); + window.loadURL(pageUrl); + + modLoader.on("forcereload", () => { + // TODO: Find a better way to manage cache when force + // reloading (use a non-persistent session?) + window.webContents.session.clearData({ dataTypes: ["cache"] }).then(() => window.reload()); + }); + + // Redirect any kind of main frame navigation to external applications + window.webContents.on("will-navigate", (ev, url) => { + if (url === window.webContents.getURL()) { + // Avoid handling reloads externally + return; + } + + ev.preventDefault(); + openExternalUrl(url); + }); + + // Also redirect window.open + window.webContents.setWindowOpenHandler(({ url }) => { + openExternalUrl(url); + return { action: "deny" }; + }); +} + +function openExternalUrl(urlString: string) { + try { + const url = new URL(urlString); + + // TODO: Let the user explicitly allow other protocols + if (["http:", "https:"].includes(url.protocol)) { + shell.openExternal(urlString); + } + } catch { + // Ignore invalid URLs + } +} + +app.on("ready", createWindow); +app.on("window-all-closed", () => { + app.quit(); +}); diff --git a/electron/src/ipc.ts b/electron/src/ipc.ts new file mode 100644 index 00000000..b3cd52a1 --- /dev/null +++ b/electron/src/ipc.ts @@ -0,0 +1,41 @@ +import { BrowserWindow, IpcMainInvokeEvent, ipcMain } from "electron"; +import { FsJob, FsJobHandler } from "./fsjob.js"; +import { ModLoader } from "./mods/loader.js"; + +export class IpcHandler { + private readonly savesHandler = new FsJobHandler("saves"); + private readonly modLoader: ModLoader; + + constructor(modLoader: ModLoader) { + this.modLoader = modLoader; + } + + install(window: BrowserWindow) { + ipcMain.handle("fs-job", this.handleFsJob.bind(this)); + ipcMain.handle("get-mods", this.getMods.bind(this)); + ipcMain.handle("set-fullscreen", this.setFullscreen.bind(this, window)); + + // Not implemented + // ipcMain.handle("open-mods-folder", ...) + } + + private handleFsJob(_event: IpcMainInvokeEvent, job: FsJob) { + if (job.id !== "saves") { + throw new Error("Storages other than saves/ are not implemented yet"); + } + + return this.savesHandler.handleJob(job); + } + + private async getMods() { + // TODO: Split mod reloads into a different IPC request + await this.modLoader.loadMods(); + return this.modLoader.getAllMods(); + } + + private setFullscreen(window: BrowserWindow, _event: IpcMainInvokeEvent, flag: boolean) { + if (window.isFullScreen() != flag) { + window.setFullScreen(flag); + } + } +} diff --git a/electron/src/mods/loader.ts b/electron/src/mods/loader.ts new file mode 100644 index 00000000..d18f571e --- /dev/null +++ b/electron/src/mods/loader.ts @@ -0,0 +1,166 @@ +import EventEmitter from "node:events"; +import fs from "node:fs/promises"; +import path from "node:path"; +import { DevelopmentModLocator, DistroModLocator, ModLocator, UserModLocator } from "./locator.js"; +import { IpcModMetadata, ModMetadata } from "./metadata.js"; + +type ModSource = "user" | "distro" | "dev"; + +interface ModLocation { + source: ModSource; + file: string; +} + +interface DisabledMod { + source: ModSource; + id: string; +} + +interface IpcMod extends ModLocation { + disabled: boolean; + metadata: IpcModMetadata; +} + +const METADATA_FILE = "mod.json"; + +class Mod { + readonly source: ModSource; + readonly file: string; + readonly metadata: ModMetadata; + + disabled = false; + + constructor(source: ModSource, file: string, metadata: ModMetadata) { + this.source = source; + this.file = file; + this.metadata = metadata; + } + + toJSON(): IpcMod { + return { + source: this.source, + file: this.file, + disabled: this.disabled, + metadata: { + ...this.metadata, + version: this.metadata.version.format(), + }, + }; + } +} + +export class ModLoader extends EventEmitter { + private mods: Mod[] = []; + private readonly locators = new Map(); + + constructor() { + super(); + + this.locators.set("user", new UserModLocator()); + this.locators.set("distro", new DistroModLocator()); + + const devLocator = new DevelopmentModLocator(); + this.locators.set("dev", devLocator); + + // If requested, restart automatically when dev mods are modified + devLocator.fsWatcher?.on("all", this.delayedForceReload()); + } + + /** + * Resets modloader state and reloads all mods, then triggers page reload. + */ + async forceReload() { + await this.loadMods(); + this.emit("forcereload"); + } + + async loadMods(): Promise { + const mods: Mod[] = []; + this.mods = mods; + + const locations = await this.locateAllMods(); + for (const location of locations) { + const metadata = await this.resolveMetadata(location); + if (metadata === null) { + continue; + } + + // TODO: Only check this after applying disabled state + if (this.isModPresent(metadata.id)) { + console.warn(`Ignoring duplicate mod ${location.source}::${location.file}`); + continue; + } + + mods.push(new Mod(location.source, location.file, metadata)); + } + + // Check for mods that should be disabled + for (const { source, id } of await this.collectDisabledMods()) { + const target = mods.find(m => m.source === source && m.metadata.id === id); + if (target !== undefined) { + target.disabled = true; + } + } + } + + getAllMods(): IpcMod[] { + return this.mods.map(mod => mod.toJSON()); + } + + isModPresent(id: string): boolean { + return this.mods.some(mod => mod.metadata.id === id); + } + + getModById(id: string): Mod | undefined { + return this.mods.find(mod => mod.metadata.id === id); + } + + private delayedForceReload() { + // Debounce the force reload manually as chokidar won't aggregate events the way we want + // NOTE: The delay chosen here (250ms) is quite arbitrary! + let timeout: NodeJS.Timeout | undefined = undefined; + return () => { + clearTimeout(timeout); + timeout = setTimeout(() => this.forceReload(), 250); + }; + } + + private async locateAllMods(): Promise { + // Sort locators by priority, lowest number is highest priority + const locators = [...this.locators.entries()].sort(([, a], [, b]) => a.priority - b.priority); + const result: ModLocation[] = []; + + for (const [source, locator] of locators) { + for (const file of await locator.locateMods()) { + result.push({ source, file }); + } + } + + return result; + } + + private async resolveMetadata(mod: ModLocation): Promise { + // TODO: This function might call validation routines + const filePath = path.join(mod.file, METADATA_FILE); + try { + const contents = await fs.readFile(filePath, "utf-8"); + return ModMetadata.parse(JSON.parse(contents)); + } catch (err) { + // TODO: Collect mod errors, show to the user once all mods are loaded + console.error("Failed to read mod metadata", err); + return null; + } + } + + private async collectDisabledMods(): Promise { + const result: DisabledMod[] = []; + + for (const [source, locator] of this.locators.entries()) { + for (const id of await locator.getDisabledMods()) { + result.push({ source, id }); + } + } + + return result; + } +} diff --git a/electron/src/mods/locator.ts b/electron/src/mods/locator.ts new file mode 100644 index 00000000..112468a6 --- /dev/null +++ b/electron/src/mods/locator.ts @@ -0,0 +1,213 @@ +import chokidar, { FSWatcher } from "chokidar"; +import { app } from "electron"; +import fs from "node:fs/promises"; +import path from "node:path"; +import { executableDir, switches, userData } from "../config.js"; + +export const MOD_FILE_SUFFIX = ".asar"; + +const DISABLED_MODS_FILE = "disabled-mods.json"; +const USER_MODS_DIR = path.join(userData, "mods"); +const DISTRO_MODS_DIR = path.join(executableDir, "mods"); + +const DEV_SWITCH = "load-mod"; +const DEV_WATCH_SWITCH = "watch"; +const DEV_USER_MOD_PREFIX = "@/"; + +export interface ModLocator { + readonly priority: number; + + /** + * Asynchronously look for mod candidates. + * + * @returns absolute file paths of located mods + */ + locateMods(): Promise; + + /** + * Mark or unmark the specified mod as disabled. + * + * @param id ID of the mod to disable or enable + * @param flag whether to disable the mod + */ + setModDisabled(id: string, flag: boolean): Promise; + + /** + * Retrieve the list of mod IDs that should not be loaded. + * + * @returns IDs of the disabled mods + */ + getDisabledMods(): Promise; +} + +abstract class DirectoryModLocator implements ModLocator { + abstract readonly priority: number; + + protected readonly directory: string; + private readonly disabledModsFile: string; + private disabledMods: Set | null = null; + + constructor(directory: string) { + this.directory = directory; + this.disabledModsFile = path.join(directory, DISABLED_MODS_FILE); + } + + async locateMods(): Promise { + if (switches.safeMode) { + return []; + } + + try { + const dir = await fs.readdir(this.directory, { withFileTypes: true }); + return dir + .filter(entry => entry.name.endsWith(MOD_FILE_SUFFIX)) + .map(entry => path.join(entry.path, entry.name)); + } catch (err) { + if ((err as NodeJS.ErrnoException).code === "ENOENT") { + // The directory does not exist + return []; + } + + // Propagate all other errors + throw err; + } + } + + setModDisabled(id: string, flag: boolean): Promise { + // Note: it is assumed that calling this before accessing + // getDisabledMods will overwrite the file. + this.disabledMods ??= new Set(); + + if (flag) { + this.disabledMods.add(id); + } else { + this.disabledMods.delete(id); + } + + return this.writeDisabledModsFile(); + } + + async getDisabledMods(): Promise { + if (this.disabledMods === null) { + await this.readDisabledModsFile(); + } + + return [...this.disabledMods!]; + } + + private async readDisabledModsFile(): Promise { + // TODO: Validate internal structure (once something is added for + // mod metadata file validation) + + try { + const contents = await fs.readFile(this.disabledModsFile, "utf-8"); + this.disabledMods = new Set(JSON.parse(contents)); + } catch (err) { + // Ensure we don't fail twice + this.disabledMods ??= new Set(); + + if ((err as NodeJS.ErrnoException).code == "ENOENT") { + // Ignore error entirely if the file is missing + return; + } + + if (err instanceof SyntaxError) { + // Malformed JSON, replace the file + return this.writeDisabledModsFile(); + } + + console.warn(`Reading ${this.disabledModsFile} failed:`, err); + } + } + + private async writeDisabledModsFile(): Promise { + try { + const contents = JSON.stringify([...(this.disabledMods ?? new Set())]); + await fs.writeFile(this.disabledModsFile, contents, "utf-8"); + } catch (err: unknown) { + // Nothing we can do + console.warn(`Writing ${this.disabledModsFile} failed:`, err); + } + } +} + +export class UserModLocator extends DirectoryModLocator { + readonly priority = 1; + + constructor() { + super(USER_MODS_DIR); + } + + async locateMods(): Promise { + // Ensure the directory exists + await fs.mkdir(this.directory, { recursive: true }); + return super.locateMods(); + } +} + +export class DistroModLocator extends DirectoryModLocator { + readonly priority = 2; + + constructor() { + super(DISTRO_MODS_DIR); + } +} + +export class DevelopmentModLocator implements ModLocator { + readonly priority = 0; + readonly fsWatcher: FSWatcher | null = null; + + private readonly modFiles: string[] = []; + private readonly disabledMods = new Set(); + + constructor() { + const switchValue = app.commandLine.getSwitchValue(DEV_SWITCH); + if (switchValue === "") { + // Empty string = switch not passed + return; + } + + const resolved = switchValue.split(",").map(f => this.resolveFile(f)); + this.modFiles.push(...resolved); + + const watchMode = app.commandLine.hasSwitch(DEV_WATCH_SWITCH); + if (!watchMode || this.modFiles.length === 0) { + // Skip setting up chokidar + return; + } + + this.fsWatcher = chokidar.watch(this.modFiles, { + persistent: false, + ignoreInitial: true, + }); + } + + locateMods(): Promise { + return Promise.resolve(this.modFiles); + } + + setModDisabled(id: string, flag: boolean): Promise { + if (flag) { + this.disabledMods.add(id); + } else { + this.disabledMods.delete(id); + } + + return Promise.resolve(); + } + + getDisabledMods(): Promise { + return Promise.resolve([...this.disabledMods]); + } + + private resolveFile(file: string) { + // Allow using @/*.asar to reference user mods directory + if (file.startsWith(DEV_USER_MOD_PREFIX)) { + file = file.slice(DEV_USER_MOD_PREFIX.length); + return path.join(USER_MODS_DIR, file); + } + + // Resolve mods relative to CWD, useful for development + return path.resolve(file); + } +} diff --git a/electron/src/mods/metadata.ts b/electron/src/mods/metadata.ts new file mode 100644 index 00000000..623bd5be --- /dev/null +++ b/electron/src/mods/metadata.ts @@ -0,0 +1,38 @@ +import SemVer from "semver/classes/semver.js"; +import { z } from "zod"; + +const semver = z.string().transform((str, ctx) => { + try { + return new SemVer(str); + } catch { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: "Not a valid SemVer version string", + }); + return z.NEVER; + } +}); + +// TBD: dependencies, icons, readme +export const ModMetadata = z.object({ + format: z.literal(1), + id: z.string().regex(/^[a-z0-9][a-z0-9_-]{0,48}[a-z0-9]$/g), + entry: z.string().nonempty(), + name: z.string().nonempty(), + description: z.ostring(), + authors: z + .object({ + name: z.string().nonempty(), + website: z.string().url().optional(), + }) + .array(), + version: semver, + savegameResident: z.boolean().default(true), + website: z.string().url().optional(), + source: z.string().url().optional(), +}); + +export type ModMetadata = z.infer; +export type IpcModMetadata = Omit & { + version: string; +}; diff --git a/electron/src/mods/protocol_handler.ts b/electron/src/mods/protocol_handler.ts new file mode 100644 index 00000000..ba586632 --- /dev/null +++ b/electron/src/mods/protocol_handler.ts @@ -0,0 +1,98 @@ +import { net, protocol } from "electron"; +import { lstat, readdir } from "node:fs/promises"; +import path from "node:path"; +import { pathToFileURL } from "node:url"; +import { ModLoader } from "./loader.js"; + +export const MOD_SCHEME = "mod"; + +export class ModProtocolHandler { + private modLoader: ModLoader; + + constructor(modLoader: ModLoader) { + this.modLoader = modLoader; + + protocol.registerSchemesAsPrivileged([ + { + scheme: MOD_SCHEME, + privileges: { + allowServiceWorkers: true, + bypassCSP: true, + secure: true, + standard: true, + stream: true, + supportFetchAPI: true, + }, + }, + ]); + } + + install() { + protocol.handle(MOD_SCHEME, this.handler.bind(this)); + } + + private async handler(request: GlobalRequest): Promise { + const fileUrl = this.getFileUrlForRequest(request); + if (fileUrl === undefined) { + return Response.error(); + } + + try { + return await net.fetch(fileUrl.toString()); + } catch (err) { + // Check if this is a directory request + const directoryIndex = await this.getDirectoryIndex(fileUrl); + if (directoryIndex !== null) { + return directoryIndex; + } + + console.error("Failed to fetch:", err); + return Response.error(); + } + } + + private async getDirectoryIndex(fileUrl: URL): Promise { + if (!fileUrl.pathname.endsWith("/")) { + return null; + } + + // Remove the trailing slash + fileUrl.pathname = fileUrl.pathname.slice(0, -1); + + try { + const stats = await lstat(fileUrl); + if (!stats.isDirectory()) { + return null; + } + + const dir = await readdir(fileUrl, { withFileTypes: true }); + const result = dir.map(entry => entry.name + (entry.isDirectory() ? "/" : "")); + + return Response.json(result); + } catch (err) { + console.error("Failed to get directory index:", err); + return null; + } + } + + private getFileUrlForRequest(request: GlobalRequest): URL | undefined { + // mod://mod-id/path/to/file + const modUrl = new URL(request.url); + const mod = this.modLoader.getModById(modUrl.hostname); + if (mod === undefined) { + return undefined; + } + + const bundle = mod.file; + const filePath = path.join(bundle, modUrl.pathname); + + // Check if the path escapes the bundle as per Electron example + // NOTE: this means file names cannot start with .. + const relative = path.relative(bundle, filePath); + if (relative.startsWith("..") || path.isAbsolute(relative)) { + return undefined; + } + + return pathToFileURL(filePath); + } +} diff --git a/electron/steam.js b/electron/steam.js deleted file mode 100644 index cdda540b..00000000 --- a/electron/steam.js +++ /dev/null @@ -1,112 +0,0 @@ -const fs = require("fs"); -const path = require("path"); -const { ipcMain } = require("electron"); - -let greenworks = null; -let appId = null; -let initialized = false; - -try { - greenworks = require("shapez.io-private-artifacts/steam/greenworks"); - appId = parseInt(fs.readFileSync(path.join(__dirname, "steam_appid.txt"), "utf8")); -} catch (err) { - // greenworks is not installed - console.warn("Failed to load steam api:", err); -} - -console.log("App ID:", appId); - -function init(isDev) { - if (!greenworks) { - return; - } - - if (!isDev) { - if (greenworks.restartAppIfNecessary(appId)) { - console.log("Restarting ..."); - process.exit(0); - } - } - - if (!greenworks.init()) { - console.log("Failed to initialize greenworks"); - process.exit(1); - } - - initialized = true; -} - -function listen() { - ipcMain.handle("steam:is-initialized", isInitialized); - - if (!initialized) { - console.warn("Steam not initialized, won't be able to listen"); - return; - } - - if (!greenworks) { - console.warn("Greenworks not loaded, won't be able to listen"); - return; - } - - console.log("Adding listeners"); - - ipcMain.handle("steam:get-achievement-names", getAchievementNames); - ipcMain.handle("steam:activate-achievement", activateAchievement); - - function bufferToHex(buffer) { - return Array.from(new Uint8Array(buffer)) - .map(b => b.toString(16).padStart(2, "0")) - .join(""); - } - - ipcMain.handle("steam:get-ticket", (event, arg) => { - console.log("Requested steam ticket ..."); - return new Promise((resolve, reject) => { - greenworks.getAuthSessionTicket( - success => { - const ticketHex = bufferToHex(success.ticket); - resolve(ticketHex); - }, - error => { - console.error("Failed to get steam ticket:", error); - reject(error); - } - ); - }); - }); - - ipcMain.handle("steam:check-app-ownership", (event, appId) => { - return Promise.resolve(greenworks.isDLCInstalled(appId)); - }); -} - -function isInitialized(event) { - return Promise.resolve(initialized); -} - -function getAchievementNames(event) { - return new Promise((resolve, reject) => { - try { - const achievements = greenworks.getAchievementNames(); - resolve(achievements); - } catch (err) { - reject(err); - } - }); -} - -function activateAchievement(event, id) { - return new Promise((resolve, reject) => { - greenworks.activateAchievement( - id, - () => resolve(), - err => reject(err) - ); - }); -} - -module.exports = { - init, - listen, -}; diff --git a/electron/steam_appid.txt b/electron/steam_appid.txt deleted file mode 100644 index a8e9e809..00000000 --- a/electron/steam_appid.txt +++ /dev/null @@ -1 +0,0 @@ -1318690 diff --git a/electron/tsconfig.json b/electron/tsconfig.json new file mode 100644 index 00000000..2ababc88 --- /dev/null +++ b/electron/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../tsconfig.json", + "include": ["./src/**/*"], + "compilerOptions": { + "noEmit": false, + "outDir": "./dist", + "strict": true + } +} diff --git a/electron/yarn.lock b/electron/yarn.lock deleted file mode 100644 index f33e463b..00000000 --- a/electron/yarn.lock +++ /dev/null @@ -1,584 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@electron/get@^1.13.0": - version "1.13.1" - resolved "https://registry.yarnpkg.com/@electron/get/-/get-1.13.1.tgz#42a0aa62fd1189638bd966e23effaebb16108368" - integrity sha512-U5vkXDZ9DwXtkPqlB45tfYnnYBN8PePp1z/XDCupnSpdrxT8/ThCv9WCwPLf9oqiSGZTkH6dx2jDUPuoXpjkcA== - dependencies: - debug "^4.1.1" - env-paths "^2.2.0" - fs-extra "^8.1.0" - got "^9.6.0" - progress "^2.0.3" - semver "^6.2.0" - sumchecker "^3.0.1" - optionalDependencies: - global-agent "^3.0.0" - global-tunnel-ng "^2.7.1" - -"@sindresorhus/is@^0.14.0": - version "0.14.0" - resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" - integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== - -"@szmarczak/http-timer@^1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" - integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA== - dependencies: - defer-to-connect "^1.0.1" - -"@types/node@^14.6.2": - version "14.18.20" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.20.tgz#268f028b36eaf51181c3300252f605488c4f0650" - integrity sha512-Q8KKwm9YqEmUBRsqJ2GWJDtXltBDxTdC4m5vTdXBolu2PeQh8LX+f6BTwU+OuXPu37fLxoN6gidqBmnky36FXA== - -async-lock@^1.2.8: - version "1.2.8" - resolved "https://registry.yarnpkg.com/async-lock/-/async-lock-1.2.8.tgz#7b02bdfa2de603c0713acecd11184cf97bbc7c4c" - integrity sha512-G+26B2jc0Gw0EG/WN2M6IczuGepBsfR1+DtqLnyFSH4p2C668qkOCtEkGNVEaaNAVlYwEMazy1+/jnLxltBkIQ== - -boolean@^3.0.1: - version "3.0.2" - resolved "https://registry.yarnpkg.com/boolean/-/boolean-3.0.2.tgz#df1baa18b6a2b0e70840475e1d93ec8fe75b2570" - integrity sha512-RwywHlpCRc3/Wh81MiCKun4ydaIFyW5Ea6JbL6sRCVx5q5irDw7pMXBUFYF/jArQ6YrG36q0kpovc9P/Kd3I4g== - -buffer-crc32@~0.2.3: - version "0.2.13" - resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" - integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= - -buffer-from@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" - integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== - -cacheable-request@^6.0.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" - integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg== - dependencies: - clone-response "^1.0.2" - get-stream "^5.1.0" - http-cache-semantics "^4.0.0" - keyv "^3.0.0" - lowercase-keys "^2.0.0" - normalize-url "^4.1.0" - responselike "^1.0.2" - -clone-response@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" - integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= - dependencies: - mimic-response "^1.0.0" - -concat-stream@^1.6.2: - version "1.6.2" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" - integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== - dependencies: - buffer-from "^1.0.0" - inherits "^2.0.3" - readable-stream "^2.2.2" - typedarray "^0.0.6" - -config-chain@^1.1.11: - version "1.1.12" - resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.12.tgz#0fde8d091200eb5e808caf25fe618c02f48e4efa" - integrity sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA== - dependencies: - ini "^1.3.4" - proto-list "~1.2.1" - -core-util-is@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= - -debug@^2.6.9: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - -debug@^4.1.0, debug@^4.1.1: - version "4.3.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" - integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== - dependencies: - ms "2.1.2" - -decompress-response@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" - integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= - dependencies: - mimic-response "^1.0.0" - -defer-to-connect@^1.0.1: - version "1.1.3" - resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" - integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== - -define-properties@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" - integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== - dependencies: - object-keys "^1.0.12" - -detect-node@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.4.tgz#014ee8f8f669c5c58023da64b8179c083a28c46c" - integrity sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw== - -duplexer3@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" - integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= - -electron-window-state@^5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/electron-window-state/-/electron-window-state-5.0.3.tgz#4f36d09e3f953d87aff103bf010f460056050aa8" - integrity sha512-1mNTwCfkolXl3kMf50yW3vE2lZj0y92P/HYWFBrb+v2S/pCka5mdwN3cagKm458A7NjndSwijynXgcLWRodsVg== - dependencies: - jsonfile "^4.0.0" - mkdirp "^0.5.1" - -electron@16.2.8: - version "16.2.8" - resolved "https://registry.yarnpkg.com/electron/-/electron-16.2.8.tgz#b7f2bd1184701e54a1bc902839d5a3ec95bb8982" - integrity sha512-KSOytY6SPLsh3iCziztqa/WgJyfDOKzCvNqku9gGIqSdT8CqtV66dTU1SOrKZQjRFLxHaF8LbyxUL1vwe4taqw== - dependencies: - "@electron/get" "^1.13.0" - "@types/node" "^14.6.2" - extract-zip "^1.0.3" - -encodeurl@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" - integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= - -end-of-stream@^1.1.0: - version "1.4.4" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" - integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== - dependencies: - once "^1.4.0" - -env-paths@^2.2.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" - integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== - -es6-error@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" - integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== - -escape-string-regexp@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" - integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== - -extract-zip@^1.0.3: - version "1.7.0" - resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.7.0.tgz#556cc3ae9df7f452c493a0cfb51cc30277940927" - integrity sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA== - dependencies: - concat-stream "^1.6.2" - debug "^2.6.9" - mkdirp "^0.5.4" - yauzl "^2.10.0" - -fd-slicer@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" - integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4= - dependencies: - pend "~1.2.0" - -fs-extra@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" - integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== - dependencies: - graceful-fs "^4.2.0" - jsonfile "^4.0.0" - universalify "^0.1.0" - -get-stream@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" - integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== - dependencies: - pump "^3.0.0" - -get-stream@^5.1.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" - integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== - dependencies: - pump "^3.0.0" - -global-agent@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/global-agent/-/global-agent-3.0.0.tgz#ae7cd31bd3583b93c5a16437a1afe27cc33a1ab6" - integrity sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q== - dependencies: - boolean "^3.0.1" - es6-error "^4.1.1" - matcher "^3.0.0" - roarr "^2.15.3" - semver "^7.3.2" - serialize-error "^7.0.1" - -global-tunnel-ng@^2.7.1: - version "2.7.1" - resolved "https://registry.yarnpkg.com/global-tunnel-ng/-/global-tunnel-ng-2.7.1.tgz#d03b5102dfde3a69914f5ee7d86761ca35d57d8f" - integrity sha512-4s+DyciWBV0eK148wqXxcmVAbFVPqtc3sEtUE/GTQfuU80rySLcMhUmHKSHI7/LDj8q0gDYI1lIhRRB7ieRAqg== - dependencies: - encodeurl "^1.0.2" - lodash "^4.17.10" - npm-conf "^1.1.3" - tunnel "^0.0.6" - -globalthis@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.2.tgz#2a235d34f4d8036219f7e34929b5de9e18166b8b" - integrity sha512-ZQnSFO1la8P7auIOQECnm0sSuoMeaSq0EEdXMBFF2QJO4uNcwbyhSgG3MruWNbFTqCLmxVwGOl7LZ9kASvHdeQ== - dependencies: - define-properties "^1.1.3" - -got@^9.6.0: - version "9.6.0" - resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" - integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q== - dependencies: - "@sindresorhus/is" "^0.14.0" - "@szmarczak/http-timer" "^1.1.2" - cacheable-request "^6.0.0" - decompress-response "^3.3.0" - duplexer3 "^0.1.4" - get-stream "^4.1.0" - lowercase-keys "^1.0.1" - mimic-response "^1.0.1" - p-cancelable "^1.0.0" - to-readable-stream "^1.0.0" - url-parse-lax "^3.0.0" - -graceful-fs@^4.1.6, graceful-fs@^4.2.0: - version "4.2.6" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" - integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== - -http-cache-semantics@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" - integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== - -inherits@^2.0.3, inherits@~2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -ini@^1.3.4: - version "1.3.8" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" - integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== - -isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= - -json-buffer@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" - integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= - -json-stringify-safe@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" - integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= - -jsonfile@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" - integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= - optionalDependencies: - graceful-fs "^4.1.6" - -keyv@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" - integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA== - dependencies: - json-buffer "3.0.0" - -lodash@^4.17.10: - version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" - integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== - -lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" - integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== - -lowercase-keys@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" - integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== - -lru-cache@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" - integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== - dependencies: - yallist "^4.0.0" - -matcher@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/matcher/-/matcher-3.0.0.tgz#bd9060f4c5b70aa8041ccc6f80368760994f30ca" - integrity sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng== - dependencies: - escape-string-regexp "^4.0.0" - -mimic-response@^1.0.0, mimic-response@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" - integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== - -minimist@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" - integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== - -mkdirp@^0.5.1, mkdirp@^0.5.4: - version "0.5.5" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" - integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== - dependencies: - minimist "^1.2.5" - -ms@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= - -ms@2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - -normalize-url@^4.1.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129" - integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ== - -npm-conf@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/npm-conf/-/npm-conf-1.1.3.tgz#256cc47bd0e218c259c4e9550bf413bc2192aff9" - integrity sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw== - dependencies: - config-chain "^1.1.11" - pify "^3.0.0" - -object-keys@^1.0.12: - version "1.1.1" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" - integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== - -once@^1.3.1, once@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= - dependencies: - wrappy "1" - -p-cancelable@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" - integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== - -pend@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" - integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= - -pify@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" - integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= - -prepend-http@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" - integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= - -process-nextick-args@~2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" - integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== - -progress@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" - integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== - -proto-list@~1.2.1: - version "1.2.4" - resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" - integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk= - -pump@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" - integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - -readable-stream@^2.2.2: - version "2.3.7" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" - integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.1.1" - util-deprecate "~1.0.1" - -responselike@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" - integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= - dependencies: - lowercase-keys "^1.0.0" - -roarr@^2.15.3: - version "2.15.4" - resolved "https://registry.yarnpkg.com/roarr/-/roarr-2.15.4.tgz#f5fe795b7b838ccfe35dc608e0282b9eba2e7afd" - integrity sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A== - dependencies: - boolean "^3.0.1" - detect-node "^2.0.4" - globalthis "^1.0.1" - json-stringify-safe "^5.0.1" - semver-compare "^1.0.0" - sprintf-js "^1.1.2" - -safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - -semver-compare@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" - integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w= - -semver@^6.2.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== - -semver@^7.3.2: - version "7.3.4" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97" - integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw== - dependencies: - lru-cache "^6.0.0" - -serialize-error@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-7.0.1.tgz#f1360b0447f61ffb483ec4157c737fab7d778e18" - integrity sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw== - dependencies: - type-fest "^0.13.1" - -"shapez.io-private-artifacts@github:tobspr/shapez.io-private-artifacts#abi-v99": - version "0.1.0" - resolved "git+ssh://git@github.com/tobspr/shapez.io-private-artifacts.git#3293b20be26060fd36e9f00ded9ab5d0bdf57338" - -sprintf-js@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.2.tgz#da1765262bf8c0f571749f2ad6c26300207ae673" - integrity sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug== - -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - -sumchecker@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/sumchecker/-/sumchecker-3.0.1.tgz#6377e996795abb0b6d348e9b3e1dfb24345a8e42" - integrity sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg== - dependencies: - debug "^4.1.0" - -to-readable-stream@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771" - integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q== - -tunnel@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c" - integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg== - -type-fest@^0.13.1: - version "0.13.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.13.1.tgz#0172cb5bce80b0bd542ea348db50c7e21834d934" - integrity sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg== - -typedarray@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" - integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= - -universalify@^0.1.0: - version "0.1.2" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" - integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== - -url-parse-lax@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" - integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww= - dependencies: - prepend-http "^2.0.0" - -util-deprecate@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= - -yallist@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" - integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== - -yauzl@^2.10.0: - version "2.10.0" - resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" - integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk= - dependencies: - buffer-crc32 "~0.2.3" - fd-slicer "~1.1.0" diff --git a/electron_gog/favicon.icns b/electron_gog/favicon.icns deleted file mode 100644 index 79e141a5..00000000 Binary files a/electron_gog/favicon.icns and /dev/null differ diff --git a/electron_gog/favicon.ico b/electron_gog/favicon.ico deleted file mode 100644 index 81a9aa5c..00000000 Binary files a/electron_gog/favicon.ico and /dev/null differ diff --git a/electron_gog/favicon.png b/electron_gog/favicon.png deleted file mode 100644 index c837c787..00000000 Binary files a/electron_gog/favicon.png and /dev/null differ diff --git a/electron_gog/index.js b/electron_gog/index.js deleted file mode 100644 index 563f187d..00000000 --- a/electron_gog/index.js +++ /dev/null @@ -1,381 +0,0 @@ -/* eslint-disable quotes,no-undef */ - -const { app, BrowserWindow, Menu, MenuItem, ipcMain, shell, dialog, session } = require("electron"); -const path = require("path"); -const url = require("url"); -const fs = require("fs"); -const asyncLock = require("async-lock"); -const windowStateKeeper = require("electron-window-state"); - -// Disable hardware key handling, i.e. being able to pause/resume the game music -// with hardware keys -app.commandLine.appendSwitch("disable-features", "HardwareMediaKeyHandling"); - -const isDev = app.commandLine.hasSwitch("dev"); -const isLocal = app.commandLine.hasSwitch("local"); -const safeMode = app.commandLine.hasSwitch("safe-mode"); -const externalMod = app.commandLine.getSwitchValue("load-mod"); - -const roamingFolder = - process.env.APPDATA || - (process.platform == "darwin" - ? process.env.HOME + "/Library/Preferences" - : process.env.HOME + "/.local/share"); - -let storePath = path.join(roamingFolder, "shapez.io", "saves"); -let modsPath = path.join(roamingFolder, "shapez.io", "mods"); - -if (!fs.existsSync(storePath)) { - // No try-catch by design - fs.mkdirSync(storePath, { recursive: true }); -} - -if (!fs.existsSync(modsPath)) { - fs.mkdirSync(modsPath, { recursive: true }); -} - -/** @type {BrowserWindow} */ -let win = null; -let menu = null; - -function createWindow() { - let faviconExtension = ".png"; - if (process.platform === "win32") { - faviconExtension = ".ico"; - } - - const mainWindowState = windowStateKeeper({ - defaultWidth: 1000, - defaultHeight: 800, - }); - - win = new BrowserWindow({ - x: mainWindowState.x, - y: mainWindowState.y, - width: mainWindowState.width, - height: mainWindowState.height, - show: false, - backgroundColor: "#222428", - useContentSize: false, - minWidth: 800, - minHeight: 600, - title: "shapez", - transparent: false, - icon: path.join(__dirname, "favicon" + faviconExtension), - // fullscreen: true, - autoHideMenuBar: !isDev, - webPreferences: { - nodeIntegration: false, - nodeIntegrationInWorker: false, - nodeIntegrationInSubFrames: false, - contextIsolation: true, - enableRemoteModule: false, - disableBlinkFeatures: "Auxclick", - - webSecurity: true, - sandbox: true, - preload: path.join(__dirname, "preload.js"), - experimentalFeatures: false, - }, - allowRunningInsecureContent: false, - }); - - mainWindowState.manage(win); - - if (isLocal) { - win.loadURL("http://localhost:3005"); - } else { - win.loadURL( - url.format({ - pathname: path.join(__dirname, "index.html"), - protocol: "file:", - slashes: true, - }) - ); - } - win.webContents.session.clearCache(); - win.webContents.session.clearStorageData(); - - ////// SECURITY - - // Disable permission requests - win.webContents.session.setPermissionRequestHandler((webContents, permission, callback) => { - callback(false); - }); - session.fromPartition("default").setPermissionRequestHandler((webContents, permission, callback) => { - callback(false); - }); - - app.on("web-contents-created", (event, contents) => { - // Disable vewbiew - contents.on("will-attach-webview", (event, webPreferences, params) => { - event.preventDefault(); - }); - // Disable navigation - contents.on("will-navigate", (event, navigationUrl) => { - event.preventDefault(); - }); - }); - - win.webContents.on("will-redirect", (contentsEvent, navigationUrl) => { - // Log and prevent the app from redirecting to a new page - console.error( - `The application tried to redirect to the following address: '${navigationUrl}'. This attempt was blocked.` - ); - contentsEvent.preventDefault(); - }); - - // Filter loading any module via remote; - // you shouldn't be using remote at all, though - // https://electronjs.org/docs/tutorial/security#16-filter-the-remote-module - app.on("remote-require", (event, webContents, moduleName) => { - event.preventDefault(); - }); - - // built-ins are modules such as "app" - app.on("remote-get-builtin", (event, webContents, moduleName) => { - event.preventDefault(); - }); - - app.on("remote-get-global", (event, webContents, globalName) => { - event.preventDefault(); - }); - - app.on("remote-get-current-window", (event, webContents) => { - event.preventDefault(); - }); - - app.on("remote-get-current-web-contents", (event, webContents) => { - event.preventDefault(); - }); - - //// END SECURITY - - win.webContents.on("new-window", (event, pth) => { - event.preventDefault(); - - if (pth.startsWith("https://")) { - shell.openExternal(pth); - } - }); - - win.on("closed", () => { - console.log("Window closed"); - win = null; - }); - - if (isDev) { - menu = new Menu(); - - win.webContents.toggleDevTools(); - - const mainItem = new MenuItem({ - label: "Toggle Dev Tools", - click: () => win.webContents.toggleDevTools(), - accelerator: "F12", - }); - menu.append(mainItem); - - const reloadItem = new MenuItem({ - label: "Reload", - click: () => win.reload(), - accelerator: "F5", - }); - menu.append(reloadItem); - - const fullscreenItem = new MenuItem({ - label: "Fullscreen", - click: () => win.setFullScreen(!win.isFullScreen()), - accelerator: "F11", - }); - menu.append(fullscreenItem); - - const mainMenu = new Menu(); - mainMenu.append( - new MenuItem({ - label: "shapez.io", - submenu: menu, - }) - ); - - Menu.setApplicationMenu(mainMenu); - } else { - Menu.setApplicationMenu(null); - } - - win.once("ready-to-show", () => { - win.show(); - win.focus(); - }); -} - -if (!app.requestSingleInstanceLock()) { - app.exit(0); -} else { - app.on("second-instance", () => { - // Someone tried to run a second instance, we should focus - if (win) { - if (win.isMinimized()) { - win.restore(); - } - win.focus(); - } - }); -} - -app.on("ready", createWindow); - -app.on("window-all-closed", () => { - console.log("All windows closed"); - app.quit(); -}); - -ipcMain.on("set-fullscreen", (event, flag) => { - win.setFullScreen(flag); -}); - -ipcMain.on("exit-app", () => { - win.close(); - app.quit(); -}); - -let renameCounter = 1; - -const fileLock = new asyncLock({ - timeout: 30000, - maxPending: 1000, -}); - -function niceFileName(filename) { - return filename.replace(storePath, "@"); -} - -async function writeFileSafe(filename, contents) { - ++renameCounter; - const prefix = "[ " + renameCounter + ":" + niceFileName(filename) + " ] "; - const transactionId = String(new Date().getTime()) + "." + renameCounter; - - if (fileLock.isBusy()) { - console.warn(prefix, "Concurrent write process on", filename); - } - - fileLock.acquire(filename, async () => { - console.log(prefix, "Starting write on", niceFileName(filename), "in transaction", transactionId); - - if (!fs.existsSync(filename)) { - // this one is easy - console.log(prefix, "Writing file instantly because it does not exist:", niceFileName(filename)); - await fs.promises.writeFile(filename, contents, "utf8"); - return; - } - - // first, write a temporary file (.tmp-XXX) - const tempName = filename + ".tmp-" + transactionId; - console.log(prefix, "Writing temporary file", niceFileName(tempName)); - await fs.promises.writeFile(tempName, contents, "utf8"); - - // now, rename the original file to (.backup-XXX) - const oldTemporaryName = filename + ".backup-" + transactionId; - console.log( - prefix, - "Renaming old file", - niceFileName(filename), - "to", - niceFileName(oldTemporaryName) - ); - await fs.promises.rename(filename, oldTemporaryName); - - // now, rename the temporary file (.tmp-XXX) to the target - console.log( - prefix, - "Renaming the temporary file", - niceFileName(tempName), - "to the original", - niceFileName(filename) - ); - await fs.promises.rename(tempName, filename); - - // we are done now, try to create a backup, but don't fail if the backup fails - try { - // check if there is an old backup file - const backupFileName = filename + ".backup"; - if (fs.existsSync(backupFileName)) { - console.log(prefix, "Deleting old backup file", niceFileName(backupFileName)); - // delete the old backup - await fs.promises.unlink(backupFileName); - } - - // rename the old file to the new backup file - console.log(prefix, "Moving", niceFileName(oldTemporaryName), "to the backup file location"); - await fs.promises.rename(oldTemporaryName, backupFileName); - } catch (ex) { - console.error(prefix, "Failed to switch backup files:", ex); - } - }); -} - -ipcMain.handle("fs-job", async (event, job) => { - const filenameSafe = job.filename.replace(/[^a-z\.\-_0-9]/gi, "_"); - const fname = path.join(storePath, filenameSafe); - switch (job.type) { - case "read": { - if (!fs.existsSync(fname)) { - // Special FILE_NOT_FOUND error code - return { error: "file_not_found" }; - } - return await fs.promises.readFile(fname, "utf8"); - } - case "write": { - await writeFileSafe(fname, job.contents); - return job.contents; - } - - case "delete": { - await fs.promises.unlink(fname); - return; - } - - default: - throw new Error("Unknown fs job: " + job.type); - } -}); - -ipcMain.handle("open-mods-folder", async () => { - shell.openPath(modsPath); -}); - -console.log("Loading mods ..."); - -function loadMods() { - if (safeMode) { - console.log("Safe Mode enabled for mods, skipping mod search"); - } - console.log("Loading mods from", modsPath); - let modFiles = safeMode - ? [] - : fs - .readdirSync(modsPath) - .filter(filename => filename.endsWith(".js")) - .map(filename => path.join(modsPath, filename)); - - if (externalMod) { - console.log("Adding external mod source:", externalMod); - const externalModPaths = externalMod.split(","); - modFiles = modFiles.concat(externalModPaths); - } - - return modFiles.map(filename => fs.readFileSync(filename, "utf8")); -} - -let mods = []; -try { - mods = loadMods(); - console.log("Loaded", mods.length, "mods"); -} catch (ex) { - console.error("Failed to load mods"); - dialog.showErrorBox("Failed to load mods:", ex); -} - -ipcMain.handle("get-mods", async () => { - return mods; -}); diff --git a/electron_gog/package.json b/electron_gog/package.json deleted file mode 100644 index 082055e4..00000000 --- a/electron_gog/package.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "electron", - "version": "1.0.0", - "main": "index.js", - "license": "MIT", - "private": true, - "scripts": { - "startDev": "electron --disable-direct-composition --in-process-gpu . --dev --local", - "startDevGpu": "electron --enable-gpu-rasterization --enable-accelerated-2d-canvas --num-raster-threads=8 --enable-zero-copy . --dev --local", - "start": "electron --disable-direct-composition --in-process-gpu ." - }, - "dependencies": { - "async-lock": "^1.2.8", - "electron": "16.2.8", - "electron-window-state": "^5.0.3" - } -} diff --git a/electron_gog/preload.js b/electron_gog/preload.js deleted file mode 100644 index c6336230..00000000 --- a/electron_gog/preload.js +++ /dev/null @@ -1,7 +0,0 @@ -const { contextBridge, ipcRenderer } = require("electron"); - -contextBridge.exposeInMainWorld("ipcRenderer", { - invoke: ipcRenderer.invoke.bind(ipcRenderer), - on: ipcRenderer.on.bind(ipcRenderer), - send: ipcRenderer.send.bind(ipcRenderer), -}); diff --git a/electron_gog/yarn.lock b/electron_gog/yarn.lock deleted file mode 100644 index c9238b1f..00000000 --- a/electron_gog/yarn.lock +++ /dev/null @@ -1,580 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@electron/get@^1.13.0": - version "1.13.1" - resolved "https://registry.yarnpkg.com/@electron/get/-/get-1.13.1.tgz#42a0aa62fd1189638bd966e23effaebb16108368" - integrity sha512-U5vkXDZ9DwXtkPqlB45tfYnnYBN8PePp1z/XDCupnSpdrxT8/ThCv9WCwPLf9oqiSGZTkH6dx2jDUPuoXpjkcA== - dependencies: - debug "^4.1.1" - env-paths "^2.2.0" - fs-extra "^8.1.0" - got "^9.6.0" - progress "^2.0.3" - semver "^6.2.0" - sumchecker "^3.0.1" - optionalDependencies: - global-agent "^3.0.0" - global-tunnel-ng "^2.7.1" - -"@sindresorhus/is@^0.14.0": - version "0.14.0" - resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" - integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== - -"@szmarczak/http-timer@^1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" - integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA== - dependencies: - defer-to-connect "^1.0.1" - -"@types/node@^14.6.2": - version "14.18.20" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.20.tgz#268f028b36eaf51181c3300252f605488c4f0650" - integrity sha512-Q8KKwm9YqEmUBRsqJ2GWJDtXltBDxTdC4m5vTdXBolu2PeQh8LX+f6BTwU+OuXPu37fLxoN6gidqBmnky36FXA== - -async-lock@^1.2.8: - version "1.2.8" - resolved "https://registry.yarnpkg.com/async-lock/-/async-lock-1.2.8.tgz#7b02bdfa2de603c0713acecd11184cf97bbc7c4c" - integrity sha512-G+26B2jc0Gw0EG/WN2M6IczuGepBsfR1+DtqLnyFSH4p2C668qkOCtEkGNVEaaNAVlYwEMazy1+/jnLxltBkIQ== - -boolean@^3.0.1: - version "3.0.2" - resolved "https://registry.yarnpkg.com/boolean/-/boolean-3.0.2.tgz#df1baa18b6a2b0e70840475e1d93ec8fe75b2570" - integrity sha512-RwywHlpCRc3/Wh81MiCKun4ydaIFyW5Ea6JbL6sRCVx5q5irDw7pMXBUFYF/jArQ6YrG36q0kpovc9P/Kd3I4g== - -buffer-crc32@~0.2.3: - version "0.2.13" - resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" - integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= - -buffer-from@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" - integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== - -cacheable-request@^6.0.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" - integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg== - dependencies: - clone-response "^1.0.2" - get-stream "^5.1.0" - http-cache-semantics "^4.0.0" - keyv "^3.0.0" - lowercase-keys "^2.0.0" - normalize-url "^4.1.0" - responselike "^1.0.2" - -clone-response@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" - integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= - dependencies: - mimic-response "^1.0.0" - -concat-stream@^1.6.2: - version "1.6.2" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" - integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== - dependencies: - buffer-from "^1.0.0" - inherits "^2.0.3" - readable-stream "^2.2.2" - typedarray "^0.0.6" - -config-chain@^1.1.11: - version "1.1.12" - resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.12.tgz#0fde8d091200eb5e808caf25fe618c02f48e4efa" - integrity sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA== - dependencies: - ini "^1.3.4" - proto-list "~1.2.1" - -core-util-is@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= - -debug@^2.6.9: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - -debug@^4.1.0, debug@^4.1.1: - version "4.3.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" - integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== - dependencies: - ms "2.1.2" - -decompress-response@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" - integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= - dependencies: - mimic-response "^1.0.0" - -defer-to-connect@^1.0.1: - version "1.1.3" - resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" - integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== - -define-properties@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" - integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== - dependencies: - object-keys "^1.0.12" - -detect-node@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.4.tgz#014ee8f8f669c5c58023da64b8179c083a28c46c" - integrity sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw== - -duplexer3@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" - integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= - -electron-window-state@^5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/electron-window-state/-/electron-window-state-5.0.3.tgz#4f36d09e3f953d87aff103bf010f460056050aa8" - integrity sha512-1mNTwCfkolXl3kMf50yW3vE2lZj0y92P/HYWFBrb+v2S/pCka5mdwN3cagKm458A7NjndSwijynXgcLWRodsVg== - dependencies: - jsonfile "^4.0.0" - mkdirp "^0.5.1" - -electron@16.2.8: - version "16.2.8" - resolved "https://registry.yarnpkg.com/electron/-/electron-16.2.8.tgz#b7f2bd1184701e54a1bc902839d5a3ec95bb8982" - integrity sha512-KSOytY6SPLsh3iCziztqa/WgJyfDOKzCvNqku9gGIqSdT8CqtV66dTU1SOrKZQjRFLxHaF8LbyxUL1vwe4taqw== - dependencies: - "@electron/get" "^1.13.0" - "@types/node" "^14.6.2" - extract-zip "^1.0.3" - -encodeurl@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" - integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= - -end-of-stream@^1.1.0: - version "1.4.4" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" - integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== - dependencies: - once "^1.4.0" - -env-paths@^2.2.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" - integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== - -es6-error@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" - integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== - -escape-string-regexp@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" - integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== - -extract-zip@^1.0.3: - version "1.7.0" - resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.7.0.tgz#556cc3ae9df7f452c493a0cfb51cc30277940927" - integrity sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA== - dependencies: - concat-stream "^1.6.2" - debug "^2.6.9" - mkdirp "^0.5.4" - yauzl "^2.10.0" - -fd-slicer@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" - integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4= - dependencies: - pend "~1.2.0" - -fs-extra@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" - integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== - dependencies: - graceful-fs "^4.2.0" - jsonfile "^4.0.0" - universalify "^0.1.0" - -get-stream@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" - integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== - dependencies: - pump "^3.0.0" - -get-stream@^5.1.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" - integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== - dependencies: - pump "^3.0.0" - -global-agent@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/global-agent/-/global-agent-3.0.0.tgz#ae7cd31bd3583b93c5a16437a1afe27cc33a1ab6" - integrity sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q== - dependencies: - boolean "^3.0.1" - es6-error "^4.1.1" - matcher "^3.0.0" - roarr "^2.15.3" - semver "^7.3.2" - serialize-error "^7.0.1" - -global-tunnel-ng@^2.7.1: - version "2.7.1" - resolved "https://registry.yarnpkg.com/global-tunnel-ng/-/global-tunnel-ng-2.7.1.tgz#d03b5102dfde3a69914f5ee7d86761ca35d57d8f" - integrity sha512-4s+DyciWBV0eK148wqXxcmVAbFVPqtc3sEtUE/GTQfuU80rySLcMhUmHKSHI7/LDj8q0gDYI1lIhRRB7ieRAqg== - dependencies: - encodeurl "^1.0.2" - lodash "^4.17.10" - npm-conf "^1.1.3" - tunnel "^0.0.6" - -globalthis@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.2.tgz#2a235d34f4d8036219f7e34929b5de9e18166b8b" - integrity sha512-ZQnSFO1la8P7auIOQECnm0sSuoMeaSq0EEdXMBFF2QJO4uNcwbyhSgG3MruWNbFTqCLmxVwGOl7LZ9kASvHdeQ== - dependencies: - define-properties "^1.1.3" - -got@^9.6.0: - version "9.6.0" - resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" - integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q== - dependencies: - "@sindresorhus/is" "^0.14.0" - "@szmarczak/http-timer" "^1.1.2" - cacheable-request "^6.0.0" - decompress-response "^3.3.0" - duplexer3 "^0.1.4" - get-stream "^4.1.0" - lowercase-keys "^1.0.1" - mimic-response "^1.0.1" - p-cancelable "^1.0.0" - to-readable-stream "^1.0.0" - url-parse-lax "^3.0.0" - -graceful-fs@^4.1.6, graceful-fs@^4.2.0: - version "4.2.6" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" - integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== - -http-cache-semantics@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" - integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== - -inherits@^2.0.3, inherits@~2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -ini@^1.3.4: - version "1.3.8" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" - integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== - -isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= - -json-buffer@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" - integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= - -json-stringify-safe@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" - integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= - -jsonfile@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" - integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= - optionalDependencies: - graceful-fs "^4.1.6" - -keyv@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" - integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA== - dependencies: - json-buffer "3.0.0" - -lodash@^4.17.10: - version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" - integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== - -lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" - integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== - -lowercase-keys@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" - integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== - -lru-cache@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" - integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== - dependencies: - yallist "^4.0.0" - -matcher@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/matcher/-/matcher-3.0.0.tgz#bd9060f4c5b70aa8041ccc6f80368760994f30ca" - integrity sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng== - dependencies: - escape-string-regexp "^4.0.0" - -mimic-response@^1.0.0, mimic-response@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" - integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== - -minimist@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" - integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== - -mkdirp@^0.5.1, mkdirp@^0.5.4: - version "0.5.5" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" - integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== - dependencies: - minimist "^1.2.5" - -ms@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= - -ms@2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - -normalize-url@^4.1.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129" - integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ== - -npm-conf@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/npm-conf/-/npm-conf-1.1.3.tgz#256cc47bd0e218c259c4e9550bf413bc2192aff9" - integrity sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw== - dependencies: - config-chain "^1.1.11" - pify "^3.0.0" - -object-keys@^1.0.12: - version "1.1.1" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" - integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== - -once@^1.3.1, once@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= - dependencies: - wrappy "1" - -p-cancelable@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" - integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== - -pend@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" - integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= - -pify@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" - integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= - -prepend-http@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" - integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= - -process-nextick-args@~2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" - integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== - -progress@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" - integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== - -proto-list@~1.2.1: - version "1.2.4" - resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" - integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk= - -pump@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" - integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - -readable-stream@^2.2.2: - version "2.3.7" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" - integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.1.1" - util-deprecate "~1.0.1" - -responselike@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" - integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= - dependencies: - lowercase-keys "^1.0.0" - -roarr@^2.15.3: - version "2.15.4" - resolved "https://registry.yarnpkg.com/roarr/-/roarr-2.15.4.tgz#f5fe795b7b838ccfe35dc608e0282b9eba2e7afd" - integrity sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A== - dependencies: - boolean "^3.0.1" - detect-node "^2.0.4" - globalthis "^1.0.1" - json-stringify-safe "^5.0.1" - semver-compare "^1.0.0" - sprintf-js "^1.1.2" - -safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - -semver-compare@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" - integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w= - -semver@^6.2.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== - -semver@^7.3.2: - version "7.3.4" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97" - integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw== - dependencies: - lru-cache "^6.0.0" - -serialize-error@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-7.0.1.tgz#f1360b0447f61ffb483ec4157c737fab7d778e18" - integrity sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw== - dependencies: - type-fest "^0.13.1" - -sprintf-js@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.2.tgz#da1765262bf8c0f571749f2ad6c26300207ae673" - integrity sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug== - -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - -sumchecker@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/sumchecker/-/sumchecker-3.0.1.tgz#6377e996795abb0b6d348e9b3e1dfb24345a8e42" - integrity sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg== - dependencies: - debug "^4.1.0" - -to-readable-stream@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771" - integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q== - -tunnel@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c" - integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg== - -type-fest@^0.13.1: - version "0.13.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.13.1.tgz#0172cb5bce80b0bd542ea348db50c7e21834d934" - integrity sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg== - -typedarray@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" - integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= - -universalify@^0.1.0: - version "0.1.2" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" - integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== - -url-parse-lax@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" - integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww= - dependencies: - prepend-http "^2.0.0" - -util-deprecate@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= - -yallist@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" - integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== - -yauzl@^2.10.0: - version "2.10.0" - resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" - integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk= - dependencies: - buffer-crc32 "~0.2.3" - fd-slicer "~1.1.0" diff --git a/electron_wegame/.gitignore b/electron_wegame/.gitignore deleted file mode 100644 index 475a7c75..00000000 --- a/electron_wegame/.gitignore +++ /dev/null @@ -1 +0,0 @@ -wegame_sdk diff --git a/electron_wegame/README.md b/electron_wegame/README.md deleted file mode 100644 index 70736caf..00000000 --- a/electron_wegame/README.md +++ /dev/null @@ -1 +0,0 @@ -To build, place the lib64 folder from the wegame sdk for electron 13 in `wegame_sdk` and run the `wegame.main.standalone` gulp task. diff --git a/electron_wegame/electron_wegame.code-workspace b/electron_wegame/electron_wegame.code-workspace deleted file mode 100644 index fc9ab864..00000000 --- a/electron_wegame/electron_wegame.code-workspace +++ /dev/null @@ -1,13 +0,0 @@ -{ - "folders": [ - { - "path": "." - } - ], - "settings": { - "files.exclude": { - "**/node_modules": true, - "**/typedefs_gen": true - } - } -} \ No newline at end of file diff --git a/electron_wegame/favicon.icns b/electron_wegame/favicon.icns deleted file mode 100644 index 79e141a5..00000000 Binary files a/electron_wegame/favicon.icns and /dev/null differ diff --git a/electron_wegame/favicon.ico b/electron_wegame/favicon.ico deleted file mode 100644 index 81a9aa5c..00000000 Binary files a/electron_wegame/favicon.ico and /dev/null differ diff --git a/electron_wegame/favicon.png b/electron_wegame/favicon.png deleted file mode 100644 index c837c787..00000000 Binary files a/electron_wegame/favicon.png and /dev/null differ diff --git a/electron_wegame/index.js b/electron_wegame/index.js deleted file mode 100644 index 2c183f15..00000000 --- a/electron_wegame/index.js +++ /dev/null @@ -1,253 +0,0 @@ -/* eslint-disable quotes,no-undef */ - -const { app, BrowserWindow, Menu, MenuItem, ipcMain, shell } = require("electron"); - -app.commandLine.appendSwitch("in-process-gpu"); - -const path = require("path"); -const url = require("url"); -const fs = require("fs"); -const wegame = require("./wegame"); -const asyncLock = require("async-lock"); - -const isDev = process.argv.indexOf("--dev") >= 0; -const isLocal = process.argv.indexOf("--local") >= 0; - -const roamingFolder = - process.env.APPDATA || - (process.platform == "darwin" - ? process.env.HOME + "/Library/Preferences" - : process.env.HOME + "/.local/share"); -let storePath = path.join(roamingFolder, "shapez.io", "saves"); - -if (!fs.existsSync(storePath)) { - // No try-catch by design - fs.mkdirSync(storePath, { recursive: true }); -} - -/** @type {BrowserWindow} */ -let win = null; -let menu = null; - -function createWindow() { - let faviconExtension = ".png"; - if (process.platform === "win32") { - faviconExtension = ".ico"; - } - - win = new BrowserWindow({ - width: 1280, - height: 800, - show: false, - backgroundColor: "#222428", - useContentSize: true, - minWidth: 800, - minHeight: 600, - title: "图形工厂", - transparent: false, - icon: path.join(__dirname, "favicon" + faviconExtension), - // fullscreen: true, - autoHideMenuBar: true, - webPreferences: { - nodeIntegration: false, - webSecurity: true, - sandbox: true, - - contextIsolation: true, - preload: path.join(__dirname, "preload.js"), - }, - allowRunningInsecureContent: false, - }); - - if (isLocal) { - win.loadURL("http://localhost:3005"); - } else { - win.loadURL( - url.format({ - pathname: path.join(__dirname, "index.html"), - protocol: "file:", - slashes: true, - }) - ); - } - win.webContents.session.clearCache(() => null); - win.webContents.session.clearStorageData(); - - win.webContents.on("new-window", (event, pth) => { - event.preventDefault(); - shell.openExternal(pth); - }); - - win.on("closed", () => { - console.log("Window closed"); - win = null; - }); - - if (isDev) { - menu = new Menu(); - - const mainItem = new MenuItem({ - label: "Toggle Dev Tools", - click: () => win.webContents.toggleDevTools(), - accelerator: "F12", - }); - menu.append(mainItem); - - const reloadItem = new MenuItem({ - label: "Restart", - click: () => win.reload(), - accelerator: "F5", - }); - menu.append(reloadItem); - - const fullscreenItem = new MenuItem({ - label: "Fullscreen", - click: () => win.setFullScreen(!win.isFullScreen()), - accelerator: "F11", - }); - menu.append(fullscreenItem); - - Menu.setApplicationMenu(menu); - } else { - Menu.setApplicationMenu(null); - } - - win.once("ready-to-show", () => { - win.show(); - win.focus(); - }); -} - -if (!app.requestSingleInstanceLock()) { - app.exit(0); -} else { - app.on("second-instance", (event, commandLine, workingDirectory) => { - // Someone tried to run a second instance, we should focus - if (win) { - if (win.isMinimized()) { - win.restore(); - } - win.focus(); - } - }); -} - -app.on("ready", createWindow); - -app.on("window-all-closed", () => { - console.log("All windows closed"); - app.quit(); -}); - -ipcMain.on("set-fullscreen", (event, flag) => { - win.setFullScreen(flag); -}); - -ipcMain.on("exit-app", (event, flag) => { - win.close(); - app.quit(); -}); - -let renameCounter = 1; - -const fileLock = new asyncLock({ - timeout: 30000, - maxPending: 1000, -}); - -function niceFileName(filename) { - return filename.replace(storePath, "@"); -} - -async function writeFileSafe(filename, contents) { - ++renameCounter; - const prefix = "[ " + renameCounter + ":" + niceFileName(filename) + " ] "; - const transactionId = String(new Date().getTime()) + "." + renameCounter; - - if (fileLock.isBusy()) { - console.warn(prefix, "Concurrent write process on", filename); - } - - fileLock.acquire(filename, async () => { - console.log(prefix, "Starting write on", niceFileName(filename), "in transaction", transactionId); - - if (!fs.existsSync(filename)) { - // this one is easy - console.log(prefix, "Writing file instantly because it does not exist:", niceFileName(filename)); - await fs.promises.writeFile(filename, contents, "utf8"); - return; - } - - // first, write a temporary file (.tmp-XXX) - const tempName = filename + ".tmp-" + transactionId; - console.log(prefix, "Writing temporary file", niceFileName(tempName)); - await fs.promises.writeFile(tempName, contents, "utf8"); - - // now, rename the original file to (.backup-XXX) - const oldTemporaryName = filename + ".backup-" + transactionId; - console.log( - prefix, - "Renaming old file", - niceFileName(filename), - "to", - niceFileName(oldTemporaryName) - ); - await fs.promises.rename(filename, oldTemporaryName); - - // now, rename the temporary file (.tmp-XXX) to the target - console.log( - prefix, - "Renaming the temporary file", - niceFileName(tempName), - "to the original", - niceFileName(filename) - ); - await fs.promises.rename(tempName, filename); - - // we are done now, try to create a backup, but don't fail if the backup fails - try { - // check if there is an old backup file - const backupFileName = filename + ".backup"; - if (fs.existsSync(backupFileName)) { - console.log(prefix, "Deleting old backup file", niceFileName(backupFileName)); - // delete the old backup - await fs.promises.unlink(backupFileName); - } - - // rename the old file to the new backup file - console.log(prefix, "Moving", niceFileName(oldTemporaryName), "to the backup file location"); - await fs.promises.rename(oldTemporaryName, backupFileName); - } catch (ex) { - console.error(prefix, "Failed to switch backup files:", ex); - } - }); -} - -ipcMain.handle("fs-job", async (event, job) => { - const filenameSafe = job.filename.replace(/[^a-z\.\-_0-9]/i, ""); - const fname = path.join(storePath, filenameSafe); - switch (job.type) { - case "read": { - if (!fs.existsSync(fname)) { - // Special FILE_NOT_FOUND error code - return { error: "file_not_found" }; - } - return await fs.promises.readFile(fname, "utf8"); - } - case "write": { - await writeFileSafe(fname, job.contents); - return job.contents; - } - - case "delete": { - await fs.promises.unlink(fname); - return; - } - - default: - throw new Error("Unknown fs job: " + job.type); - } -}); - -wegame.init(isDev); -wegame.listen(); diff --git a/electron_wegame/package.json b/electron_wegame/package.json deleted file mode 100644 index aba5bb6a..00000000 --- a/electron_wegame/package.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "name": "electron", - "version": "1.0.0", - "main": "index.js", - "license": "MIT", - "private": true, - "scripts": { - "startDev": "electron --disable-direct-composition --in-process-gpu . --dev --local", - "startDevGpu": "electron --enable-gpu-rasterization --enable-accelerated-2d-canvas --num-raster-threads=8 --enable-zero-copy . --dev --local", - "start": "electron --disable-direct-composition --in-process-gpu ." - }, - "devDependencies": { - "electron": "^13.1.6" - }, - "dependencies": { - "async-lock": "^1.2.8" - } -} diff --git a/electron_wegame/wegame.js b/electron_wegame/wegame.js deleted file mode 100644 index 05a0e186..00000000 --- a/electron_wegame/wegame.js +++ /dev/null @@ -1,63 +0,0 @@ -const railsdk = require("./wegame_sdk/railsdk.js"); -const { dialog, app, remote, ipcMain } = require("electron"); - -function init(isDev) { - console.log("Step 1: wegame: init"); - - try { - console.log("Step 2: Calling need restart app"); - const need_restart = railsdk.RailNeedRestartAppForCheckingEnvironment( - 2001639, - [`--rail_render_pid=${process.pid}`] //,"--rail_debug_mode", - ); - console.log("Step 3: Needs restart =", need_restart); - if (need_restart) { - console.error("Step 4: Need restart"); - dialog.showErrorBox("加载RailSDK失败", "请先运行WeGame开发者版本"); - return; - } - } catch (err) { - console.error("Rail SDK error:", err); - dialog.showErrorBox("加载RailSDK失败", err); - return; - } - - console.log("Step 5: starting rail sdk"); - if (railsdk.RailInitialize() === false) { - console.error("RailInitialize() = false"); - dialog.showErrorBox("RailInitialize调用失败", "请先运行WeGame开发者版本"); - return; - } - - console.log("Initialize RailSDK success!"); - - railsdk.RailRegisterEvent(railsdk.RailEventID.kRailEventSystemStateChanged, event => { - console.log(event); - if (event.result === railsdk.RailResult.kSuccess) { - if ( - event.state === railsdk.RailSystemState.kSystemStatePlatformOffline || - event.state === railsdk.RailSystemState.kSystemStatePlatformExit || - event.state === railsdk.RailSystemState.kSystemStateGameExitByAntiAddiction - ) { - app.exit(); - } - } - }); -} - -function listen() { - console.log("wegame: listen"); - ipcMain.handle("profanity-check", async (event, data) => { - if (data.length === 0) { - return ""; - } - const result = railsdk.RailUtils.DirtyWordsFilter(data, true); - if (result.check_result.dirty_type !== 0 /** kRailDirtyWordsTypeNormalAllowWords */) { - return result.check_result.replace_string; - } - - return data; - }); -} - -module.exports = { init, listen }; diff --git a/electron_wegame/yarn.lock b/electron_wegame/yarn.lock deleted file mode 100644 index 69c595ea..00000000 --- a/electron_wegame/yarn.lock +++ /dev/null @@ -1,578 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@electron/get@^1.0.1": - version "1.12.4" - resolved "https://registry.yarnpkg.com/@electron/get/-/get-1.12.4.tgz#a5971113fc1bf8fa12a8789dc20152a7359f06ab" - integrity sha512-6nr9DbJPUR9Xujw6zD3y+rS95TyItEVM0NVjt1EehY2vUWfIgPiIPVHxCvaTS0xr2B+DRxovYVKbuOWqC35kjg== - dependencies: - debug "^4.1.1" - env-paths "^2.2.0" - fs-extra "^8.1.0" - got "^9.6.0" - progress "^2.0.3" - semver "^6.2.0" - sumchecker "^3.0.1" - optionalDependencies: - global-agent "^2.0.2" - global-tunnel-ng "^2.7.1" - -"@sindresorhus/is@^0.14.0": - version "0.14.0" - resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" - integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== - -"@szmarczak/http-timer@^1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" - integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA== - dependencies: - defer-to-connect "^1.0.1" - -"@types/node@^14.6.2": - version "14.17.4" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.17.4.tgz#218712242446fc868d0e007af29a4408c7765bc0" - integrity sha512-8kQ3+wKGRNN0ghtEn7EGps/B8CzuBz1nXZEIGGLP2GnwbqYn4dbTs7k+VKLTq1HvZLRCIDtN3Snx1Ege8B7L5A== - -async-lock@^1.2.8: - version "1.2.8" - resolved "https://registry.yarnpkg.com/async-lock/-/async-lock-1.2.8.tgz#7b02bdfa2de603c0713acecd11184cf97bbc7c4c" - integrity sha512-G+26B2jc0Gw0EG/WN2M6IczuGepBsfR1+DtqLnyFSH4p2C668qkOCtEkGNVEaaNAVlYwEMazy1+/jnLxltBkIQ== - -boolean@^3.0.1: - version "3.1.2" - resolved "https://registry.yarnpkg.com/boolean/-/boolean-3.1.2.tgz#e30f210a26b02458482a8cc353ab06f262a780c2" - integrity sha512-YN6UmV0FfLlBVvRvNPx3pz5W/mUoYB24J4WSXOKP/OOJpi+Oq6WYqPaNTHzjI0QzwWtnvEd5CGYyQPgp1jFxnw== - -buffer-crc32@~0.2.3: - version "0.2.13" - resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" - integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= - -buffer-from@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" - integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== - -cacheable-request@^6.0.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" - integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg== - dependencies: - clone-response "^1.0.2" - get-stream "^5.1.0" - http-cache-semantics "^4.0.0" - keyv "^3.0.0" - lowercase-keys "^2.0.0" - normalize-url "^4.1.0" - responselike "^1.0.2" - -clone-response@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" - integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= - dependencies: - mimic-response "^1.0.0" - -concat-stream@^1.6.2: - version "1.6.2" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" - integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== - dependencies: - buffer-from "^1.0.0" - inherits "^2.0.3" - readable-stream "^2.2.2" - typedarray "^0.0.6" - -config-chain@^1.1.11: - version "1.1.13" - resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.13.tgz#fad0795aa6a6cdaff9ed1b68e9dff94372c232f4" - integrity sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ== - dependencies: - ini "^1.3.4" - proto-list "~1.2.1" - -core-js@^3.6.5: - version "3.15.2" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.15.2.tgz#740660d2ff55ef34ce664d7e2455119c5bdd3d61" - integrity sha512-tKs41J7NJVuaya8DxIOCnl8QuPHx5/ZVbFo1oKgVl1qHFBBrDctzQGtuLjPpRdNTWmKPH6oEvgN/MUID+l485Q== - -core-util-is@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= - -debug@^2.6.9: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - -debug@^4.1.0, debug@^4.1.1: - version "4.3.2" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" - integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== - dependencies: - ms "2.1.2" - -decompress-response@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" - integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= - dependencies: - mimic-response "^1.0.0" - -defer-to-connect@^1.0.1: - version "1.1.3" - resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" - integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== - -define-properties@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" - integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== - dependencies: - object-keys "^1.0.12" - -detect-node@^2.0.4: - version "2.1.0" - resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" - integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== - -duplexer3@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" - integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= - -electron@^13.1.6: - version "13.1.6" - resolved "https://registry.yarnpkg.com/electron/-/electron-13.1.6.tgz#6ecaf969255d62ce82cc0b5c948bf26e7dfb489b" - integrity sha512-XiB55/JTaQpDFQrD9pulYnOGwaWeMyRIub5ispvoE2bWBvM5zVMLptwMLb0m3KTMrfSkzhedZvOu7fwYvR7L7Q== - dependencies: - "@electron/get" "^1.0.1" - "@types/node" "^14.6.2" - extract-zip "^1.0.3" - -encodeurl@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" - integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= - -end-of-stream@^1.1.0: - version "1.4.4" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" - integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== - dependencies: - once "^1.4.0" - -env-paths@^2.2.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" - integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== - -es6-error@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" - integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== - -escape-string-regexp@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" - integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== - -extract-zip@^1.0.3: - version "1.7.0" - resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.7.0.tgz#556cc3ae9df7f452c493a0cfb51cc30277940927" - integrity sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA== - dependencies: - concat-stream "^1.6.2" - debug "^2.6.9" - mkdirp "^0.5.4" - yauzl "^2.10.0" - -fd-slicer@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" - integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4= - dependencies: - pend "~1.2.0" - -fs-extra@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" - integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== - dependencies: - graceful-fs "^4.2.0" - jsonfile "^4.0.0" - universalify "^0.1.0" - -get-stream@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" - integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== - dependencies: - pump "^3.0.0" - -get-stream@^5.1.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" - integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== - dependencies: - pump "^3.0.0" - -global-agent@^2.0.2: - version "2.2.0" - resolved "https://registry.yarnpkg.com/global-agent/-/global-agent-2.2.0.tgz#566331b0646e6bf79429a16877685c4a1fbf76dc" - integrity sha512-+20KpaW6DDLqhG7JDiJpD1JvNvb8ts+TNl7BPOYcURqCrXqnN1Vf+XVOrkKJAFPqfX+oEhsdzOj1hLWkBTdNJg== - dependencies: - boolean "^3.0.1" - core-js "^3.6.5" - es6-error "^4.1.1" - matcher "^3.0.0" - roarr "^2.15.3" - semver "^7.3.2" - serialize-error "^7.0.1" - -global-tunnel-ng@^2.7.1: - version "2.7.1" - resolved "https://registry.yarnpkg.com/global-tunnel-ng/-/global-tunnel-ng-2.7.1.tgz#d03b5102dfde3a69914f5ee7d86761ca35d57d8f" - integrity sha512-4s+DyciWBV0eK148wqXxcmVAbFVPqtc3sEtUE/GTQfuU80rySLcMhUmHKSHI7/LDj8q0gDYI1lIhRRB7ieRAqg== - dependencies: - encodeurl "^1.0.2" - lodash "^4.17.10" - npm-conf "^1.1.3" - tunnel "^0.0.6" - -globalthis@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.2.tgz#2a235d34f4d8036219f7e34929b5de9e18166b8b" - integrity sha512-ZQnSFO1la8P7auIOQECnm0sSuoMeaSq0EEdXMBFF2QJO4uNcwbyhSgG3MruWNbFTqCLmxVwGOl7LZ9kASvHdeQ== - dependencies: - define-properties "^1.1.3" - -got@^9.6.0: - version "9.6.0" - resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" - integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q== - dependencies: - "@sindresorhus/is" "^0.14.0" - "@szmarczak/http-timer" "^1.1.2" - cacheable-request "^6.0.0" - decompress-response "^3.3.0" - duplexer3 "^0.1.4" - get-stream "^4.1.0" - lowercase-keys "^1.0.1" - mimic-response "^1.0.1" - p-cancelable "^1.0.0" - to-readable-stream "^1.0.0" - url-parse-lax "^3.0.0" - -graceful-fs@^4.1.6, graceful-fs@^4.2.0: - version "4.2.6" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" - integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== - -http-cache-semantics@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" - integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== - -inherits@^2.0.3, inherits@~2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -ini@^1.3.4: - version "1.3.8" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" - integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== - -isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= - -json-buffer@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" - integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= - -json-stringify-safe@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" - integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= - -jsonfile@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" - integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= - optionalDependencies: - graceful-fs "^4.1.6" - -keyv@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" - integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA== - dependencies: - json-buffer "3.0.0" - -lodash@^4.17.10: - version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" - integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== - -lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" - integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== - -lowercase-keys@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" - integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== - -lru-cache@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" - integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== - dependencies: - yallist "^4.0.0" - -matcher@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/matcher/-/matcher-3.0.0.tgz#bd9060f4c5b70aa8041ccc6f80368760994f30ca" - integrity sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng== - dependencies: - escape-string-regexp "^4.0.0" - -mimic-response@^1.0.0, mimic-response@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" - integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== - -minimist@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" - integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== - -mkdirp@^0.5.4: - version "0.5.5" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" - integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== - dependencies: - minimist "^1.2.5" - -ms@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= - -ms@2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - -normalize-url@^4.1.0: - version "4.5.1" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a" - integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA== - -npm-conf@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/npm-conf/-/npm-conf-1.1.3.tgz#256cc47bd0e218c259c4e9550bf413bc2192aff9" - integrity sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw== - dependencies: - config-chain "^1.1.11" - pify "^3.0.0" - -object-keys@^1.0.12: - version "1.1.1" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" - integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== - -once@^1.3.1, once@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= - dependencies: - wrappy "1" - -p-cancelable@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" - integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== - -pend@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" - integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= - -pify@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" - integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= - -prepend-http@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" - integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= - -process-nextick-args@~2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" - integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== - -progress@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" - integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== - -proto-list@~1.2.1: - version "1.2.4" - resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" - integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk= - -pump@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" - integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - -readable-stream@^2.2.2: - version "2.3.7" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" - integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.1.1" - util-deprecate "~1.0.1" - -responselike@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" - integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= - dependencies: - lowercase-keys "^1.0.0" - -roarr@^2.15.3: - version "2.15.4" - resolved "https://registry.yarnpkg.com/roarr/-/roarr-2.15.4.tgz#f5fe795b7b838ccfe35dc608e0282b9eba2e7afd" - integrity sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A== - dependencies: - boolean "^3.0.1" - detect-node "^2.0.4" - globalthis "^1.0.1" - json-stringify-safe "^5.0.1" - semver-compare "^1.0.0" - sprintf-js "^1.1.2" - -safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - -semver-compare@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" - integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w= - -semver@^6.2.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== - -semver@^7.3.2: - version "7.3.5" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" - integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== - dependencies: - lru-cache "^6.0.0" - -serialize-error@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-7.0.1.tgz#f1360b0447f61ffb483ec4157c737fab7d778e18" - integrity sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw== - dependencies: - type-fest "^0.13.1" - -sprintf-js@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.2.tgz#da1765262bf8c0f571749f2ad6c26300207ae673" - integrity sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug== - -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - -sumchecker@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/sumchecker/-/sumchecker-3.0.1.tgz#6377e996795abb0b6d348e9b3e1dfb24345a8e42" - integrity sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg== - dependencies: - debug "^4.1.0" - -to-readable-stream@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771" - integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q== - -tunnel@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c" - integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg== - -type-fest@^0.13.1: - version "0.13.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.13.1.tgz#0172cb5bce80b0bd542ea348db50c7e21834d934" - integrity sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg== - -typedarray@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" - integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= - -universalify@^0.1.0: - version "0.1.2" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" - integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== - -url-parse-lax@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" - integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww= - dependencies: - prepend-http "^2.0.0" - -util-deprecate@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= - -yallist@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" - integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== - -yauzl@^2.10.0: - version "2.10.0" - resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" - integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk= - dependencies: - buffer-crc32 "~0.2.3" - fd-slicer "~1.1.0" diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 00000000..38c002cf --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,61 @@ +import eslint from "@eslint/js"; +import globals from "globals"; +import tseslint from "typescript-eslint"; + +const baseConfig = tseslint.config( + eslint.configs.recommended, + ...tseslint.configs.recommendedTypeChecked, + // Enable type-aware linting + { + languageOptions: { + parserOptions: { + project: true, + tsconfigRootDir: import.meta.dirname, + }, + }, + }, + // Disable type-aware linting for JS files as it causes issues + { + files: ["*.js"], + ...tseslint.configs.disableTypeChecked, + } +); + +const nodeConfig = tseslint.config(...baseConfig, { + languageOptions: { + sourceType: "module", + globals: { + ...globals.node, + }, + }, +}); + +const runtimeConfig = tseslint.config(...baseConfig, { + languageOptions: { + sourceType: "module", + globals: { + ...globals.browser, + }, + }, + rules: { + // Mostly caused by JSDoc imports, disable for now + "@typescript-eslint/no-unused-vars": "off", + // FIXME: enforce when we're ready to + "prefer-const": "warn", + }, +}); + +// I don't know what the ESLint devs were thinking about. This is just horrible +export default [ + { + ignores: ["build/*"], + }, + ...nodeConfig.map(config => ({ + ...config, + files: ["*.{ts,js}", "{gulp,electron}/**/*.{ts,js}"], + })), + ...runtimeConfig.map(config => ({ + ...config, + files: ["src/**/*.{ts,js,tsx,jsx}"], + })), +]; diff --git a/gulp/.babelrc b/gulp/.babelrc deleted file mode 100644 index a0765e18..00000000 --- a/gulp/.babelrc +++ /dev/null @@ -1,3 +0,0 @@ -{ - "presets": ["es2015"] -} diff --git a/gulp/.gitignore b/gulp/.gitignore deleted file mode 100644 index 80dc3c89..00000000 --- a/gulp/.gitignore +++ /dev/null @@ -1 +0,0 @@ -additional_build_files diff --git a/gulp/atlas2json.js b/gulp/atlas2json.js index b77a47f3..bd3fc72e 100644 --- a/gulp/atlas2json.js +++ b/gulp/atlas2json.js @@ -1,13 +1,13 @@ -const { join, resolve } = require("path"); -const { readFileSync, readdirSync, writeFileSync } = require("fs"); +import { join, resolve } from "path/posix"; +import { readFileSync, readdirSync, writeFileSync } from "fs"; const suffixToScale = { lq: "0.25", mq: "0.5", - hq: "0.75" + hq: "0.75", }; -function convert(srcDir) { +export default function convert(srcDir) { const full = resolve(srcDir); const srcFiles = readdirSync(full) .filter(n => n.endsWith(".atlas")) @@ -65,7 +65,7 @@ function convert(srcDir) { x: xy[0], y: xy[1], w: size[0], - h: size[1] + h: size[1], }, // Whether image was rotated @@ -75,21 +75,21 @@ function convert(srcDir) { // How is the image trimmed spriteSourceSize: { x: offset[0], - y: (orig[1] - size[1]) - offset[1], + y: orig[1] - size[1] - offset[1], w: size[0], - h: size[1] + h: size[1], }, - + sourceSize: { w: orig[0], - h: orig[1] - } - } + h: orig[1], + }, + }; } // Simple object that will hold other metadata current = { - name: line + name: line, }; } else { // Read and set current image metadata @@ -108,20 +108,14 @@ function convert(srcDir) { format: srcMeta.format, size: { w: atlasSize[0], - h: atlasSize[1] + h: atlasSize[1], }, - scale: atlasScale.toString() - } + scale: atlasScale.toString(), + }, }); writeFileSync(atlas.replace(".atlas", ".json"), result, { - encoding: "utf-8" + encoding: "utf-8", }); } } - -if (require.main == module) { - convert(process.argv[2]); -} - -module.exports = { convert }; diff --git a/gulp/babel-es6.config.js b/gulp/babel-es6.config.js deleted file mode 100644 index b3f088f9..00000000 --- a/gulp/babel-es6.config.js +++ /dev/null @@ -1,47 +0,0 @@ -module.exports = function (api) { - api.cache(true); - const presets = [ - [ - "@babel/preset-env", - { - targets: "> 5%", - useBuiltIns: "usage", - corejs: 3, - loose: true, - spec: false, - modules: "auto", - }, - ], - ]; - const plugins = [ - "closure-elimination", - // var is faster than let and const! - [ - "@babel/plugin-transform-block-scoping", - { - throwIfClosureRequired: false, - }, - ], - [ - "@babel/plugin-transform-classes", - { - loose: true, - }, - ], - ]; - return { - presets, - plugins, - highlightCode: true, - sourceType: "module", - sourceMaps: false, - parserOpts: {}, - only: ["../src/js"], - generatorOpts: { - retainLines: false, - compact: true, - minified: true, - comments: true, - }, - }; -}; diff --git a/gulp/babel.config.js b/gulp/babel.config.js deleted file mode 100644 index 62839421..00000000 --- a/gulp/babel.config.js +++ /dev/null @@ -1,55 +0,0 @@ -module.exports = function (api) { - api.cache(true); - const presets = [ - [ - "@babel/preset-env", - { - // targets: ">0.01%", - targets: { - edge: 10, - firefox: 37, - chrome: 24, - safari: 10, - ie: 10, - }, - useBuiltIns: "usage", - corejs: 3, - loose: true, - spec: false, - modules: "auto", - }, - ], - ]; - const plugins = [ - "@babel/plugin-transform-arrow-functions", - "closure-elimination", - // var is faster than let and const! - // [ - // "@babel/plugin-transform-block-scoping", - // { - // throwIfClosureRequired: true, - // }, - // ], - [ - "@babel/plugin-transform-classes", - { - loose: true, - }, - ], - ]; - return { - presets, - plugins, - highlightCode: true, - sourceType: "unambiguous", - sourceMaps: false, - parserOpts: {}, - exclude: /(core-js|babel-core|babel-runtime)/, - generatorOpts: { - retainLines: false, - compact: true, - minified: true, - comments: true, - }, - }; -}; diff --git a/gulp/build_variants.js b/gulp/build_variants.js index 4f67bf9e..aecb3ba7 100644 --- a/gulp/build_variants.js +++ b/gulp/build_variants.js @@ -1,74 +1,10 @@ /** * @type {Record} + * standalone: boolean + * }>} */ -const BUILD_VARIANTS = { - "web-localhost": { - standalone: false, - environment: "dev", - buildArgs: {}, - }, - "web-shapezio-beta": { - standalone: false, - environment: "staging", - buildArgs: {}, - }, - "web-shapezio": { - standalone: false, - environment: "prod", - buildArgs: {}, - }, - "standalone-steam": { +export const BUILD_VARIANTS = { + standalone: { standalone: true, - executableName: "shapez", - steamAppId: 1318690, - buildArgs: {}, - }, - "standalone-steam-china": { - standalone: true, - steamAppId: 1318690, - buildArgs: { - chineseVersion: true, - }, - }, - "standalone-steam-demo": { - standalone: true, - steamAppId: 1930750, - buildArgs: { - steamDemo: true, - }, - }, - "standalone-steam-china-demo": { - standalone: true, - steamAppId: 1930750, - buildArgs: { - steamDemo: true, - chineseVersion: true, - }, - }, - "standalone-wegame": { - standalone: true, - electronBaseDir: "electron_wegame", - buildArgs: { - wegameVersion: true, - }, - }, - "standalone-gog": { - standalone: true, - electronBaseDir: "electron_gog", - buildArgs: { - gogVersion: true, - }, }, }; -module.exports = { BUILD_VARIANTS }; diff --git a/gulp/buildutils.js b/gulp/buildutils.js index 8b5e389b..f2900df1 100644 --- a/gulp/buildutils.js +++ b/gulp/buildutils.js @@ -1,47 +1,22 @@ -const glob = require("glob"); -const execSync = require("child_process").execSync; -const trim = require("trim"); -const fs = require("fs"); -const path = require("path"); +import { execSync } from "child_process"; +import fs from "fs"; -module.exports = { - getRevision: function (useLast = false) { - const commitHash = execSync("git rev-parse --short " + (useLast ? "HEAD^1" : "HEAD")).toString( - "ascii" - ); - return commitHash.replace(/^\s+|\s+$/g, ""); - }, +export function getRevision(useLast = false) { + const commitHash = execSync("git rev-parse --short " + (useLast ? "HEAD^1" : "HEAD")).toString("ascii"); + return commitHash.trim(); +} - getAllResourceImages() { - return glob - .sync("res/**/*.@(png|svg|jpg)", { cwd: ".." }) - .map(f => f.replace(/^res\//gi, "")) - .filter(f => { - if (f.indexOf("ui") >= 0) { - // We drop all ui images except for the noinline ones - return f.indexOf("noinline") >= 0; - } - return true; - }); - }, - - getTag() { - try { - return execSync("git describe --tag --exact-match").toString("ascii"); - } catch (e) { - throw new Error("Current git HEAD is not a version tag"); +export function getAllResourceImages() { + return fs.globSync("./**/*.@(png|svg|jpg)", { cwd: "../res" }).filter(f => { + if (f.indexOf("ui") >= 0) { + // We drop all ui images except for the noinline ones + return f.indexOf("noinline") >= 0; } - }, + return true; + }); +} - getVersion() { - return trim(fs.readFileSync(path.join(__dirname, "..", "version")).toString()); - }, - - /** - * @param {string} url - * @param {string} commitHash - */ - cachebust(url, commitHash) { - return "/v/" + commitHash + "/" + url; - }, -}; +export function getVersion() { + // Use the version number specified in package.json + return JSON.parse(fs.readFileSync("../package.json", "utf-8")).version; +} diff --git a/gulp/config.js b/gulp/config.js new file mode 100644 index 00000000..5555de69 --- /dev/null +++ b/gulp/config.js @@ -0,0 +1,23 @@ +import BrowserSync from "browser-sync"; +import path from "path/posix"; + +export const baseDir = path.resolve(".."); +export const buildFolder = path.join(baseDir, "build"); +export const buildOutputFolder = path.join(baseDir, "build_output"); +export const generatedCodeFolder = path.join(baseDir, "src/js/built-temp"); + +// Globs for atlas resources +export const rawImageResourcesGlobs = ["../res_raw/atlas.json", "../res_raw/**/*.png"]; + +// Globs for non-ui resources +export const nonImageResourcesGlobs = ["../res/**/*.woff2", "../res/*.ico", "../res/**/*.webm"]; + +// Globs for ui resources +export const imageResourcesGlobs = [ + "../res/**/*.png", + "../res/**/*.svg", + "../res/**/*.jpg", + "../res/**/*.gif", +]; + +export const browserSync = BrowserSync.create(); diff --git a/gulp/css.js b/gulp/css.js index 46e44247..3b28ff50 100644 --- a/gulp/css.js +++ b/gulp/css.js @@ -1,136 +1,97 @@ -const path = require("path"); -const buildUtils = require("./buildutils"); - -function gulptasksCSS($, gulp, buildFolder, browserSync) { - // The assets plugin copies the files - const commitHash = buildUtils.getRevision(); - const postcssAssetsPlugin = cachebust => - $.postcssAssets({ - loadPaths: [path.join(buildFolder, "res", "ui")], - basePath: buildFolder, - baseUrl: ".", - cachebuster: cachebust - ? (filePath, urlPathname) => ({ - pathname: buildUtils.cachebust(urlPathname, commitHash), - }) - : "", - }); - - // Postcss configuration - const postcssPlugins = (prod, { cachebust = false }) => { - const plugins = [postcssAssetsPlugin(cachebust)]; - if (prod) { - plugins.unshift( - $.postcssPresetEnv({ - browsers: ["> 0.1%"], - }) - ); - - plugins.push( - $.cssMqpacker({ - sort: true, - }), - $.cssnano({ - preset: [ - "advanced", - { - cssDeclarationSorter: false, - discardUnused: true, - mergeIdents: false, - reduceIdents: true, - zindex: true, - }, - ], - }), - $.postcssRoundSubpixels() - ); - } - return plugins; - }; - - // Performs linting on css - gulp.task("css.lint", () => { - return gulp - .src(["../src/css/**/*.scss"]) - .pipe($.sassLint({ configFile: ".sasslint.yml" })) - .pipe($.sassLint.format()) - .pipe($.sassLint.failOnError()); - }); - - function resourcesTask({ cachebust, isProd }) { - return gulp - .src("../src/css/main.scss", { cwd: __dirname }) - .pipe($.plumber()) - .pipe($.dartSass.sync().on("error", $.dartSass.logError)) - .pipe( - $.postcss([ - $.postcssCriticalSplit({ - blockTag: "@load-async", - }), - ]) - ) - .pipe($.rename("async-resources.css")) - .pipe($.postcss(postcssPlugins(isProd, { cachebust }))) - .pipe(gulp.dest(buildFolder)) - .pipe(browserSync.stream()); - } - - // Builds the css resources - gulp.task("css.resources.dev", () => { - return resourcesTask({ cachebust: false, isProd: false }); - }); - - // Builds the css resources in prod (=minified) - gulp.task("css.resources.prod", () => { - return resourcesTask({ cachebust: true, isProd: true }); - }); - - // Builds the css resources in prod (=minified), without cachebusting - gulp.task("css.resources.prod-standalone", () => { - return resourcesTask({ cachebust: false, isProd: true }); - }); - - function mainTask({ cachebust, isProd }) { - return gulp - .src("../src/css/main.scss", { cwd: __dirname }) - .pipe($.plumber()) - .pipe($.dartSass.sync().on("error", $.dartSass.logError)) - .pipe( - $.postcss([ - $.postcssCriticalSplit({ - blockTag: "@load-async", - output: "rest", - }), - ]) - ) - .pipe($.postcss(postcssPlugins(isProd, { cachebust }))) - .pipe(gulp.dest(buildFolder)) - .pipe(browserSync.stream()); - } - - // Builds the css main - gulp.task("css.main.dev", () => { - return mainTask({ cachebust: false, isProd: false }); - }); - - // Builds the css main in prod (=minified) - gulp.task("css.main.prod", () => { - return mainTask({ cachebust: true, isProd: true }); - }); - - // Builds the css main in prod (=minified), without cachebusting - gulp.task("css.main.prod-standalone", () => { - return mainTask({ cachebust: false, isProd: true }); - }); - - gulp.task("css.dev", gulp.parallel("css.main.dev", "css.resources.dev")); - gulp.task("css.prod", gulp.parallel("css.main.prod", "css.resources.prod")); - gulp.task( - "css.prod-standalone", - gulp.parallel("css.main.prod-standalone", "css.resources.prod-standalone") - ); -} - -module.exports = { - gulptasksCSS, -}; +import gulp from "gulp"; +import path from "path/posix"; +import { buildFolder } from "./config.js"; + +import cssMqpacker from "css-mqpacker"; +import cssnano from "cssnano"; +import gulpDartSass from "gulp-dart-sass"; +import gulpPlumber from "gulp-plumber"; +import gulpPostcss from "gulp-postcss"; +import gulpRename from "gulp-rename"; +import postcssAssets from "postcss-assets"; +import postcssCriticalSplit from "postcss-critical-split"; + +// The assets plugin copies the files +const postcssAssetsPlugin = postcssAssets({ + loadPaths: [path.join(buildFolder, "res", "ui")], + basePath: buildFolder, + baseUrl: ".", +}); + +// Postcss configuration +const postcssPlugins = prod => { + const plugins = [postcssAssetsPlugin]; + if (prod) { + plugins.push( + cssMqpacker({ + sort: true, + }), + cssnano({ + preset: [ + "advanced", + { + cssDeclarationSorter: false, + discardUnused: true, + mergeIdents: false, + reduceIdents: true, + zindex: true, + }, + ], + }) + ); + } + return plugins; +}; + +function resourcesTask({ isProd }) { + return gulp + .src("../src/css/main.scss") + .pipe(gulpPlumber()) + .pipe(gulpDartSass.sync().on("error", gulpDartSass.logError)) + .pipe( + gulpPostcss([ + postcssCriticalSplit({ + blockTag: "@load-async", + }), + ]) + ) + .pipe(gulpRename("async-resources.css")) + .pipe(gulpPostcss(postcssPlugins(isProd))) + .pipe(gulp.dest(buildFolder)); +} + +export const resources = { + // Builds the css resources + dev: () => resourcesTask({ isProd: false }), + + // Builds the css resources in prod (=minified) + prod: () => resourcesTask({ isProd: true }), +}; + +function mainTask({ isProd }) { + return gulp + .src("../src/css/main.scss") + .pipe(gulpPlumber()) + .pipe(gulpDartSass.sync().on("error", gulpDartSass.logError)) + .pipe( + gulpPostcss([ + postcssCriticalSplit({ + blockTag: "@load-async", + output: "rest", + }), + ]) + ) + .pipe(gulpPostcss(postcssPlugins(isProd))) + .pipe(gulp.dest(buildFolder)); +} + +export const main = { + // Builds the css main + dev: () => mainTask({ isProd: false }), + + // Builds the css main in prod (=minified) + prod: () => mainTask({ isProd: true }), +}; + +export const dev = gulp.parallel(main.dev, resources.dev); +export const prod = gulp.parallel(main.prod, resources.prod); diff --git a/gulp/docs.js b/gulp/docs.js deleted file mode 100644 index 399e0fa1..00000000 --- a/gulp/docs.js +++ /dev/null @@ -1,39 +0,0 @@ -const path = require("path"); -const fs = require("fs"); - -function gulptasksDocs($, gulp, buildFolder) { - gulp.task("docs.convertJsToTs", () => { - return gulp - .src(path.join("..", "src", "js", "**", "*.js")) - .pipe( - $.rename(path => { - path.extname = ".ts"; - }) - ) - .pipe(gulp.dest(path.join("..", "tsc_temp"))); - }); - - gulp.task("docs.copyTsconfigForHints", cb => { - const src = fs.readFileSync(path.join("..", "src", "js", "tsconfig.json")).toString(); - const baseConfig = JSON.parse($.stripJsonComments(src)); - - baseConfig.allowJs = false; - baseConfig.checkJs = false; - baseConfig.declaration = true; - baseConfig.noEmit = false; - baseConfig.strict = false; - baseConfig.strictFunctionTypes = false; - baseConfig.strictBindCallApply = false; - baseConfig.alwaysStrict = false; - baseConfig.composite = true; - baseConfig.outFile = "bundled-ts.js"; - fs.writeFileSync(path.join("..", "tsc_temp", "tsconfig.json"), JSON.stringify(baseConfig)); - cb(); - }); - - gulp.task("main.prepareDocs", gulp.series("docs.convertJsToTs", "docs.copyTsconfigForHints")); -} - -module.exports = { - gulptasksDocs, -}; diff --git a/gulp/entitlements.plist b/gulp/entitlements.plist deleted file mode 100644 index 51e30452..00000000 --- a/gulp/entitlements.plist +++ /dev/null @@ -1,18 +0,0 @@ - - - - - com.apple.security.cs.allow-jit - - com.apple.security.cs.allow-unsigned-executable-memory - - com.apple.security.cs.debugger - - com.apple.security.cs.disable-library-validation - - com.apple.security.cs.allow-dyld-environment-variables - - com.apple.security.cs.disable-executable-page-protection - - - \ No newline at end of file diff --git a/gulp/environment.js b/gulp/environment.js new file mode 100644 index 00000000..f5ab22b0 --- /dev/null +++ b/gulp/environment.js @@ -0,0 +1,49 @@ +import { exec } from "child_process"; +import fs from "fs/promises"; +import gulp from "gulp"; +import { promisify } from "util"; + +const texturePackerUrl = + "https://libgdx-nightlies.s3.amazonaws.com/libgdx-runnables/runnable-texturepacker.jar"; + +const configTemplatePath = "../src/js/core/config.local.template.js"; +const configPath = "../src/js/core/config.local.js"; + +export async function checkJava() { + try { + const { stderr } = await promisify(exec)("java -version"); + console.log(`Found Java:`, stderr); + } catch { + throw new Error("Java is required to build the texture atlas, but was not found"); + } +} + +export async function downloadTexturePacker() { + const destination = "./runnable-texturepacker.jar"; + + try { + // If the file exists already, we're done + await fs.access(destination); + return; + } catch { + // File does not exist, need to download + } + + console.log(`Downloading ${destination}...`); + const response = await fetch(texturePackerUrl); + if (!response.ok) { + throw new Error(`Failed to download Texture Packer: ${response.statusText}`); + } + + await fs.writeFile(destination, response.body); +} + +export async function createLocalConfig() { + try { + await fs.copyFile(configTemplatePath, configPath, fs.constants.COPYFILE_EXCL); + } catch { + // The file is already there + } +} + +export const prepare = gulp.parallel(gulp.series(checkJava, downloadTexturePacker), createLocalConfig); diff --git a/gulp/ftp.js b/gulp/ftp.js deleted file mode 100644 index d41bccb6..00000000 --- a/gulp/ftp.js +++ /dev/null @@ -1,101 +0,0 @@ -const path = require("path"); -const fs = require("fs"); - -const buildUtils = require("./buildutils"); - -function gulptasksFTP($, gulp, buildFolder) { - const commitHash = buildUtils.getRevision(); - - const additionalFolder = path.join(__dirname, "additional_build_files"); - - const additionalFiles = [ - path.join(additionalFolder, "*"), - path.join(additionalFolder, "*.*"), - path.join(additionalFolder, ".*"), - ]; - - const credentials = { - alpha: { - host: process.env.SHAPEZ_CLI_SERVER_HOST, - user: process.env.SHAPEZ_CLI_ALPHA_FTP_USER, - pass: process.env.SHAPEZ_CLI_ALPHA_FTP_PW, - }, - staging: { - host: process.env.SHAPEZ_CLI_SERVER_HOST, - user: process.env.SHAPEZ_CLI_STAGING_FTP_USER, - pass: process.env.SHAPEZ_CLI_STAGING_FTP_PW, - }, - prod: { - host: process.env.SHAPEZ_CLI_SERVER_HOST, - user: process.env.SHAPEZ_CLI_LIVE_FTP_USER, - pass: process.env.SHAPEZ_CLI_LIVE_FTP_PW, - }, - }; - - // Write the "commit.txt" file - gulp.task("ftp.writeVersion", cb => { - fs.writeFileSync( - path.join(buildFolder, "version.json"), - JSON.stringify( - { - commit: buildUtils.getRevision(), - appVersion: buildUtils.getVersion(), - buildTime: new Date().getTime(), - }, - null, - 4 - ) - ); - cb(); - }); - - const gameSrcGlobs = [ - path.join(buildFolder, "**/*.*"), - path.join(buildFolder, "**/.*"), - path.join(buildFolder, "**/*"), - path.join(buildFolder, "!**/index.html"), - ]; - - for (const deployEnv of ["alpha", "prod", "staging"]) { - const deployCredentials = credentials[deployEnv]; - - gulp.task(`ftp.upload.${deployEnv}.game`, () => { - return gulp - .src(gameSrcGlobs, { base: buildFolder }) - .pipe( - $.rename(pth => { - pth.dirname = path.join("v", commitHash, pth.dirname); - }) - ) - .pipe($.sftp(deployCredentials)); - }); - - gulp.task(`ftp.upload.${deployEnv}.indexHtml`, () => { - return gulp - .src([path.join(buildFolder, "index.html"), path.join(buildFolder, "version.json")], { - base: buildFolder, - }) - .pipe($.sftp(deployCredentials)); - }); - - gulp.task(`ftp.upload.${deployEnv}.additionalFiles`, () => { - return gulp - .src(additionalFiles, { base: additionalFolder }) // - .pipe($.sftp(deployCredentials)); - }); - - gulp.task( - `ftp.upload.${deployEnv}`, - gulp.series( - "ftp.writeVersion", - `ftp.upload.${deployEnv}.game`, - `ftp.upload.${deployEnv}.indexHtml`, - `ftp.upload.${deployEnv}.additionalFiles` - ) - ); - } -} - -module.exports = { - gulptasksFTP, -}; diff --git a/gulp/gulpfile.js b/gulp/gulpfile.js index 04d3c9c9..27b1107c 100644 --- a/gulp/gulpfile.js +++ b/gulp/gulpfile.js @@ -1,317 +1,23 @@ -/* eslint-disable */ - -require("colors"); - -const gulp = require("gulp"); -const browserSync = require("browser-sync").create({}); -const path = require("path"); -const deleteEmpty = require("delete-empty"); -const execSync = require("child_process").execSync; - -// Load other plugins dynamically -const $ = require("gulp-load-plugins")({ - scope: ["devDependencies"], - pattern: "*", -}); - -// Check environment variables - -const envVars = [ - "SHAPEZ_CLI_SERVER_HOST", - "SHAPEZ_CLI_ALPHA_FTP_USER", - "SHAPEZ_CLI_ALPHA_FTP_PW", - "SHAPEZ_CLI_STAGING_FTP_USER", - "SHAPEZ_CLI_STAGING_FTP_PW", - "SHAPEZ_CLI_LIVE_FTP_USER", - "SHAPEZ_CLI_LIVE_FTP_PW", - "SHAPEZ_CLI_APPLE_ID", - "SHAPEZ_CLI_APPLE_CERT_NAME", - "SHAPEZ_CLI_GITHUB_USER", - "SHAPEZ_CLI_GITHUB_TOKEN", -]; - -for (let i = 0; i < envVars.length; ++i) { - if (!process.env[envVars[i]]) { - console.warn("Unset environment variable, might cause issues:", envVars[i]); - } -} - -const baseDir = path.join(__dirname, ".."); -const buildFolder = path.join(baseDir, "build"); -const buildOuptutFolder = path.join(baseDir, "build_output"); - -const imgres = require("./image-resources"); -imgres.gulptasksImageResources($, gulp, buildFolder); - -const css = require("./css"); -css.gulptasksCSS($, gulp, buildFolder, browserSync); - -const sounds = require("./sounds"); -sounds.gulptasksSounds($, gulp, buildFolder); - -const localConfig = require("./local-config"); -localConfig.gulptasksLocalConfig($, gulp); - -const js = require("./js"); -js.gulptasksJS($, gulp, buildFolder, browserSync); - -const html = require("./html"); -html.gulptasksHTML($, gulp, buildFolder); - -const ftp = require("./ftp"); -ftp.gulptasksFTP($, gulp, buildFolder); - -const docs = require("./docs"); -docs.gulptasksDocs($, gulp, buildFolder); - -const standalone = require("./standalone"); -standalone.gulptasksStandalone($, gulp); - -const translations = require("./translations"); -const { BUILD_VARIANTS } = require("./build_variants"); -translations.gulptasksTranslations($, gulp); - -///////////////////// BUILD TASKS ///////////////////// - -// Cleans up everything -gulp.task("utils.cleanBuildFolder", () => { - return gulp.src(buildFolder, { read: false, allowEmpty: true }).pipe($.clean({ force: true })); -}); -gulp.task("utils.cleanBuildOutputFolder", () => { - return gulp.src(buildOuptutFolder, { read: false, allowEmpty: true }).pipe($.clean({ force: true })); -}); -gulp.task("utils.cleanBuildTempFolder", () => { - return gulp - .src(path.join(__dirname, "..", "src", "js", "built-temp"), { read: false, allowEmpty: true }) - .pipe($.clean({ force: true })); -}); -gulp.task("utils.cleanImageBuildFolder", () => { - return gulp - .src(path.join(__dirname, "res_built"), { read: false, allowEmpty: true }) - .pipe($.clean({ force: true })); -}); - -gulp.task( - "utils.cleanup", - gulp.series("utils.cleanBuildFolder", "utils.cleanImageBuildFolder", "utils.cleanBuildTempFolder") -); - -// Requires no uncomitted files -gulp.task("utils.requireCleanWorkingTree", cb => { - let output = $.trim(execSync("git status -su").toString("ascii")).replace(/\r/gi, "").split("\n"); - - // Filter files which are OK to be untracked - output = output - .map(x => x.replace(/[\r\n]+/gi, "")) - .filter(x => x.indexOf(".local.js") < 0) - .filter(x => x.length > 0); - if (output.length > 0) { - console.error("\n\nYou have unstaged changes, please commit everything first!"); - console.error("Unstaged files:"); - console.error(output.map(x => "'" + x + "'").join("\n")); - process.exit(1); - } - cb(); -}); - -gulp.task("utils.copyAdditionalBuildFiles", cb => { - const additionalFolder = path.join("additional_build_files"); - const additionalSrcGlobs = [ - path.join(additionalFolder, "**/*.*"), - path.join(additionalFolder, "**/.*"), - path.join(additionalFolder, "**/*"), - ]; - - return gulp.src(additionalSrcGlobs).pipe(gulp.dest(buildFolder)); -}); - -// Starts a webserver on the built directory (useful for testing prod build) -gulp.task("main.webserver", () => { - return gulp.src(buildFolder).pipe( - $.webserver({ - livereload: { - enable: true, - }, - directoryListing: false, - open: true, - port: 3005, - }) - ); -}); - -/** - * - * @param {object} param0 - * @param {keyof typeof BUILD_VARIANTS} param0.version - */ -function serveHTML({ version = "web-dev" }) { - browserSync.init({ - server: [buildFolder, path.join(baseDir, "mod_examples")], - port: 3005, - ghostMode: { - clicks: false, - scroll: false, - location: false, - forms: false, - }, - logLevel: "info", - logPrefix: "BS", - online: false, - xip: false, - notify: false, - reloadDebounce: 100, - reloadOnRestart: true, - watchEvents: ["add", "change"], - }); - - // Watch .scss files, those trigger a css rebuild - gulp.watch(["../src/**/*.scss"], gulp.series("css.dev")); - - // Watch .html files, those trigger a html rebuild - gulp.watch("../src/**/*.html", gulp.series("html." + version + ".dev")); - gulp.watch("./preloader/*.*", gulp.series("html." + version + ".dev")); - - // Watch translations - gulp.watch("../translations/**/*.yaml", gulp.series("translations.convertToJson")); - - gulp.watch( - ["../res_raw/sounds/sfx/*.mp3", "../res_raw/sounds/sfx/*.wav"], - gulp.series("sounds.sfx", "sounds.copy") - ); - gulp.watch( - ["../res_raw/sounds/music/*.mp3", "../res_raw/sounds/music/*.wav"], - gulp.series("sounds.music", "sounds.copy") - ); - - // Watch resource files and copy them on change - gulp.watch(imgres.rawImageResourcesGlobs, gulp.series("imgres.buildAtlas")); - gulp.watch(imgres.nonImageResourcesGlobs, gulp.series("imgres.copyNonImageResources")); - gulp.watch(imgres.imageResourcesGlobs, gulp.series("imgres.copyImageResources")); - - // Watch .atlas files and recompile the atlas on change - gulp.watch("../res_built/atlas/*.atlas", gulp.series("imgres.atlasToJson")); - gulp.watch("../res_built/atlas/*.json", gulp.series("imgres.atlas")); - - // Watch the build folder and reload when anything changed - const extensions = ["html", "js", "png", "gif", "jpg", "svg", "mp3", "ico", "woff2", "json"]; - gulp.watch(extensions.map(ext => path.join(buildFolder, "**", "*." + ext))).on("change", function (path) { - return gulp.src(path).pipe(browserSync.reload({ stream: true })); - }); - - gulp.watch("../src/js/built-temp/*.json").on("change", function (path) { - return gulp.src(path).pipe(browserSync.reload({ stream: true })); - }); - - gulp.series("js." + version + ".dev.watch")(() => true); -} - -// Pre and postbuild -gulp.task("step.baseResources", gulp.series("imgres.allOptimized")); -gulp.task("step.deleteEmpty", cb => { - deleteEmpty.sync(buildFolder); - cb(); -}); - -gulp.task("step.postbuild", gulp.series("imgres.cleanupUnusedCssInlineImages", "step.deleteEmpty")); - -///////////////////// RUNNABLE TASKS ///////////////////// - -// Builds everything (dev) -gulp.task( - "build.prepare.dev", - gulp.series( - "utils.cleanup", - "utils.copyAdditionalBuildFiles", - "localConfig.findOrCreate", - "imgres.buildAtlas", - "imgres.atlasToJson", - "imgres.atlas", - "sounds.dev", - "imgres.copyImageResources", - "imgres.copyNonImageResources", - "translations.fullBuild", - "css.dev" - ) -); - -// Builds everything for every variant -for (const variant in BUILD_VARIANTS) { - const data = BUILD_VARIANTS[variant]; - const buildName = "build." + variant; - - // build - gulp.task( - buildName + ".code", - gulp.series( - data.standalone ? "sounds.fullbuildHQ" : "sounds.fullbuild", - "translations.fullBuild", - "js." + variant + ".prod" - ) - ); - - gulp.task(buildName + ".resourcesAndCode", gulp.parallel("step.baseResources", buildName + ".code")); - - gulp.task( - buildName + ".all", - gulp.series(buildName + ".resourcesAndCode", "css.prod-standalone", "html." + variant + ".prod") - ); - - gulp.task(buildName, gulp.series("utils.cleanup", buildName + ".all", "step.postbuild")); - - // bundle - if (data.standalone) { - gulp.task( - "bundle." + variant + ".from-windows", - gulp.series(buildName, "standalone." + variant + ".build-from-windows") - ); - gulp.task( - "bundle." + variant + ".from-darwin", - gulp.series(buildName, "standalone." + variant + ".build-from-darwin") - ); - } - - // serve - gulp.task( - "serve." + variant, - gulp.series("build.prepare.dev", "html." + variant + ".dev", () => serveHTML({ version: variant })) - ); -} - -// Deploying! -gulp.task( - "deploy.staging", - gulp.series("utils.requireCleanWorkingTree", "build.web-shapezio-beta", "ftp.upload.staging") -); -gulp.task( - "deploy.prod", - gulp.series("utils.requireCleanWorkingTree", "build.web-shapezio", "ftp.upload.prod") -); - -// Bundling (pre upload) -gulp.task( - "bundle.steam.from-darwin", - gulp.series("utils.cleanBuildOutputFolder", "bundle.standalone-steam.from-darwin") -); -gulp.task( - "bundle.steam.from-windows", - gulp.series( - "utils.cleanBuildOutputFolder", - "bundle.standalone-steam.from-windows", - "bundle.standalone-steam-china.from-windows" - ) -); -gulp.task( - "bundle.steam-demo.from-darwin", - gulp.series("utils.cleanBuildOutputFolder", "bundle.standalone-steam-demo.from-darwin") -); -gulp.task( - "bundle.steam-demo.from-windows", - gulp.series( - "utils.cleanBuildOutputFolder", - "bundle.standalone-steam-demo.from-windows", - "bundle.standalone-steam-china-demo.from-windows" - ) -); - -// Default task (dev, localhost) -gulp.task("default", gulp.series("serve.web-localhost")); +import gulp from "gulp"; +import * as tasks from "./tasks.js"; + +/** + * @typedef {import("gulp").TaskFunction} TaskFunction + * @typedef {TaskFunction | { [k: string]: Tasks }} Tasks + */ + +/** + * @param {Tasks} tasks + * @param {string=} prefix + */ +function register(tasks, prefix) { + if (tasks instanceof Function) { + gulp.task(prefix, tasks); + return; + } + for (const [k, v] of Object.entries(tasks)) { + register(v, prefix == null ? k : `${prefix}.${k}`); + } +} + +register(tasks); diff --git a/gulp/html.js b/gulp/html.js index a32451c7..62074891 100644 --- a/gulp/html.js +++ b/gulp/html.js @@ -1,205 +1,42 @@ -const buildUtils = require("./buildutils"); -const fs = require("fs"); -const path = require("path"); -const crypto = require("crypto"); -const { BUILD_VARIANTS } = require("./build_variants"); - -function computeIntegrityHash(fullPath, algorithm = "sha256") { - const file = fs.readFileSync(fullPath); - const hash = crypto.createHash(algorithm).update(file).digest("base64"); - return algorithm + "-" + hash; -} - -/** - * PROVIDES (per ) - * - * html..dev - * html..prod - */ -function gulptasksHTML($, gulp, buildFolder) { - const commitHash = buildUtils.getRevision(); - async function buildHtml({ - googleAnalytics = false, - standalone = false, - integrity = true, - enableCachebust = true, - }) { - function cachebust(url) { - if (enableCachebust) { - return buildUtils.cachebust(url, commitHash); - } - return url; - } - - const hasLocalFiles = standalone; - - return gulp - .src("../src/html/" + (standalone ? "index.standalone.html" : "index.html")) - .pipe( - $.dom( - /** @this {Document} **/ function () { - const document = this; - - // Append css - const css = document.createElement("link"); - css.rel = "stylesheet"; - css.type = "text/css"; - css.media = "none"; - css.setAttribute("onload", "this.media='all'"); - css.href = cachebust("main.css"); - if (integrity) { - css.setAttribute( - "integrity", - computeIntegrityHash(path.join(buildFolder, "main.css")) - ); - } - document.head.appendChild(css); - - // Google analytics - if (googleAnalytics && false) { - const tagManagerScript = document.createElement("script"); - tagManagerScript.src = - "https://www.googletagmanager.com/gtag/js?id=UA-165342524-1"; - tagManagerScript.setAttribute("async", ""); - document.head.appendChild(tagManagerScript); - - const initScript = document.createElement("script"); - initScript.textContent = ` - window.dataLayer = window.dataLayer || []; - function gtag(){dataLayer.push(arguments);} - gtag('js', new Date()); - gtag('config', 'UA-165342524-1', { anonymize_ip: true }); - `; - document.head.appendChild(initScript); - } - - // Do not need to preload in app or standalone - if (!hasLocalFiles) { - // Preload essentials - const preloads = [ - "res/fonts/GameFont.woff2", - // "async-resources.css", - // "res/sounds/music/theme-short.mp3", - ]; - - preloads.forEach(src => { - const preloadLink = document.createElement("link"); - preloadLink.rel = "preload"; - preloadLink.href = cachebust(src); - if (src.endsWith(".woff2")) { - preloadLink.setAttribute("crossorigin", "anonymous"); - preloadLink.setAttribute("as", "font"); - } else if (src.endsWith(".css")) { - preloadLink.setAttribute("as", "style"); - } else if (src.endsWith(".mp3")) { - preloadLink.setAttribute("as", "audio"); - } else { - preloadLink.setAttribute("as", "image"); - } - document.head.appendChild(preloadLink); - }); - } - - let fontCss = ` - @font-face { - font-family: "GameFont"; - font-style: normal; - font-weight: normal; - font-display: swap; - src: url('${cachebust("res/fonts/GameFont.woff2")}') format("woff2"); - } - `; - let loadingCss = - fontCss + - fs.readFileSync(path.join(__dirname, "preloader", "preloader.css")).toString(); - - const style = document.createElement("style"); - style.setAttribute("type", "text/css"); - style.textContent = loadingCss; - document.head.appendChild(style); - - let bodyContent = fs - .readFileSync(path.join(__dirname, "preloader", "preloader.html")) - .toString(); - - // Append loader, but not in standalone (directly include bundle there) - if (standalone) { - const bundleScript = document.createElement("script"); - bundleScript.type = "text/javascript"; - bundleScript.src = "bundle.js"; - if (integrity) { - bundleScript.setAttribute( - "integrity", - computeIntegrityHash(path.join(buildFolder, "bundle.js")) - ); - } - document.head.appendChild(bundleScript); - } else { - const loadJs = document.createElement("script"); - loadJs.type = "text/javascript"; - let scriptContent = ""; - scriptContent += `var bundleSrc = '${cachebust("bundle.js")}';\n`; - - if (integrity) { - scriptContent += - "var bundleIntegrity = '" + - computeIntegrityHash(path.join(buildFolder, "bundle.js")) + - "';\n"; - } else { - scriptContent += "var bundleIntegrity = null;\n"; - scriptContent += "var bundleIntegrityTranspiled = null;\n"; - } - - scriptContent += fs - .readFileSync(path.join(__dirname, "preloader", "preloader.js")) - .toString(); - loadJs.textContent = scriptContent; - document.head.appendChild(loadJs); - } - - document.body.innerHTML = bodyContent; - } - ) - ) - .pipe( - $.htmlmin({ - caseSensitive: true, - collapseBooleanAttributes: true, - collapseInlineTagWhitespace: true, - collapseWhitespace: true, - preserveLineBreaks: true, - minifyJS: true, - minifyCSS: true, - quoteCharacter: '"', - useShortDoctype: true, - }) - ) - .pipe($.htmlBeautify()) - .pipe($.rename("index.html")) - .pipe(gulp.dest(buildFolder)); - } - - for (const variant in BUILD_VARIANTS) { - const data = BUILD_VARIANTS[variant]; - gulp.task("html." + variant + ".dev", () => { - return buildHtml({ - googleAnalytics: false, - standalone: data.standalone, - integrity: false, - enableCachebust: false, - }); - }); - gulp.task("html." + variant + ".prod", () => { - return buildHtml({ - googleAnalytics: !data.standalone, - standalone: data.standalone, - integrity: true, - enableCachebust: !data.standalone, - }); - }); - } -} - -module.exports = { - gulptasksHTML, -}; +import fs from "fs"; +import gulp from "gulp"; +import path from "path/posix"; +import { buildFolder } from "./config.js"; + +import gulpDom from "gulp-dom"; +import gulpHtmlmin from "gulp-htmlmin"; +import gulpRename from "gulp-rename"; + +/** + * PROVIDES + * + * html + */ + +async function buildHtml() { + return gulp + .src("../src/html/index.html") + .pipe( + gulpDom(function () { + const style = this.createElement("style"); + style.textContent = fs.readFileSync(path.join("preloader", "preloader.css"), "utf-8"); + this.head.appendChild(style); + }) + ) + .pipe( + gulpHtmlmin({ + caseSensitive: true, + collapseBooleanAttributes: true, + collapseInlineTagWhitespace: true, + collapseWhitespace: true, + preserveLineBreaks: true, + minifyJS: true, + minifyCSS: true, + quoteCharacter: '"', + }) + ) + .pipe(gulpRename("index.html")) + .pipe(gulp.dest(buildFolder)); +} + +export default buildHtml; diff --git a/gulp/image-resources.js b/gulp/image-resources.js index 61786883..507e1f94 100644 --- a/gulp/image-resources.js +++ b/gulp/image-resources.js @@ -1,205 +1,162 @@ -const { existsSync } = require("fs"); -// @ts-ignore -const path = require("path"); -const atlasToJson = require("./atlas2json"); - -const execute = command => - require("child_process").execSync(command, { - encoding: "utf-8", - }); - -// Globs for atlas resources -const rawImageResourcesGlobs = ["../res_raw/atlas.json", "../res_raw/**/*.png"]; - -// Globs for non-ui resources -const nonImageResourcesGlobs = ["../res/**/*.woff2", "../res/*.ico", "../res/**/*.webm"]; - -// Globs for ui resources -const imageResourcesGlobs = ["../res/**/*.png", "../res/**/*.svg", "../res/**/*.jpg", "../res/**/*.gif"]; - -// Link to download LibGDX runnable-texturepacker.jar -const runnableTPSource = "https://libgdx-nightlies.s3.eu-central-1.amazonaws.com/libgdx-runnables/runnable-texturepacker.jar"; - -function gulptasksImageResources($, gulp, buildFolder) { - // Lossless options - const minifyImagesOptsLossless = () => [ - $.imageminJpegtran({ - progressive: true, - }), - $.imagemin.svgo({}), - $.imagemin.optipng({ - optimizationLevel: 3, - }), - $.imageminGifsicle({ - optimizationLevel: 3, - colors: 128, - }), - ]; - - // Lossy options - const minifyImagesOpts = () => [ - $.imagemin.mozjpeg({ - quality: 80, - maxMemory: 1024 * 1024 * 8, - }), - $.imagemin.svgo({}), - $.imageminPngquant({ - speed: 1, - strip: true, - quality: [0.65, 0.9], - dithering: false, - verbose: false, - }), - $.imagemin.optipng({ - optimizationLevel: 3, - }), - $.imageminGifsicle({ - optimizationLevel: 3, - colors: 128, - }), - ]; - - // Where the resources folder are - const resourcesDestFolder = path.join(buildFolder, "res"); - - /** - * Determines if an atlas must use lossless compression - * @param {string} fname - */ - function fileMustBeLossless(fname) { - return fname.indexOf("lossless") >= 0; - } - - /////////////// ATLAS ///////////////////// - - gulp.task("imgres.buildAtlas", cb => { - const config = JSON.stringify("../res_raw/atlas.json"); - const source = JSON.stringify("../res_raw"); - const dest = JSON.stringify("../res_built/atlas"); - - try { - // First check whether Java is installed - execute("java -version"); - // Now check and try downloading runnable-texturepacker.jar (22MB) - if (!existsSync("./runnable-texturepacker.jar")) { - const safeLink = JSON.stringify(runnableTPSource); - const commands = [ - // linux/macos if installed - `wget -O runnable-texturepacker.jar ${safeLink}`, - // linux/macos, latest windows 10 - `curl -o runnable-texturepacker.jar ${safeLink}`, - // windows 10 / updated windows 7+ - "powershell.exe -Command (new-object System.Net.WebClient)" + - `.DownloadFile(${safeLink.replace(/"/g, "'")}, 'runnable-texturepacker.jar')`, - // windows 7+, vulnerability exploit - `certutil.exe -urlcache -split -f ${safeLink} runnable-texturepacker.jar`, - ]; - - while (commands.length) { - try { - execute(commands.shift()); - break; - } catch { - if (!commands.length) { - throw new Error("Failed to download runnable-texturepacker.jar!"); - } - } - } - } - - execute(`java -jar runnable-texturepacker.jar ${source} ${dest} atlas0 ${config}`); - } catch { - console.warn("Building atlas failed. Java not found / unsupported version?"); - } - cb(); - }); - - // Converts .atlas LibGDX files to JSON - gulp.task("imgres.atlasToJson", cb => { - atlasToJson.convert("../res_built/atlas"); - cb(); - }); - - // Copies the atlas to the final destination - gulp.task("imgres.atlas", () => { - return gulp.src(["../res_built/atlas/*.png"]).pipe(gulp.dest(resourcesDestFolder)); - }); - - // Copies the atlas to the final destination after optimizing it (lossy compression) - gulp.task("imgres.atlasOptimized", () => { - return gulp - .src(["../res_built/atlas/*.png"]) - .pipe( - $.if( - fname => fileMustBeLossless(fname.history[0]), - $.imagemin(minifyImagesOptsLossless()), - $.imagemin(minifyImagesOpts()) - ) - ) - .pipe(gulp.dest(resourcesDestFolder)); - }); - - //////////////////// RESOURCES ////////////////////// - - // Copies all resources which are no ui resources - gulp.task("imgres.copyNonImageResources", () => { - return gulp.src(nonImageResourcesGlobs).pipe(gulp.dest(resourcesDestFolder)); - }); - - // Copies all ui resources - gulp.task("imgres.copyImageResources", () => { - return gulp - .src(imageResourcesGlobs) - - .pipe($.cached("imgres.copyImageResources")) - .pipe(gulp.dest(path.join(resourcesDestFolder))); - }); - - // Copies all ui resources and optimizes them - gulp.task("imgres.copyImageResourcesOptimized", () => { - return gulp - .src(imageResourcesGlobs) - .pipe( - $.if( - fname => fileMustBeLossless(fname.history[0]), - $.imagemin(minifyImagesOptsLossless()), - $.imagemin(minifyImagesOpts()) - ) - ) - .pipe(gulp.dest(path.join(resourcesDestFolder))); - }); - - // Copies all resources and optimizes them - gulp.task( - "imgres.allOptimized", - gulp.parallel( - "imgres.buildAtlas", - "imgres.atlasToJson", - "imgres.atlasOptimized", - "imgres.copyNonImageResources", - "imgres.copyImageResourcesOptimized" - ) - ); - - // Cleans up unused images which are instead inline into the css - gulp.task("imgres.cleanupUnusedCssInlineImages", () => { - return gulp - .src( - [ - path.join(buildFolder, "res", "ui", "**", "*.png"), - path.join(buildFolder, "res", "ui", "**", "*.jpg"), - path.join(buildFolder, "res", "ui", "**", "*.svg"), - path.join(buildFolder, "res", "ui", "**", "*.gif"), - ], - { read: false } - ) - .pipe($.if(fname => fname.history[0].indexOf("noinline") < 0, $.clean({ force: true }))); - }); -} - -module.exports = { - rawImageResourcesGlobs, - nonImageResourcesGlobs, - imageResourcesGlobs, - gulptasksImageResources, -}; +import gulp from "gulp"; +import path from "path/posix"; +import atlas2Json from "./atlas2json.js"; +import { buildFolder } from "./config.js"; + +import childProcess from "child_process"; +import { promisify } from "util"; +const exec = promisify(childProcess.exec); +const execute = command => { + const promise = exec(command, { + encoding: "utf-8", + }); + promise.child.stderr.pipe(process.stderr); + return promise; +}; + +import gulpCached from "gulp-cached"; +import gulpClean from "gulp-clean"; +import gulpIf from "gulp-if"; +import gulpImagemin from "gulp-imagemin"; +import imageminGifsicle from "imagemin-gifsicle"; +import imageminJpegtran from "imagemin-jpegtran"; +import imageminPngquant from "imagemin-pngquant"; +import { imageResourcesGlobs, nonImageResourcesGlobs } from "./config.js"; + +// Lossless options +const minifyImagesOptsLossless = () => [ + imageminJpegtran({ + progressive: true, + }), + gulpImagemin.svgo({}), + gulpImagemin.optipng({ + optimizationLevel: 3, + }), + imageminGifsicle({ + optimizationLevel: 3, + colors: 128, + }), +]; + +// Lossy options +const minifyImagesOpts = () => [ + gulpImagemin.mozjpeg({ + quality: 80, + maxMemory: 1024 * 1024 * 8, + }), + gulpImagemin.svgo({}), + imageminPngquant({ + speed: 1, + strip: true, + quality: [0.65, 0.9], + dithering: false, + verbose: false, + }), + gulpImagemin.optipng({ + optimizationLevel: 3, + }), + imageminGifsicle({ + optimizationLevel: 3, + colors: 128, + }), +]; + +// Where the resources folder are +const resourcesDestFolder = path.join(buildFolder, "res"); + +/** + * Determines if an atlas must use lossless compression + * @param {string} fname + */ +function fileMustBeLossless(fname) { + return fname.indexOf("lossless") >= 0; +} + +/////////////// ATLAS ///////////////////// + +export async function buildAtlas() { + const config = JSON.stringify("../res_raw/atlas.json"); + const source = JSON.stringify("../res_raw"); + const dest = JSON.stringify("../res_built/atlas"); + + try { + await execute(`java -jar runnable-texturepacker.jar ${source} ${dest} atlas0 ${config}`); + } catch { + console.warn("Building atlas failed. Java not found / unsupported version?"); + } +} + +// Converts .atlas LibGDX files to JSON +export async function atlasToJson() { + atlas2Json("../res_built/atlas"); +} + +// Copies the atlas to the final destination +export function atlas() { + return gulp.src("../res_built/atlas/*.png").pipe(gulp.dest(resourcesDestFolder)); +} + +// Copies the atlas to the final destination after optimizing it (lossy compression) +export function atlasOptimized() { + return gulp + .src(["../res_built/atlas/*.png"]) + .pipe( + gulpIf( + fname => fileMustBeLossless(fname.history[0]), + gulpImagemin(minifyImagesOptsLossless()), + gulpImagemin(minifyImagesOpts()) + ) + ) + .pipe(gulp.dest(resourcesDestFolder)); +} + +//////////////////// RESOURCES ////////////////////// + +// Copies all resources which are no ui resources +export function copyNonImageResources() { + return gulp.src(nonImageResourcesGlobs).pipe(gulp.dest(resourcesDestFolder)); +} + +// Copies all ui resources +export function copyImageResources() { + return gulp + .src(imageResourcesGlobs) + .pipe(gulpCached("imgres.copyImageResources")) + .pipe(gulp.dest(path.join(resourcesDestFolder))); +} + +// Copies all ui resources and optimizes them +export function copyImageResourcesOptimized() { + return gulp + .src(imageResourcesGlobs) + .pipe( + gulpIf( + fname => fileMustBeLossless(fname.history[0]), + gulpImagemin(minifyImagesOptsLossless()), + gulpImagemin(minifyImagesOpts()) + ) + ) + .pipe(gulp.dest(path.join(resourcesDestFolder))); +} + +// Copies all resources and optimizes them +export const allOptimized = gulp.parallel( + gulp.series(buildAtlas, atlasToJson, atlasOptimized), + copyNonImageResources, + copyImageResourcesOptimized +); + +// Cleans up unused images which are instead inline into the css +export function cleanupUnusedCssInlineImages() { + return gulp + .src( + [ + path.join(buildFolder, "res", "ui", "**", "*.png"), + path.join(buildFolder, "res", "ui", "**", "*.jpg"), + path.join(buildFolder, "res", "ui", "**", "*.svg"), + path.join(buildFolder, "res", "ui", "**", "*.gif"), + ], + { read: false } + ) + .pipe(gulpIf(fname => fname.history[0].indexOf("noinline") < 0, gulpClean({ force: true }))); +} diff --git a/gulp/js.js b/gulp/js.js index 085a9d74..06a4ae3d 100644 --- a/gulp/js.js +++ b/gulp/js.js @@ -1,10 +1,12 @@ -const path = require("path"); -const { BUILD_VARIANTS } = require("./build_variants"); +import gulp from "gulp"; +import webpack from "webpack"; +import { BUILD_VARIANTS } from "./build_variants.js"; +import { buildFolder } from "./config.js"; -function requireUncached(module) { - delete require.cache[require.resolve(module)]; - return require(module); -} +import webpackConfig from "./webpack.config.js"; +import webpackProductionConfig from "./webpack.production.config.js"; + +import webpackStream from "webpack-stream"; /** * PROVIDES (per ) @@ -15,115 +17,28 @@ function requireUncached(module) { * */ -function gulptasksJS($, gulp, buildFolder, browserSync) { - //// DEV - - for (const variant in BUILD_VARIANTS) { - const data = BUILD_VARIANTS[variant]; - - gulp.task("js." + variant + ".dev.watch", () => { - return gulp - .src("../src/js/main.js") - .pipe( - $.webpackStream( - requireUncached("./webpack.config.js")({ - ...data.buildArgs, - standalone: data.standalone, - watch: true, - }) - ) - ) - .pipe(gulp.dest(buildFolder)) - .pipe(browserSync.stream()); - }); - - if (!data.standalone) { - // WEB - - gulp.task("js." + variant + ".dev", () => { +// TODO: Move webpack config to build_variants.js and use a separate +// build variant for development +export default Object.fromEntries( + Object.entries(BUILD_VARIANTS).map(([variant, data]) => { + const dev = { + build() { return gulp .src("../src/js/main.js") - .pipe( - $.webpackStream( - requireUncached("./webpack.config.js")({ - ...data.buildArgs, - }) - ) - ) + .pipe(webpackStream(webpackConfig, webpack)) .pipe(gulp.dest(buildFolder)); - }); + }, + }; - gulp.task("js." + variant + ".prod.transpiled", () => { + const prod = { + build() { return gulp .src("../src/js/main.js") - .pipe( - $.webpackStream( - requireUncached("./webpack.production.config.js")({ - es6: false, - environment: data.environment, - ...data.buildArgs, - }) - ) - ) - .pipe($.rename("bundle-transpiled.js")) + .pipe(webpackStream(webpackProductionConfig, webpack)) .pipe(gulp.dest(buildFolder)); - }); + }, + }; - gulp.task("js." + variant + ".prod.es6", () => { - return gulp - .src("../src/js/main.js") - .pipe( - $.webpackStream( - requireUncached("./webpack.production.config.js")({ - es6: true, - environment: data.environment, - ...data.buildArgs, - }) - ) - ) - .pipe(gulp.dest(buildFolder)); - }); - gulp.task( - "js." + variant + ".prod", - - // transpiled currently not used - // gulp.parallel("js." + variant + ".prod.transpiled", "js." + variant + ".prod.es6") - gulp.parallel("js." + variant + ".prod.es6") - ); - } else { - // STANDALONE - gulp.task("js." + variant + ".dev", () => { - return gulp - .src("../src/js/main.js") - .pipe( - $.webpackStream( - requireUncached("./webpack.config.js")({ - ...data.buildArgs, - standalone: true, - }) - ) - ) - .pipe(gulp.dest(buildFolder)); - }); - gulp.task("js." + variant + ".prod", () => { - return gulp - .src("../src/js/main.js") - .pipe( - $.webpackStream( - requireUncached("./webpack.production.config.js")({ - ...data.buildArgs, - environment: "prod", - es6: true, - standalone: true, - }) - ) - ) - .pipe(gulp.dest(buildFolder)); - }); - } - } -} - -module.exports = { - gulptasksJS, -}; + return [variant, { dev, prod }]; + }) +); diff --git a/gulp/jsconfig.json b/gulp/jsconfig.json deleted file mode 100644 index e28a1c04..00000000 --- a/gulp/jsconfig.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "compilerOptions": { - "target": "es6", - "checkJs": true - } -} diff --git a/gulp/loader.compressjson.js b/gulp/loader.compressjson.js deleted file mode 100644 index 9e80298f..00000000 --- a/gulp/loader.compressjson.js +++ /dev/null @@ -1,11 +0,0 @@ -"use strict"; - -const lzString = require("lz-string"); - -module.exports = function (source) { - const compressed = lzString.compressToEncodedURIComponent(source); - const sourcecode = `module.exports = (function() { - return JSON.parse(require("global-compression").decompressX64("${compressed}")); - })()`; - return sourcecode; -}; diff --git a/gulp/loader.strip_block.js b/gulp/loader.strip_block.js deleted file mode 100644 index 31ae5432..00000000 --- a/gulp/loader.strip_block.js +++ /dev/null @@ -1,21 +0,0 @@ -/*jslint node:true */ -"use strict"; - -const startComment = "typehints:start"; -const endComment = "typehints:end"; -const regexPattern = new RegExp( - "[\\t ]*\\/\\* ?" + startComment + " ?\\*\\/[\\s\\S]*?\\/\\* ?" + endComment + " ?\\*\\/[\\t ]*\\n?", - "g" -); - -function StripBlockLoader(content) { - if (content.indexOf(startComment) >= 0) { - content = content.replace(regexPattern, ""); - } - if (this.cacheable) { - this.cacheable(true); - } - return content; -} - -module.exports = StripBlockLoader; diff --git a/gulp/local-config.js b/gulp/local-config.js deleted file mode 100644 index 108749e0..00000000 --- a/gulp/local-config.js +++ /dev/null @@ -1,18 +0,0 @@ -const path = require("path"); -const fs = require("fs"); -const fse = require("fs-extra"); - -const configTemplatePath = path.join(__dirname, "../src/js/core/config.local.template.js"); -const configPath = path.join(__dirname, "../src/js/core/config.local.js"); - -function gulptasksLocalConfig($, gulp) { - gulp.task("localConfig.findOrCreate", cb => { - if (!fs.existsSync(configPath)) { - fse.copySync(configTemplatePath, configPath); - } - - cb(); - }); -} - -module.exports = { gulptasksLocalConfig }; diff --git a/gulp/mod.js b/gulp/mod.js deleted file mode 100644 index ad6cd4ae..00000000 --- a/gulp/mod.js +++ /dev/null @@ -1,39 +0,0 @@ -const oneExport = exp => { - return `${exp}=v`; // No checks needed -}; - -const twoExports = (exp1, exp2) => { - return `n=="${exp1}"?${exp1}=v:${exp2}=v`; -}; - -const multiExports = exps => { - exps = exps.map(exp => `case "${exp}":${exp}=v;break;`); - - return `switch(n){${exps.toString().replaceAll(";,", ";")} }`; -}; - -const defineFnBody = source => { - const regex = /export (?:let|class) (?\w+)/g; - let names = [...source.matchAll(regex)].map(n => n.groups.name); - switch (names.length) { - case 0: - return false; - case 1: - return oneExport(names[0]); - case 2: - return twoExports(names[0], names[1]); - default: - return multiExports(names); - } -}; -/** - * - * @param {string} source - * @param {*} map - * @returns - */ -module.exports = function (source, map) { - const body = defineFnBody(source); - if (!body) return source; - return source + `\nexport const __$S__=(n,v)=>{${body}}`; -}; diff --git a/gulp/package.json b/gulp/package.json deleted file mode 100644 index 61dfcaf9..00000000 --- a/gulp/package.json +++ /dev/null @@ -1,124 +0,0 @@ -{ - "name": "builder", - "version": "1.0.0", - "description": "builder", - "private": true, - "scripts": { - "gulp": "gulp" - }, - "author": "tobspr", - "license": "private", - "browserslist": "> 0.01%", - "dependencies": { - "@babel/core": "^7.9.0", - "@babel/plugin-transform-arrow-functions": "^7.17.12", - "@babel/plugin-transform-block-scoping": "^7.4.4", - "@babel/plugin-transform-classes": "^7.5.5", - "@babel/preset-env": "^7.5.4", - "@types/cordova": "^0.0.34", - "@types/filesystem": "^0.0.29", - "@types/node": "^12.7.5", - "ajv": "^6.10.2", - "are-you-es5": "^2.1.2", - "audiosprite": "^0.7.2", - "babel-core": "^6.26.3", - "babel-loader": "^8.1.0", - "browser-sync": "^2.26.10", - "circular-dependency-plugin": "^5.0.2", - "circular-json": "^0.5.9", - "clipboard-copy": "^3.1.0", - "colors": "^1.3.3", - "core-js": "3", - "crypto": "^1.0.1", - "cssnano-preset-advanced": "^4.0.7", - "delete-empty": "^3.0.0", - "email-validator": "^2.0.4", - "eslint": "^5.9.0", - "fastdom": "^1.0.9", - "flatted": "^2.0.1", - "fs-extra": "^8.1.0", - "gifsicle": "^5.2.0", - "gulp-audiosprite": "^1.1.0", - "howler": "^2.1.2", - "html-loader": "^0.5.5", - "ignore-loader": "^0.1.2", - "lz-string": "^1.4.4", - "markdown-loader": "^5.1.0", - "node-sri": "^1.1.1", - "phonegap-plugin-mobile-accessibility": "^1.0.5", - "postcss": ">=5.0.0", - "promise-polyfill": "^8.1.0", - "query-string": "^6.8.1", - "raw-loader": "^4.0.2", - "rusha": "^0.8.13", - "stream-browserify": "^3.0.0", - "strictdom": "^1.0.1", - "string-replace-webpack-plugin": "^0.1.3", - "strip-indent": "^3.0.0", - "terser-webpack-plugin": "^1.1.0", - "through2": "^3.0.1", - "uglify-template-string-loader": "^1.1.0", - "unused-files-webpack-plugin": "^3.4.0", - "webpack": "^4.43.0", - "webpack-cli": "^3.1.0", - "webpack-deep-scope-plugin": "^1.6.0", - "webpack-plugin-replace": "^1.1.1", - "webpack-strip-block": "^0.2.0", - "whatwg-fetch": "^3.0.0", - "worker-loader": "^2.0.0", - "yaml": "^1.10.0" - }, - "devDependencies": { - "autoprefixer": "^9.4.3", - "babel-plugin-closure-elimination": "^1.3.0", - "babel-plugin-console-source": "^2.0.2", - "babel-plugin-danger-remove-unused-import": "^1.1.2", - "css-mqpacker": "^7.0.0", - "cssnano": "^4.1.10", - "electron-notarize": "^1.2.1", - "electron-packager": "^15.4.0", - "faster.js": "^1.1.0", - "glob": "^7.1.3", - "gulp": "^4.0.2", - "gulp-cache": "^1.1.3", - "gulp-cached": "^1.1.1", - "gulp-clean": "^0.4.0", - "gulp-dart-sass": "^1.0.2", - "gulp-dom": "^1.0.0", - "gulp-flatten": "^0.4.0", - "gulp-fluent-ffmpeg": "^2.0.0", - "gulp-html-beautify": "^1.0.1", - "gulp-htmlmin": "^5.0.1", - "gulp-if": "^3.0.0", - "gulp-imagemin": "^7.1.0", - "gulp-load-plugins": "^2.0.3", - "gulp-phonegap-build": "^0.1.5", - "gulp-plumber": "^1.2.1", - "gulp-pngquant": "^1.0.13", - "gulp-postcss": "^8.0.0", - "gulp-rename": "^2.0.0", - "gulp-sass-lint": "^1.4.0", - "gulp-sftp": "git+https://git@github.com/webksde/gulp-sftp", - "gulp-terser": "^1.2.0", - "gulp-webserver": "^0.9.1", - "gulp-yaml": "^2.0.4", - "imagemin-gifsicle": "^7.0.0", - "imagemin-jpegtran": "^7.0.0", - "imagemin-pngquant": "^9.0.0", - "jimp": "^0.6.1", - "js-yaml": "^3.13.1", - "postcss-assets": "^5.0.0", - "postcss-critical-split": "^2.5.3", - "postcss-preset-env": "^6.5.0", - "postcss-round-subpixels": "^1.2.0", - "postcss-unprefix": "^2.1.3", - "sass-unused": "^0.3.0", - "strip-json-comments": "^3.0.1", - "trim": "^0.0.1", - "webpack-stream": "^5.2.1", - "yaml-loader": "^0.6.0" - }, - "optionalDependencies": { - "tobspr-osx-sign": "^1.0.1" - } -} diff --git a/gulp/preloader/preloader.css b/gulp/preloader/preloader.css index f6775f76..3faba330 100644 --- a/gulp/preloader/preloader.css +++ b/gulp/preloader/preloader.css @@ -1,16 +1,21 @@ +@font-face { + font-family: "GameFont"; + font-style: normal; + font-weight: normal; + font-display: swap; + src: url("res/fonts/GameFont.woff2") format("woff2"); +} + * { margin: 0; padding: 0; - touch-action: pan-x pan-y !important; + touch-action: pan-x pan-y; pointer-events: none; -webkit-tap-highlight-color: rgba(255, 255, 255, 0); } html { position: fixed; - -ms-touch-action: pan-x, pan-y; - touch-action: pan-x, pan-y; - -ms-content-zooming: none; top: 0; left: 0; bottom: 0; @@ -21,29 +26,10 @@ html { body { color: #555; user-select: none; - -moz-user-select: none; - -ms-user-select: none; - background: inherit !important; - text-transform: none; - white-space: normal; - word-break: normal; - word-spacing: normal; - word-wrap: break-word; - font-style: normal; - line-break: auto; - font-stretch: 100%; + background: inherit; + overflow-wrap: break-word; text-rendering: optimizeLegibility; - text-decoration: none; - text-size-adjust: 100%; - letter-spacing: normal; - scrollbar-width: 6px; -webkit-font-smoothing: antialiased; - -webkit-touch-callout: none; - /* prevent callout to copy image, etc when tap to hold */ - -webkit-text-size-adjust: none; - /* prevent webkit from resizing text to fit */ - scrollbar-face-color: #888; - scrollbar-track-color: rgba(255, 255, 255, 0.1); } #ll_fp { @@ -65,8 +51,7 @@ body { right: 0; bottom: 0; justify-content: center; - justify-items: center; - align-items: center; + place-items: center; background: #d5d8de; grid-template-rows: 1fr 200px; grid-gap: 40px; @@ -75,7 +60,7 @@ body { } #ll_p * { - line-height: 1em; + line-height: 1; } #ll_loader { @@ -92,7 +77,7 @@ body { font-family: "GameFont", Arial, sans-serif; font-size: 24px; height: 30px; - line-height: 1.2em; + line-height: 1.2; } #ll_progressbar { @@ -134,62 +119,12 @@ body { min-width: 4%; } -#ll_progressbar > #ll_loadinglabel { - position: absolute; - z-index: 20; - top: 50%; - text-transform: uppercase; - border-radius: 7px; - left: 50%; - transform: translate(-50%, -50%); - font-size: 16px; - color: #33373f; -} - -@keyframes ShowStandaloneBannerAfterDelay { - 0% { - opacity: 0; - } - 95% { - opacity: 0; - } - 100% { - opacity: 1; - } -} - -#ll_standalone { - text-align: center; - color: #777a7f; - margin-top: 30px; - display: block; - font-size: 16px; - animation: ShowStandaloneBannerAfterDelay 60s linear; -} - -#ll_standalone a { - color: #39f; - margin-left: 5px; - font-weight: bold; -} - -#ll_logo { -} - #ll_logo > img { width: 40vw; max-width: 700px; min-width: 150px; } -#ll_loader > .ll_spinner { - width: 80px; - height: 80px; - display: inline-flex; - background: center center / contain no-repeat; - display: none; -} - #ll_preload_status { position: absolute; top: 40px; @@ -204,59 +139,3 @@ body { text-transform: uppercase; text-align: center; } - -#ll_preload_error { - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 999999; - background: #d5d8de; - display: flex; - justify-content: center; - align-items: center; -} - -#ll_preload_error > .inner { - color: #fff; - font-family: Arial, "sans-serif"; - font-size: 15px; - padding: 0; - text-align: center; -} - -#ll_preload_error > .inner > .heading { - color: #ef5072; - margin-bottom: 40px; - font-size: 45px; -} - -#ll_preload_error > .inner > .content { - color: #55585f; - font-family: monospace; - text-align: left; - background-color: #fff; - padding: 20px; - border-radius: 10px; - - box-shadow: 0 5px 10px rgba(0, 0, 0, 0.1); -} - -#ll_preload_error > .inner .discordLink { - color: #333; - margin-top: 20px; - margin-bottom: 20px; - font-family: Arial; -} - -#ll_preload_error > .inner .discordLink a { - color: #39f; -} -#ll_preload_error > .inner .discordLink strong { - font-weight: 900 !important; -} - -#ll_preload_error > .inner .source { - color: #777; -} diff --git a/gulp/preloader/preloader.html b/gulp/preloader/preloader.html deleted file mode 100644 index 48d3579c..00000000 --- a/gulp/preloader/preloader.html +++ /dev/null @@ -1,21 +0,0 @@ -
_
-
- -
- -
-
- -
Downloading Game
-
-
- Page does not load? Try the - Steam Version! -
-
-
diff --git a/gulp/preloader/preloader.js b/gulp/preloader/preloader.js deleted file mode 100644 index 7e2fe518..00000000 --- a/gulp/preloader/preloader.js +++ /dev/null @@ -1,165 +0,0 @@ -(function () { - var loadTimeout = null; - var callbackDone = false; - - var searchString = window.location.search; - if (searchString.includes("steam_sso_auth_token=")) { - var pos = searchString.indexOf("steam_sso_auth_token"); - const authToken = searchString.substring(pos + 21, pos + 57); - try { - window.localStorage.setItem("steam_sso_auth_token", authToken); - window.location.replace(window.location.protocol + "//" + window.location.host); - } catch (ex) { - alert("Failed to login via Steam SSO: " + ex); - window.location.replace("https://shapez.io"); - } - return; - } - - // Catch load errors - - function errorHandler(event, source, lineno, colno, error) { - if (("" + event).indexOf("Script error.") >= 0) { - console.warn("Thirdparty script error:", event); - return; - } - - if (("" + event).indexOf("NS_ERROR_FAILURE") >= 0) { - console.warn("Firefox NS_ERROR_FAILURE error:", event); - return; - } - - if (("" + event).indexOf("Cannot read property 'postMessage' of null") >= 0) { - console.warn("Safari can not read post message error:", event); - return; - } - - if (("" + event).indexOf("Possible side-effect in debug-evaluate") >= 0) { - console.warn("Chrome debug-evaluate error:", event); - return; - } - - if (("" + source).indexOf("shapez.io") < 0) { - console.warn("Thirdparty error:", event); - return; - } - - console.error("👀 App Error:", event, source, lineno, colno, error); - var element = document.createElement("div"); - element.id = "ll_preload_error"; - - var inner = document.createElement("div"); - inner.classList.add("inner"); - element.appendChild(inner); - - var heading = document.createElement("h3"); - heading.classList.add("heading"); - heading.innerText = "Fatal Error"; - inner.appendChild(heading); - - var content = document.createElement("p"); - content.classList.add("content"); - content.innerText = error || (event && event.message) || event || "Unknown Error"; - inner.appendChild(content); - - var discordLink = document.createElement("p"); - discordLink.classList.add("discordLink"); - discordLink.innerHTML = - "Please report this error in the #bugs channel of the official discord!"; - - inner.appendChild(discordLink); - - if (source) { - var sourceElement = document.createElement("p"); - sourceElement.classList.add("source"); - sourceElement.innerText = source + ":" + lineno + ":" + colno; - inner.appendChild(sourceElement); - } - - document.documentElement.appendChild(element); - - window.APP_ERROR_OCCURED = true; - } - - window.onerror = errorHandler; - - function expectJsParsed() { - if (!callbackDone) { - console.error("👀 Got no core callback"); - throw new Error("Core thread failed to respond within time."); - } - } - - function onJsLoaded() { - console.log("👀 Core loaded at", Math.floor(performance.now()), "ms"); - loadTimeout = setTimeout(expectJsParsed, 120000); - window.removeEventListener("unhandledrejection", errorHandler); - } - - window.coreThreadLoadedCb = function () { - console.log("👀 Core responded at", Math.floor(performance.now()), "ms"); - clearTimeout(loadTimeout); - loadTimeout = null; - callbackDone = true; - }; - - function progressHandler(progress) { - var progressElement = document.querySelector("#ll_preload_status"); - if (progressElement) { - progressElement.innerText = "Downloading Bundle (" + Math.round(progress * 1200) + " / 1200 KB)"; - } - var barElement = document.querySelector("#ll_progressbar span"); - if (barElement) { - barElement.style.width = (5 + progress * 75.0).toFixed(2) + "%"; - } - } - - function startBundleDownload() { - var xhr = new XMLHttpRequest(); - var notifiedNotComputable = false; - - xhr.open("GET", bundleSrc, true); - xhr.responseType = "arraybuffer"; - xhr.onprogress = function (ev) { - if (ev.lengthComputable) { - progressHandler(ev.loaded / ev.total); - } else { - // Hardcoded length - progressHandler(Math.min(1, ev.loaded / 2349009)); - } - }; - - xhr.onloadend = function () { - if (!xhr.status.toString().match(/^2/)) { - throw new Error("Failed to load bundle: " + xhr.status + " " + xhr.statusText); - } else { - if (!notifiedNotComputable) { - progressHandler(1); - } - - var options = {}; - var headers = xhr.getAllResponseHeaders(); - var m = headers.match(/^Content-Type\:\s*(.*?)$/im); - - if (m && m[1]) { - options.type = m[1]; - } - - var blob = new Blob([this.response], options); - var script = document.createElement("script"); - script.addEventListener("load", onJsLoaded); - script.src = window.URL.createObjectURL(blob); - script.type = "text/javascript"; - script.charset = "utf-8"; - if (bundleIntegrity) { - script.setAttribute("integrity", bundleIntegrity); - } - document.head.appendChild(script); - } - }; - xhr.send(); - } - - console.log("Start bundle download ..."); - window.addEventListener("load", startBundleDownload); -})(); diff --git a/gulp/sounds.js b/gulp/sounds.js index 1cffd897..be0bd3ba 100644 --- a/gulp/sounds.js +++ b/gulp/sounds.js @@ -1,134 +1,130 @@ -const path = require("path"); -const audiosprite = require("gulp-audiosprite"); +import gulp from "gulp"; +import path from "path/posix"; +import { buildFolder, generatedCodeFolder } from "./config.js"; -function gulptasksSounds($, gulp, buildFolder) { - // Gather some basic infos - const soundsDir = path.join(__dirname, "..", "res_raw", "sounds"); - const builtSoundsDir = path.join(__dirname, "..", "res_built", "sounds"); +import gulpAudiosprite from "gulp-audiosprite"; +import gulpCache from "gulp-cache"; +import gulpClean from "gulp-clean"; +import gulpFluentFfmpeg from "gulp-fluent-ffmpeg"; +import gulpPlumber from "gulp-plumber"; - gulp.task("sounds.clear", () => { - return gulp.src(builtSoundsDir, { read: false, allowEmpty: true }).pipe($.clean({ force: true })); - }); +// Gather some basic infos +const soundsDir = path.join("..", "res_raw", "sounds"); +const builtSoundsDir = path.join("..", "res_built", "sounds"); - const filters = ["volume=0.2"]; +export function clear() { + return gulp.src(builtSoundsDir, { read: false, allowEmpty: true }).pipe(gulpClean({ force: true })); +} - const fileCache = new $.cache.Cache({ - cacheDirName: "shapezio-precompiled-sounds", - }); +const filters = ["volume=0.2"]; - function getFileCacheValue(file) { - const { _isVinyl, base, cwd, contents, history, stat, path } = file; - const encodedContents = Buffer.from(contents).toString("base64"); - return { _isVinyl, base, cwd, contents: encodedContents, history, stat, path }; - } +const fileCache = new gulpCache.Cache({ + cacheDirName: "shapezio-precompiled-sounds", +}); - // Encodes the game music - gulp.task("sounds.music", () => { - return gulp - .src([path.join(soundsDir, "music", "**", "*.wav"), path.join(soundsDir, "music", "**", "*.mp3")]) - .pipe($.plumber()) - .pipe( - $.cache( - $.fluentFfmpeg("mp3", function (cmd) { - return cmd - .audioBitrate(48) - .audioChannels(1) - .audioFrequency(22050) - .audioCodec("libmp3lame") - .audioFilters(["volume=0.15"]); - }), - { - name: "music", - fileCache, - value: getFileCacheValue, - } - ) - ) - .pipe(gulp.dest(path.join(builtSoundsDir, "music"))); - }); +function getFileCacheValue(file) { + const { _isVinyl, base, cwd, contents, history, stat, path } = file; + const encodedContents = Buffer.from(contents).toString("base64"); + return { _isVinyl, base, cwd, contents: encodedContents, history, stat, path }; +} - // Encodes the game music in high quality for the standalone - gulp.task("sounds.musicHQ", () => { - return gulp - .src([path.join(soundsDir, "music", "**", "*.wav"), path.join(soundsDir, "music", "**", "*.mp3")]) - .pipe($.plumber()) - .pipe( - $.cache( - $.fluentFfmpeg("mp3", function (cmd) { - return cmd - .audioBitrate(256) - .audioChannels(2) - .audioFrequency(44100) - .audioCodec("libmp3lame") - .audioFilters(["volume=0.15"]); - }), - { - name: "music-high-quality", - fileCache, - value: getFileCacheValue, - } - ) - ) - .pipe(gulp.dest(path.join(builtSoundsDir, "music"))); - }); - - // Encodes the ui sounds - gulp.task("sounds.sfxGenerateSprites", () => { - return gulp - .src([path.join(soundsDir, "sfx", "**", "*.wav"), path.join(soundsDir, "sfx", "**", "*.mp3")]) - .pipe($.plumber()) - .pipe( - audiosprite({ - format: "howler", - output: "sfx", - gap: 0.1, - export: "mp3", - }) - ) - .pipe(gulp.dest(path.join(builtSoundsDir))); - }); - gulp.task("sounds.sfxOptimize", () => { - return gulp - .src([path.join(builtSoundsDir, "sfx.mp3")]) - .pipe($.plumber()) - .pipe( - $.fluentFfmpeg("mp3", function (cmd) { +// Encodes the game music +export function music() { + return gulp + .src([path.join(soundsDir, "music", "**", "*.wav"), path.join(soundsDir, "music", "**", "*.mp3")]) + .pipe(gulpPlumber()) + .pipe( + gulpCache( + gulpFluentFfmpeg("mp3", function (cmd) { return cmd - .audioBitrate(128) + .audioBitrate(48) .audioChannels(1) .audioFrequency(22050) .audioCodec("libmp3lame") - .audioFilters(filters); - }) + .audioFilters(["volume=0.15"]); + }), + { + name: "music", + fileCache, + value: getFileCacheValue, + } ) - .pipe(gulp.dest(path.join(builtSoundsDir))); - }); - gulp.task("sounds.sfxCopyAtlas", () => { - return gulp - .src([path.join(builtSoundsDir, "sfx.json")]) - .pipe(gulp.dest(path.join(__dirname, "..", "src", "js", "built-temp"))); - }); - - gulp.task( - "sounds.sfx", - gulp.series("sounds.sfxGenerateSprites", "sounds.sfxOptimize", "sounds.sfxCopyAtlas") - ); - - gulp.task("sounds.copy", () => { - return gulp - .src(path.join(builtSoundsDir, "**", "*.mp3")) - .pipe($.plumber()) - .pipe(gulp.dest(path.join(buildFolder, "res", "sounds"))); - }); - - gulp.task("sounds.buildall", gulp.parallel("sounds.music", "sounds.sfx")); - gulp.task("sounds.buildallHQ", gulp.parallel("sounds.musicHQ", "sounds.sfx")); - - gulp.task("sounds.fullbuild", gulp.series("sounds.clear", "sounds.buildall", "sounds.copy")); - gulp.task("sounds.fullbuildHQ", gulp.series("sounds.clear", "sounds.buildallHQ", "sounds.copy")); - gulp.task("sounds.dev", gulp.series("sounds.buildall", "sounds.copy")); + ) + .pipe(gulp.dest(path.join(builtSoundsDir, "music"))); } -module.exports = { - gulptasksSounds, -}; +// Encodes the game music in high quality for the standalone +export function musicHQ() { + return gulp + .src([path.join(soundsDir, "music", "**", "*.wav"), path.join(soundsDir, "music", "**", "*.mp3")]) + .pipe(gulpPlumber()) + .pipe( + gulpCache( + gulpFluentFfmpeg("mp3", function (cmd) { + return cmd + .audioBitrate(256) + .audioChannels(2) + .audioFrequency(44100) + .audioCodec("libmp3lame") + .audioFilters(["volume=0.15"]); + }), + { + name: "music-high-quality", + fileCache, + value: getFileCacheValue, + } + ) + ) + .pipe(gulp.dest(path.join(builtSoundsDir, "music"))); +} + +// Encodes the ui sounds +export function sfxGenerateSprites() { + return gulp + .src([path.join(soundsDir, "sfx", "**", "*.wav"), path.join(soundsDir, "sfx", "**", "*.mp3")]) + .pipe(gulpPlumber()) + .pipe( + gulpAudiosprite({ + format: "howler", + output: "sfx", + gap: 0.1, + export: "mp3", + }) + ) + .pipe(gulp.dest(path.join(builtSoundsDir))); +} +export function sfxOptimize() { + return gulp + .src([path.join(builtSoundsDir, "sfx.mp3")]) + .pipe(gulpPlumber()) + .pipe( + gulpFluentFfmpeg("mp3", function (cmd) { + return cmd + .audioBitrate(128) + .audioChannels(1) + .audioFrequency(22050) + .audioCodec("libmp3lame") + .audioFilters(filters); + }) + ) + .pipe(gulp.dest(path.join(builtSoundsDir))); +} +export function sfxCopyAtlas() { + return gulp.src([path.join(builtSoundsDir, "sfx.json")]).pipe(gulp.dest(generatedCodeFolder)); +} + +export const sfx = gulp.series(sfxGenerateSprites, sfxOptimize, sfxCopyAtlas); + +export function copy() { + return gulp + .src(path.join(builtSoundsDir, "**", "*.mp3")) + .pipe(gulpPlumber()) + .pipe(gulp.dest(path.join(buildFolder, "res", "sounds"))); +} + +export const buildall = gulp.parallel(music, sfx); +export const buildallHQ = gulp.parallel(musicHQ, sfx); + +export const fullbuild = gulp.series(clear, buildall, copy); +export const fullbuildHQ = gulp.series(clear, buildallHQ, copy); +export const dev = gulp.series(buildall, copy); diff --git a/gulp/standalone.js b/gulp/standalone.js index b70ef04c..9eabdc0d 100644 --- a/gulp/standalone.js +++ b/gulp/standalone.js @@ -1,339 +1,121 @@ -require("colors"); -const packager = require("electron-packager"); -const pj = require("../electron/package.json"); -const path = require("path"); -const { getVersion } = require("./buildutils"); -const fs = require("fs"); -const fse = require("fs-extra"); -const buildutils = require("./buildutils"); -const execSync = require("child_process").execSync; -const electronNotarize = require("electron-notarize"); -const { BUILD_VARIANTS } = require("./build_variants"); +import { packager } from "@electron/packager"; +import fs from "fs/promises"; +import gulp from "gulp"; +import path from "path/posix"; +import electronPackageJson from "../electron/package.json" with { type: "json" }; +import { BUILD_VARIANTS } from "./build_variants.js"; +import { getVersion } from "./buildutils.js"; +import { buildProject } from "./typescript.js"; -let signAsync; -try { - signAsync = require("tobspr-osx-sign").signAsync; -} catch (ex) { - console.warn("tobspr-osx-sign not installed, can not create osx builds"); -} +import gulpClean from "gulp-clean"; -function gulptasksStandalone($, gulp) { - for (const variant in BUILD_VARIANTS) { - const variantData = BUILD_VARIANTS[variant]; - if (!variantData.standalone) { - continue; - } - const tempDestDir = path.join(__dirname, "..", "build_output", variant); - const taskPrefix = "standalone." + variant; - const electronBaseDir = path.join(__dirname, "..", variantData.electronBaseDir || "electron"); - const tempDestBuildDir = path.join(tempDestDir, "built"); +const platforms = /** @type {const} */ (["win32", "linux", "darwin"]); +const architectures = /** @type {const} */ (["x64", "arm64"]); - gulp.task(taskPrefix + ".prepare.cleanup", () => { - return gulp.src(tempDestDir, { read: false, allowEmpty: true }).pipe($.clean({ force: true })); - }); +export default Object.fromEntries( + Object.entries(BUILD_VARIANTS) + .filter(([variant, variantData]) => variantData.standalone) + .map(([variant, variantData]) => { + const tempDestDir = path.join("..", "build_output", variant); + const electronBaseDir = path.join("..", "electron"); + const tempDestBuildDir = path.join(tempDestDir, "built"); - gulp.task(taskPrefix + ".prepare.copyPrefab", () => { - const requiredFiles = [ - path.join(electronBaseDir, "node_modules", "**", "*.*"), - path.join(electronBaseDir, "node_modules", "**", ".*"), - path.join(electronBaseDir, "wegame_sdk", "**", "*.*"), - path.join(electronBaseDir, "wegame_sdk", "**", ".*"), - path.join(electronBaseDir, "favicon*"), - ]; - return gulp.src(requiredFiles, { base: electronBaseDir }).pipe(gulp.dest(tempDestBuildDir)); - }); + function cleanup() { + return gulp + .src(tempDestDir, { read: false, allowEmpty: true }) + .pipe(gulpClean({ force: true })); + } - gulp.task(taskPrefix + ".prepare.writeAppId", cb => { - if (variantData.steamAppId) { - fs.writeFileSync( - path.join(tempDestBuildDir, "steam_appid.txt"), - String(variantData.steamAppId) + function copyPrefab() { + const requiredFiles = ["preload.cjs", "node_modules/**/*", "favicon*"]; + return gulp + .src(requiredFiles, { cwd: electronBaseDir, cwdbase: true, dot: true }) + .pipe(gulp.dest(tempDestBuildDir)); + } + + async function transpileTypeScript() { + const tsconfigPath = path.join(electronBaseDir, "tsconfig.json"); + const outDir = path.join(tempDestBuildDir, "dist"); + + buildProject(tsconfigPath, undefined, outDir); + return Promise.resolve(); + } + + async function writePackageJson() { + const pkgJson = structuredClone(electronPackageJson); + pkgJson.version = getVersion(); + delete pkgJson.scripts; + + const packageJsonString = JSON.stringify(pkgJson); + await fs.writeFile(path.join(tempDestBuildDir, "package.json"), packageJsonString); + } + + function copyGamefiles() { + return gulp.src("../build/**/*.*", { base: "../build" }).pipe(gulp.dest(tempDestBuildDir)); + } + + const prepare = { + cleanup, + copyPrefab, + transpileTypeScript, + writePackageJson, + copyGamefiles, + all: gulp.series(cleanup, copyPrefab, transpileTypeScript, writePackageJson, copyGamefiles), + }; + + /** + * + * @param {typeof platforms[number] | (typeof platforms[number])[]} platform + * @param {typeof architectures[number] | (typeof architectures[number])[]} arch + */ + async function packageStandalone(platform, arch) { + const appPaths = await packager({ + dir: tempDestBuildDir, + appCopyright: "tobspr Games", + appVersion: getVersion(), + buildVersion: "1.0.0", + arch, + platform, + asar: true, + executableName: "shapezio", + icon: path.join(electronBaseDir, "favicon"), + name: "shapez", + out: tempDestDir, + overwrite: true, + appBundleId: "tobspr.shapezio." + variant, + appCategoryType: "public.app-category.games", + }); + + console.log("Packages created:", appPaths); + await Promise.all( + appPaths.map(async appPath => { + await fs.writeFile( + path.join(appPath, "LICENSE"), + await fs.readFile(path.join("..", "LICENSE")) + ); + }) ); } - cb(); - }); - gulp.task(taskPrefix + ".prepare.writePackageJson", cb => { - const packageJsonString = JSON.stringify( + const pack = { + ...Object.fromEntries( + platforms.flatMap(platform => + architectures.map(arch => [ + `${platform}-${arch}`, + () => packageStandalone(platform, arch), + ]) + ) + ), + // TODO: Review this hack forced by readonly types + all: () => packageStandalone([...platforms], [...architectures]), + }; + + return [ + variant, { - scripts: { - start: pj.scripts.start, - }, - devDependencies: pj.devDependencies, - dependencies: pj.dependencies, - optionalDependencies: pj.optionalDependencies, + prepare, + package: pack, }, - null, - 4 - ); - - fs.writeFileSync(path.join(tempDestBuildDir, "package.json"), packageJsonString); - - cb(); - }); - - gulp.task(taskPrefix + ".prepare.minifyCode", () => { - return gulp.src(path.join(electronBaseDir, "*.js")).pipe(gulp.dest(tempDestBuildDir)); - }); - - gulp.task(taskPrefix + ".prepare.copyGamefiles", () => { - return gulp.src("../build/**/*.*", { base: "../build" }).pipe(gulp.dest(tempDestBuildDir)); - }); - - gulp.task(taskPrefix + ".killRunningInstances", cb => { - try { - execSync("taskkill /F /IM shapezio.exe"); - } catch (ex) { - console.warn("Failed to kill running instances, maybe none are up."); - } - cb(); - }); - - gulp.task( - taskPrefix + ".prepare", - gulp.series( - taskPrefix + ".killRunningInstances", - taskPrefix + ".prepare.cleanup", - taskPrefix + ".prepare.copyPrefab", - taskPrefix + ".prepare.writePackageJson", - taskPrefix + ".prepare.minifyCode", - taskPrefix + ".prepare.copyGamefiles", - taskPrefix + ".prepare.writeAppId" - ) - ); - - /** - * - * @param {'win32'|'linux'|'darwin'} platform - * @param {'x64'|'ia32'} arch - * @param {function():void} cb - */ - function packageStandalone(platform, arch, cb, isRelease = true) { - const privateArtifactsPath = "node_modules/shapez.io-private-artifacts"; - - // Only use asar on steam builds (not supported by wegame) - let asar = Boolean(variantData.steamAppId); - - // Unpack private artifacts though - if (asar && fs.existsSync(path.join(tempDestBuildDir, privateArtifactsPath))) { - // @ts-expect-error - asar = { unpackDir: privateArtifactsPath }; - } - - packager({ - dir: tempDestBuildDir, - appCopyright: "tobspr Games", - appVersion: getVersion(), - buildVersion: "1.0.0", - arch, - platform, - asar: asar, - executableName: "shapezio", - icon: path.join(electronBaseDir, "favicon"), - name: "shapez", - out: tempDestDir, - overwrite: true, - appBundleId: "tobspr.shapezio." + variant, - appCategoryType: "public.app-category.games", - ...(isRelease && - platform === "darwin" && { - osxSign: { - "identity": process.env.SHAPEZ_CLI_APPLE_CERT_NAME, - "hardenedRuntime": true, - "entitlements": "entitlements.plist", - "entitlements-inherit": "entitlements.plist", - // @ts-ignore - "signatureFlags": ["library"], - "version": "16.0.7", - }, - osxNotarize: { - appleId: process.env.SHAPEZ_CLI_APPLE_ID, - appleIdPassword: process.env.SHAPEZ_CLI_APPLE_APP_PW, - }, - }), - }).then( - appPaths => { - console.log("Packages created:", appPaths); - appPaths.forEach(appPath => { - if (!fs.existsSync(appPath)) { - console.error("Bad app path:", appPath); - return; - } - - if (variantData.steamAppId) { - fs.writeFileSync( - path.join(appPath, "LICENSE"), - fs.readFileSync(path.join(__dirname, "..", "LICENSE")) - ); - - fs.writeFileSync( - path.join(appPath, "steam_appid.txt"), - String(variantData.steamAppId) - ); - - if (platform === "linux") { - // Write launcher script - fs.writeFileSync( - path.join(appPath, "play.sh"), - '#!/usr/bin/env bash\n./shapezio --no-sandbox "$@"\n' - ); - fs.chmodSync(path.join(appPath, "play.sh"), 0o775); - } - - if (platform === "darwin") { - if (!isRelease) { - // Needs special location - fs.writeFileSync( - path.join( - appPath, - "shapez.app", - "Contents", - "MacOS", - "steam_appid.txt" - ), - String(variantData.steamAppId) - ); - } - } - } - }); - - cb(); - }, - err => { - console.error("Packaging error:", err); - cb(); - } - ); - } - - // Manual signing with patched @electron/osx-sign (we need --no-strict) - gulp.task(taskPrefix + ".package.darwin64", cb => - packageStandalone( - "darwin", - "x64", - () => { - const appFile = path.join(tempDestDir, "shapez-darwin-x64"); - const appFileInner = path.join(appFile, "shapez.app"); - console.warn("++ Signing ++"); - - if (variantData.steamAppId) { - const appIdDest = path.join( - path.join(appFileInner, "Contents", "MacOS"), - "steam_appid.txt" - ); - // console.warn("++ Preparing ++"); - // fse.copySync(path.join(tempDestBuildDir, "steam_appid.txt"), appIdDest); - - console.warn("Signing steam_appid.txt"); - - execSync( - `codesign --force --verbose --options runtime --timestamp --no-strict --sign "${ - process.env.SHAPEZ_CLI_APPLE_CERT_NAME - }" --entitlements "${path.join(__dirname, "entitlements.plist")}" ${appIdDest}`, - { - cwd: appFile, - } - ); - } - - console.warn("Base dir:", appFile); - - signAsync({ - app: appFileInner, - hardenedRuntime: true, - identity: process.env.SHAPEZ_CLI_APPLE_CERT_NAME, - strictVerify: false, - - version: "16.0.7", - type: "distribution", - optionsForFile: f => { - return { - entitlements: path.join(__dirname, "entitlements.plist"), - hardenedRuntime: true, - signatureFlags: ["runtime"], - }; - }, - }).then(() => { - execSync(`codesign --verify --verbose ${path.join(appFile, "shapez.app")}`, { - cwd: appFile, - }); - - console.warn("++ Notarizing ++"); - electronNotarize - .notarize({ - appPath: path.join(appFile, "shapez.app"), - tool: "legacy", - appBundleId: "tobspr.shapezio.standalone", - - appleId: process.env.SHAPEZ_CLI_APPLE_ID, - appleIdPassword: process.env.SHAPEZ_CLI_APPLE_APP_PW, - teamId: process.env.SHAPEZ_CLI_APPLE_TEAM_ID, - }) - .then(() => { - console.warn("-> Notarized!"); - cb(); - }); - }); - }, - false - ) - ); - - gulp.task(taskPrefix + ".package.win64", cb => packageStandalone("win32", "x64", cb)); - gulp.task(taskPrefix + ".package.linux64", cb => packageStandalone("linux", "x64", cb)); - gulp.task( - taskPrefix + ".build-from-windows", - gulp.series( - taskPrefix + ".prepare", - gulp.parallel(taskPrefix + ".package.win64", taskPrefix + ".package.linux64") - ) - ); - gulp.task( - taskPrefix + ".build-from-darwin", - gulp.series(taskPrefix + ".prepare", gulp.parallel(taskPrefix + ".package.darwin64")) - ); - } - - // Steam helpers - gulp.task("standalone.prepareVDF", cb => { - const hash = buildutils.getRevision(); - const version = buildutils.getVersion(); - - // for (const platform of ["steampipe", "steampipe-darwin"]) { - const templatesSource = path.join(__dirname, "steampipe", "templates"); - const templatesDest = path.join(__dirname, "steampipe", "built_vdfs"); - - const variables = { - PROJECT_DIR: path.resolve(path.join(__dirname, "..")).replace(/\\/g, "/"), - BUNDLE_DIR: path.resolve(path.join(__dirname, "..", "build_output")).replace(/\\/g, "/"), - - TMP_DIR: path.resolve(path.join(__dirname, "steampipe", "tmp")).replace(/\\/g, "/"), - // BUILD_DESC: "v" + version + " @ " + hash, - VDF_DIR: path.resolve(path.join(__dirname, "steampipe", "built_vdfs")).replace(/\\/g, "/"), - }; - - const files = fs.readdirSync(templatesSource); - for (const file of files) { - if (!file.endsWith(".vdf")) { - continue; - } - - variables.BUILD_DESC = file.replace(".vdf", "") + " - v" + version + " @ " + hash; - - let content = fs.readFileSync(path.join(templatesSource, file)).toString("utf-8"); - content = content.replace(/\$([^$]+)\$/gi, (_, variable) => { - if (!variables[variable]) { - throw new Error("Unknown variable " + variable + " in " + file); - } - - return variables[variable]; - }); - - fs.writeFileSync(path.join(templatesDest, file), content, { encoding: "utf8" }); - } - cb(); - }); -} - -module.exports = { gulptasksStandalone }; + ]; + }) +); diff --git a/gulp/steampipe/.gitignore b/gulp/steampipe/.gitignore deleted file mode 100644 index 0fced5d4..00000000 --- a/gulp/steampipe/.gitignore +++ /dev/null @@ -1 +0,0 @@ -steamtemp diff --git a/gulp/steampipe/templates/app-darwin-demo.vdf b/gulp/steampipe/templates/app-darwin-demo.vdf deleted file mode 100644 index 4bdb46b1..00000000 --- a/gulp/steampipe/templates/app-darwin-demo.vdf +++ /dev/null @@ -1,14 +0,0 @@ -"appbuild" -{ - "appid" "1930750" - "desc" "$BUILD_DESC$" - "buildoutput" "$TMP_DIR$" - "contentroot" "" - "setlive" "" - "preview" "0" - "local" "" - "depots" - { - "1930756" "$VDF_DIR$/demo-darwin.vdf" - } -} diff --git a/gulp/steampipe/templates/app-darwin.vdf b/gulp/steampipe/templates/app-darwin.vdf deleted file mode 100644 index fa63b846..00000000 --- a/gulp/steampipe/templates/app-darwin.vdf +++ /dev/null @@ -1,14 +0,0 @@ -"appbuild" -{ - "appid" "1318690" - "desc" "$BUILD_DESC$" - "buildoutput" "$TMP_DIR$" - "contentroot" "" - "setlive" "" - "preview" "0" - "local" "" - "depots" - { - "1318693" "$VDF_DIR$/standalone-darwin.vdf" - } -} diff --git a/gulp/steampipe/templates/app-winlinux-demo.vdf b/gulp/steampipe/templates/app-winlinux-demo.vdf deleted file mode 100644 index b4859b8b..00000000 --- a/gulp/steampipe/templates/app-winlinux-demo.vdf +++ /dev/null @@ -1,17 +0,0 @@ -"appbuild" -{ - "appid" "1930750" - "desc" "$BUILD_DESC$" - "buildoutput" "$TMP_DIR$" - "contentroot" "" - "setlive" "" - "preview" "0" - "local" "" - "depots" - { - "1930753" "$VDF_DIR$/demo-windows.vdf" - "1930754" "$VDF_DIR$/demo-china-windows.vdf" - "1930752" "$VDF_DIR$/demo-linux.vdf" - "1930755" "$VDF_DIR$/demo-china-linux.vdf" - } -} diff --git a/gulp/steampipe/templates/app-winlinux.vdf b/gulp/steampipe/templates/app-winlinux.vdf deleted file mode 100644 index 9fd7f9df..00000000 --- a/gulp/steampipe/templates/app-winlinux.vdf +++ /dev/null @@ -1,17 +0,0 @@ -"appbuild" -{ - "appid" "1318690" - "desc" "$BUILD_DESC$" - "buildoutput" "$TMP_DIR$" - "contentroot" "" - "setlive" "" - "preview" "0" - "local" "" - "depots" - { - "1318691" "$VDF_DIR$\standalone-windows.vdf" - "1318694" "$VDF_DIR$\standalone-china-windows.vdf" - "1318692" "$VDF_DIR$\standalone-linux.vdf" - "1318695" "$VDF_DIR$\standalone-china-linux.vdf" - } -} diff --git a/gulp/steampipe/templates/demo-china-linux.vdf b/gulp/steampipe/templates/demo-china-linux.vdf deleted file mode 100644 index 2ec63419..00000000 --- a/gulp/steampipe/templates/demo-china-linux.vdf +++ /dev/null @@ -1,12 +0,0 @@ -"DepotBuildConfig" -{ - "DepotID" "1930755" - "contentroot" "$BUNDLE_DIR$\standalone-steam-china-demo\shapez-linux-x64" - "FileMapping" - { - "LocalPath" "*" - "DepotPath" "." - "recursive" "1" - } - "FileExclusion" "*.pdb" -} \ No newline at end of file diff --git a/gulp/steampipe/templates/demo-china-windows.vdf b/gulp/steampipe/templates/demo-china-windows.vdf deleted file mode 100644 index a06b4e9e..00000000 --- a/gulp/steampipe/templates/demo-china-windows.vdf +++ /dev/null @@ -1,12 +0,0 @@ -"DepotBuildConfig" -{ - "DepotID" "1930754" - "contentroot" "$BUNDLE_DIR$\standalone-steam-china-demo\shapez-win32-x64" - "FileMapping" - { - "LocalPath" "*" - "DepotPath" "." - "recursive" "1" - } - "FileExclusion" "*.pdb" -} \ No newline at end of file diff --git a/gulp/steampipe/templates/demo-darwin.vdf b/gulp/steampipe/templates/demo-darwin.vdf deleted file mode 100644 index d0e8f1e2..00000000 --- a/gulp/steampipe/templates/demo-darwin.vdf +++ /dev/null @@ -1,12 +0,0 @@ -"DepotBuildConfig" -{ - "DepotID" "1930756" - "contentroot" "$BUNDLE_DIR$\standalone-steam-demo\shapez-darwin-x64" - "FileMapping" - { - "LocalPath" "*" - "DepotPath" "." - "recursive" "1" - } - "FileExclusion" "*.pdb" -} \ No newline at end of file diff --git a/gulp/steampipe/templates/demo-linux.vdf b/gulp/steampipe/templates/demo-linux.vdf deleted file mode 100644 index 4f2d274f..00000000 --- a/gulp/steampipe/templates/demo-linux.vdf +++ /dev/null @@ -1,12 +0,0 @@ -"DepotBuildConfig" -{ - "DepotID" "1930752" - "contentroot" "$BUNDLE_DIR$\standalone-steam-demo\shapez-linux-x64" - "FileMapping" - { - "LocalPath" "*" - "DepotPath" "." - "recursive" "1" - } - "FileExclusion" "*.pdb" -} \ No newline at end of file diff --git a/gulp/steampipe/templates/demo-windows.vdf b/gulp/steampipe/templates/demo-windows.vdf deleted file mode 100644 index 1b6cdbc7..00000000 --- a/gulp/steampipe/templates/demo-windows.vdf +++ /dev/null @@ -1,12 +0,0 @@ -"DepotBuildConfig" -{ - "DepotID" "1930753" - "contentroot" "$BUNDLE_DIR$\standalone-steam-demo\shapez-win32-x64" - "FileMapping" - { - "LocalPath" "*" - "DepotPath" "." - "recursive" "1" - } - "FileExclusion" "*.pdb" -} \ No newline at end of file diff --git a/gulp/steampipe/templates/standalone-china-linux.vdf b/gulp/steampipe/templates/standalone-china-linux.vdf deleted file mode 100644 index 56b9fe3c..00000000 --- a/gulp/steampipe/templates/standalone-china-linux.vdf +++ /dev/null @@ -1,12 +0,0 @@ -"DepotBuildConfig" -{ - "DepotID" "1318695" - "contentroot" "$BUNDLE_DIR$\standalone-steam-china\shapez-linux-x64" - "FileMapping" - { - "LocalPath" "*" - "DepotPath" "." - "recursive" "1" - } - "FileExclusion" "*.pdb" -} \ No newline at end of file diff --git a/gulp/steampipe/templates/standalone-china-windows.vdf b/gulp/steampipe/templates/standalone-china-windows.vdf deleted file mode 100644 index 469158db..00000000 --- a/gulp/steampipe/templates/standalone-china-windows.vdf +++ /dev/null @@ -1,12 +0,0 @@ -"DepotBuildConfig" -{ - "DepotID" "1318694" - "contentroot" "$BUNDLE_DIR$\standalone-steam-china\shapez-win32-x64" - "FileMapping" - { - "LocalPath" "*" - "DepotPath" "." - "recursive" "1" - } - "FileExclusion" "*.pdb" -} \ No newline at end of file diff --git a/gulp/steampipe/templates/standalone-darwin.vdf b/gulp/steampipe/templates/standalone-darwin.vdf deleted file mode 100644 index 026ab768..00000000 --- a/gulp/steampipe/templates/standalone-darwin.vdf +++ /dev/null @@ -1,12 +0,0 @@ -"DepotBuildConfig" -{ - "DepotID" "1318693" - "contentroot" "$BUNDLE_DIR$\standalone-steam\shapez-darwin-x64" - "FileMapping" - { - "LocalPath" "*" - "DepotPath" "." - "recursive" "1" - } - "FileExclusion" "*.pdb" -} \ No newline at end of file diff --git a/gulp/steampipe/templates/standalone-linux.vdf b/gulp/steampipe/templates/standalone-linux.vdf deleted file mode 100644 index 9edb1963..00000000 --- a/gulp/steampipe/templates/standalone-linux.vdf +++ /dev/null @@ -1,12 +0,0 @@ -"DepotBuildConfig" -{ - "DepotID" "1318692" - "contentroot" "$BUNDLE_DIR$\standalone-steam\shapez-linux-x64" - "FileMapping" - { - "LocalPath" "*" - "DepotPath" "." - "recursive" "1" - } - "FileExclusion" "*.pdb" -} \ No newline at end of file diff --git a/gulp/steampipe/templates/standalone-windows.vdf b/gulp/steampipe/templates/standalone-windows.vdf deleted file mode 100644 index 6f7cb408..00000000 --- a/gulp/steampipe/templates/standalone-windows.vdf +++ /dev/null @@ -1,12 +0,0 @@ -"DepotBuildConfig" -{ - "DepotID" "1318691" - "contentroot" "$BUNDLE_DIR$\standalone-steam\shapez-win32-x64" - "FileMapping" - { - "LocalPath" "*" - "DepotPath" "." - "recursive" "1" - } - "FileExclusion" "*.pdb" -} \ No newline at end of file diff --git a/gulp/steampipe/upload-darwin-demo.sh b/gulp/steampipe/upload-darwin-demo.sh deleted file mode 100755 index 77bb29dc..00000000 --- a/gulp/steampipe/upload-darwin-demo.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -yarn gulp standalone.prepareVDF -steamcmd.sh +login $STEAM_UPLOAD_SHAPEZ_ID $STEAM_UPLOAD_SHAPEZ_USER +run_app_build $PWD/built_vdfs/app-darwin-demo.vdf +quit diff --git a/gulp/steampipe/upload-darwin.sh b/gulp/steampipe/upload-darwin.sh deleted file mode 100755 index 06412dcd..00000000 --- a/gulp/steampipe/upload-darwin.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -yarn gulp standalone.prepareVDF -steamcmd.sh +login $STEAM_UPLOAD_SHAPEZ_ID $STEAM_UPLOAD_SHAPEZ_USER +run_app_build $PWD/built_vdfs/app-darwin.vdf +quit diff --git a/gulp/steampipe/upload-winlinux-demo.bat b/gulp/steampipe/upload-winlinux-demo.bat deleted file mode 100644 index e19f7f55..00000000 --- a/gulp/steampipe/upload-winlinux-demo.bat +++ /dev/null @@ -1,3 +0,0 @@ -@echo off -cmd /c yarn gulp standalone.prepareVDF -steamcmd +login %STEAM_UPLOAD_SHAPEZ_ID% %STEAM_UPLOAD_SHAPEZ_USER% +run_app_build %cd%/built_vdfs/app-winlinux-demo.vdf +quit diff --git a/gulp/steampipe/upload-winlinux.bat b/gulp/steampipe/upload-winlinux.bat deleted file mode 100644 index 1c9bdfe7..00000000 --- a/gulp/steampipe/upload-winlinux.bat +++ /dev/null @@ -1,3 +0,0 @@ -@echo off -cmd /c yarn gulp standalone.prepareVDF -steamcmd +login %STEAM_UPLOAD_SHAPEZ_ID% %STEAM_UPLOAD_SHAPEZ_USER% +run_app_build %cd%/built_vdfs/app-winlinux.vdf +quit diff --git a/gulp/tasks.js b/gulp/tasks.js new file mode 100644 index 00000000..a378c68e --- /dev/null +++ b/gulp/tasks.js @@ -0,0 +1,272 @@ +import childProcess from "child_process"; +import delEmpty from "delete-empty"; +import gulp from "gulp"; +import pathNative from "path"; +import path from "path/posix"; +import { promisify } from "util"; +import { BUILD_VARIANTS } from "./build_variants.js"; +import { + browserSync, + buildFolder, + buildOutputFolder, + generatedCodeFolder, + imageResourcesGlobs, + nonImageResourcesGlobs, + rawImageResourcesGlobs, +} from "./config.js"; +const exec = promisify(childProcess.exec); + +// Load other plugins +import gulpClean from "gulp-clean"; +import gulpWebserver from "gulp-webserver"; + +import * as css from "./css.js"; +import * as environment from "./environment.js"; +import html from "./html.js"; +import * as imgres from "./image-resources.js"; +import js from "./js.js"; +import * as sounds from "./sounds.js"; +import standalone from "./standalone.js"; +import * as translations from "./translations.js"; + +export { css, environment, html, imgres, js, sounds, standalone, translations }; + +///////////////////// BUILD TASKS ///////////////////// + +// Cleans up everything +function cleanBuildFolder() { + return gulp.src(buildFolder, { read: false, allowEmpty: true }).pipe(gulpClean({ force: true })); +} +function cleanBuildOutputFolder() { + return gulp.src(buildOutputFolder, { read: false, allowEmpty: true }).pipe(gulpClean({ force: true })); +} +function cleanBuildTempFolder() { + return gulp.src(generatedCodeFolder, { read: false, allowEmpty: true }).pipe(gulpClean({ force: true })); +} +function cleanImageBuildFolder() { + return gulp + .src(path.join("res_built"), { read: false, allowEmpty: true }) + .pipe(gulpClean({ force: true })); +} + +const cleanup = gulp.parallel(cleanBuildFolder, cleanImageBuildFolder, cleanBuildTempFolder); + +// Requires no uncomitted files +async function requireCleanWorkingTree() { + let output = (await exec("git status -su", { encoding: "buffer" })).stdout + .toString("ascii") + .trim() + .replace(/\r/gi, "") + .split("\n"); + + // Filter files which are OK to be untracked + output = output + .map(x => x.replace(/[\r\n]+/gi, "")) + .filter(x => x.indexOf(".local.js") < 0) + .filter(x => x.length > 0); + if (output.length > 0) { + console.error("\n\nYou have unstaged changes, please commit everything first!"); + console.error("Unstaged files:"); + console.error(output.map(x => "'" + x + "'").join("\n")); + process.exit(1); + } +} + +function copyAdditionalBuildFiles() { + const additionalFolder = path.join("additional_build_files"); + const additionalSrcGlobs = [ + path.join(additionalFolder, "**/*.*"), + path.join(additionalFolder, "**/.*"), + path.join(additionalFolder, "**/*"), + ]; + + return gulp.src(additionalSrcGlobs).pipe(gulp.dest(buildFolder)); +} + +export const utils = { + cleanBuildFolder, + cleanBuildOutputFolder, + cleanBuildTempFolder, + cleanImageBuildFolder, + cleanup, + requireCleanWorkingTree, + copyAdditionalBuildFiles, +}; + +// Starts a webserver on the built directory (useful for testing prod build) +function webserver() { + return gulp.src(buildFolder).pipe( + gulpWebserver({ + livereload: { + enable: true, + }, + directoryListing: false, + open: true, + port: 3005, + }) + ); +} + +/** + * + * @param {object} param0 + * @param {keyof typeof BUILD_VARIANTS} param0.version + */ +async function serveHTML({ version = "web-dev" }) { + browserSync.init({ + server: buildFolder, + port: 3005, + ghostMode: false, + logLevel: "info", + logPrefix: "BS", + online: false, + notify: false, + reloadDebounce: 100, + watchEvents: ["add", "change"], + open: false, + }); + + gulp.watch("../src/js/**", js[version].dev.build); + + // Watch .scss files, those trigger a css rebuild + gulp.watch("../src/css/**", css.dev); + + // Watch .html files, those trigger a html rebuild + gulp.watch("../src/html/**", html); + + // Watch translations + gulp.watch("../translations/*.yaml", translations.convertToJson); + + gulp.watch( + ["../res_raw/sounds/sfx/**/*.mp3", "../res_raw/sounds/sfx/**/*.wav"], + gulp.series(sounds.sfx, sounds.copy) + ); + gulp.watch( + ["../res_raw/sounds/music/**/*.mp3", "../res_raw/sounds/music/**/*.wav"], + gulp.series(sounds.music, sounds.copy) + ); + + // Watch resource files and copy them on change + gulp.watch(rawImageResourcesGlobs, imgres.buildAtlas); + gulp.watch(nonImageResourcesGlobs, imgres.copyNonImageResources); + gulp.watch(imageResourcesGlobs, imgres.copyImageResources); + + // Watch .atlas files and recompile the atlas on change + gulp.watch("../res_built/atlas/*.atlas", imgres.atlasToJson); + gulp.watch("../res_built/atlas/*.json", imgres.atlas); + + // Watch the build folder and reload when anything changed + gulp.watch(path.join(buildFolder, "**")).on("change", p => + gulp.src(pathNative.resolve(p).replaceAll(pathNative.sep, path.sep)).pipe(browserSync.stream()) + ); +} + +// Pre and postbuild +const baseResources = imgres.allOptimized; +async function deleteEmpty() { + await delEmpty(buildFolder); +} + +const postbuild = gulp.series(imgres.cleanupUnusedCssInlineImages, deleteEmpty); + +export const step = { + baseResources, + deleteEmpty, + postbuild, +}; + +///////////////////// RUNNABLE TASKS ///////////////////// + +// Builds everything (dev) +const prepare = { + dev: variant => + gulp.series( + utils.cleanup, + gulp.parallel( + utils.copyAdditionalBuildFiles, + gulp.series(imgres.buildAtlas, gulp.parallel(imgres.atlasToJson, imgres.atlas)), + gulp.series(imgres.copyImageResources, css.dev), + imgres.copyNonImageResources, + html, + gulp.series(gulp.parallel(sounds.dev, translations.fullBuild), js[variant].dev.build) + ) + ), +}; + +/** + * @typedef {import("gulp").TaskFunction} TaskFunction + */ + +export const build = + /** + * @type {Record & { prepare: typeof prepare }} + */ + ({ + prepare, + }); +/** + * @type {Record>} + */ +const pack = {}; +export { pack as package }; +/** @type {Record} */ +export const serve = {}; + +// Builds everything for every variant +for (const variant in BUILD_VARIANTS) { + const data = BUILD_VARIANTS[variant]; + + // build + const code = gulp.series( + data.standalone ? sounds.fullbuildHQ : sounds.fullbuild, + translations.fullBuild, + js[variant].prod.build + ); + + const resourcesAndCode = gulp.parallel(step.baseResources, code); + + const all = gulp.series(resourcesAndCode, css.prod, html); + + const full = gulp.series(utils.cleanup, all, step.postbuild); + + build[variant] = { code, resourcesAndCode, all, full }; + + // Tasks for creating packages. These packages are already distributable, but usually can be further + // wrapped in a different format (an installer for Windows, tarball for Linux, DMG for macOS). + if (data.standalone) { + const packageTasks = [ + "win32-x64", + "win32-arm64", + "linux-x64", + "linux-arm64", + "darwin-x64", + "darwin-arm64", + "all", + ]; + + pack[variant] = {}; + for (const task of packageTasks) { + pack[variant][task] = gulp.series( + full, + utils.cleanBuildOutputFolder, + standalone[variant].prepare.all, + standalone[variant].package[task] + ); + } + } + + // serve + serve[variant] = gulp.series(build.prepare.dev(variant), () => serveHTML({ version: variant })); +} + +export const main = { + webserver, +}; + +// Default task (dev, localhost) +export default gulp.series(serve["standalone"]); diff --git a/gulp/translations.js b/gulp/translations.js index 88afa989..8813a069 100644 --- a/gulp/translations.js +++ b/gulp/translations.js @@ -1,64 +1,18 @@ -const path = require("path"); -const fs = require("fs"); -const gulpYaml = require("gulp-yaml"); -const YAML = require("yaml"); -const stripIndent = require("strip-indent"); -const trim = require("trim"); - -const translationsSourceDir = path.join(__dirname, "..", "translations"); -const translationsJsonDir = path.join(__dirname, "..", "src", "js", "built-temp"); - -function gulptasksTranslations($, gulp) { - gulp.task("translations.convertToJson", () => { - return gulp - .src(path.join(translationsSourceDir, "*.yaml")) - .pipe($.plumber()) - .pipe(gulpYaml({ space: 2, safe: true })) - .pipe(gulp.dest(translationsJsonDir)); - }); - - gulp.task("translations.fullBuild", gulp.series("translations.convertToJson")); - - gulp.task("translations.prepareSteamPage", cb => { - const files = fs.readdirSync(translationsSourceDir); - - files - .filter(name => name.endsWith(".yaml")) - .forEach(fname => { - console.log("Loading", fname); - const languageName = fname.replace(".yaml", ""); - const abspath = path.join(translationsSourceDir, fname); - - const destpath = path.join(translationsSourceDir, "tmp", languageName + "-store.txt"); - - const contents = fs.readFileSync(abspath, { encoding: "utf-8" }); - const data = YAML.parse(contents); - - const storePage = data.steamPage; - - const content = ` - [img]{STEAM_APP_IMAGE}/extras/store_page_gif.gif[/img] - - ${storePage.intro.replace(/\n/gi, "\n\n")} - - [h2]${storePage.what_others_say}[/h2] - - [list] - [*] [i]${storePage.nothernlion_comment}[/i] [b]- Northernlion, YouTube[/b] - [*] [i]${storePage.notch_comment}[/i] [b]- Notch[/b] - [*] [i]${storePage.steam_review_comment}[/i] [b]- Steam User[/b] - [/list] - `; - - fs.writeFileSync(destpath, trim(content.replace(/(\n[ \t\r]*)/gi, "\n")), { - encoding: "utf-8", - }); - }); - - cb(); - }); -} - -module.exports = { - gulptasksTranslations, -}; +import gulp from "gulp"; +import path from "path/posix"; + +import gulpPlumber from "gulp-plumber"; +import gulpYaml from "gulp-yaml"; +import { generatedCodeFolder } from "./config.js"; + +const translationsSourceDir = path.join("..", "translations"); + +export function convertToJson() { + return gulp + .src(path.join(translationsSourceDir, "*.yaml")) + .pipe(gulpPlumber()) + .pipe(gulpYaml({ space: 2, safe: true })) + .pipe(gulp.dest(generatedCodeFolder)); +} + +export const fullBuild = convertToJson; diff --git a/gulp/tsconfig.json b/gulp/tsconfig.json deleted file mode 100644 index 0de5c39f..00000000 --- a/gulp/tsconfig.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "compilerOptions": { - /* Basic Options */ - "target": "es6" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */, - "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */, - // "lib": [], /* Specify library files to be included in the compilation. */ - "allowJs": true /* Allow javascript files to be compiled. */, - "checkJs": true /* Report errors in .js files. */, - // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ - // "declaration": true, /* Generates corresponding '.d.ts' file. */ - // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ - // "sourceMap": true, /* Generates corresponding '.map' file. */ - // "outFile": "./typedefs_gen", /* Concatenate and emit output to single file. */ - // "outDir": "./typedefs_gen", /* Redirect output structure to the directory. */ - // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ - // "composite": true, /* Enable project compilation */ - // "incremental": true, /* Enable incremental compilation */ - // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ - // "removeComments": true, /* Do not emit comments to output. */ - "noEmit": true /* Do not emit outputs. */, - // "importHelpers": true, /* Import emit helpers from 'tslib'. */ - // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ - // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ - /* Strict Type-Checking Options */ - // "strict": true, /* Enable all strict type-checking options. */ - // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ - // "strictNullChecks": true, /* Enable strict null checks. */ - "strictFunctionTypes": true /* Enable strict checking of function types. */, - "strictBindCallApply": true /* Enable strict 'bind', 'call', and 'apply' methods on functions. */, - // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ - "noImplicitThis": true /* Raise error on 'this' expressions with an implied 'any' type. */, - "alwaysStrict": true /* Parse in strict mode and emit "use strict" for each source file. */, - /* Additional Checks */ - // "noUnusedLocals": true, /* Report errors on unused locals. */ - // "noUnusedParameters": true, /* Report errors on unused parameters. */ - // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ - "noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */, - /* Module Resolution Options */ - "moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */, - // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ - // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ - // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ - // "typeRoots": [], /* List of folders to include type definitions from. */ - // "types": [], /* Type declaration files to be included in compilation. */ - // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ - "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, - // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ - /* Source Map Options */ - // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ - // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ - /* Experimental Options */ - // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ - // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ - "resolveJsonModule": true - }, - "exclude": ["backend/shared/gameserver_base_impl"] -} diff --git a/gulp/typescript.js b/gulp/typescript.js new file mode 100644 index 00000000..48cb7f72 --- /dev/null +++ b/gulp/typescript.js @@ -0,0 +1,66 @@ +import * as path from "path"; +import ts from "typescript"; + +/** + * @param {ts.Diagnostic} diagnostic + */ +function printDiagnostic(diagnostic) { + if (!diagnostic.file) { + console.log(ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n")); + return; + } + + const { line, character } = ts.getLineAndCharacterOfPosition(diagnostic.file, diagnostic.start); + const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n"); + console.log(`${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`); +} + +/** + * Reads the TypeScript compiler configuration from the specified path. + * @param {string} configPath Path to the tsconfig.json file + * @param {string} baseDir Directory used to resolve relative file paths + * @param {string?} outDir Optional override for output directory + */ +function readConfig(configPath, baseDir, outDir) { + // Please forgive me for this sin, copied from random parts of TS itself + const cfgSource = ts.sys.readFile(configPath); + const result = ts.parseJsonText(configPath, cfgSource); + + return ts.parseJsonSourceFileConfigFileContent( + result, + ts.sys, + baseDir, + outDir ? { outDir } : undefined, + configPath + ); +} + +/** + * Builds a TypeScript project. + * Mostly based on https://github.com/microsoft/TypeScript/wiki/Using-the-Compiler-API + * @param {string} configPath Path to the tsconfig.json file + * @param {string?} baseDir Directory used to resolve relative file paths + * @param {string?} outDir Optional override for output directory + */ +export function buildProject(configPath, baseDir = undefined, outDir = undefined) { + configPath = path.resolve(configPath); + + if (baseDir === undefined) { + baseDir = path.dirname(configPath); + } + baseDir = path.resolve(baseDir); + + const config = readConfig(configPath, baseDir, outDir); + const program = ts.createProgram(config.fileNames, config.options); + const result = program.emit(); + + const diagnostics = ts.getPreEmitDiagnostics(program).concat(result.diagnostics); + for (const diagnostic of diagnostics) { + printDiagnostic(diagnostic); + } + + const success = !result.emitSkipped; + if (!success) { + throw new Error("TypeScript compilation failed! Relevant errors may have been displayed above."); + } +} diff --git a/gulp/webpack.config.js b/gulp/webpack.config.js index d0ee4d5f..90c19a4f 100644 --- a/gulp/webpack.config.js +++ b/gulp/webpack.config.js @@ -1,127 +1,89 @@ -// @ts-nocheck +import CircularDependencyPlugin from "circular-dependency-plugin"; +import { resolve } from "path/posix"; +import webpack from "webpack"; +import { getAllResourceImages, getRevision, getVersion } from "./buildutils.js"; -const path = require("path"); -const webpack = require("webpack"); -const { getRevision, getVersion, getAllResourceImages } = require("./buildutils"); -const CircularDependencyPlugin = require("circular-dependency-plugin"); +const globalDefs = { + assert: "window.assert", + assertAlways: "window.assert", + abstract: + "window.assert(false, 'abstract method called of: ' + " + + "(this.name || (this.constructor && this.constructor.name)));", + G_IS_DEV: "true", + G_APP_ENVIRONMENT: JSON.stringify("development"), + G_BUILD_TIME: new Date().getTime().toString(), + G_BUILD_COMMIT_HASH: JSON.stringify(getRevision()), + G_BUILD_VERSION: JSON.stringify(getVersion()), + G_ALL_UI_IMAGES: JSON.stringify(getAllResourceImages()), -module.exports = ({ - watch = false, - standalone = false, - chineseVersion = false, - wegameVersion = false, - steamDemo = false, - gogVersion = false, -}) => { - return { - mode: "development", - devtool: "cheap-source-map", - entry: { - "bundle.js": [path.resolve(__dirname, "../src/js/main.js")], - }, - watch, - node: { - fs: "empty", - }, - resolve: { - alias: { - "global-compression": path.resolve(__dirname, "..", "src", "js", "core", "lzstring.js"), - }, - }, - context: path.resolve(__dirname, ".."), - plugins: [ - new webpack.DefinePlugin({ - assert: "window.assert", - assertAlways: "window.assert", - abstract: - "window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));", - G_HAVE_ASSERT: "true", - G_APP_ENVIRONMENT: JSON.stringify("dev"), - G_CHINA_VERSION: JSON.stringify(chineseVersion), - G_WEGAME_VERSION: JSON.stringify(wegameVersion), - G_GOG_VERSION: JSON.stringify(gogVersion), - G_IS_DEV: "true", - G_IS_RELEASE: "false", - G_IS_BROWSER: "true", - G_IS_STANDALONE: JSON.stringify(standalone), - G_IS_STEAM_DEMO: JSON.stringify(steamDemo), - G_BUILD_TIME: "" + new Date().getTime(), - G_BUILD_COMMIT_HASH: JSON.stringify(getRevision()), - G_BUILD_VERSION: JSON.stringify(getVersion()), - G_ALL_UI_IMAGES: JSON.stringify(getAllResourceImages()), - }), - - new CircularDependencyPlugin({ - // exclude detection of files based on a RegExp - exclude: /node_modules/, - - // add errors to webpack instead of warnings - failOnError: true, - - // allow import cycles that include an asyncronous import, - // e.g. via import(/* webpackMode: "weak" */ './file.js') - allowAsyncCycles: false, - - // set the current working directory for displaying module paths - cwd: path.join(__dirname, "..", "src", "js"), - }), - ], - module: { - rules: [ - { - test: /\.json$/, - enforce: "pre", - use: ["./gulp/loader.compressjson"], - type: "javascript/auto", - }, - { test: /\.(png|jpe?g|svg)$/, loader: "ignore-loader" }, - { test: /\.nobuild/, loader: "ignore-loader" }, - { - test: /\.md$/, - use: [ - { - loader: "html-loader", - }, - "markdown-loader", - ], - }, - { - test: /\.js$/, - enforce: "pre", - exclude: /node_modules/, - use: [ - { - loader: "webpack-strip-block", - options: { - start: "typehints:start", - end: "typehints:end", - }, - }, - { - loader: path.resolve(__dirname, "mod.js"), - }, - ], - }, - { - test: /\.worker\.js$/, - use: { - loader: "worker-loader", - options: { - fallback: false, - inline: true, - }, - }, - }, - { - test: /\.ya?ml$/, - type: "json", // Required by Webpack v4 - use: "yaml-loader", - }, - ], - }, - output: { - filename: "bundle.js", - path: path.resolve(__dirname, "..", "build"), - }, - }; + G_IS_RELEASE: "false", +}; + +/** @type {import("webpack").RuleSetRule[]} */ +const moduleRules = [ + { + test: /\.jsx?$/, + enforce: "pre", + exclude: /node_modules/, + use: [ + { + loader: "webpack-strip-block", + options: { + start: "typehints:start", + end: "typehints:end", + }, + }, + ], + }, + { + test: /\.[jt]sx?$/, + use: [ + { + loader: "ts-loader", + + options: { + configFile: resolve("../src/tsconfig.json"), + onlyCompileBundledFiles: true, + transpileOnly: true, + experimentalWatchApi: true, + }, + }, + ], + resolve: { + fullySpecified: false, + }, + }, +]; + +/** @type {import("webpack").Configuration} */ +export default { + mode: "development", + entry: resolve("../src/js/main.js"), + context: resolve(".."), + output: { + path: resolve("../build"), + filename: "bundle.js", + }, + resolve: { + fallback: { fs: false }, + alias: { + "@": resolve("../src/js/"), + }, + fullySpecified: false, + extensions: [".ts", ".js", ".tsx", ".jsx"], + }, + devtool: "cheap-source-map", + cache: false, + plugins: [ + new webpack.DefinePlugin(globalDefs), + new webpack.IgnorePlugin({ resourceRegExp: /\.(png|jpe?g|svg)$/ }), + new webpack.IgnorePlugin({ resourceRegExp: /\.nobuild/ }), + new CircularDependencyPlugin({ + exclude: /node_modules/, + failOnError: true, + allowAsyncCycles: false, + cwd: resolve("../src/js"), + }), + ], + module: { rules: moduleRules }, }; diff --git a/gulp/webpack.production.config.js b/gulp/webpack.production.config.js index e324d675..91dd3217 100644 --- a/gulp/webpack.production.config.js +++ b/gulp/webpack.production.config.js @@ -1,269 +1,139 @@ -// @ts-nocheck +import { resolve } from "path/posix"; +import TerserPlugin from "terser-webpack-plugin"; +import webpack from "webpack"; +import DeadCodePlugin from "webpack-deadcode-plugin"; +import { getAllResourceImages, getRevision, getVersion } from "./buildutils.js"; +const { DefinePlugin, IgnorePlugin } = webpack; -const path = require("path"); -const webpack = require("webpack"); -const { getRevision, getVersion, getAllResourceImages } = require("./buildutils"); +const globalDefs = { + "assert": "false && window.assert", + "assertAlways": "window.assert", + "abstract": "window.assert(false, 'abstract method called');", + "globalConfig.debug": "({})", + "G_IS_DEV": "false", + "G_APP_ENVIRONMENT": JSON.stringify("release"), + "G_BUILD_TIME": new Date().getTime().toString(), + "G_BUILD_COMMIT_HASH": JSON.stringify(getRevision()), + "G_BUILD_VERSION": JSON.stringify(getVersion()), + "G_ALL_UI_IMAGES": JSON.stringify(getAllResourceImages()), -const TerserPlugin = require("terser-webpack-plugin"); -const StringReplacePlugin = require("string-replace-webpack-plugin"); -const UnusedFilesPlugin = require("unused-files-webpack-plugin").UnusedFilesWebpackPlugin; + "G_IS_RELEASE": "true", +}; -module.exports = ({ - environment, - es6 = false, - - standalone = false, - isBrowser = true, - - chineseVersion = false, - wegameVersion = false, - steamDemo = false, - gogVersion = false, -}) => { - const globalDefs = { - assert: "false && window.assert", - assertAlways: "window.assert", - abstract: "window.assert(false, 'abstract method called');", - G_IS_DEV: "false", - - G_CHINA_VERSION: JSON.stringify(chineseVersion), - G_WEGAME_VERSION: JSON.stringify(wegameVersion), - G_GOG_VERSION: JSON.stringify(gogVersion), - G_IS_RELEASE: environment === "prod" ? "true" : "false", - G_IS_STANDALONE: standalone ? "true" : "false", - G_IS_STEAM_DEMO: JSON.stringify(steamDemo), - G_IS_BROWSER: isBrowser ? "true" : "false", - G_APP_ENVIRONMENT: JSON.stringify(environment), - G_HAVE_ASSERT: "false", - G_BUILD_TIME: "" + new Date().getTime(), - G_BUILD_COMMIT_HASH: JSON.stringify(getRevision()), - G_BUILD_VERSION: JSON.stringify(getVersion()), - G_ALL_UI_IMAGES: JSON.stringify(getAllResourceImages()), - }; - - const minifyNames = false; - - return { - mode: "production", - entry: { - "bundle.js": [path.resolve(__dirname, "..", "src", "js", "main.js")], - }, - node: { - fs: "empty", - }, - output: { - filename: "bundle.js", - path: path.resolve(__dirname, "..", "build"), - }, - context: path.resolve(__dirname, ".."), - stats: { - // Examine all modules - maxModules: Infinity, - // Display bailout reasons - optimizationBailout: true, - }, - devtool: false, - resolve: { - alias: { - "global-compression": path.resolve(__dirname, "..", "src", "js", "core", "lzstring.js"), +/** @type {import("webpack").RuleSetRule[]} */ +const moduleRules = [ + { + test: /\.jsx?$/, + enforce: "pre", + exclude: /node_modules/, + use: [ + { + loader: "webpack-strip-block", + options: { + start: "typehints:start", + end: "typehints:end", + }, }, + { + // TODO: Consider removing this separation + loader: "webpack-strip-block", + options: { + start: "dev:start", + end: "dev:end", + }, + }, + ], + }, + { + test: /\.[jt]sx?$/, + use: [ + { + loader: "ts-loader", + + options: { + configFile: resolve("../src/tsconfig.json"), + onlyCompileBundledFiles: true, + transpileOnly: true, + experimentalWatchApi: true, + }, + }, + ], + resolve: { + fullySpecified: false, }, - optimization: { - minimize: true, - // namedModules: true, + }, +]; - noEmitOnErrors: true, - removeAvailableModules: true, - removeEmptyChunks: true, - mergeDuplicateChunks: true, - flagIncludedChunks: true, - occurrenceOrder: true, - providedExports: true, - usedExports: true, - concatenateModules: true, - sideEffects: true, - - minimizer: [ - new TerserPlugin({ - parallel: true, - sourceMap: false, - cache: false, - terserOptions: { - ecma: es6 ? 6 : 5, - parse: {}, - module: true, - toplevel: true, - keep_classnames: !minifyNames, - keep_fnames: !minifyNames, - keep_fargs: !minifyNames, - safari10: true, - compress: { - arguments: false, // breaks - drop_console: false, - global_defs: globalDefs, - keep_fargs: !minifyNames, - keep_infinity: true, - passes: 2, - module: true, - pure_funcs: [ - "Math.radians", - "Math.degrees", - "Math.round", - "Math.ceil", - "Math.floor", - "Math.sqrt", - "Math.hypot", - "Math.abs", - "Math.max", - "Math.min", - "Math.sin", - "Math.cos", - "Math.tan", - "Math.sign", - "Math.pow", - "Math.atan2", - ], - toplevel: true, - unsafe_math: true, - unsafe_arrows: false, - warnings: true, - }, - mangle: { - reserved: ["__$S__"], - eval: true, - keep_classnames: !minifyNames, - keep_fnames: !minifyNames, - module: true, - toplevel: true, - safari10: true, - }, - output: { - comments: false, - ascii_only: true, - beautify: false, - braces: false, - ecma: es6 ? 6 : 5, - preamble: - "/* shapez.io Codebase - Copyright 2022 tobspr Games - " + - getVersion() + - " @ " + - getRevision() + - " */", - }, +/** @type {import("webpack").Configuration} */ +export default { + mode: "production", + entry: resolve("../src/js/main.js"), + context: resolve(".."), + output: { + path: resolve("../build"), + filename: "bundle.js", + }, + resolve: { + fallback: { fs: false }, + alias: { + "@": resolve("../src/js/"), + }, + fullySpecified: false, + extensions: [".ts", ".js", ".tsx", ".jsx"], + }, + stats: { optimizationBailout: true }, + optimization: { + removeAvailableModules: true, + minimizer: [ + new TerserPlugin({ + parallel: true, + terserOptions: { + ecma: 2020, + module: true, + keep_fnames: true, + compress: { + global_defs: globalDefs, + keep_infinity: true, + passes: 2, + pure_funcs: [ + "Math.radians", + "Math.degrees", + "Math.round", + "Math.ceil", + "Math.floor", + "Math.sqrt", + "Math.hypot", + "Math.abs", + "Math.max", + "Math.min", + "Math.sin", + "Math.cos", + "Math.tan", + "Math.sign", + "Math.pow", + "Math.atan2", + ], + unsafe_math: true, }, - }), - ], - }, - performance: { - maxEntrypointSize: 5120000, - maxAssetSize: 5120000, - }, - plugins: [ - new webpack.DefinePlugin(globalDefs), - - new UnusedFilesPlugin({ - failOnUnused: false, - cwd: path.join(__dirname, "..", "src", "js"), - patterns: ["../src/js/**/*.js"], + format: { + comments: false, + ascii_only: true, + }, + }, }), ], - module: { - rules: [ - { - test: /\.json$/, - enforce: "pre", - use: ["./gulp/loader.compressjson"], - type: "javascript/auto", - }, - { test: /\.(png|jpe?g|svg)$/, loader: "ignore-loader" }, - { test: /\.nobuild/, loader: "ignore-loader" }, - { - test: /\.js$/, - enforce: "pre", - exclude: /node_modules/, - use: [ - { - loader: "webpack-strip-block", - options: { - start: "typehints:start", - end: "typehints:end", - }, - }, - { - loader: "webpack-strip-block", - options: { - start: "dev:start", - end: "dev:end", - }, - }, - { - loader: "webpack-strip-block", - options: { - start: "wires:start", - end: "wires:end", - }, - }, - ], - }, - { - test: /\.js$/, - use: [ - // "thread-loader", - { - loader: path.resolve(__dirname, "mod.js"), - }, - { - loader: "babel-loader?cacheDirectory", - options: { - configFile: require.resolve( - es6 ? "./babel-es6.config.js" : "./babel.config.js" - ), - }, - }, - "uglify-template-string-loader", // Finally found this plugin - StringReplacePlugin.replace({ - replacements: [ - { pattern: /globalConfig\.tileSize/g, replacement: () => "32" }, - { pattern: /globalConfig\.halfTileSize/g, replacement: () => "16" }, - { - pattern: /globalConfig\.beltSpeedItemsPerSecond/g, - replacement: () => "2.0", - }, - { pattern: /globalConfig\.debug/g, replacement: () => "''" }, - ], - }), - ], - }, - { - test: /\.worker\.js$/, - use: [ - { - loader: "worker-loader", - options: { - fallback: false, - inline: true, - }, - }, - { - loader: "babel-loader?cacheDirectory", - options: { - configFile: require.resolve( - es6 ? "./babel-es6.config.js" : "./babel.config.js" - ), - }, - }, - ], - }, - { - test: /\.md$/, - use: ["html-loader", "markdown-loader"], - }, - { - test: /\.ya?ml$/, - type: "json", // Required by Webpack v4 - use: "yaml-loader", - }, - ], - }, - }; + }, + plugins: [ + new DefinePlugin(globalDefs), + new IgnorePlugin({ resourceRegExp: /\.(png|jpe?g|svg)$/ }), + new IgnorePlugin({ resourceRegExp: /\.nobuild/ }), + new DeadCodePlugin({ + patterns: ["../src/js/**/*.js"], + }), + ], + module: { rules: moduleRules }, + performance: { + maxEntrypointSize: 5120000, + maxAssetSize: 5120000, + }, }; diff --git a/gulp/yarn.lock b/gulp/yarn.lock deleted file mode 100644 index 04f974f3..00000000 --- a/gulp/yarn.lock +++ /dev/null @@ -1,13476 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz" - integrity sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g== - dependencies: - "@babel/highlight" "^7.8.3" - -"@babel/core@^7.9.0": - version "7.9.0" - resolved "https://registry.npmjs.org/@babel/core/-/core-7.9.0.tgz" - integrity sha512-kWc7L0fw1xwvI0zi8OKVBuxRVefwGOrKSQMvrQ3dW+bIIavBY3/NpXmpjMy7bQnLgwgzWQZ8TlM57YHpHNHz4w== - dependencies: - "@babel/code-frame" "^7.8.3" - "@babel/generator" "^7.9.0" - "@babel/helper-module-transforms" "^7.9.0" - "@babel/helpers" "^7.9.0" - "@babel/parser" "^7.9.0" - "@babel/template" "^7.8.6" - "@babel/traverse" "^7.9.0" - "@babel/types" "^7.9.0" - convert-source-map "^1.7.0" - debug "^4.1.0" - gensync "^1.0.0-beta.1" - json5 "^2.1.2" - lodash "^4.17.13" - resolve "^1.3.2" - semver "^5.4.1" - source-map "^0.5.0" - -"@babel/generator@^7.9.0", "@babel/generator@^7.9.5": - version "7.9.5" - resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.9.5.tgz" - integrity sha512-GbNIxVB3ZJe3tLeDm1HSn2AhuD/mVcyLDpgtLXa5tplmWrJdF/elxB56XNqCuD6szyNkDi6wuoKXln3QeBmCHQ== - dependencies: - "@babel/types" "^7.9.5" - jsesc "^2.5.1" - lodash "^4.17.13" - source-map "^0.5.0" - -"@babel/helper-annotate-as-pure@^7.0.0": - version "7.0.0" - resolved "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.0.0.tgz" - integrity sha512-3UYcJUj9kvSLbLbUIfQTqzcy5VX7GRZ/CCDrnOaZorFFM01aXp1+GJwuFGV4NDDoAS+mOUyHcO6UD/RfqOks3Q== - dependencies: - "@babel/types" "^7.0.0" - -"@babel/helper-builder-binary-assignment-operator-visitor@^7.1.0": - version "7.1.0" - resolved "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.1.0.tgz" - integrity sha512-qNSR4jrmJ8M1VMM9tibvyRAHXQs2PmaksQF7c1CGJNipfe3D8p+wgNwgso/P2A2r2mdgBWAXljNWR0QRZAMW8w== - dependencies: - "@babel/helper-explode-assignable-expression" "^7.1.0" - "@babel/types" "^7.0.0" - -"@babel/helper-call-delegate@^7.4.4": - version "7.4.4" - resolved "https://registry.npmjs.org/@babel/helper-call-delegate/-/helper-call-delegate-7.4.4.tgz" - integrity sha512-l79boDFJ8S1c5hvQvG+rc+wHw6IuH7YldmRKsYtpbawsxURu/paVy57FZMomGK22/JckepaikOkY0MoAmdyOlQ== - dependencies: - "@babel/helper-hoist-variables" "^7.4.4" - "@babel/traverse" "^7.4.4" - "@babel/types" "^7.4.4" - -"@babel/helper-define-map@^7.5.5": - version "7.5.5" - resolved "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.5.5.tgz" - integrity sha512-fTfxx7i0B5NJqvUOBBGREnrqbTxRh7zinBANpZXAVDlsZxYdclDp467G1sQ8VZYMnAURY3RpBUAgOYT9GfzHBg== - dependencies: - "@babel/helper-function-name" "^7.1.0" - "@babel/types" "^7.5.5" - lodash "^4.17.13" - -"@babel/helper-explode-assignable-expression@^7.1.0": - version "7.1.0" - resolved "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.1.0.tgz" - integrity sha512-NRQpfHrJ1msCHtKjbzs9YcMmJZOg6mQMmGRB+hbamEdG5PNpaSm95275VD92DvJKuyl0s2sFiDmMZ+EnnvufqA== - dependencies: - "@babel/traverse" "^7.1.0" - "@babel/types" "^7.0.0" - -"@babel/helper-function-name@^7.1.0", "@babel/helper-function-name@^7.9.5": - version "7.9.5" - resolved "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.9.5.tgz" - integrity sha512-JVcQZeXM59Cd1qanDUxv9fgJpt3NeKUaqBqUEvfmQ+BCOKq2xUgaWZW2hr0dkbyJgezYuplEoh5knmrnS68efw== - dependencies: - "@babel/helper-get-function-arity" "^7.8.3" - "@babel/template" "^7.8.3" - "@babel/types" "^7.9.5" - -"@babel/helper-get-function-arity@^7.0.0", "@babel/helper-get-function-arity@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz" - integrity sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA== - dependencies: - "@babel/types" "^7.8.3" - -"@babel/helper-hoist-variables@^7.4.4": - version "7.4.4" - resolved "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.4.4.tgz" - integrity sha512-VYk2/H/BnYbZDDg39hr3t2kKyifAm1W6zHRfhx8jGjIHpQEBv9dry7oQ2f3+J703TLu69nYdxsovl0XYfcnK4w== - dependencies: - "@babel/types" "^7.4.4" - -"@babel/helper-member-expression-to-functions@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz" - integrity sha512-fO4Egq88utkQFjbPrSHGmGLFqmrshs11d46WI+WZDESt7Wu7wN2G2Iu+NMMZJFDOVRHAMIkB5SNh30NtwCA7RA== - dependencies: - "@babel/types" "^7.8.3" - -"@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz" - integrity sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg== - dependencies: - "@babel/types" "^7.8.3" - -"@babel/helper-module-transforms@^7.1.0", "@babel/helper-module-transforms@^7.4.4", "@babel/helper-module-transforms@^7.9.0": - version "7.9.0" - resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.9.0.tgz" - integrity sha512-0FvKyu0gpPfIQ8EkxlrAydOWROdHpBmiCiRwLkUiBGhCUPRRbVD2/tm3sFr/c/GWFrQ/ffutGUAnx7V0FzT2wA== - dependencies: - "@babel/helper-module-imports" "^7.8.3" - "@babel/helper-replace-supers" "^7.8.6" - "@babel/helper-simple-access" "^7.8.3" - "@babel/helper-split-export-declaration" "^7.8.3" - "@babel/template" "^7.8.6" - "@babel/types" "^7.9.0" - lodash "^4.17.13" - -"@babel/helper-optimise-call-expression@^7.0.0", "@babel/helper-optimise-call-expression@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz" - integrity sha512-Kag20n86cbO2AvHca6EJsvqAd82gc6VMGule4HwebwMlwkpXuVqrNRj6CkCV2sKxgi9MyAUnZVnZ6lJ1/vKhHQ== - dependencies: - "@babel/types" "^7.8.3" - -"@babel/helper-plugin-utils@^7.0.0": - version "7.0.0" - resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz" - integrity sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA== - -"@babel/helper-plugin-utils@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.17.12.tgz#86c2347da5acbf5583ba0a10aed4c9bf9da9cf96" - integrity sha512-JDkf04mqtN3y4iAbO1hv9U2ARpPyPL1zqyWs/2WG1pgSq9llHFjStX5jdxb84himgJm+8Ng+x0oiWF/nw/XQKA== - -"@babel/helper-regex@^7.0.0", "@babel/helper-regex@^7.4.4": - version "7.5.5" - resolved "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.5.5.tgz" - integrity sha512-CkCYQLkfkiugbRDO8eZn6lRuR8kzZoGXCg3149iTk5se7g6qykSpy3+hELSwquhu+TgHn8nkLiBwHvNX8Hofcw== - dependencies: - lodash "^4.17.13" - -"@babel/helper-remap-async-to-generator@^7.1.0": - version "7.1.0" - resolved "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.1.0.tgz" - integrity sha512-3fOK0L+Fdlg8S5al8u/hWE6vhufGSn0bN09xm2LXMy//REAF8kDCrYoOBKYmA8m5Nom+sV9LyLCwrFynA8/slg== - dependencies: - "@babel/helper-annotate-as-pure" "^7.0.0" - "@babel/helper-wrap-function" "^7.1.0" - "@babel/template" "^7.1.0" - "@babel/traverse" "^7.1.0" - "@babel/types" "^7.0.0" - -"@babel/helper-replace-supers@^7.5.5", "@babel/helper-replace-supers@^7.8.6": - version "7.8.6" - resolved "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.8.6.tgz" - integrity sha512-PeMArdA4Sv/Wf4zXwBKPqVj7n9UF/xg6slNRtZW84FM7JpE1CbG8B612FyM4cxrf4fMAMGO0kR7voy1ForHHFA== - dependencies: - "@babel/helper-member-expression-to-functions" "^7.8.3" - "@babel/helper-optimise-call-expression" "^7.8.3" - "@babel/traverse" "^7.8.6" - "@babel/types" "^7.8.6" - -"@babel/helper-simple-access@^7.1.0", "@babel/helper-simple-access@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.8.3.tgz" - integrity sha512-VNGUDjx5cCWg4vvCTR8qQ7YJYZ+HBjxOgXEl7ounz+4Sn7+LMD3CFrCTEU6/qXKbA2nKg21CwhhBzO0RpRbdCw== - dependencies: - "@babel/template" "^7.8.3" - "@babel/types" "^7.8.3" - -"@babel/helper-split-export-declaration@^7.4.4", "@babel/helper-split-export-declaration@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz" - integrity sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA== - dependencies: - "@babel/types" "^7.8.3" - -"@babel/helper-validator-identifier@^7.9.0", "@babel/helper-validator-identifier@^7.9.5": - version "7.9.5" - resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz" - integrity sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g== - -"@babel/helper-wrap-function@^7.1.0": - version "7.2.0" - resolved "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.2.0.tgz" - integrity sha512-o9fP1BZLLSrYlxYEYyl2aS+Flun5gtjTIG8iln+XuEzQTs0PLagAGSXUcqruJwD5fM48jzIEggCKpIfWTcR7pQ== - dependencies: - "@babel/helper-function-name" "^7.1.0" - "@babel/template" "^7.1.0" - "@babel/traverse" "^7.1.0" - "@babel/types" "^7.2.0" - -"@babel/helpers@^7.9.0": - version "7.9.2" - resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.9.2.tgz" - integrity sha512-JwLvzlXVPjO8eU9c/wF9/zOIN7X6h8DYf7mG4CiFRZRvZNKEF5dQ3H3V+ASkHoIB3mWhatgl5ONhyqHRI6MppA== - dependencies: - "@babel/template" "^7.8.3" - "@babel/traverse" "^7.9.0" - "@babel/types" "^7.9.0" - -"@babel/highlight@^7.8.3": - version "7.9.0" - resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz" - integrity sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ== - dependencies: - "@babel/helper-validator-identifier" "^7.9.0" - chalk "^2.0.0" - js-tokens "^4.0.0" - -"@babel/parser@^7.8.6", "@babel/parser@^7.9.0": - version "7.9.4" - resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.9.4.tgz" - integrity sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA== - -"@babel/plugin-proposal-async-generator-functions@^7.2.0": - version "7.2.0" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.2.0.tgz" - integrity sha512-+Dfo/SCQqrwx48ptLVGLdE39YtWRuKc/Y9I5Fy0P1DDBB9lsAHpjcEJQt+4IifuSOSTLBKJObJqMvaO1pIE8LQ== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-remap-async-to-generator" "^7.1.0" - "@babel/plugin-syntax-async-generators" "^7.2.0" - -"@babel/plugin-proposal-dynamic-import@^7.5.0": - version "7.5.0" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.5.0.tgz" - integrity sha512-x/iMjggsKTFHYC6g11PL7Qy58IK8H5zqfm9e6hu4z1iH2IRyAp9u9dL80zA6R76yFovETFLKz2VJIC2iIPBuFw== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-syntax-dynamic-import" "^7.2.0" - -"@babel/plugin-proposal-json-strings@^7.2.0": - version "7.2.0" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.2.0.tgz" - integrity sha512-MAFV1CA/YVmYwZG0fBQyXhmj0BHCB5egZHCKWIFVv/XCxAeVGIHfos3SwDck4LvCllENIAg7xMKOG5kH0dzyUg== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-syntax-json-strings" "^7.2.0" - -"@babel/plugin-proposal-object-rest-spread@^7.5.5": - version "7.5.5" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.5.5.tgz" - integrity sha512-F2DxJJSQ7f64FyTVl5cw/9MWn6naXGdk3Q3UhDbFEEHv+EilCPoeRD3Zh/Utx1CJz4uyKlQ4uH+bJPbEhMV7Zw== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-syntax-object-rest-spread" "^7.2.0" - -"@babel/plugin-proposal-optional-catch-binding@^7.2.0": - version "7.2.0" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.2.0.tgz" - integrity sha512-mgYj3jCcxug6KUcX4OBoOJz3CMrwRfQELPQ5560F70YQUBZB7uac9fqaWamKR1iWUzGiK2t0ygzjTScZnVz75g== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-syntax-optional-catch-binding" "^7.2.0" - -"@babel/plugin-proposal-unicode-property-regex@^7.4.4": - version "7.4.4" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.4.4.tgz" - integrity sha512-j1NwnOqMG9mFUOH58JTFsA/+ZYzQLUZ/drqWUqxCYLGeu2JFZL8YrNC9hBxKmWtAuOCHPcRpgv7fhap09Fb4kA== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-regex" "^7.4.4" - regexpu-core "^4.5.4" - -"@babel/plugin-syntax-async-generators@^7.2.0": - version "7.2.0" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.2.0.tgz" - integrity sha512-1ZrIRBv2t0GSlcwVoQ6VgSLpLgiN/FVQUzt9znxo7v2Ov4jJrs8RY8tv0wvDmFN3qIdMKWrmMMW6yZ0G19MfGg== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/plugin-syntax-dynamic-import@^7.2.0": - version "7.2.0" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.2.0.tgz" - integrity sha512-mVxuJ0YroI/h/tbFTPGZR8cv6ai+STMKNBq0f8hFxsxWjl94qqhsb+wXbpNMDPU3cfR1TIsVFzU3nXyZMqyK4w== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/plugin-syntax-json-strings@^7.2.0": - version "7.2.0" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.2.0.tgz" - integrity sha512-5UGYnMSLRE1dqqZwug+1LISpA403HzlSfsg6P9VXU6TBjcSHeNlw4DxDx7LgpF+iKZoOG/+uzqoRHTdcUpiZNg== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/plugin-syntax-object-rest-spread@^7.2.0": - version "7.2.0" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.2.0.tgz" - integrity sha512-t0JKGgqk2We+9may3t0xDdmneaXmyxq0xieYcKHxIsrJO64n1OiMWNUtc5gQK1PA0NpdCRrtZp4z+IUaKugrSA== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/plugin-syntax-optional-catch-binding@^7.2.0": - version "7.2.0" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.2.0.tgz" - integrity sha512-bDe4xKNhb0LI7IvZHiA13kff0KEfaGX/Hv4lMA9+7TEc63hMNvfKo6ZFpXhKuEp+II/q35Gc4NoMeDZyaUbj9w== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/plugin-transform-arrow-functions@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.17.12.tgz#dddd783b473b1b1537ef46423e3944ff24898c45" - integrity sha512-PHln3CNi/49V+mza4xMwrg+WGYevSF1oaiXaC2EQfdp4HWlSjRsrDXWJiQBKpP7749u6vQ9mcry2uuFOv5CXvA== - dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - -"@babel/plugin-transform-arrow-functions@^7.2.0": - version "7.2.0" - resolved "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.2.0.tgz" - integrity sha512-ER77Cax1+8/8jCB9fo4Ud161OZzWN5qawi4GusDuRLcDbDG+bIGYY20zb2dfAFdTRGzrfq2xZPvF0R64EHnimg== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/plugin-transform-async-to-generator@^7.5.0": - version "7.5.0" - resolved "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.5.0.tgz" - integrity sha512-mqvkzwIGkq0bEF1zLRRiTdjfomZJDV33AH3oQzHVGkI2VzEmXLpKKOBvEVaFZBJdN0XTyH38s9j/Kiqr68dggg== - dependencies: - "@babel/helper-module-imports" "^7.0.0" - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-remap-async-to-generator" "^7.1.0" - -"@babel/plugin-transform-block-scoped-functions@^7.2.0": - version "7.2.0" - resolved "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.2.0.tgz" - integrity sha512-ntQPR6q1/NKuphly49+QiQiTN0O63uOwjdD6dhIjSWBI5xlrbUFh720TIpzBhpnrLfv2tNH/BXvLIab1+BAI0w== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/plugin-transform-block-scoping@^7.4.4", "@babel/plugin-transform-block-scoping@^7.6.0": - version "7.6.0" - resolved "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.6.0.tgz" - integrity sha512-tIt4E23+kw6TgL/edACZwP1OUKrjOTyMrFMLoT5IOFrfMRabCgekjqFd5o6PaAMildBu46oFkekIdMuGkkPEpA== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - lodash "^4.17.13" - -"@babel/plugin-transform-classes@^7.5.5": - version "7.5.5" - resolved "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.5.5.tgz" - integrity sha512-U2htCNK/6e9K7jGyJ++1p5XRU+LJjrwtoiVn9SzRlDT2KubcZ11OOwy3s24TjHxPgxNwonCYP7U2K51uVYCMDg== - dependencies: - "@babel/helper-annotate-as-pure" "^7.0.0" - "@babel/helper-define-map" "^7.5.5" - "@babel/helper-function-name" "^7.1.0" - "@babel/helper-optimise-call-expression" "^7.0.0" - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-replace-supers" "^7.5.5" - "@babel/helper-split-export-declaration" "^7.4.4" - globals "^11.1.0" - -"@babel/plugin-transform-computed-properties@^7.2.0": - version "7.2.0" - resolved "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.2.0.tgz" - integrity sha512-kP/drqTxY6Xt3NNpKiMomfgkNn4o7+vKxK2DDKcBG9sHj51vHqMBGy8wbDS/J4lMxnqs153/T3+DmCEAkC5cpA== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/plugin-transform-destructuring@^7.6.0": - version "7.6.0" - resolved "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.6.0.tgz" - integrity sha512-2bGIS5P1v4+sWTCnKNDZDxbGvEqi0ijeqM/YqHtVGrvG2y0ySgnEEhXErvE9dA0bnIzY9bIzdFK0jFA46ASIIQ== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/plugin-transform-dotall-regex@^7.4.4": - version "7.4.4" - resolved "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.4.4.tgz" - integrity sha512-P05YEhRc2h53lZDjRPk/OektxCVevFzZs2Gfjd545Wde3k+yFDbXORgl2e0xpbq8mLcKJ7Idss4fAg0zORN/zg== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-regex" "^7.4.4" - regexpu-core "^4.5.4" - -"@babel/plugin-transform-duplicate-keys@^7.5.0": - version "7.5.0" - resolved "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.5.0.tgz" - integrity sha512-igcziksHizyQPlX9gfSjHkE2wmoCH3evvD2qR5w29/Dk0SMKE/eOI7f1HhBdNhR/zxJDqrgpoDTq5YSLH/XMsQ== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/plugin-transform-exponentiation-operator@^7.2.0": - version "7.2.0" - resolved "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.2.0.tgz" - integrity sha512-umh4hR6N7mu4Elq9GG8TOu9M0bakvlsREEC+ialrQN6ABS4oDQ69qJv1VtR3uxlKMCQMCvzk7vr17RHKcjx68A== - dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor" "^7.1.0" - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/plugin-transform-for-of@^7.4.4": - version "7.4.4" - resolved "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.4.4.tgz" - integrity sha512-9T/5Dlr14Z9TIEXLXkt8T1DU7F24cbhwhMNUziN3hB1AXoZcdzPcTiKGRn/6iOymDqtTKWnr/BtRKN9JwbKtdQ== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/plugin-transform-function-name@^7.4.4": - version "7.4.4" - resolved "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.4.4.tgz" - integrity sha512-iU9pv7U+2jC9ANQkKeNF6DrPy4GBa4NWQtl6dHB4Pb3izX2JOEvDTFarlNsBj/63ZEzNNIAMs3Qw4fNCcSOXJA== - dependencies: - "@babel/helper-function-name" "^7.1.0" - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/plugin-transform-literals@^7.2.0": - version "7.2.0" - resolved "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.2.0.tgz" - integrity sha512-2ThDhm4lI4oV7fVQ6pNNK+sx+c/GM5/SaML0w/r4ZB7sAneD/piDJtwdKlNckXeyGK7wlwg2E2w33C/Hh+VFCg== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/plugin-transform-member-expression-literals@^7.2.0": - version "7.2.0" - resolved "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.2.0.tgz" - integrity sha512-HiU3zKkSU6scTidmnFJ0bMX8hz5ixC93b4MHMiYebmk2lUVNGOboPsqQvx5LzooihijUoLR/v7Nc1rbBtnc7FA== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/plugin-transform-modules-amd@^7.5.0": - version "7.5.0" - resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.5.0.tgz" - integrity sha512-n20UsQMKnWrltocZZm24cRURxQnWIvsABPJlw/fvoy9c6AgHZzoelAIzajDHAQrDpuKFFPPcFGd7ChsYuIUMpg== - dependencies: - "@babel/helper-module-transforms" "^7.1.0" - "@babel/helper-plugin-utils" "^7.0.0" - babel-plugin-dynamic-import-node "^2.3.0" - -"@babel/plugin-transform-modules-commonjs@^7.6.0": - version "7.6.0" - resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.6.0.tgz" - integrity sha512-Ma93Ix95PNSEngqomy5LSBMAQvYKVe3dy+JlVJSHEXZR5ASL9lQBedMiCyVtmTLraIDVRE3ZjTZvmXXD2Ozw3g== - dependencies: - "@babel/helper-module-transforms" "^7.4.4" - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-simple-access" "^7.1.0" - babel-plugin-dynamic-import-node "^2.3.0" - -"@babel/plugin-transform-modules-systemjs@^7.5.0": - version "7.5.0" - resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.5.0.tgz" - integrity sha512-Q2m56tyoQWmuNGxEtUyeEkm6qJYFqs4c+XyXH5RAuYxObRNz9Zgj/1g2GMnjYp2EUyEy7YTrxliGCXzecl/vJg== - dependencies: - "@babel/helper-hoist-variables" "^7.4.4" - "@babel/helper-plugin-utils" "^7.0.0" - babel-plugin-dynamic-import-node "^2.3.0" - -"@babel/plugin-transform-modules-umd@^7.2.0": - version "7.2.0" - resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.2.0.tgz" - integrity sha512-BV3bw6MyUH1iIsGhXlOK6sXhmSarZjtJ/vMiD9dNmpY8QXFFQTj+6v92pcfy1iqa8DeAfJFwoxcrS/TUZda6sw== - dependencies: - "@babel/helper-module-transforms" "^7.1.0" - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/plugin-transform-named-capturing-groups-regex@^7.6.0": - version "7.6.0" - resolved "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.6.0.tgz" - integrity sha512-jem7uytlmrRl3iCAuQyw8BpB4c4LWvSpvIeXKpMb+7j84lkx4m4mYr5ErAcmN5KM7B6BqrAvRGjBIbbzqCczew== - dependencies: - regexp-tree "^0.1.13" - -"@babel/plugin-transform-new-target@^7.4.4": - version "7.4.4" - resolved "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.4.4.tgz" - integrity sha512-r1z3T2DNGQwwe2vPGZMBNjioT2scgWzK9BCnDEh+46z8EEwXBq24uRzd65I7pjtugzPSj921aM15RpESgzsSuA== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/plugin-transform-object-super@^7.5.5": - version "7.5.5" - resolved "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.5.5.tgz" - integrity sha512-un1zJQAhSosGFBduPgN/YFNvWVpRuHKU7IHBglLoLZsGmruJPOo6pbInneflUdmq7YvSVqhpPs5zdBvLnteltQ== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-replace-supers" "^7.5.5" - -"@babel/plugin-transform-parameters@^7.4.4": - version "7.4.4" - resolved "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.4.4.tgz" - integrity sha512-oMh5DUO1V63nZcu/ZVLQFqiihBGo4OpxJxR1otF50GMeCLiRx5nUdtokd+u9SuVJrvvuIh9OosRFPP4pIPnwmw== - dependencies: - "@babel/helper-call-delegate" "^7.4.4" - "@babel/helper-get-function-arity" "^7.0.0" - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/plugin-transform-property-literals@^7.2.0": - version "7.2.0" - resolved "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.2.0.tgz" - integrity sha512-9q7Dbk4RhgcLp8ebduOpCbtjh7C0itoLYHXd9ueASKAG/is5PQtMR5VJGka9NKqGhYEGn5ITahd4h9QeBMylWQ== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/plugin-transform-regenerator@^7.4.5": - version "7.4.5" - resolved "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.4.5.tgz" - integrity sha512-gBKRh5qAaCWntnd09S8QC7r3auLCqq5DI6O0DlfoyDjslSBVqBibrMdsqO+Uhmx3+BlOmE/Kw1HFxmGbv0N9dA== - dependencies: - regenerator-transform "^0.14.0" - -"@babel/plugin-transform-reserved-words@^7.2.0": - version "7.2.0" - resolved "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.2.0.tgz" - integrity sha512-fz43fqW8E1tAB3DKF19/vxbpib1fuyCwSPE418ge5ZxILnBhWyhtPgz8eh1RCGGJlwvksHkyxMxh0eenFi+kFw== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/plugin-transform-shorthand-properties@^7.2.0": - version "7.2.0" - resolved "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.2.0.tgz" - integrity sha512-QP4eUM83ha9zmYtpbnyjTLAGKQritA5XW/iG9cjtuOI8s1RuL/3V6a3DeSHfKutJQ+ayUfeZJPcnCYEQzaPQqg== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/plugin-transform-spread@^7.2.0": - version "7.2.2" - resolved "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.2.2.tgz" - integrity sha512-KWfky/58vubwtS0hLqEnrWJjsMGaOeSBn90Ezn5Jeg9Z8KKHmELbP1yGylMlm5N6TPKeY9A2+UaSYLdxahg01w== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/plugin-transform-sticky-regex@^7.2.0": - version "7.2.0" - resolved "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.2.0.tgz" - integrity sha512-KKYCoGaRAf+ckH8gEL3JHUaFVyNHKe3ASNsZ+AlktgHevvxGigoIttrEJb8iKN03Q7Eazlv1s6cx2B2cQ3Jabw== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-regex" "^7.0.0" - -"@babel/plugin-transform-template-literals@^7.4.4": - version "7.4.4" - resolved "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.4.4.tgz" - integrity sha512-mQrEC4TWkhLN0z8ygIvEL9ZEToPhG5K7KDW3pzGqOfIGZ28Jb0POUkeWcoz8HnHvhFy6dwAT1j8OzqN8s804+g== - dependencies: - "@babel/helper-annotate-as-pure" "^7.0.0" - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/plugin-transform-typeof-symbol@^7.2.0": - version "7.2.0" - resolved "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.2.0.tgz" - integrity sha512-2LNhETWYxiYysBtrBTqL8+La0jIoQQnIScUJc74OYvUGRmkskNY4EzLCnjHBzdmb38wqtTaixpo1NctEcvMDZw== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/plugin-transform-unicode-regex@^7.4.4": - version "7.4.4" - resolved "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.4.4.tgz" - integrity sha512-il+/XdNw01i93+M9J9u4T7/e/Ue/vWfNZE4IRUQjplu2Mqb/AFTDimkw2tdEdSH50wuQXZAbXSql0UphQke+vA== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-regex" "^7.4.4" - regexpu-core "^4.5.4" - -"@babel/preset-env@^7.5.4": - version "7.6.0" - resolved "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.6.0.tgz" - integrity sha512-1efzxFv/TcPsNXlRhMzRnkBFMeIqBBgzwmZwlFDw5Ubj0AGLeufxugirwZmkkX/ayi3owsSqoQ4fw8LkfK9SYg== - dependencies: - "@babel/helper-module-imports" "^7.0.0" - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-proposal-async-generator-functions" "^7.2.0" - "@babel/plugin-proposal-dynamic-import" "^7.5.0" - "@babel/plugin-proposal-json-strings" "^7.2.0" - "@babel/plugin-proposal-object-rest-spread" "^7.5.5" - "@babel/plugin-proposal-optional-catch-binding" "^7.2.0" - "@babel/plugin-proposal-unicode-property-regex" "^7.4.4" - "@babel/plugin-syntax-async-generators" "^7.2.0" - "@babel/plugin-syntax-dynamic-import" "^7.2.0" - "@babel/plugin-syntax-json-strings" "^7.2.0" - "@babel/plugin-syntax-object-rest-spread" "^7.2.0" - "@babel/plugin-syntax-optional-catch-binding" "^7.2.0" - "@babel/plugin-transform-arrow-functions" "^7.2.0" - "@babel/plugin-transform-async-to-generator" "^7.5.0" - "@babel/plugin-transform-block-scoped-functions" "^7.2.0" - "@babel/plugin-transform-block-scoping" "^7.6.0" - "@babel/plugin-transform-classes" "^7.5.5" - "@babel/plugin-transform-computed-properties" "^7.2.0" - "@babel/plugin-transform-destructuring" "^7.6.0" - "@babel/plugin-transform-dotall-regex" "^7.4.4" - "@babel/plugin-transform-duplicate-keys" "^7.5.0" - "@babel/plugin-transform-exponentiation-operator" "^7.2.0" - "@babel/plugin-transform-for-of" "^7.4.4" - "@babel/plugin-transform-function-name" "^7.4.4" - "@babel/plugin-transform-literals" "^7.2.0" - "@babel/plugin-transform-member-expression-literals" "^7.2.0" - "@babel/plugin-transform-modules-amd" "^7.5.0" - "@babel/plugin-transform-modules-commonjs" "^7.6.0" - "@babel/plugin-transform-modules-systemjs" "^7.5.0" - "@babel/plugin-transform-modules-umd" "^7.2.0" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.6.0" - "@babel/plugin-transform-new-target" "^7.4.4" - "@babel/plugin-transform-object-super" "^7.5.5" - "@babel/plugin-transform-parameters" "^7.4.4" - "@babel/plugin-transform-property-literals" "^7.2.0" - "@babel/plugin-transform-regenerator" "^7.4.5" - "@babel/plugin-transform-reserved-words" "^7.2.0" - "@babel/plugin-transform-shorthand-properties" "^7.2.0" - "@babel/plugin-transform-spread" "^7.2.0" - "@babel/plugin-transform-sticky-regex" "^7.2.0" - "@babel/plugin-transform-template-literals" "^7.4.4" - "@babel/plugin-transform-typeof-symbol" "^7.2.0" - "@babel/plugin-transform-unicode-regex" "^7.4.4" - "@babel/types" "^7.6.0" - browserslist "^4.6.0" - core-js-compat "^3.1.1" - invariant "^2.2.2" - js-levenshtein "^1.1.3" - semver "^5.5.0" - -"@babel/runtime@^7.5.5": - version "7.9.2" - resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.2.tgz" - integrity sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q== - dependencies: - regenerator-runtime "^0.13.4" - -"@babel/template@^7.1.0", "@babel/template@^7.8.3", "@babel/template@^7.8.6": - version "7.8.6" - resolved "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz" - integrity sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg== - dependencies: - "@babel/code-frame" "^7.8.3" - "@babel/parser" "^7.8.6" - "@babel/types" "^7.8.6" - -"@babel/traverse@^7.1.0", "@babel/traverse@^7.4.4", "@babel/traverse@^7.8.6", "@babel/traverse@^7.9.0": - version "7.9.5" - resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.9.5.tgz" - integrity sha512-c4gH3jsvSuGUezlP6rzSJ6jf8fYjLj3hsMZRx/nX0h+fmHN0w+ekubRrHPqnMec0meycA2nwCsJ7dC8IPem2FQ== - dependencies: - "@babel/code-frame" "^7.8.3" - "@babel/generator" "^7.9.5" - "@babel/helper-function-name" "^7.9.5" - "@babel/helper-split-export-declaration" "^7.8.3" - "@babel/parser" "^7.9.0" - "@babel/types" "^7.9.5" - debug "^4.1.0" - globals "^11.1.0" - lodash "^4.17.13" - -"@babel/types@^7.0.0", "@babel/types@^7.2.0", "@babel/types@^7.4.4", "@babel/types@^7.5.5", "@babel/types@^7.6.0", "@babel/types@^7.8.3", "@babel/types@^7.8.6", "@babel/types@^7.9.0", "@babel/types@^7.9.5": - version "7.9.5" - resolved "https://registry.npmjs.org/@babel/types/-/types-7.9.5.tgz" - integrity sha512-XjnvNqenk818r5zMaba+sLQjnbda31UfUURv3ei0qPQw4u+j2jMyJ5b11y8ZHYTRSI3NnInQkkkRT4fLqqPdHg== - dependencies: - "@babel/helper-validator-identifier" "^7.9.5" - lodash "^4.17.13" - to-fast-properties "^2.0.0" - -"@csstools/convert-colors@^1.4.0": - version "1.4.0" - resolved "https://registry.npmjs.org/@csstools/convert-colors/-/convert-colors-1.4.0.tgz" - integrity sha512-5a6wqoJV/xEdbRNKVo6I4hO3VjyDq//8q2f9I6PBAvMesJHFauXDorcNCsr9RzvsZnaWi5NYCcfyqP1QeFHFbw== - -"@electron/get@^1.6.0": - version "1.14.1" - resolved "https://registry.yarnpkg.com/@electron/get/-/get-1.14.1.tgz#16ba75f02dffb74c23965e72d617adc721d27f40" - integrity sha512-BrZYyL/6m0ZXz/lDxy/nlVhQz+WF+iPS6qXolEU8atw7h6v1aYkjwJZ63m+bJMBTxDE66X+r2tPS4a/8C82sZw== - dependencies: - debug "^4.1.1" - env-paths "^2.2.0" - fs-extra "^8.1.0" - got "^9.6.0" - progress "^2.0.3" - semver "^6.2.0" - sumchecker "^3.0.1" - optionalDependencies: - global-agent "^3.0.0" - global-tunnel-ng "^2.7.1" - -"@jimp/bmp@^0.6.8": - version "0.6.8" - resolved "https://registry.npmjs.org/@jimp/bmp/-/bmp-0.6.8.tgz" - integrity sha512-uxVgSkI62uAzk5ZazYHEHBehow590WAkLKmDXLzkr/XP/Hv2Fx1T4DKwJ/15IY5ktq5VAhAUWGXTyd8KWFsx7w== - dependencies: - "@jimp/utils" "^0.6.8" - bmp-js "^0.1.0" - core-js "^2.5.7" - -"@jimp/core@^0.6.8": - version "0.6.8" - resolved "https://registry.npmjs.org/@jimp/core/-/core-0.6.8.tgz" - integrity sha512-JOFqBBcSNiDiMZJFr6OJqC6viXj5NVBQISua0eacoYvo4YJtTajOIxC4MqWyUmGrDpRMZBR8QhSsIOwsFrdROA== - dependencies: - "@jimp/utils" "^0.6.8" - any-base "^1.1.0" - buffer "^5.2.0" - core-js "^2.5.7" - exif-parser "^0.1.12" - file-type "^9.0.0" - load-bmfont "^1.3.1" - mkdirp "0.5.1" - phin "^2.9.1" - pixelmatch "^4.0.2" - tinycolor2 "^1.4.1" - -"@jimp/custom@^0.6.8": - version "0.6.8" - resolved "https://registry.npmjs.org/@jimp/custom/-/custom-0.6.8.tgz" - integrity sha512-FrYlzZRVXP2vuVwd7Nc2dlK+iZk4g6IaT1Ib8Z6vU5Kkwlt83FJIPJ2UUFABf3bF5big0wkk8ZUihWxE4Nzdng== - dependencies: - "@jimp/core" "^0.6.8" - core-js "^2.5.7" - -"@jimp/gif@^0.6.8": - version "0.6.8" - resolved "https://registry.npmjs.org/@jimp/gif/-/gif-0.6.8.tgz" - integrity sha512-yyOlujjQcgz9zkjM5ihZDEppn9d1brJ7jQHP5rAKmqep0G7FU1D0AKcV+Ql18RhuI/CgWs10wAVcrQpmLnu4Yw== - dependencies: - "@jimp/utils" "^0.6.8" - core-js "^2.5.7" - omggif "^1.0.9" - -"@jimp/jpeg@^0.6.8": - version "0.6.8" - resolved "https://registry.npmjs.org/@jimp/jpeg/-/jpeg-0.6.8.tgz" - integrity sha512-rGtXbYpFXAn471qLpTGvhbBMNHJo5KiufN+vC5AWyufntmkt5f0Ox2Cx4ijuBMDtirZchxbMLtrfGjznS4L/ew== - dependencies: - "@jimp/utils" "^0.6.8" - core-js "^2.5.7" - jpeg-js "^0.3.4" - -"@jimp/plugin-blit@^0.6.8": - version "0.6.8" - resolved "https://registry.npmjs.org/@jimp/plugin-blit/-/plugin-blit-0.6.8.tgz" - integrity sha512-7Tl6YpKTSpvwQbnGNhsfX2zyl3jRVVopd276Y2hF2zpDz9Bycow7NdfNU/4Nx1jaf96X6uWOtSVINcQ7rGd47w== - dependencies: - "@jimp/utils" "^0.6.8" - core-js "^2.5.7" - -"@jimp/plugin-blur@^0.6.8": - version "0.6.8" - resolved "https://registry.npmjs.org/@jimp/plugin-blur/-/plugin-blur-0.6.8.tgz" - integrity sha512-NpZCMKxXHLDQsX9zPlWtpMA660DQStY6/z8ZetyxCDbqrLe9YCXpeR4MNhdJdABIiwTm1W5FyFF4kp81PHJx3Q== - dependencies: - "@jimp/utils" "^0.6.8" - core-js "^2.5.7" - -"@jimp/plugin-color@^0.6.8": - version "0.6.8" - resolved "https://registry.npmjs.org/@jimp/plugin-color/-/plugin-color-0.6.8.tgz" - integrity sha512-jjFyU0zNmGOH2rjzHuOMU4kaia0oo82s/7UYfn5h7OUkmUZTd6Do3ZSK1PiXA7KR+s4B76/Omm6Doh/0SGb7BQ== - dependencies: - "@jimp/utils" "^0.6.8" - core-js "^2.5.7" - tinycolor2 "^1.4.1" - -"@jimp/plugin-contain@^0.6.8": - version "0.6.8" - resolved "https://registry.npmjs.org/@jimp/plugin-contain/-/plugin-contain-0.6.8.tgz" - integrity sha512-p/P2wCXhAzbmEgXvGsvmxLmbz45feF6VpR4m9suPSOr8PC/i/XvTklTqYEUidYYAft4vHgsYJdS74HKSMnH8lw== - dependencies: - "@jimp/utils" "^0.6.8" - core-js "^2.5.7" - -"@jimp/plugin-cover@^0.6.8": - version "0.6.8" - resolved "https://registry.npmjs.org/@jimp/plugin-cover/-/plugin-cover-0.6.8.tgz" - integrity sha512-2PvWgk+PJfRsfWDI1G8Fpjrsu0ZlpNyZxO2+fqWlVo6y/y2gP4v08FqvbkcqSjNlOu2IDWIFXpgyU0sTINWZLg== - dependencies: - "@jimp/utils" "^0.6.8" - core-js "^2.5.7" - -"@jimp/plugin-crop@^0.6.8": - version "0.6.8" - resolved "https://registry.npmjs.org/@jimp/plugin-crop/-/plugin-crop-0.6.8.tgz" - integrity sha512-CbrcpWE2xxPK1n/JoTXzhRUhP4mO07mTWaSavenCg664oQl/9XCtL+A0FekuNHzIvn4myEqvkiTwN7FsbunS/Q== - dependencies: - "@jimp/utils" "^0.6.8" - core-js "^2.5.7" - -"@jimp/plugin-displace@^0.6.8": - version "0.6.8" - resolved "https://registry.npmjs.org/@jimp/plugin-displace/-/plugin-displace-0.6.8.tgz" - integrity sha512-RmV2bPxoPE6mrPxtYSPtHxm2cGwBQr5a2p+9gH6SPy+eUMrbGjbvjwKNfXWUYD0leML+Pt5XOmAS9pIROmuruQ== - dependencies: - "@jimp/utils" "^0.6.8" - core-js "^2.5.7" - -"@jimp/plugin-dither@^0.6.8": - version "0.6.8" - resolved "https://registry.npmjs.org/@jimp/plugin-dither/-/plugin-dither-0.6.8.tgz" - integrity sha512-x6V/qjxe+xypjpQm7GbiMNqci1EW5UizrcebOhHr8AHijOEqHd2hjXh5f6QIGfrkTFelc4/jzq1UyCsYntqz9Q== - dependencies: - "@jimp/utils" "^0.6.8" - core-js "^2.5.7" - -"@jimp/plugin-flip@^0.6.8": - version "0.6.8" - resolved "https://registry.npmjs.org/@jimp/plugin-flip/-/plugin-flip-0.6.8.tgz" - integrity sha512-4il6Da6G39s9MyWBEee4jztEOUGJ40E6OlPjkMrdpDNvge6hYEAB31BczTYBP/CEY74j4LDSoY5LbcU4kv06yA== - dependencies: - "@jimp/utils" "^0.6.8" - core-js "^2.5.7" - -"@jimp/plugin-gaussian@^0.6.8": - version "0.6.8" - resolved "https://registry.npmjs.org/@jimp/plugin-gaussian/-/plugin-gaussian-0.6.8.tgz" - integrity sha512-pVOblmjv7stZjsqloi4YzHVwAPXKGdNaHPhp4KP4vj41qtc6Hxd9z/+VWGYRTunMFac84gUToe0UKIXd6GhoKw== - dependencies: - "@jimp/utils" "^0.6.8" - core-js "^2.5.7" - -"@jimp/plugin-invert@^0.6.8": - version "0.6.8" - resolved "https://registry.npmjs.org/@jimp/plugin-invert/-/plugin-invert-0.6.8.tgz" - integrity sha512-11zuLiXDHr6tFv4U8aieXqNXQEKbDbSBG/h+X62gGTNFpyn8EVPpncHhOqrAFtZUaPibBqMFlNJ15SzwC7ExsQ== - dependencies: - "@jimp/utils" "^0.6.8" - core-js "^2.5.7" - -"@jimp/plugin-mask@^0.6.8": - version "0.6.8" - resolved "https://registry.npmjs.org/@jimp/plugin-mask/-/plugin-mask-0.6.8.tgz" - integrity sha512-hZJ0OiKGJyv7hDSATwJDkunB1Ie80xJnONMgpUuUseteK45YeYNBOiZVUe8vum8QI1UwavgBzcvQ9u4fcgXc9g== - dependencies: - "@jimp/utils" "^0.6.8" - core-js "^2.5.7" - -"@jimp/plugin-normalize@^0.6.8": - version "0.6.8" - resolved "https://registry.npmjs.org/@jimp/plugin-normalize/-/plugin-normalize-0.6.8.tgz" - integrity sha512-Q4oYhU+sSyTJI7pMZlg9/mYh68ujLfOxXzQGEXuw0sHGoGQs3B0Jw7jmzGa6pIS06Hup5hD2Zuh1ppvMdjJBfQ== - dependencies: - "@jimp/utils" "^0.6.8" - core-js "^2.5.7" - -"@jimp/plugin-print@^0.6.8": - version "0.6.8" - resolved "https://registry.npmjs.org/@jimp/plugin-print/-/plugin-print-0.6.8.tgz" - integrity sha512-2aokejGn4Drv1FesnZGqh5KEq0FQtR0drlmtyZrBH+r9cx7hh0Qgf4D1BOTDEgXkfSSngjGRjKKRW/fwOrVXYw== - dependencies: - "@jimp/utils" "^0.6.8" - core-js "^2.5.7" - load-bmfont "^1.4.0" - -"@jimp/plugin-resize@^0.6.8": - version "0.6.8" - resolved "https://registry.npmjs.org/@jimp/plugin-resize/-/plugin-resize-0.6.8.tgz" - integrity sha512-27nPh8L1YWsxtfmV/+Ub5dOTpXyC0HMF2cu52RQSCYxr+Lm1+23dJF70AF1poUbUe+FWXphwuUxQzjBJza9UoA== - dependencies: - "@jimp/utils" "^0.6.8" - core-js "^2.5.7" - -"@jimp/plugin-rotate@^0.6.8": - version "0.6.8" - resolved "https://registry.npmjs.org/@jimp/plugin-rotate/-/plugin-rotate-0.6.8.tgz" - integrity sha512-GbjETvL05BDoLdszNUV4Y0yLkHf177MnqGqilA113LIvx9aD0FtUopGXYfRGVvmtTOTouoaGJUc+K6qngvKxww== - dependencies: - "@jimp/utils" "^0.6.8" - core-js "^2.5.7" - -"@jimp/plugin-scale@^0.6.8": - version "0.6.8" - resolved "https://registry.npmjs.org/@jimp/plugin-scale/-/plugin-scale-0.6.8.tgz" - integrity sha512-GzIYWR/oCUK2jAwku23zt19V1ssaEU4pL0x2XsLNKuuJEU6DvEytJyTMXCE7OLG/MpDBQcQclJKHgiyQm5gIOQ== - dependencies: - "@jimp/utils" "^0.6.8" - core-js "^2.5.7" - -"@jimp/plugins@^0.6.8": - version "0.6.8" - resolved "https://registry.npmjs.org/@jimp/plugins/-/plugins-0.6.8.tgz" - integrity sha512-fMcTI72Vn/Lz6JftezTURmyP5ml/xGMe0Ljx2KRJ85IWyP33vDmGIUuutFiBEbh2+y7lRT+aTSmjs0QGa/xTmQ== - dependencies: - "@jimp/plugin-blit" "^0.6.8" - "@jimp/plugin-blur" "^0.6.8" - "@jimp/plugin-color" "^0.6.8" - "@jimp/plugin-contain" "^0.6.8" - "@jimp/plugin-cover" "^0.6.8" - "@jimp/plugin-crop" "^0.6.8" - "@jimp/plugin-displace" "^0.6.8" - "@jimp/plugin-dither" "^0.6.8" - "@jimp/plugin-flip" "^0.6.8" - "@jimp/plugin-gaussian" "^0.6.8" - "@jimp/plugin-invert" "^0.6.8" - "@jimp/plugin-mask" "^0.6.8" - "@jimp/plugin-normalize" "^0.6.8" - "@jimp/plugin-print" "^0.6.8" - "@jimp/plugin-resize" "^0.6.8" - "@jimp/plugin-rotate" "^0.6.8" - "@jimp/plugin-scale" "^0.6.8" - core-js "^2.5.7" - timm "^1.6.1" - -"@jimp/png@^0.6.8": - version "0.6.8" - resolved "https://registry.npmjs.org/@jimp/png/-/png-0.6.8.tgz" - integrity sha512-JHHg/BZ7KDtHQrcG+a7fztw45rdf7okL/YwkN4qU5FH7Fcrp41nX5QnRviDtD9hN+GaNC7kvjvcqRAxW25qjew== - dependencies: - "@jimp/utils" "^0.6.8" - core-js "^2.5.7" - pngjs "^3.3.3" - -"@jimp/tiff@^0.6.8": - version "0.6.8" - resolved "https://registry.npmjs.org/@jimp/tiff/-/tiff-0.6.8.tgz" - integrity sha512-iWHbxd+0IKWdJyJ0HhoJCGYmtjPBOusz1z1HT/DnpePs/Lo3TO4d9ALXqYfUkyG74ZK5jULZ69KLtwuhuJz1bg== - dependencies: - core-js "^2.5.7" - utif "^2.0.1" - -"@jimp/types@^0.6.8": - version "0.6.8" - resolved "https://registry.npmjs.org/@jimp/types/-/types-0.6.8.tgz" - integrity sha512-vCZ/Cp2osy69VP21XOBACfHI5HeR60Rfd4Jidj4W73UL+HrFWOtyQiJ7hlToyu1vI5mR/NsUQpzyQvz56ADm5A== - dependencies: - "@jimp/bmp" "^0.6.8" - "@jimp/gif" "^0.6.8" - "@jimp/jpeg" "^0.6.8" - "@jimp/png" "^0.6.8" - "@jimp/tiff" "^0.6.8" - core-js "^2.5.7" - timm "^1.6.1" - -"@jimp/utils@^0.6.8": - version "0.6.8" - resolved "https://registry.npmjs.org/@jimp/utils/-/utils-0.6.8.tgz" - integrity sha512-7RDfxQ2C/rarNG9iso5vmnKQbcvlQjBIlF/p7/uYj72WeZgVCB+5t1fFBKJSU4WhniHX4jUMijK+wYGE3Y3bGw== - dependencies: - core-js "^2.5.7" - -"@malept/cross-spawn-promise@^1.1.0": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz#504af200af6b98e198bce768bc1730c6936ae01d" - integrity sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ== - dependencies: - cross-spawn "^7.0.1" - -"@nodelib/fs.scandir@2.1.3": - 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== - dependencies: - "@nodelib/fs.stat" "2.0.3" - run-parallel "^1.1.9" - -"@nodelib/fs.stat@2.0.3", "@nodelib/fs.stat@^2.0.2": - version "2.0.3" - resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz" - integrity sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA== - -"@nodelib/fs.walk@^1.2.3": - version "1.2.4" - resolved "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz" - integrity sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ== - dependencies: - "@nodelib/fs.scandir" "2.1.3" - fastq "^1.6.0" - -"@sindresorhus/is@^0.14.0": - version "0.14.0" - resolved "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz" - integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== - -"@sindresorhus/is@^0.7.0": - version "0.7.0" - resolved "https://registry.npmjs.org/@sindresorhus/is/-/is-0.7.0.tgz" - integrity sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow== - -"@szmarczak/http-timer@^1.1.2": - version "1.1.2" - resolved "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz" - integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA== - dependencies: - defer-to-connect "^1.0.1" - -"@types/color-name@^1.1.1": - version "1.1.1" - resolved "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz" - integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== - -"@types/cordova@^0.0.34": - version "0.0.34" - resolved "https://registry.npmjs.org/@types/cordova/-/cordova-0.0.34.tgz" - integrity sha1-6nrd907Ow9dimCegw54smt3HPQQ= - -"@types/filesystem@^0.0.29": - version "0.0.29" - resolved "https://registry.npmjs.org/@types/filesystem/-/filesystem-0.0.29.tgz" - integrity sha512-85/1KfRedmfPGsbK8YzeaQUyV1FQAvMPMTuWFQ5EkLd2w7szhNO96bk3Rh/SKmOfd9co2rCLf0Voy4o7ECBOvw== - dependencies: - "@types/filewriter" "*" - -"@types/filewriter@*": - version "0.0.28" - resolved "https://registry.npmjs.org/@types/filewriter/-/filewriter-0.0.28.tgz" - integrity sha1-wFTor02d11205jq8dviFFocU1LM= - -"@types/glob@^7.1.1": - version "7.1.2" - resolved "https://registry.npmjs.org/@types/glob/-/glob-7.1.2.tgz" - integrity sha512-VgNIkxK+j7Nz5P7jvUZlRvhuPSmsEfS03b0alKcq5V/STUKAa3Plemsn5mrQUO7am6OErJ4rhGEGJbACclrtRA== - dependencies: - "@types/minimatch" "*" - "@types/node" "*" - -"@types/json-schema@^7.0.8": - version "7.0.9" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d" - integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ== - -"@types/minimatch@*": - version "3.0.3" - resolved "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz" - integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== - -"@types/node@*", "@types/node@^12.7.5": - version "12.7.5" - resolved "https://registry.npmjs.org/@types/node/-/node-12.7.5.tgz" - integrity sha512-9fq4jZVhPNW8r+UYKnxF1e2HkDWOWKM5bC2/7c9wPV835I0aOrVbS/Hw/pWPk2uKrNXQqg9Z959Kz+IYDd5p3w== - -"@types/q@^1.5.1": - version "1.5.2" - resolved "https://registry.npmjs.org/@types/q/-/q-1.5.2.tgz" - integrity sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw== - -"@types/yauzl@^2.9.1": - version "2.10.0" - resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.10.0.tgz#b3248295276cf8c6f153ebe6a9aba0c988cb2599" - integrity sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw== - dependencies: - "@types/node" "*" - -"@webassemblyjs/ast@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz" - integrity sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA== - dependencies: - "@webassemblyjs/helper-module-context" "1.9.0" - "@webassemblyjs/helper-wasm-bytecode" "1.9.0" - "@webassemblyjs/wast-parser" "1.9.0" - -"@webassemblyjs/floating-point-hex-parser@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz" - integrity sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA== - -"@webassemblyjs/helper-api-error@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz" - integrity sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw== - -"@webassemblyjs/helper-buffer@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz" - integrity sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA== - -"@webassemblyjs/helper-code-frame@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz" - integrity sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA== - dependencies: - "@webassemblyjs/wast-printer" "1.9.0" - -"@webassemblyjs/helper-fsm@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz" - integrity sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw== - -"@webassemblyjs/helper-module-context@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz" - integrity sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g== - dependencies: - "@webassemblyjs/ast" "1.9.0" - -"@webassemblyjs/helper-wasm-bytecode@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz" - integrity sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw== - -"@webassemblyjs/helper-wasm-section@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz" - integrity sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-buffer" "1.9.0" - "@webassemblyjs/helper-wasm-bytecode" "1.9.0" - "@webassemblyjs/wasm-gen" "1.9.0" - -"@webassemblyjs/ieee754@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz" - integrity sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg== - dependencies: - "@xtuc/ieee754" "^1.2.0" - -"@webassemblyjs/leb128@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.9.0.tgz" - integrity sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw== - dependencies: - "@xtuc/long" "4.2.2" - -"@webassemblyjs/utf8@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.9.0.tgz" - integrity sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w== - -"@webassemblyjs/wasm-edit@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz" - integrity sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-buffer" "1.9.0" - "@webassemblyjs/helper-wasm-bytecode" "1.9.0" - "@webassemblyjs/helper-wasm-section" "1.9.0" - "@webassemblyjs/wasm-gen" "1.9.0" - "@webassemblyjs/wasm-opt" "1.9.0" - "@webassemblyjs/wasm-parser" "1.9.0" - "@webassemblyjs/wast-printer" "1.9.0" - -"@webassemblyjs/wasm-gen@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz" - integrity sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-wasm-bytecode" "1.9.0" - "@webassemblyjs/ieee754" "1.9.0" - "@webassemblyjs/leb128" "1.9.0" - "@webassemblyjs/utf8" "1.9.0" - -"@webassemblyjs/wasm-opt@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz" - integrity sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-buffer" "1.9.0" - "@webassemblyjs/wasm-gen" "1.9.0" - "@webassemblyjs/wasm-parser" "1.9.0" - -"@webassemblyjs/wasm-parser@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz" - integrity sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-api-error" "1.9.0" - "@webassemblyjs/helper-wasm-bytecode" "1.9.0" - "@webassemblyjs/ieee754" "1.9.0" - "@webassemblyjs/leb128" "1.9.0" - "@webassemblyjs/utf8" "1.9.0" - -"@webassemblyjs/wast-parser@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz" - integrity sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/floating-point-hex-parser" "1.9.0" - "@webassemblyjs/helper-api-error" "1.9.0" - "@webassemblyjs/helper-code-frame" "1.9.0" - "@webassemblyjs/helper-fsm" "1.9.0" - "@xtuc/long" "4.2.2" - -"@webassemblyjs/wast-printer@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz" - integrity sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/wast-parser" "1.9.0" - "@xtuc/long" "4.2.2" - -"@xtuc/ieee754@^1.2.0": - version "1.2.0" - resolved "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz" - integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== - -"@xtuc/long@4.2.2": - version "4.2.2" - resolved "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz" - integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== - -abab@^2.0.0: - version "2.0.1" - resolved "https://registry.npmjs.org/abab/-/abab-2.0.1.tgz" - integrity sha512-1zSbbCuoIjafKZ3mblY5ikvAb0ODUbqBnFuUb7f6uLeQhhGJ0vEV4ntmtxKLT2WgXCO94E07BjunsIw1jOMPZw== - -abbrev@1: - version "1.1.1" - resolved "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz" - integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== - -accepts@~1.3.4: - version "1.3.7" - resolved "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz" - integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== - dependencies: - mime-types "~2.1.24" - negotiator "0.6.2" - -acorn-globals@^4.3.0: - version "4.3.4" - resolved "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.4.tgz" - integrity sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A== - dependencies: - acorn "^6.0.1" - acorn-walk "^6.0.1" - -acorn-jsx@^3.0.0: - version "3.0.1" - resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz" - integrity sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s= - dependencies: - acorn "^3.0.4" - -acorn-jsx@^5.0.0: - version "5.0.2" - resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.2.tgz" - integrity sha512-tiNTrP1MP0QrChmD2DdupCr6HWSFeKVw5d/dHTu4Y7rkAkRhU/Dt7dphAfIUyxtHpl/eBVip5uTNSpQJHylpAw== - -acorn-walk@^6.0.1: - version "6.2.0" - resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.2.0.tgz" - integrity sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA== - -acorn@^3.0.4: - version "3.3.0" - resolved "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz" - integrity sha1-ReN/s56No/JbruP/U2niu18iAXo= - -acorn@^5.5.0: - version "5.7.3" - resolved "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz" - integrity sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw== - -acorn@^6.0.1, acorn@^6.0.2, acorn@^6.0.7, acorn@^6.4.1: - version "6.4.1" - resolved "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz" - integrity sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA== - -acorn@^6.0.6: - version "6.4.2" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" - integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== - -after@0.8.2: - version "0.8.2" - resolved "https://registry.npmjs.org/after/-/after-0.8.2.tgz" - integrity sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8= - -ajv-errors@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz" - integrity sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ== - -ajv-keywords@^1.0.0: - version "1.5.1" - resolved "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-1.5.1.tgz" - integrity sha1-MU3QpLM2j609/NxU7eYXG4htrzw= - -ajv-keywords@^3.1.0, ajv-keywords@^3.4.1: - version "3.4.1" - resolved "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz" - integrity sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ== - -ajv-keywords@^3.5.2: - version "3.5.2" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" - integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== - -ajv@^4.7.0: - version "4.11.8" - resolved "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz" - integrity sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY= - dependencies: - co "^4.6.0" - json-stable-stringify "^1.0.1" - -ajv@^6.1.0, ajv@^6.10.2, ajv@^6.12.0, ajv@^6.5.5, ajv@^6.9.1: - version "6.12.0" - resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz" - integrity sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw== - 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" - -ajv@^6.12.5: - version "6.12.6" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" - integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== - 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" - -alphanum-sort@^1.0.0: - version "1.0.2" - resolved "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz" - integrity sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM= - -amdefine@>=0.0.4: - version "1.0.1" - resolved "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz" - integrity sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU= - -ansi-colors@^1.0.1: - version "1.1.0" - resolved "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz" - integrity sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA== - dependencies: - ansi-wrap "^0.1.0" - -ansi-colors@^4.1.0: - version "4.1.1" - resolved "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz" - integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== - -ansi-cyan@^0.1.1: - version "0.1.1" - resolved "https://registry.npmjs.org/ansi-cyan/-/ansi-cyan-0.1.1.tgz" - integrity sha1-U4rlKK+JgvKK4w2G8vF0VtJgmHM= - dependencies: - ansi-wrap "0.1.0" - -ansi-escapes@^1.1.0: - version "1.4.0" - resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz" - integrity sha1-06ioOzGapneTZisT52HHkRQiMG4= - -ansi-escapes@^3.2.0: - version "3.2.0" - resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz" - integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== - -ansi-gray@^0.1.1: - version "0.1.1" - resolved "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz" - integrity sha1-KWLPVOyXksSFEKPetSRDaGHvclE= - dependencies: - ansi-wrap "0.1.0" - -ansi-red@^0.1.1: - version "0.1.1" - resolved "https://registry.npmjs.org/ansi-red/-/ansi-red-0.1.1.tgz" - integrity sha1-jGOPnRCAgAo1PJwoyKgcpHBdlGw= - dependencies: - ansi-wrap "0.1.0" - -ansi-regex@^0.2.0, ansi-regex@^0.2.1: - version "0.2.1" - resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz" - integrity sha1-DY6UaWej2BQ/k+JOKYUl/BsiNfk= - -ansi-regex@^2.0.0: - version "2.1.1" - resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz" - integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= - -ansi-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz" - integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= - -ansi-regex@^4.1.0: - version "4.1.0" - resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz" - integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== - -ansi-regex@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz" - integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== - -ansi-styles@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz" - integrity sha1-6uy/Zs1waIJ2Cy9GkVgrj1XXp94= - -ansi-styles@^2.2.1: - version "2.2.1" - resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz" - integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= - -ansi-styles@^3.2.0, ansi-styles@^3.2.1: - 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== - dependencies: - color-convert "^1.9.0" - -ansi-styles@^4.0.0, ansi-styles@^4.1.0: - version "4.2.1" - resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz" - integrity sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA== - dependencies: - "@types/color-name" "^1.1.1" - color-convert "^2.0.1" - -ansi-wrap@0.1.0, ansi-wrap@^0.1.0: - version "0.1.0" - resolved "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz" - integrity sha1-qCJQ3bABXponyoLoLqYDu/pF768= - -any-base@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/any-base/-/any-base-1.1.0.tgz" - integrity sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg== - -anymatch@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz" - integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== - dependencies: - micromatch "^3.1.4" - normalize-path "^2.1.1" - -anymatch@~3.1.1: - version "3.1.1" - resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz" - integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg== - dependencies: - normalize-path "^3.0.0" - picomatch "^2.0.4" - -append-buffer@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/append-buffer/-/append-buffer-1.0.2.tgz" - integrity sha1-2CIM9GYIFSXv6lBhTz3mUU36WPE= - dependencies: - buffer-equal "^1.0.0" - -aproba@^1.1.1: - version "1.2.0" - resolved "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz" - integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== - -arch@^2.1.0: - version "2.1.1" - resolved "https://registry.npmjs.org/arch/-/arch-2.1.1.tgz" - integrity sha512-BLM56aPo9vLLFVa8+/+pJLnrZ7QGGTVHWsCwieAWT9o9K8UeGaQbzZbGoabWLOo2ksBCztoXdqBZBplqLDDCSg== - -archive-type@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/archive-type/-/archive-type-4.0.0.tgz" - integrity sha1-+S5yIzBW38aWlHJ0nCZ72wRrHXA= - dependencies: - file-type "^4.2.0" - -archiver@~0.11.0: - version "0.11.0" - resolved "https://registry.npmjs.org/archiver/-/archiver-0.11.0.tgz" - integrity sha1-mBd9p6bAGSt/J5jzDNbquKvXZpA= - dependencies: - async "~0.9.0" - buffer-crc32 "~0.2.1" - glob "~3.2.6" - lazystream "~0.1.0" - lodash "~2.4.1" - readable-stream "~1.0.26" - tar-stream "~0.4.0" - zip-stream "~0.4.0" - -archy@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz" - integrity sha1-+cjBN1fMHde8N5rHeyxipcKGjEA= - -are-you-es5@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/are-you-es5/-/are-you-es5-2.1.2.tgz#d75511a174a3f842d70cc784aec0bcaff5a57547" - integrity sha512-gkT2bLCfzyJJr3lYoxZKScdD/Yp5zzghi+3KawONTvH/7rrgU3RMXYvGQnOTlqEFVgZFpEGj/6bZ6sF/9YtddA== - dependencies: - acorn "^6.0.6" - array-flatten "^2.1.0" - commander "^2.19.0" - find-up "^4.1.0" - -argparse@^1.0.7: - version "1.0.10" - resolved "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz" - integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== - dependencies: - sprintf-js "~1.0.2" - -arr-diff@^1.0.1: - version "1.1.0" - resolved "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz" - integrity sha1-aHwydYFjWI/vfeezb6vklesaOZo= - dependencies: - arr-flatten "^1.0.1" - array-slice "^0.2.3" - -arr-diff@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz" - integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= - -arr-filter@^1.1.1: - version "1.1.2" - resolved "https://registry.npmjs.org/arr-filter/-/arr-filter-1.1.2.tgz" - integrity sha1-Q/3d0JHo7xGqTEXZzcGOLf8XEe4= - dependencies: - make-iterator "^1.0.0" - -arr-flatten@^1.0.1, arr-flatten@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz" - integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== - -arr-map@^2.0.0, arr-map@^2.0.2: - version "2.0.2" - resolved "https://registry.npmjs.org/arr-map/-/arr-map-2.0.2.tgz" - integrity sha1-Onc0X/wc814qkYJWAfnljy4kysQ= - dependencies: - make-iterator "^1.0.0" - -arr-union@^2.0.1: - version "2.1.0" - resolved "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz" - integrity sha1-IPnqtexw9cfSFbEHexw5Fh0pLH0= - -arr-union@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz" - integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= - -array-differ@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz" - integrity sha1-7/UuN1gknTO+QCuLuOVkuytdQDE= - -array-each@^1.0.0, array-each@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz" - integrity sha1-p5SvDAWrF1KEbudTofIRoFugxE8= - -array-equal@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz" - integrity sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM= - -array-find-index@^1.0.1: - version "1.0.2" - resolved "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz" - integrity sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E= - -array-flatten@^2.1.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099" - integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ== - -array-initial@^1.0.0: - version "1.1.0" - resolved "https://registry.npmjs.org/array-initial/-/array-initial-1.1.0.tgz" - integrity sha1-L6dLJnOTccOUe9enrcc74zSz15U= - dependencies: - array-slice "^1.0.0" - is-number "^4.0.0" - -array-last@^1.1.1: - version "1.3.0" - resolved "https://registry.npmjs.org/array-last/-/array-last-1.3.0.tgz" - integrity sha512-eOCut5rXlI6aCOS7Z7kCplKRKyiFQ6dHFBem4PwlwKeNFk2/XxTrhRh5T9PyaEWGy/NHTZWbY+nsZlNFJu9rYg== - dependencies: - is-number "^4.0.0" - -array-slice@^0.2.3: - version "0.2.3" - resolved "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz" - integrity sha1-3Tz7gO15c6dRF82sabC5nshhhvU= - -array-slice@^1.0.0: - version "1.1.0" - resolved "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz" - integrity sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w== - -array-sort@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/array-sort/-/array-sort-1.0.0.tgz" - integrity sha512-ihLeJkonmdiAsD7vpgN3CRcx2J2S0TiYW+IS/5zHBI7mKUq3ySvBdzzBfD236ubDBQFiiyG3SWCPc+msQ9KoYg== - dependencies: - default-compare "^1.0.0" - get-value "^2.0.6" - kind-of "^5.0.2" - -array-union@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz" - integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== - -array-uniq@^1.0.2: - version "1.0.3" - resolved "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz" - integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY= - -array-uniq@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/array-uniq/-/array-uniq-2.1.0.tgz" - integrity sha512-bdHxtev7FN6+MXI1YFW0Q8mQ8dTJc2S8AMfju+ZR77pbg2yAdVyDlwkaUI7Har0LyOMRFPHrJ9lYdyjZZswdlQ== - -array-unique@^0.3.2: - version "0.3.2" - resolved "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz" - integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= - -arraybuffer.slice@~0.0.7: - version "0.0.7" - resolved "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz" - integrity sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog== - -asar@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/asar/-/asar-3.1.0.tgz#70b0509449fe3daccc63beb4d3c7d2e24d3c6473" - integrity sha512-vyxPxP5arcAqN4F/ebHd/HhwnAiZtwhglvdmc7BR2f0ywbVNTOpSeyhLDbGXtE/y58hv1oC75TaNIXutnsOZsQ== - dependencies: - chromium-pickle-js "^0.2.0" - commander "^5.0.0" - glob "^7.1.6" - minimatch "^3.0.4" - optionalDependencies: - "@types/glob" "^7.1.1" - -asn1.js@^4.0.0: - version "4.10.1" - resolved "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz" - integrity sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw== - dependencies: - bn.js "^4.0.0" - inherits "^2.0.1" - minimalistic-assert "^1.0.0" - -asn1@~0.2.0, asn1@~0.2.3: - version "0.2.4" - resolved "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz" - integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== - dependencies: - safer-buffer "~2.1.0" - -assert-plus@1.0.0, assert-plus@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" - integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= - -assert@^1.1.1: - version "1.5.0" - resolved "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz" - integrity sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA== - dependencies: - object-assign "^4.1.1" - util "0.10.3" - -assets@^3.0.0: - version "3.0.1" - resolved "https://registry.npmjs.org/assets/-/assets-3.0.1.tgz" - integrity sha512-fTyLNf/9V24y5zO83f4DAEuvaKj7MWBixbnqdZneAhsv1r21yQ/6ogZfvXHmphJAHsz4DhuOwHeJKVbGqqvk0Q== - dependencies: - async "^2.5.0" - bluebird "^3.4.6" - calipers "^2.0.0" - calipers-gif "^2.0.0" - calipers-jpeg "^2.0.0" - calipers-png "^2.0.0" - calipers-svg "^2.0.0" - calipers-webp "^2.0.0" - glob "^7.0.6" - lodash "^4.15.0" - mime "^2.4.0" - -assign-symbols@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz" - integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= - -ast-types@0.9.6: - version "0.9.6" - resolved "https://registry.npmjs.org/ast-types/-/ast-types-0.9.6.tgz" - integrity sha1-ECyenpAF0+fjgpvwxPok7oYu6bk= - -astral-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz" - integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== - -async-done@^1.2.0, async-done@^1.2.2: - version "1.3.2" - resolved "https://registry.npmjs.org/async-done/-/async-done-1.3.2.tgz" - integrity sha512-uYkTP8dw2og1tu1nmza1n1CMW0qb8gWWlwqMmLb7MhBVs4BXrFziT6HXUd+/RlRA/i4H9AkofYloUbs1fwMqlw== - dependencies: - end-of-stream "^1.1.0" - once "^1.3.2" - process-nextick-args "^2.0.0" - stream-exhaust "^1.0.1" - -async-each-series@0.1.1: - version "0.1.1" - resolved "https://registry.npmjs.org/async-each-series/-/async-each-series-0.1.1.tgz" - integrity sha1-dhfBkXQB/Yykooqtzj266Yr+tDI= - -async-each@^1.0.1: - version "1.0.3" - resolved "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz" - integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== - -async-limiter@~1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz" - integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== - -async-settle@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/async-settle/-/async-settle-1.0.0.tgz" - integrity sha1-HQqRS7Aldb7IqPOnTlCA9yssDGs= - dependencies: - async-done "^1.2.2" - -async@1.5.2: - version "1.5.2" - resolved "https://registry.npmjs.org/async/-/async-1.5.2.tgz" - integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo= - -async@>=0.2.9, async@~0.9.0: - version "0.9.2" - resolved "https://registry.npmjs.org/async/-/async-0.9.2.tgz" - integrity sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0= - -async@^2.5.0: - version "2.6.3" - resolved "https://registry.npmjs.org/async/-/async-2.6.3.tgz" - integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg== - dependencies: - lodash "^4.17.14" - -async@~0.2.10: - version "0.2.10" - resolved "https://registry.npmjs.org/async/-/async-0.2.10.tgz" - integrity sha1-trvgsGdLnXGXCMo43owjfLUmw9E= - -async@~1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/async/-/async-1.0.0.tgz" - integrity sha1-+PwEyjoTeErenhZBr5hXjPvWR6k= - -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" - integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= - -at-least-node@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" - integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== - -atob@^2.1.1: - version "2.1.2" - resolved "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz" - integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== - -audiosprite@*, audiosprite@^0.7.2: - version "0.7.2" - resolved "https://registry.npmjs.org/audiosprite/-/audiosprite-0.7.2.tgz" - integrity sha512-9Z6UwUuv4To5nUQNRIw5/Q3qA7HYm0ANzoW5EDGPEsU2oIRVgmIlLlm9YZfpPKoeUxt54vMStl2/762189VmJw== - dependencies: - async "~0.9.0" - glob "^6.0.4" - mkdirp "^0.5.0" - optimist "~0.6.1" - underscore "~1.8.3" - winston "~1.0.0" - -author-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/author-regex/-/author-regex-1.0.0.tgz" - integrity sha1-0IiFvmubv5Q5/gh8dihyRfCoFFA= - -autoprefixer@^9.4.3, autoprefixer@^9.4.7, autoprefixer@^9.6.1: - version "9.6.1" - resolved "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.6.1.tgz" - integrity sha512-aVo5WxR3VyvyJxcJC3h4FKfwCQvQWb1tSI5VHNibddCVWrcD1NvlxEweg3TSgiPztMnWfjpy2FURKA2kvDE+Tw== - dependencies: - browserslist "^4.6.3" - caniuse-lite "^1.0.30000980" - chalk "^2.4.2" - normalize-range "^0.1.2" - num2fraction "^1.2.2" - postcss "^7.0.17" - postcss-value-parser "^4.0.0" - -aws-sign2@~0.7.0: - version "0.7.0" - resolved "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz" - integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= - -aws4@^1.8.0: - version "1.8.0" - resolved "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz" - integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ== - -axios@0.19.0: - version "0.19.0" - resolved "https://registry.npmjs.org/axios/-/axios-0.19.0.tgz" - integrity sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ== - dependencies: - follow-redirects "1.5.10" - is-buffer "^2.0.2" - -babel-code-frame@^6.26.0: - version "6.26.0" - resolved "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz" - integrity sha1-Y/1D99weO7fONZR9uP42mj9Yx0s= - dependencies: - chalk "^1.1.3" - esutils "^2.0.2" - js-tokens "^3.0.2" - -babel-core@^6.26.0, babel-core@^6.26.3: - version "6.26.3" - resolved "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz" - integrity sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA== - dependencies: - babel-code-frame "^6.26.0" - babel-generator "^6.26.0" - babel-helpers "^6.24.1" - babel-messages "^6.23.0" - babel-register "^6.26.0" - babel-runtime "^6.26.0" - babel-template "^6.26.0" - babel-traverse "^6.26.0" - babel-types "^6.26.0" - babylon "^6.18.0" - convert-source-map "^1.5.1" - debug "^2.6.9" - json5 "^0.5.1" - lodash "^4.17.4" - minimatch "^3.0.4" - path-is-absolute "^1.0.1" - private "^0.1.8" - slash "^1.0.0" - source-map "^0.5.7" - -babel-generator@^6.26.0: - version "6.26.1" - resolved "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz" - integrity sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA== - dependencies: - babel-messages "^6.23.0" - babel-runtime "^6.26.0" - babel-types "^6.26.0" - detect-indent "^4.0.0" - jsesc "^1.3.0" - lodash "^4.17.4" - source-map "^0.5.7" - trim-right "^1.0.1" - -babel-helpers@^6.24.1: - version "6.24.1" - resolved "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz" - integrity sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI= - dependencies: - babel-runtime "^6.22.0" - babel-template "^6.24.1" - -babel-loader@^8.1.0: - version "8.1.0" - resolved "https://registry.npmjs.org/babel-loader/-/babel-loader-8.1.0.tgz" - integrity sha512-7q7nC1tYOrqvUrN3LQK4GwSk/TQorZSOlO9C+RZDZpODgyN4ZlCqE5q9cDsyWOliN+aU9B4JX01xK9eJXowJLw== - dependencies: - find-cache-dir "^2.1.0" - loader-utils "^1.4.0" - mkdirp "^0.5.3" - pify "^4.0.1" - schema-utils "^2.6.5" - -babel-messages@^6.23.0: - version "6.23.0" - resolved "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz" - integrity sha1-8830cDhYA1sqKVHG7F7fbGLyYw4= - dependencies: - babel-runtime "^6.22.0" - -babel-plugin-closure-elimination@^1.3.0: - version "1.3.0" - resolved "https://registry.npmjs.org/babel-plugin-closure-elimination/-/babel-plugin-closure-elimination-1.3.0.tgz" - integrity sha512-ClKuSxKLLNhe69bvTMuONDI0dQDW49lXB2qtQyyKCzvwegRGel/q4/e+1EoDNDN97Hf1QkxGMbzpAGPmU4Tfjw== - -babel-plugin-console-source@^2.0.2: - version "2.0.4" - resolved "https://registry.npmjs.org/babel-plugin-console-source/-/babel-plugin-console-source-2.0.4.tgz" - integrity sha512-OGhrdhuMjiEW0Ma0P9e2B4dFddCpJ/xN/RRaM/4wwDLl+6ZKf+Xd77FtVjpNeDzNRNk8wjRdStA4hpZizXzl1g== - -babel-plugin-danger-remove-unused-import@^1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/babel-plugin-danger-remove-unused-import/-/babel-plugin-danger-remove-unused-import-1.1.2.tgz" - integrity sha512-3bNmVAaakP3b1aROj7O3bOWj2kBa85sZR5naZ3Rn8L9buiZaAyZLgjfrPDL3zhX4wySOA5jrTm/wSmJPsMm3cg== - -babel-plugin-dynamic-import-node@^2.3.0: - version "2.3.0" - resolved "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz" - integrity sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ== - dependencies: - object.assign "^4.1.0" - -babel-register@^6.26.0: - version "6.26.0" - resolved "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz" - integrity sha1-btAhFz4vy0htestFxgCahW9kcHE= - dependencies: - babel-core "^6.26.0" - babel-runtime "^6.26.0" - core-js "^2.5.0" - home-or-tmp "^2.0.0" - lodash "^4.17.4" - mkdirp "^0.5.1" - source-map-support "^0.4.15" - -babel-runtime@^6.22.0, babel-runtime@^6.26.0: - version "6.26.0" - resolved "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz" - integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4= - dependencies: - core-js "^2.4.0" - regenerator-runtime "^0.11.0" - -babel-runtime@^7.0.0-beta.3: - version "7.0.0-beta.3" - resolved "https://registry.npmjs.org/babel-runtime/-/babel-runtime-7.0.0-beta.3.tgz" - integrity sha512-jlzZ8RACjt0QGxq+wqsw5bCQE9RcUyWpw987mDY3GYxTpOQT2xoyNoG++oVCHzr/nACLBIprfVBNvv/If1ZYcg== - dependencies: - core-js "^2.4.0" - regenerator-runtime "^0.11.0" - -babel-template@^6.24.1, babel-template@^6.26.0: - version "6.26.0" - resolved "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz" - integrity sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI= - dependencies: - babel-runtime "^6.26.0" - babel-traverse "^6.26.0" - babel-types "^6.26.0" - babylon "^6.18.0" - lodash "^4.17.4" - -babel-traverse@^6.26.0: - version "6.26.0" - resolved "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz" - integrity sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4= - dependencies: - babel-code-frame "^6.26.0" - babel-messages "^6.23.0" - babel-runtime "^6.26.0" - babel-types "^6.26.0" - babylon "^6.18.0" - debug "^2.6.8" - globals "^9.18.0" - invariant "^2.2.2" - lodash "^4.17.4" - -babel-types@^6.26.0: - version "6.26.0" - resolved "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz" - integrity sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc= - dependencies: - babel-runtime "^6.26.0" - esutils "^2.0.2" - lodash "^4.17.4" - to-fast-properties "^1.0.3" - -babylon@^6.18.0: - version "6.18.0" - resolved "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz" - integrity sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ== - -bach@^1.0.0: - version "1.2.0" - resolved "https://registry.npmjs.org/bach/-/bach-1.2.0.tgz" - integrity sha1-Szzpa/JxNPeaG0FKUcFONMO9mIA= - dependencies: - arr-filter "^1.1.1" - arr-flatten "^1.0.1" - arr-map "^2.0.0" - array-each "^1.0.0" - array-initial "^1.0.0" - array-last "^1.1.1" - async-done "^1.2.2" - async-settle "^1.0.0" - now-and-later "^2.0.0" - -backo2@1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz" - integrity sha1-MasayLEpNjRj41s+u2n038+6eUc= - -balanced-match@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz" - integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= - -base64-arraybuffer@0.1.5: - version "0.1.5" - resolved "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz" - integrity sha1-c5JncZI7Whl0etZmqlzUv5xunOg= - -base64-js@^1.0.2, base64-js@^1.2.3: - version "1.3.1" - resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz" - integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g== - -base64-js@^1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" - integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== - -base64id@1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz" - integrity sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY= - -base@^0.11.1: - version "0.11.2" - resolved "https://registry.npmjs.org/base/-/base-0.11.2.tgz" - integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== - dependencies: - cache-base "^1.0.1" - class-utils "^0.3.5" - component-emitter "^1.2.1" - define-property "^1.0.0" - isobject "^3.0.1" - mixin-deep "^1.2.0" - pascalcase "^0.1.1" - -batch@0.6.1: - version "0.6.1" - resolved "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz" - integrity sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY= - -bcrypt-pbkdf@^1.0.0: - version "1.0.2" - resolved "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz" - integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= - dependencies: - tweetnacl "^0.14.3" - -beeper@^1.0.0: - version "1.1.1" - resolved "https://registry.npmjs.org/beeper/-/beeper-1.1.1.tgz" - integrity sha1-5tXqjF2tABMEpwsiY4RH9pyy+Ak= - -better-assert@~1.0.0: - version "1.0.2" - resolved "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz" - integrity sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI= - dependencies: - callsite "1.0.0" - -big.js@^3.1.3: - version "3.2.0" - resolved "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz" - integrity sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q== - -big.js@^5.2.2: - version "5.2.2" - resolved "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz" - integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== - -bin-build@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/bin-build/-/bin-build-3.0.0.tgz" - integrity sha512-jcUOof71/TNAI2uM5uoUaDq2ePcVBQ3R/qhxAz1rX7UfvduAL/RXD3jXzvn8cVcDJdGVkiR1shal3OH0ImpuhA== - dependencies: - decompress "^4.0.0" - download "^6.2.2" - execa "^0.7.0" - p-map-series "^1.0.0" - tempfile "^2.0.0" - -bin-check@^4.1.0: - version "4.1.0" - resolved "https://registry.npmjs.org/bin-check/-/bin-check-4.1.0.tgz" - integrity sha512-b6weQyEUKsDGFlACWSIOfveEnImkJyK/FGW6FAG42loyoquvjdtOIqO6yBFzHyqyVVhNgNkQxxx09SFLK28YnA== - dependencies: - execa "^0.7.0" - executable "^4.1.0" - -bin-version-check@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/bin-version-check/-/bin-version-check-4.0.0.tgz" - integrity sha512-sR631OrhC+1f8Cvs8WyVWOA33Y8tgwjETNPyyD/myRBXLkfS/vl74FmH/lFcRl9KY3zwGh7jFhvyk9vV3/3ilQ== - dependencies: - bin-version "^3.0.0" - semver "^5.6.0" - semver-truncate "^1.1.2" - -bin-version@^3.0.0: - version "3.1.0" - resolved "https://registry.npmjs.org/bin-version/-/bin-version-3.1.0.tgz" - integrity sha512-Mkfm4iE1VFt4xd4vH+gx+0/71esbfus2LsnCGe8Pi4mndSPyT+NGES/Eg99jx8/lUGWfu3z2yuB/bt5UB+iVbQ== - dependencies: - execa "^1.0.0" - find-versions "^3.0.0" - -bin-wrapper@^4.0.0, bin-wrapper@^4.0.1: - version "4.1.0" - resolved "https://registry.npmjs.org/bin-wrapper/-/bin-wrapper-4.1.0.tgz" - integrity sha512-hfRmo7hWIXPkbpi0ZltboCMVrU+0ClXR/JgbCKKjlDjQf6igXa7OwdqNcFWQZPZTgiY7ZpzE3+LjjkLiTN2T7Q== - dependencies: - bin-check "^4.1.0" - bin-version-check "^4.0.0" - download "^7.1.0" - import-lazy "^3.1.0" - os-filter-obj "^2.0.0" - pify "^4.0.1" - -binary-extensions@^1.0.0: - version "1.13.1" - resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz" - integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== - -binary-extensions@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz" - integrity sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ== - -bindings@^1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" - integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== - dependencies: - file-uri-to-path "1.0.0" - -bl@^0.7.0: - version "0.7.0" - resolved "https://registry.npmjs.org/bl/-/bl-0.7.0.tgz" - integrity sha1-P7BnBgKsKHjrdw3CA58YNr5irls= - dependencies: - readable-stream "~1.0.2" - -bl@^0.9.0: - version "0.9.5" - resolved "https://registry.npmjs.org/bl/-/bl-0.9.5.tgz" - integrity sha1-wGt5evCF6gC8Unr8jvzxHeIjIFQ= - dependencies: - readable-stream "~1.0.26" - -bl@^1.0.0: - version "1.2.2" - resolved "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz" - integrity sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA== - dependencies: - readable-stream "^2.3.5" - safe-buffer "^5.1.1" - -blob@0.0.5: - version "0.0.5" - resolved "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz" - integrity sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig== - -bluebird@3.x.x, bluebird@^3.1.1, bluebird@^3.4.6, bluebird@^3.5.0, bluebird@^3.5.5: - version "3.5.5" - resolved "https://registry.npmjs.org/bluebird/-/bluebird-3.5.5.tgz" - integrity sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w== - -bmp-js@^0.1.0: - version "0.1.0" - resolved "https://registry.npmjs.org/bmp-js/-/bmp-js-0.1.0.tgz" - integrity sha1-4Fpj95amwf8l9Hcex62twUjAcjM= - -bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: - version "4.11.8" - resolved "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz" - integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA== - -body-parser@~1.8.0: - version "1.8.4" - resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.8.4.tgz" - integrity sha1-1JfgS8E7P5qL2McLsM3Bby4CiJg= - dependencies: - bytes "1.0.0" - depd "0.4.5" - iconv-lite "0.4.4" - media-typer "0.3.0" - on-finished "2.1.0" - qs "2.2.4" - raw-body "1.3.0" - type-is "~1.5.1" - -boolbase@^1.0.0, boolbase@~1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz" - integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= - -boolean@^3.0.1: - version "3.2.0" - resolved "https://registry.yarnpkg.com/boolean/-/boolean-3.2.0.tgz#9e5294af4e98314494cbb17979fa54ca159f116b" - integrity sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw== - -brace-expansion@^1.0.0, brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -braces@^2.3.1, braces@^2.3.2: - version "2.3.2" - resolved "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz" - integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== - dependencies: - arr-flatten "^1.1.0" - array-unique "^0.3.2" - extend-shallow "^2.0.1" - fill-range "^4.0.0" - isobject "^3.0.1" - repeat-element "^1.1.2" - snapdragon "^0.8.1" - snapdragon-node "^2.0.1" - split-string "^3.0.2" - to-regex "^3.0.1" - -braces@^3.0.1, braces@~3.0.2: - version "3.0.2" - resolved "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz" - integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== - dependencies: - fill-range "^7.0.1" - -brorand@^1.0.1: - version "1.1.0" - resolved "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz" - integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= - -browser-process-hrtime@^0.1.2: - version "0.1.3" - resolved "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz" - integrity sha512-bRFnI4NnjO6cnyLmOV/7PVoDEMJChlcfN0z4s1YMBY989/SvlfMI1lgCnkFUs53e9gQF+w7qu7XdllSTiSl8Aw== - -browser-sync-client@^2.26.10: - version "2.26.10" - resolved "https://registry.npmjs.org/browser-sync-client/-/browser-sync-client-2.26.10.tgz" - integrity sha512-8pYitKwpVva7hzXJI8lTljNDbA9fjMEobHSxWqegIUon/GjJAG3UgHB/+lBWnOLzTY8rGX66MvGqL1Aknyrj7g== - dependencies: - etag "1.8.1" - fresh "0.5.2" - mitt "^1.1.3" - rxjs "^5.5.6" - -browser-sync-ui@^2.26.10: - version "2.26.10" - resolved "https://registry.npmjs.org/browser-sync-ui/-/browser-sync-ui-2.26.10.tgz" - integrity sha512-UfNSBItlXcmEvJ9RE4JooNtIsiIfHowp+7/52Jz4VFfQD4v78QK5/NV9DVrG41oMM3zLyhW4f/RliOb4ysStZg== - dependencies: - async-each-series "0.1.1" - connect-history-api-fallback "^1" - immutable "^3" - server-destroy "1.0.1" - socket.io-client "^2.0.4" - stream-throttle "^0.1.3" - -browser-sync@^2.26.10: - version "2.26.10" - resolved "https://registry.npmjs.org/browser-sync/-/browser-sync-2.26.10.tgz" - integrity sha512-JeVQP3CARvNA1DELj+ZGWj+/0pzE8+Omvq1WNgzaTXVdP3lNEbGxZbkjvLK7hHpQywjQ1sDJWlJQZT6V59XDTg== - dependencies: - browser-sync-client "^2.26.10" - browser-sync-ui "^2.26.10" - bs-recipes "1.3.4" - bs-snippet-injector "^2.0.1" - chokidar "^3.4.1" - connect "3.6.6" - connect-history-api-fallback "^1" - dev-ip "^1.0.1" - easy-extender "^2.3.4" - eazy-logger "^3" - etag "^1.8.1" - fresh "^0.5.2" - fs-extra "3.0.1" - http-proxy "^1.18.1" - immutable "^3" - localtunnel "^2.0.0" - micromatch "^4.0.2" - opn "5.3.0" - portscanner "2.1.1" - qs "6.2.3" - raw-body "^2.3.2" - resp-modifier "6.0.2" - rx "4.1.0" - send "0.16.2" - serve-index "1.9.1" - serve-static "1.13.2" - server-destroy "1.0.1" - socket.io "2.1.1" - ua-parser-js "^0.7.18" - yargs "^15.4.1" - -browserify-aes@^1.0.0, browserify-aes@^1.0.4: - version "1.2.0" - resolved "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz" - integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== - dependencies: - buffer-xor "^1.0.3" - cipher-base "^1.0.0" - create-hash "^1.1.0" - evp_bytestokey "^1.0.3" - inherits "^2.0.1" - safe-buffer "^5.0.1" - -browserify-cipher@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz" - integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w== - dependencies: - browserify-aes "^1.0.4" - browserify-des "^1.0.0" - evp_bytestokey "^1.0.0" - -browserify-des@^1.0.0: - version "1.0.2" - resolved "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz" - integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A== - dependencies: - cipher-base "^1.0.1" - des.js "^1.0.0" - inherits "^2.0.1" - safe-buffer "^5.1.2" - -browserify-rsa@^4.0.0: - version "4.0.1" - resolved "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz" - integrity sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ= - dependencies: - bn.js "^4.1.0" - randombytes "^2.0.1" - -browserify-sign@^4.0.0: - version "4.0.4" - resolved "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz" - integrity sha1-qk62jl17ZYuqa/alfmMMvXqT0pg= - dependencies: - bn.js "^4.1.1" - browserify-rsa "^4.0.0" - create-hash "^1.1.0" - create-hmac "^1.1.2" - elliptic "^6.0.0" - inherits "^2.0.1" - parse-asn1 "^5.0.0" - -browserify-zlib@^0.2.0: - version "0.2.0" - resolved "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz" - integrity sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA== - dependencies: - pako "~1.0.5" - -browserslist@^4.0.0, browserslist@^4.6.0, browserslist@^4.6.3, browserslist@^4.6.4, browserslist@^4.6.6: - version "4.7.0" - resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.7.0.tgz" - integrity sha512-9rGNDtnj+HaahxiVV38Gn8n8Lr8REKsel68v1sPFfIGEK6uSXTY3h9acgiT1dZVtOOUtifo/Dn8daDQ5dUgVsA== - dependencies: - caniuse-lite "^1.0.30000989" - electron-to-chromium "^1.3.247" - node-releases "^1.1.29" - -bs-recipes@1.3.4: - version "1.3.4" - resolved "https://registry.npmjs.org/bs-recipes/-/bs-recipes-1.3.4.tgz" - integrity sha1-DS1NSKcYyMBEdp/cT4lZLci2lYU= - -bs-snippet-injector@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/bs-snippet-injector/-/bs-snippet-injector-2.0.1.tgz" - integrity sha1-YbU5PxH1JVntEgaTEANDtu2wTdU= - -buffer-alloc-unsafe@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz" - integrity sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg== - -buffer-alloc@^1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz" - integrity sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow== - dependencies: - buffer-alloc-unsafe "^1.1.0" - buffer-fill "^1.0.0" - -buffer-crc32@~0.2.1, buffer-crc32@~0.2.3: - version "0.2.13" - resolved "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz" - integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= - -buffer-equal@0.0.1: - version "0.0.1" - resolved "https://registry.npmjs.org/buffer-equal/-/buffer-equal-0.0.1.tgz" - integrity sha1-kbx0sR6kBbyRa8aqkI+q+ltKrEs= - -buffer-equal@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.0.tgz" - integrity sha1-WWFrSYME1Var1GaWayLu2j7KX74= - -buffer-fill@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz" - integrity sha1-+PeLdniYiO858gXNY39o5wISKyw= - -buffer-from@^1.0.0: - version "1.1.1" - resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz" - integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== - -buffer-xor@^1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz" - integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= - -buffer@^4.3.0: - version "4.9.1" - resolved "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz" - integrity sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg= - dependencies: - base64-js "^1.0.2" - ieee754 "^1.1.4" - isarray "^1.0.0" - -buffer@^5.2.0, buffer@^5.2.1: - version "5.4.3" - resolved "https://registry.npmjs.org/buffer/-/buffer-5.4.3.tgz" - integrity sha512-zvj65TkFeIt3i6aj5bIvJDzjjQQGs4o/sNoezg1F1kYap9Nu2jcUdpwzRSJTHMMzG0H7bZkn4rNQpImhuxWX2A== - dependencies: - base64-js "^1.0.2" - ieee754 "^1.1.4" - -bufferstreams@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/bufferstreams/-/bufferstreams-2.0.1.tgz" - integrity sha512-ZswyIoBfFb3cVDsnZLLj2IDJ/0ppYdil/v2EGlZXvoefO689FokEmFEldhN5dV7R2QBxFneqTJOMIpfqhj+n0g== - dependencies: - readable-stream "^2.3.6" - -builtin-status-codes@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz" - integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug= - -bytes@1, bytes@1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/bytes/-/bytes-1.0.0.tgz" - integrity sha1-NWnt6Lo0MV+rmcPpLLBMciDeH6g= - -bytes@3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz" - integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== - -cacache@^12.0.2: - version "12.0.3" - resolved "https://registry.npmjs.org/cacache/-/cacache-12.0.3.tgz" - integrity sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw== - dependencies: - bluebird "^3.5.5" - chownr "^1.1.1" - figgy-pudding "^3.5.1" - glob "^7.1.4" - graceful-fs "^4.1.15" - infer-owner "^1.0.3" - lru-cache "^5.1.1" - mississippi "^3.0.0" - mkdirp "^0.5.1" - move-concurrently "^1.0.1" - promise-inflight "^1.0.1" - rimraf "^2.6.3" - ssri "^6.0.1" - unique-filename "^1.1.1" - y18n "^4.0.0" - -cache-base@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz" - integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== - dependencies: - collection-visit "^1.0.0" - component-emitter "^1.2.1" - get-value "^2.0.6" - has-value "^1.0.0" - isobject "^3.0.1" - set-value "^2.0.0" - to-object-path "^0.3.0" - union-value "^1.0.0" - unset-value "^1.0.0" - -cache-swap@^0.3.0: - version "0.3.0" - resolved "https://registry.npmjs.org/cache-swap/-/cache-swap-0.3.0.tgz" - integrity sha1-HFQaoQilAQb2ML3Zj+HeyLoTP1E= - dependencies: - graceful-fs "^4.1.2" - mkdirp "^0.5.1" - object-assign "^4.0.1" - rimraf "^2.4.0" - -cacheable-request@^2.1.1: - version "2.1.4" - resolved "https://registry.npmjs.org/cacheable-request/-/cacheable-request-2.1.4.tgz" - integrity sha1-DYCIAbY0KtM8kd+dC0TcCbkeXD0= - dependencies: - clone-response "1.0.2" - get-stream "3.0.0" - http-cache-semantics "3.8.1" - keyv "3.0.0" - lowercase-keys "1.0.0" - normalize-url "2.0.1" - responselike "1.0.2" - -cacheable-request@^6.0.0: - version "6.1.0" - resolved "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz" - integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg== - dependencies: - clone-response "^1.0.2" - get-stream "^5.1.0" - http-cache-semantics "^4.0.0" - keyv "^3.0.0" - lowercase-keys "^2.0.0" - normalize-url "^4.1.0" - responselike "^1.0.2" - -calipers-gif@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/calipers-gif/-/calipers-gif-2.0.0.tgz" - integrity sha1-te7+wwZKd8bc29W9xRc1oBuv3Dc= - dependencies: - bluebird "3.x.x" - -calipers-jpeg@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/calipers-jpeg/-/calipers-jpeg-2.0.0.tgz" - integrity sha1-BtVqU/YnF92AnLlWz2RCPOaTRls= - dependencies: - bluebird "3.x.x" - -calipers-png@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/calipers-png/-/calipers-png-2.0.0.tgz" - integrity sha1-HQ0g5cGuX3m3TVKGoul/Wbtwtlg= - dependencies: - bluebird "3.x.x" - -calipers-svg@^2.0.0: - version "2.0.1" - resolved "https://registry.npmjs.org/calipers-svg/-/calipers-svg-2.0.1.tgz" - integrity sha512-3PROqHARmj8wWudUC7DzXm1+mSocqgY7jNuehFNHgrUVrKf8o7MqDjS92vJz5LvZsAofJsoAFMajkqwbxBROSQ== - dependencies: - bluebird "3.x.x" - -calipers-webp@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/calipers-webp/-/calipers-webp-2.0.0.tgz" - integrity sha1-4Sbs4vhM1xd5YSv6KyZTzZXOp3o= - dependencies: - bluebird "3.x.x" - -calipers@^2.0.0: - version "2.0.1" - resolved "https://registry.npmjs.org/calipers/-/calipers-2.0.1.tgz" - integrity sha512-AP4Ui2Z8fZf69d8Dx4cfJgPjQHY3m+QXGFCaAGu8pfNQjyajkosS+Kkf1n6pQDMZcelN5h3MdcjweUqxcsS4pg== - dependencies: - bluebird "3.x.x" - -caller-callsite@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz" - integrity sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ= - dependencies: - callsites "^2.0.0" - -caller-path@^0.1.0: - version "0.1.0" - resolved "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz" - integrity sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8= - dependencies: - callsites "^0.2.0" - -caller-path@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz" - integrity sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ= - dependencies: - caller-callsite "^2.0.0" - -callsite@1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz" - integrity sha1-KAOY5dZkvXQDi28JBRU+borxvCA= - -callsites@^0.2.0: - version "0.2.0" - resolved "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz" - integrity sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo= - -callsites@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz" - integrity sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA= - -callsites@^3.0.0: - version "3.1.0" - resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" - integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== - -camel-case@3.0.x: - version "3.0.0" - resolved "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz" - integrity sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M= - dependencies: - no-case "^2.2.0" - upper-case "^1.1.1" - -camelcase-keys@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz" - integrity sha1-MIvur/3ygRkFHvodkyITyRuPkuc= - dependencies: - camelcase "^2.0.0" - map-obj "^1.0.0" - -camelcase@^2.0.0: - version "2.1.1" - resolved "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz" - integrity sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8= - -camelcase@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz" - integrity sha1-MvxLn82vhF/N9+c7uXysImHwqwo= - -camelcase@^5.0.0: - version "5.3.1" - resolved "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz" - integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== - -caniuse-api@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz" - integrity sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw== - dependencies: - browserslist "^4.0.0" - caniuse-lite "^1.0.0" - lodash.memoize "^4.1.2" - lodash.uniq "^4.5.0" - -caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000980, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30000989: - version "1.0.30000989" - resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000989.tgz" - integrity sha512-vrMcvSuMz16YY6GSVZ0dWDTJP8jqk3iFQ/Aq5iqblPwxSVVZI+zxDyTX0VPqtQsDnfdrBDcsmhgTEOh5R8Lbpw== - -caseless@~0.12.0: - version "0.12.0" - resolved "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz" - integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= - -caw@^2.0.0, caw@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/caw/-/caw-2.0.1.tgz" - integrity sha512-Cg8/ZSBEa8ZVY9HspcGUYaK63d/bN7rqS3CYCzEGUxuYv6UlmcjzDUz2fCFFHyTvUW5Pk0I+3hkA3iXlIj6guA== - dependencies: - get-proxy "^2.0.0" - isurl "^1.0.0-alpha5" - tunnel-agent "^0.6.0" - url-to-options "^1.0.1" - -chalk@2.4.2, chalk@^2.0.0, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.4.1, chalk@^2.4.2: - version "2.4.2" - resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - -chalk@^0.5.0: - version "0.5.1" - resolved "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz" - integrity sha1-Zjs6ZItotV0EaQ1JFnqoN4WPIXQ= - dependencies: - ansi-styles "^1.1.0" - escape-string-regexp "^1.0.0" - has-ansi "^0.1.0" - strip-ansi "^0.3.0" - supports-color "^0.2.0" - -chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: - version "1.1.3" - resolved "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz" - integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= - dependencies: - ansi-styles "^2.2.1" - escape-string-regexp "^1.0.2" - has-ansi "^2.0.0" - strip-ansi "^3.0.0" - supports-color "^2.0.0" - -chalk@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz" - integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -chardet@^0.7.0: - version "0.7.0" - resolved "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz" - integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== - -"chokidar@>=2.0.0 <4.0.0", chokidar@^3.4.0, chokidar@^3.4.1: - version "3.4.1" - resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.4.1.tgz" - integrity sha512-TQTJyr2stihpC4Sya9hs2Xh+O2wf+igjL36Y75xx2WdHuiICcn/XJza46Jwt0eT5hVpQOzo3FpY3cj3RVYLX0g== - dependencies: - anymatch "~3.1.1" - braces "~3.0.2" - glob-parent "~5.1.0" - is-binary-path "~2.1.0" - is-glob "~4.0.1" - normalize-path "~3.0.0" - readdirp "~3.4.0" - optionalDependencies: - fsevents "~2.1.2" - -chokidar@^2.0.0, chokidar@^2.1.8: - version "2.1.8" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" - integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg== - dependencies: - anymatch "^2.0.0" - async-each "^1.0.1" - braces "^2.3.2" - glob-parent "^3.1.0" - inherits "^2.0.3" - is-binary-path "^1.0.0" - is-glob "^4.0.0" - normalize-path "^3.0.0" - path-is-absolute "^1.0.0" - readdirp "^2.2.1" - upath "^1.1.1" - optionalDependencies: - fsevents "^1.2.7" - -chownr@^1.1.1: - version "1.1.2" - resolved "https://registry.npmjs.org/chownr/-/chownr-1.1.2.tgz" - integrity sha512-GkfeAQh+QNy3wquu9oIZr6SS5x7wGdSgNQvD10X3r+AZr1Oys22HW8kAmDMvNg2+Dm0TeGaEuO8gFwdBXxwO8A== - -chrome-trace-event@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz" - integrity sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ== - dependencies: - tslib "^1.9.0" - -chromium-pickle-js@^0.2.0: - version "0.2.0" - resolved "https://registry.npmjs.org/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz" - integrity sha1-BKEGZywYsIWrd02YPfo+oTjyIgU= - -cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: - version "1.0.4" - resolved "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz" - integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== - dependencies: - inherits "^2.0.1" - safe-buffer "^5.0.1" - -circular-dependency-plugin@^5.0.2: - version "5.2.0" - resolved "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-5.2.0.tgz" - integrity sha512-7p4Kn/gffhQaavNfyDFg7LS5S/UT1JAjyGd4UqR2+jzoYF02eDkj0Ec3+48TsIa4zghjLY87nQHIh/ecK9qLdw== - -circular-json@^0.3.1: - version "0.3.3" - resolved "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz" - integrity sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A== - -circular-json@^0.5.9: - version "0.5.9" - resolved "https://registry.npmjs.org/circular-json/-/circular-json-0.5.9.tgz" - integrity sha512-4ivwqHpIFJZBuhN3g/pEcdbnGUywkBblloGbkglyloVjjR3uT6tieI89MVOfbP2tHX5sgb01FuLgAOzebNlJNQ== - -class-utils@^0.3.5: - version "0.3.6" - resolved "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz" - integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== - dependencies: - arr-union "^3.1.0" - define-property "^0.2.5" - isobject "^3.0.0" - static-extend "^0.1.1" - -clean-css@4.2.x: - version "4.2.1" - resolved "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz" - integrity sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g== - dependencies: - source-map "~0.6.0" - -cli-cursor@^1.0.1: - version "1.0.2" - resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz" - integrity sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc= - dependencies: - restore-cursor "^1.0.1" - -cli-cursor@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz" - integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU= - dependencies: - restore-cursor "^2.0.0" - -cli-width@^2.0.0: - version "2.2.0" - resolved "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz" - integrity sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk= - -clipboard-copy@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/clipboard-copy/-/clipboard-copy-3.1.0.tgz" - integrity sha512-Xsu1NddBXB89IUauda5BIq3Zq73UWkjkaQlPQbLNvNsd5WBMnTWPNKYR6HGaySOxGYZ+BKxP2E9X4ElnI3yiPA== - -cliui@^3.2.0: - version "3.2.0" - resolved "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz" - integrity sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0= - dependencies: - string-width "^1.0.1" - strip-ansi "^3.0.1" - wrap-ansi "^2.0.0" - -cliui@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz" - integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA== - dependencies: - string-width "^3.1.0" - strip-ansi "^5.2.0" - wrap-ansi "^5.1.0" - -cliui@^6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz" - integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.0" - wrap-ansi "^6.2.0" - -clone-buffer@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz" - integrity sha1-4+JbIHrE5wGvch4staFnksrD3Fg= - -clone-response@1.0.2, clone-response@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz" - integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= - dependencies: - mimic-response "^1.0.0" - -clone-stats@^0.0.1, clone-stats@~0.0.1: - version "0.0.1" - resolved "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz" - integrity sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE= - -clone-stats@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz" - integrity sha1-s3gt/4u1R04Yuba/D9/ngvh3doA= - -clone@^0.2.0: - version "0.2.0" - resolved "https://registry.npmjs.org/clone/-/clone-0.2.0.tgz" - integrity sha1-xhJqkK1Pctv1rNskPMN3JP6T/B8= - -clone@^1.0.0, clone@^1.0.2: - version "1.0.4" - resolved "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz" - integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= - -clone@^2.1.1: - version "2.1.2" - resolved "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz" - integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= - -cloneable-readable@^1.0.0: - version "1.1.3" - resolved "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz" - integrity sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ== - dependencies: - inherits "^2.0.1" - process-nextick-args "^2.0.0" - readable-stream "^2.3.5" - -co@^4.6.0: - version "4.6.0" - resolved "https://registry.npmjs.org/co/-/co-4.6.0.tgz" - integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= - -coa@^2.0.2: - version "2.0.2" - resolved "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz" - integrity sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA== - dependencies: - "@types/q" "^1.5.1" - chalk "^2.4.1" - q "^1.1.2" - -code-point-at@^1.0.0: - version "1.1.0" - resolved "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz" - integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= - -collection-map@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/collection-map/-/collection-map-1.0.0.tgz" - integrity sha1-rqDwb40mx4DCt1SUOFVEsiVa8Yw= - dependencies: - arr-map "^2.0.2" - for-own "^1.0.0" - make-iterator "^1.0.0" - -collection-visit@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz" - integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= - dependencies: - map-visit "^1.0.0" - object-visit "^1.0.0" - -color-convert@^1.9.0, color-convert@^1.9.1: - version "1.9.3" - resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz" - integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== - dependencies: - color-name "1.1.3" - -color-convert@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz" - integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== - dependencies: - color-name "~1.1.4" - -color-name@1.1.3, color-name@^1.0.0: - version "1.1.3" - resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" - integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= - -color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - -color-string@^1.5.2: - version "1.5.3" - resolved "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz" - integrity sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw== - dependencies: - color-name "^1.0.0" - simple-swizzle "^0.2.2" - -color-support@^1.1.3: - version "1.1.3" - resolved "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz" - integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== - -color@^3.0.0: - version "3.1.2" - resolved "https://registry.npmjs.org/color/-/color-3.1.2.tgz" - integrity sha512-vXTJhHebByxZn3lDvDJYw4lR5+uB3vuoHsuYA5AKuxRVn5wzzIfQKGLBmgdVRHKTJYeK5rvJcHnrd0Li49CFpg== - dependencies: - color-convert "^1.9.1" - color-string "^1.5.2" - -colorette@^1.2.1: - version "1.2.1" - resolved "https://registry.npmjs.org/colorette/-/colorette-1.2.1.tgz" - integrity sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw== - -colors@1.0.x: - version "1.0.3" - resolved "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz" - integrity sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs= - -colors@^1.3.3: - version "1.3.3" - resolved "https://registry.npmjs.org/colors/-/colors-1.3.3.tgz" - integrity sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg== - -combined-stream@^1.0.6, combined-stream@~1.0.6: - version "1.0.8" - resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz" - integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== - dependencies: - delayed-stream "~1.0.0" - -commander@2.17.x: - version "2.17.1" - resolved "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz" - integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg== - -commander@^2.19.0, commander@^2.2.0, commander@^2.20.0, commander@^2.8.1: - version "2.20.0" - resolved "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz" - integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ== - -commander@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae" - integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg== - -commander@~2.19.0: - version "2.19.0" - resolved "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz" - integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg== - -commander@~2.8.1: - version "2.8.1" - resolved "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz" - integrity sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ= - dependencies: - graceful-readlink ">= 1.0.0" - -commondir@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz" - integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= - -compare-version@^0.1.2: - version "0.1.2" - resolved "https://registry.npmjs.org/compare-version/-/compare-version-0.1.2.tgz" - integrity sha1-AWLsLZNR9d3VmpICy6k1NmpyUIA= - -component-bind@1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz" - integrity sha1-AMYIq33Nk4l8AAllGx06jh5zu9E= - -component-emitter@1.2.1, component-emitter@^1.2.1: - version "1.2.1" - resolved "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz" - integrity sha1-E3kY1teCg/ffemt8WmPhQOaUJeY= - -component-inherit@0.0.3: - version "0.0.3" - resolved "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz" - integrity sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM= - -compress-commons@~0.1.0: - version "0.1.6" - resolved "https://registry.npmjs.org/compress-commons/-/compress-commons-0.1.6.tgz" - integrity sha1-DHQIcP3ljLpRbwrAyCLjOguF36M= - dependencies: - buffer-crc32 "~0.2.1" - crc32-stream "~0.3.1" - readable-stream "~1.0.26" - -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" - integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= - -concat-stream@^1.4.6, concat-stream@^1.5.0, concat-stream@^1.6.0: - version "1.6.2" - resolved "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz" - integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== - dependencies: - buffer-from "^1.0.0" - inherits "^2.0.3" - readable-stream "^2.2.2" - typedarray "^0.0.6" - -concat-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz" - integrity sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A== - dependencies: - buffer-from "^1.0.0" - inherits "^2.0.3" - readable-stream "^3.0.2" - typedarray "^0.0.6" - -config-chain@^1.1.11, config-chain@^1.1.12: - version "1.1.12" - resolved "https://registry.npmjs.org/config-chain/-/config-chain-1.1.12.tgz" - integrity sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA== - dependencies: - ini "^1.3.4" - proto-list "~1.2.1" - -connect-history-api-fallback@^1: - version "1.6.0" - resolved "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz" - integrity sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg== - -connect-livereload@^0.4.0: - version "0.4.1" - resolved "https://registry.npmjs.org/connect-livereload/-/connect-livereload-0.4.1.tgz" - integrity sha1-D4oagWvJuv+uRjfM6pF0Yv41kXo= - -connect@3.6.6, connect@^3.0.1: - version "3.6.6" - resolved "https://registry.npmjs.org/connect/-/connect-3.6.6.tgz" - integrity sha1-Ce/2xVr3I24TcTWnJXSFi2eG9SQ= - dependencies: - debug "2.6.9" - finalhandler "1.1.0" - parseurl "~1.3.2" - utils-merge "1.0.1" - -console-browserify@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz" - integrity sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA= - dependencies: - date-now "^0.1.4" - -console-stream@^0.1.1: - version "0.1.1" - resolved "https://registry.npmjs.org/console-stream/-/console-stream-0.1.1.tgz" - integrity sha1-oJX+B7IEZZVfL6/Si11yvM2UnUQ= - -constants-browserify@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz" - integrity sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U= - -content-disposition@^0.5.2: - version "0.5.3" - resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz" - integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== - dependencies: - safe-buffer "5.1.2" - -convert-source-map@^1.5.0, convert-source-map@^1.5.1, convert-source-map@^1.7.0: - version "1.7.0" - resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz" - integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== - dependencies: - safe-buffer "~5.1.1" - -cookie@0.3.1: - version "0.3.1" - resolved "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz" - integrity sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s= - -copy-concurrently@^1.0.0: - version "1.0.5" - resolved "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz" - integrity sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A== - dependencies: - aproba "^1.1.1" - fs-write-stream-atomic "^1.0.8" - iferr "^0.1.5" - mkdirp "^0.5.1" - rimraf "^2.5.4" - run-queue "^1.0.0" - -copy-descriptor@^0.1.0: - version "0.1.1" - resolved "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz" - integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= - -copy-props@^2.0.1: - version "2.0.4" - resolved "https://registry.npmjs.org/copy-props/-/copy-props-2.0.4.tgz" - integrity sha512-7cjuUME+p+S3HZlbllgsn2CDwS+5eCCX16qBgNC4jgSTf49qR1VKy/Zhl400m0IQXl/bPGEVqncgUUMjrr4s8A== - dependencies: - each-props "^1.3.0" - is-plain-object "^2.0.1" - -core-js-compat@^3.1.1: - version "3.2.1" - resolved "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.2.1.tgz" - integrity sha512-MwPZle5CF9dEaMYdDeWm73ao/IflDH+FjeJCWEADcEgFSE9TLimFKwJsfmkwzI8eC0Aj0mgvMDjeQjrElkz4/A== - dependencies: - browserslist "^4.6.6" - semver "^6.3.0" - -core-js@3: - version "3.2.1" - resolved "https://registry.npmjs.org/core-js/-/core-js-3.2.1.tgz" - integrity sha512-Qa5XSVefSVPRxy2XfUC13WbvqkxhkwB3ve+pgCQveNgYzbM/UxZeu1dcOX/xr4UmfUd+muuvsaxilQzCyUurMw== - -core-js@^2.4.0, core-js@^2.5.7: - version "2.6.9" - resolved "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz" - integrity sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A== - -core-js@^2.5.0: - version "2.6.11" - resolved "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz" - integrity sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg== - -core-util-is@1.0.2, core-util-is@~1.0.0: - version "1.0.2" - resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" - integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= - -cosmiconfig@^5.0.0: - version "5.2.1" - resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz" - integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA== - dependencies: - import-fresh "^2.0.0" - is-directory "^0.3.1" - js-yaml "^3.13.1" - parse-json "^4.0.0" - -crc32-stream@~0.3.1: - version "0.3.4" - resolved "https://registry.npmjs.org/crc32-stream/-/crc32-stream-0.3.4.tgz" - integrity sha1-c7wltF+sHbZjIjGnv86JJ+nwZVI= - dependencies: - buffer-crc32 "~0.2.1" - readable-stream "~1.0.24" - -create-ecdh@^4.0.0: - version "4.0.3" - resolved "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz" - integrity sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw== - dependencies: - bn.js "^4.1.0" - elliptic "^6.0.0" - -create-hash@^1.1.0, create-hash@^1.1.2: - version "1.2.0" - resolved "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz" - integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== - dependencies: - cipher-base "^1.0.1" - inherits "^2.0.1" - md5.js "^1.3.4" - ripemd160 "^2.0.1" - sha.js "^2.4.0" - -create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: - version "1.1.7" - resolved "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz" - integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== - dependencies: - cipher-base "^1.0.3" - create-hash "^1.1.0" - inherits "^2.0.1" - ripemd160 "^2.0.0" - safe-buffer "^5.0.1" - sha.js "^2.4.8" - -cross-spawn-windows-exe@^1.1.0, cross-spawn-windows-exe@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/cross-spawn-windows-exe/-/cross-spawn-windows-exe-1.2.0.tgz#46253b0f497676e766faf4a7061004618b5ac5ec" - integrity sha512-mkLtJJcYbDCxEG7Js6eUnUNndWjyUZwJ3H7bErmmtOYU/Zb99DyUkpamuIZE0b3bhmJyZ7D90uS6f+CGxRRjOw== - dependencies: - "@malept/cross-spawn-promise" "^1.1.0" - is-wsl "^2.2.0" - which "^2.0.2" - -cross-spawn@6.0.5, cross-spawn@^6.0.0, cross-spawn@^6.0.5: - version "6.0.5" - resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz" - integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== - dependencies: - nice-try "^1.0.4" - path-key "^2.0.1" - semver "^5.5.0" - shebang-command "^1.2.0" - which "^1.2.9" - -cross-spawn@^5.0.1: - version "5.1.0" - resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz" - integrity sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk= - dependencies: - lru-cache "^4.0.1" - shebang-command "^1.2.0" - which "^1.2.9" - -cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.3: - version "7.0.3" - resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== - dependencies: - path-key "^3.1.0" - shebang-command "^2.0.0" - which "^2.0.1" - -crypto-browserify@^3.11.0: - version "3.12.0" - resolved "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz" - integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg== - dependencies: - browserify-cipher "^1.0.0" - browserify-sign "^4.0.0" - create-ecdh "^4.0.0" - create-hash "^1.1.0" - create-hmac "^1.1.0" - diffie-hellman "^5.0.0" - inherits "^2.0.1" - pbkdf2 "^3.0.3" - public-encrypt "^4.0.0" - randombytes "^2.0.0" - randomfill "^1.0.3" - -crypto@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz" - integrity sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig== - -css-blank-pseudo@^0.1.4: - version "0.1.4" - resolved "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-0.1.4.tgz" - integrity sha512-LHz35Hr83dnFeipc7oqFDmsjHdljj3TQtxGGiNWSOsTLIAubSm4TEz8qCaKFpk7idaQ1GfWscF4E6mgpBysA1w== - dependencies: - postcss "^7.0.5" - -css-color-names@0.0.4, css-color-names@^0.0.4: - version "0.0.4" - resolved "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz" - integrity sha1-gIrcLnnPhHOAabZGyyDsJ762KeA= - -css-declaration-sorter@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz" - integrity sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA== - dependencies: - postcss "^7.0.1" - timsort "^0.3.0" - -css-has-pseudo@^0.10.0: - version "0.10.0" - resolved "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-0.10.0.tgz" - integrity sha512-Z8hnfsZu4o/kt+AuFzeGpLVhFOGO9mluyHBaA2bA8aCGTwah5sT3WV/fTHH8UNZUytOIImuGPrl/prlb4oX4qQ== - dependencies: - postcss "^7.0.6" - postcss-selector-parser "^5.0.0-rc.4" - -css-loader@^0.9.1: - version "0.9.1" - resolved "https://registry.npmjs.org/css-loader/-/css-loader-0.9.1.tgz" - integrity sha1-LhqgDOfjDvLGp6SzAKCAp8l54Nw= - dependencies: - csso "1.3.x" - loader-utils "~0.2.2" - source-map "~0.1.38" - -css-mqpacker@^7.0.0: - version "7.0.0" - resolved "https://registry.npmjs.org/css-mqpacker/-/css-mqpacker-7.0.0.tgz" - integrity sha512-temVrWS+sB4uocE2quhW8ru/KguDmGhCU7zN213KxtDvWOH3WS/ZUStfpF4fdCT7W8fPpFrQdWRFqtFtPPfBLA== - dependencies: - minimist "^1.2.0" - postcss "^7.0.0" - -css-prefers-color-scheme@^3.1.1: - version "3.1.1" - resolved "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-3.1.1.tgz" - integrity sha512-MTu6+tMs9S3EUqzmqLXEcgNRbNkkD/TGFvowpeoWJn5Vfq7FMgsmRQs9X5NXAURiOBmOxm/lLjsDNXDE6k9bhg== - dependencies: - postcss "^7.0.5" - -css-select-base-adapter@^0.1.1: - version "0.1.1" - resolved "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz" - integrity sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w== - -css-select@^2.0.0: - version "2.0.2" - resolved "https://registry.npmjs.org/css-select/-/css-select-2.0.2.tgz" - integrity sha512-dSpYaDVoWaELjvZ3mS6IKZM/y2PMPa/XYoEfYNZePL4U/XgyxZNroHEHReDx/d+VgXh9VbCTtFqLkFbmeqeaRQ== - dependencies: - boolbase "^1.0.0" - css-what "^2.1.2" - domutils "^1.7.0" - nth-check "^1.0.2" - -css-tree@1.0.0-alpha.29: - version "1.0.0-alpha.29" - resolved "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.29.tgz" - integrity sha512-sRNb1XydwkW9IOci6iB2xmy8IGCj6r/fr+JWitvJ2JxQRPzN3T4AGGVWCMlVmVwM1gtgALJRmGIlWv5ppnGGkg== - dependencies: - mdn-data "~1.1.0" - source-map "^0.5.3" - -css-tree@1.0.0-alpha.33: - version "1.0.0-alpha.33" - resolved "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.33.tgz" - integrity sha512-SPt57bh5nQnpsTBsx/IXbO14sRc9xXu5MtMAVuo0BaQQmyf0NupNPPSoMaqiAF5tDFafYsTkfeH4Q/HCKXkg4w== - dependencies: - mdn-data "2.0.4" - source-map "^0.5.3" - -css-unit-converter@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/css-unit-converter/-/css-unit-converter-1.1.1.tgz" - integrity sha1-2bkoGtz9jO2TW9urqDeGiX9k6ZY= - -css-what@^2.1.2: - version "2.1.3" - resolved "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz" - integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg== - -cssdb@^4.4.0: - version "4.4.0" - resolved "https://registry.npmjs.org/cssdb/-/cssdb-4.4.0.tgz" - integrity sha512-LsTAR1JPEM9TpGhl/0p3nQecC2LJ0kD8X5YARu1hk/9I1gril5vDtMZyNxcEpxxDj34YNck/ucjuoUd66K03oQ== - -cssesc@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz" - integrity sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg== - -cssnano-preset-advanced@^4.0.7: - version "4.0.7" - resolved "https://registry.npmjs.org/cssnano-preset-advanced/-/cssnano-preset-advanced-4.0.7.tgz" - integrity sha512-j1O5/DQnaAqEyFFQfC+Z/vRlLXL3LxJHN+lvsfYqr7KgPH74t69+Rsy2yXkovWNaJjZYBpdz2Fj8ab2nH7pZXw== - dependencies: - autoprefixer "^9.4.7" - cssnano-preset-default "^4.0.7" - postcss-discard-unused "^4.0.1" - postcss-merge-idents "^4.0.1" - postcss-reduce-idents "^4.0.2" - postcss-zindex "^4.0.1" - -cssnano-preset-default@^4.0.7: - version "4.0.7" - resolved "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-4.0.7.tgz" - integrity sha512-x0YHHx2h6p0fCl1zY9L9roD7rnlltugGu7zXSKQx6k2rYw0Hi3IqxcoAGF7u9Q5w1nt7vK0ulxV8Lo+EvllGsA== - dependencies: - css-declaration-sorter "^4.0.1" - cssnano-util-raw-cache "^4.0.1" - postcss "^7.0.0" - postcss-calc "^7.0.1" - postcss-colormin "^4.0.3" - postcss-convert-values "^4.0.1" - postcss-discard-comments "^4.0.2" - postcss-discard-duplicates "^4.0.2" - postcss-discard-empty "^4.0.1" - postcss-discard-overridden "^4.0.1" - postcss-merge-longhand "^4.0.11" - postcss-merge-rules "^4.0.3" - postcss-minify-font-values "^4.0.2" - postcss-minify-gradients "^4.0.2" - postcss-minify-params "^4.0.2" - postcss-minify-selectors "^4.0.2" - postcss-normalize-charset "^4.0.1" - postcss-normalize-display-values "^4.0.2" - postcss-normalize-positions "^4.0.2" - postcss-normalize-repeat-style "^4.0.2" - postcss-normalize-string "^4.0.2" - postcss-normalize-timing-functions "^4.0.2" - postcss-normalize-unicode "^4.0.1" - postcss-normalize-url "^4.0.1" - postcss-normalize-whitespace "^4.0.2" - postcss-ordered-values "^4.1.2" - postcss-reduce-initial "^4.0.3" - postcss-reduce-transforms "^4.0.2" - postcss-svgo "^4.0.2" - postcss-unique-selectors "^4.0.1" - -cssnano-util-get-arguments@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz" - integrity sha1-7ToIKZ8h11dBsg87gfGU7UnMFQ8= - -cssnano-util-get-match@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz" - integrity sha1-wOTKB/U4a7F+xeUiULT1lhNlFW0= - -cssnano-util-raw-cache@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz" - integrity sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA== - dependencies: - postcss "^7.0.0" - -cssnano-util-same-parent@^4.0.0: - version "4.0.1" - resolved "https://registry.npmjs.org/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz" - integrity sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q== - -cssnano@^4.1.10: - version "4.1.10" - resolved "https://registry.npmjs.org/cssnano/-/cssnano-4.1.10.tgz" - integrity sha512-5wny+F6H4/8RgNlaqab4ktc3e0/blKutmq8yNlBFXA//nSFFAqAngjNVRzUvCgYROULmZZUoosL/KSoZo5aUaQ== - dependencies: - cosmiconfig "^5.0.0" - cssnano-preset-default "^4.0.7" - is-resolvable "^1.0.0" - postcss "^7.0.0" - -csso@1.3.x: - version "1.3.12" - resolved "https://registry.npmjs.org/csso/-/csso-1.3.12.tgz" - integrity sha1-/GKGlKLTiTiqrEmWdTIY/TEc254= - -csso@^3.5.1: - version "3.5.1" - resolved "https://registry.npmjs.org/csso/-/csso-3.5.1.tgz" - integrity sha512-vrqULLffYU1Q2tLdJvaCYbONStnfkfimRxXNaGjxMldI0C7JPBC4rB1RyjhfdZ4m1frm8pM9uRPKH3d2knZ8gg== - dependencies: - css-tree "1.0.0-alpha.29" - -cssom@0.3.x, cssom@^0.3.4: - version "0.3.8" - resolved "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz" - integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== - -cssstyle@^1.1.1: - version "1.4.0" - resolved "https://registry.npmjs.org/cssstyle/-/cssstyle-1.4.0.tgz" - integrity sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA== - dependencies: - cssom "0.3.x" - -currently-unhandled@^0.4.1: - version "0.4.1" - resolved "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz" - integrity sha1-mI3zP+qxke95mmE2nddsF635V+o= - dependencies: - array-find-index "^1.0.1" - -cycle@1.0.x: - version "1.0.3" - resolved "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz" - integrity sha1-IegLK+hYD5i0aPN5QwZisEbDStI= - -cyclist@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz" - integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk= - -d@1, d@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/d/-/d-1.0.1.tgz" - integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== - dependencies: - es5-ext "^0.10.50" - type "^1.0.1" - -dashdash@^1.12.0: - version "1.14.1" - resolved "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz" - integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= - dependencies: - assert-plus "^1.0.0" - -data-urls@^1.0.1: - version "1.1.0" - resolved "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz" - integrity sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ== - dependencies: - abab "^2.0.0" - whatwg-mimetype "^2.2.0" - whatwg-url "^7.0.0" - -date-now@^0.1.4: - version "0.1.4" - resolved "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz" - integrity sha1-6vQ5/U1ISK105cx9vvIAZyueNFs= - -dateformat@^1.0.7-1.2.3: - version "1.0.12" - resolved "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz" - integrity sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk= - dependencies: - get-stdin "^4.0.1" - meow "^3.3.0" - -dateformat@^2.0.0: - version "2.2.0" - resolved "https://registry.npmjs.org/dateformat/-/dateformat-2.2.0.tgz" - integrity sha1-QGXiATz5+5Ft39gu+1Bq1MZ2kGI= - -debug@2.6.9, debug@^2.1.1, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9: - version "2.6.9" - resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - -debug@4.1.1, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@~4.1.0: - version "4.1.1" - resolved "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz" - integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== - dependencies: - ms "^2.1.1" - -debug@=3.1.0, debug@~3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz" - integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== - dependencies: - ms "2.0.0" - -debug@^3.1.0, debug@^3.2.6: - version "3.2.6" - resolved "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz" - integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== - dependencies: - ms "^2.1.1" - -debug@^4.3.4: - version "4.3.4" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" - integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== - dependencies: - ms "2.1.2" - -debug@~0.8.1: - version "0.8.1" - resolved "https://registry.npmjs.org/debug/-/debug-0.8.1.tgz" - integrity sha1-IP9NJvXkIstoobrLu2EDmtjBwTA= - -decamelize@^1.1.1, decamelize@^1.1.2, decamelize@^1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz" - integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= - -decode-uri-component@^0.2.0: - version "0.2.0" - resolved "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz" - integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= - -decompress-response@^3.2.0, decompress-response@^3.3.0: - version "3.3.0" - resolved "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz" - integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= - dependencies: - mimic-response "^1.0.0" - -decompress-tar@^4.0.0, decompress-tar@^4.1.0, decompress-tar@^4.1.1: - version "4.1.1" - resolved "https://registry.npmjs.org/decompress-tar/-/decompress-tar-4.1.1.tgz" - integrity sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ== - dependencies: - file-type "^5.2.0" - is-stream "^1.1.0" - tar-stream "^1.5.2" - -decompress-tarbz2@^4.0.0: - version "4.1.1" - resolved "https://registry.npmjs.org/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz" - integrity sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A== - dependencies: - decompress-tar "^4.1.0" - file-type "^6.1.0" - is-stream "^1.1.0" - seek-bzip "^1.0.5" - unbzip2-stream "^1.0.9" - -decompress-targz@^4.0.0: - version "4.1.1" - resolved "https://registry.npmjs.org/decompress-targz/-/decompress-targz-4.1.1.tgz" - integrity sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w== - dependencies: - decompress-tar "^4.1.1" - file-type "^5.2.0" - is-stream "^1.1.0" - -decompress-unzip@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/decompress-unzip/-/decompress-unzip-4.0.1.tgz" - integrity sha1-3qrM39FK6vhVePczroIQ+bSEj2k= - dependencies: - file-type "^3.8.0" - get-stream "^2.2.0" - pify "^2.3.0" - yauzl "^2.4.2" - -decompress@^4.0.0, decompress@^4.2.0: - version "4.2.0" - resolved "https://registry.npmjs.org/decompress/-/decompress-4.2.0.tgz" - integrity sha1-eu3YVCflqS2s/lVnSnxQXpbQH50= - dependencies: - decompress-tar "^4.0.0" - decompress-tarbz2 "^4.0.0" - decompress-targz "^4.0.0" - decompress-unzip "^4.0.1" - graceful-fs "^4.1.10" - make-dir "^1.0.0" - pify "^2.3.0" - strip-dirs "^2.0.0" - -deep-is@~0.1.3: - version "0.1.3" - resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz" - integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= - -deep-scope-analyser@^1.7.0: - version "1.7.0" - resolved "https://registry.npmjs.org/deep-scope-analyser/-/deep-scope-analyser-1.7.0.tgz" - integrity sha512-rl5Dmt2IZkFpZo6XbEY1zG8st2Wpq8Pi/dV2gz8ZF6BDYt3fnor2JNxHwdO1WLo0k6JbmYp0x8MNy8kE4l1NtA== - dependencies: - esrecurse "^4.2.1" - estraverse "^4.2.0" - -default-compare@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/default-compare/-/default-compare-1.0.0.tgz" - integrity sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ== - dependencies: - kind-of "^5.0.2" - -default-resolution@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/default-resolution/-/default-resolution-2.0.0.tgz" - integrity sha1-vLgrqnKtebQmp2cy8aga1t8m1oQ= - -defaults@^1.0.0: - version "1.0.3" - resolved "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz" - integrity sha1-xlYFHpgX2f8I7YgUd/P+QBnz730= - dependencies: - clone "^1.0.2" - -defer-to-connect@^1.0.1: - version "1.0.2" - resolved "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.0.2.tgz" - integrity sha512-k09hcQcTDY+cwgiwa6PYKLm3jlagNzQ+RSvhjzESOGOx+MNOuXkxTfEvPrO1IOQ81tArCFYQgi631clB70RpQw== - -define-properties@^1.1.2, define-properties@^1.1.3: - version "1.1.3" - resolved "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz" - integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== - dependencies: - object-keys "^1.0.12" - -define-property@^0.2.5: - version "0.2.5" - resolved "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz" - integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= - dependencies: - is-descriptor "^0.1.0" - -define-property@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz" - integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= - dependencies: - is-descriptor "^1.0.0" - -define-property@^2.0.2: - version "2.0.2" - resolved "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz" - integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== - dependencies: - is-descriptor "^1.0.2" - isobject "^3.0.1" - -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" - integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= - -delete-empty@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/delete-empty/-/delete-empty-3.0.0.tgz" - integrity sha512-ZUyiwo76W+DYnKsL3Kim6M/UOavPdBJgDYWOmuQhYaZvJH0AXAHbUNyEDtRbBra8wqqr686+63/0azfEk1ebUQ== - dependencies: - ansi-colors "^4.1.0" - minimist "^1.2.0" - path-starts-with "^2.0.0" - rimraf "^2.6.2" - -depd@0.4.5: - version "0.4.5" - resolved "https://registry.npmjs.org/depd/-/depd-0.4.5.tgz" - integrity sha1-GmZLUziLSmVz6K5ntfdnxpPKl/E= - -depd@~1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz" - integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= - -deprecated@^0.0.1: - version "0.0.1" - resolved "https://registry.npmjs.org/deprecated/-/deprecated-0.0.1.tgz" - integrity sha1-+cmvVGSvoeepcUWKi97yqpTVuxk= - -des.js@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz" - integrity sha1-wHTS4qpqipoH29YfmhXCzYPsjsw= - dependencies: - inherits "^2.0.1" - minimalistic-assert "^1.0.0" - -destroy@~1.0.4: - version "1.0.4" - resolved "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz" - integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= - -detect-file@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz" - integrity sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc= - -detect-indent@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz" - integrity sha1-920GQ1LN9Docts5hnE7jqUdd4gg= - dependencies: - repeating "^2.0.0" - -detect-node@^2.0.4: - version "2.1.0" - resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" - integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== - -dev-ip@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/dev-ip/-/dev-ip-1.0.1.tgz" - integrity sha1-p2o+0YVb56ASu4rBbLgPPADcKPA= - -diffie-hellman@^5.0.0: - version "5.0.3" - resolved "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz" - integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg== - dependencies: - bn.js "^4.1.0" - miller-rabin "^4.0.0" - randombytes "^2.0.0" - -dir-glob@^3.0.1: - version "3.0.1" - resolved "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz" - integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== - dependencies: - path-type "^4.0.0" - -doctrine@^1.2.2: - version "1.5.0" - resolved "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz" - integrity sha1-N53Ocw9hZvds76TmcHoVmwLFpvo= - dependencies: - esutils "^2.0.2" - isarray "^1.0.0" - -doctrine@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz" - integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== - dependencies: - esutils "^2.0.2" - -dom-serializer@0: - version "0.2.1" - resolved "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.1.tgz" - integrity sha512-sK3ujri04WyjwQXVoK4PU3y8ula1stq10GJZpqHIUgoGZdsGzAGu65BnU3d08aTVSvO7mGPZUc0wTEDL+qGE0Q== - dependencies: - domelementtype "^2.0.1" - entities "^2.0.0" - -dom-walk@^0.1.0: - version "0.1.1" - resolved "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.1.tgz" - integrity sha1-ZyIm3HTI95mtNTB9+TaroRrNYBg= - -domain-browser@^1.1.1: - version "1.2.0" - resolved "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz" - integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA== - -domelementtype@1: - version "1.3.1" - resolved "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz" - integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== - -domelementtype@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz" - integrity sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ== - -domexception@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz" - integrity sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug== - dependencies: - webidl-conversions "^4.0.2" - -domutils@^1.7.0: - version "1.7.0" - resolved "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz" - integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg== - dependencies: - dom-serializer "0" - domelementtype "1" - -dot-prop@^4.1.1: - version "4.2.0" - resolved "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz" - integrity sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ== - dependencies: - is-obj "^1.0.0" - -download@^6.2.2: - version "6.2.5" - resolved "https://registry.npmjs.org/download/-/download-6.2.5.tgz" - integrity sha512-DpO9K1sXAST8Cpzb7kmEhogJxymyVUd5qz/vCOSyvwtp2Klj2XcDt5YUuasgxka44SxF0q5RriKIwJmQHG2AuA== - dependencies: - caw "^2.0.0" - content-disposition "^0.5.2" - decompress "^4.0.0" - ext-name "^5.0.0" - file-type "5.2.0" - filenamify "^2.0.0" - get-stream "^3.0.0" - got "^7.0.0" - make-dir "^1.0.0" - p-event "^1.0.0" - pify "^3.0.0" - -download@^7.1.0: - version "7.1.0" - resolved "https://registry.npmjs.org/download/-/download-7.1.0.tgz" - integrity sha512-xqnBTVd/E+GxJVrX5/eUJiLYjCGPwMpdL+jGhGU57BvtcA7wwhtHVbXBeUk51kOpW3S7Jn3BQbN9Q1R1Km2qDQ== - dependencies: - archive-type "^4.0.0" - caw "^2.0.1" - content-disposition "^0.5.2" - decompress "^4.2.0" - ext-name "^5.0.0" - file-type "^8.1.0" - filenamify "^2.0.0" - get-stream "^3.0.0" - got "^8.3.1" - make-dir "^1.2.0" - p-event "^2.1.0" - pify "^3.0.0" - -duplexer2@0.0.2: - version "0.0.2" - resolved "https://registry.npmjs.org/duplexer2/-/duplexer2-0.0.2.tgz" - integrity sha1-xhTc9n4vsUmVqRcR5aYX6KYKMds= - dependencies: - readable-stream "~1.1.9" - -duplexer3@^0.1.4: - version "0.1.4" - resolved "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz" - integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= - -duplexify@^3.4.2, duplexify@^3.6.0: - version "3.7.1" - resolved "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz" - integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g== - dependencies: - end-of-stream "^1.0.0" - inherits "^2.0.1" - readable-stream "^2.0.0" - stream-shift "^1.0.0" - -duplexify@^4.1.1: - version "4.1.1" - resolved "https://registry.npmjs.org/duplexify/-/duplexify-4.1.1.tgz" - integrity sha512-DY3xVEmVHTv1wSzKNbwoU6nVjzI369Y6sPoqfYr0/xlx3IdX2n94xIszTcjPO8W8ZIv0Wb0PXNcjuZyT4wiICA== - dependencies: - end-of-stream "^1.4.1" - inherits "^2.0.3" - readable-stream "^3.1.1" - stream-shift "^1.0.0" - -each-props@^1.3.0: - version "1.3.2" - resolved "https://registry.npmjs.org/each-props/-/each-props-1.3.2.tgz" - integrity sha512-vV0Hem3zAGkJAyU7JSjixeU66rwdynTAa1vofCrSA5fEln+m67Az9CcnkVD776/fsN/UjIWmBDoNRS6t6G9RfA== - dependencies: - is-plain-object "^2.0.1" - object.defaults "^1.1.0" - -easy-extender@^2.3.4: - version "2.3.4" - resolved "https://registry.npmjs.org/easy-extender/-/easy-extender-2.3.4.tgz" - integrity sha512-8cAwm6md1YTiPpOvDULYJL4ZS6WfM5/cTeVVh4JsvyYZAoqlRVUpHL9Gr5Fy7HA6xcSZicUia3DeAgO3Us8E+Q== - dependencies: - lodash "^4.17.10" - -eazy-logger@^3: - version "3.0.2" - resolved "https://registry.npmjs.org/eazy-logger/-/eazy-logger-3.0.2.tgz" - integrity sha1-oyWqXlPROiIliJsqxBE7K5Y29Pw= - dependencies: - tfunk "^3.0.1" - -ecc-jsbn@~0.1.1: - version "0.1.2" - resolved "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz" - integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= - dependencies: - jsbn "~0.1.0" - safer-buffer "^2.1.0" - -editorconfig@^0.15.3: - version "0.15.3" - resolved "https://registry.npmjs.org/editorconfig/-/editorconfig-0.15.3.tgz" - integrity sha512-M9wIMFx96vq0R4F+gRpY3o2exzb8hEj/n9S8unZtHSvYjibBp/iMufSzvmOcV/laG0ZtuTVGtiJggPOSW2r93g== - dependencies: - commander "^2.19.0" - lru-cache "^4.1.5" - semver "^5.6.0" - sigmund "^1.0.1" - -ee-first@1.0.5: - version "1.0.5" - resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.0.5.tgz" - integrity sha1-jJshKJjYzZ8alDZlDOe+ICyen/A= - -ee-first@1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz" - integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= - -electron-notarize@^1.1.1, electron-notarize@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/electron-notarize/-/electron-notarize-1.2.1.tgz#347c18eca8e29dddadadee511b870c13d4008baf" - integrity sha512-u/ECWhIrhkSQpZM4cJzVZ5TsmkaqrRo5LDC/KMbGF0sPkm53Ng59+M0zp8QVaql0obfJy9vlVT+4iOkAi2UDlA== - dependencies: - debug "^4.1.1" - fs-extra "^9.0.1" - -electron-osx-sign@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/electron-osx-sign/-/electron-osx-sign-0.5.0.tgz#fc258c5e896859904bbe3d01da06902c04b51c3a" - integrity sha512-icoRLHzFz/qxzDh/N4Pi2z4yVHurlsCAYQvsCSG7fCedJ4UJXBS6PoQyGH71IfcqKupcKeK7HX/NkyfG+v6vlQ== - dependencies: - bluebird "^3.5.0" - compare-version "^0.1.2" - debug "^2.6.8" - isbinaryfile "^3.0.2" - minimist "^1.2.0" - plist "^3.0.1" - -electron-packager@^15.4.0: - version "15.4.0" - resolved "https://registry.yarnpkg.com/electron-packager/-/electron-packager-15.4.0.tgz#07ea036b70cde2062d4c8dce4d907d793b303998" - integrity sha512-JrrLcBP15KGrPj0cZ/ALKGmaQ4gJkn3mocf0E3bRKdR3kxKWYcDRpCvdhksYDXw/r3I6tMEcZ7XzyApWFXdVpw== - dependencies: - "@electron/get" "^1.6.0" - asar "^3.1.0" - cross-spawn-windows-exe "^1.2.0" - debug "^4.0.1" - electron-notarize "^1.1.1" - electron-osx-sign "^0.5.0" - extract-zip "^2.0.0" - filenamify "^4.1.0" - fs-extra "^9.0.0" - galactus "^0.2.1" - get-package-info "^1.0.0" - junk "^3.1.0" - parse-author "^2.0.0" - plist "^3.0.0" - rcedit "^3.0.1" - resolve "^1.1.6" - semver "^7.1.3" - yargs-parser "^20.0.0" - -electron-to-chromium@^1.3.247: - version "1.3.264" - resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.264.tgz" - integrity sha512-z8E7WkrrquCuGYv+kKyybuZIbdms+4PeHp7Zm2uIgEhAigP0bOwqXILItwj0YO73o+QyHY/7XtEfP5DsHOWQgQ== - -elliptic@^6.0.0: - version "6.5.1" - resolved "https://registry.npmjs.org/elliptic/-/elliptic-6.5.1.tgz" - integrity sha512-xvJINNLbTeWQjrl6X+7eQCrIy/YPv5XCpKW6kB5mKvtnGILoLDcySuwomfdzt0BMdLNVnuRNTuzKNHj0bva1Cg== - dependencies: - bn.js "^4.4.0" - brorand "^1.0.1" - hash.js "^1.0.0" - hmac-drbg "^1.0.0" - inherits "^2.0.1" - minimalistic-assert "^1.0.0" - minimalistic-crypto-utils "^1.0.0" - -email-validator@^2.0.4: - version "2.0.4" - resolved "https://registry.npmjs.org/email-validator/-/email-validator-2.0.4.tgz" - integrity sha512-gYCwo7kh5S3IDyZPLZf6hSS0MnZT8QmJFqYvbqlDZSbwdZlY6QZWxJ4i/6UhITOJ4XzyI647Bm2MXKCLqnJ4nQ== - -emoji-regex@^7.0.1: - version "7.0.3" - resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz" - integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== - -emoji-regex@^8.0.0: - version "8.0.0" - resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" - integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== - -emojis-list@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz" - integrity sha1-TapNnbAPmBmIDHn6RXrlsJof04k= - -emojis-list@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz" - integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== - -encodeurl@^1.0.2, encodeurl@~1.0.1, encodeurl@~1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz" - integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= - -end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1: - version "1.4.1" - resolved "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz" - integrity sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q== - dependencies: - once "^1.4.0" - -end-of-stream@~0.1.5: - version "0.1.5" - resolved "https://registry.npmjs.org/end-of-stream/-/end-of-stream-0.1.5.tgz" - integrity sha1-jhdyBsPICDfYVjLouTWd/osvbq8= - dependencies: - once "~1.3.0" - -engine.io-client@~3.2.0: - version "3.2.1" - resolved "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.2.1.tgz" - integrity sha512-y5AbkytWeM4jQr7m/koQLc5AxpRKC1hEVUb/s1FUAWEJq5AzJJ4NLvzuKPuxtDi5Mq755WuDvZ6Iv2rXj4PTzw== - dependencies: - component-emitter "1.2.1" - component-inherit "0.0.3" - debug "~3.1.0" - engine.io-parser "~2.1.1" - has-cors "1.1.0" - indexof "0.0.1" - parseqs "0.0.5" - parseuri "0.0.5" - ws "~3.3.1" - xmlhttprequest-ssl "~1.5.4" - yeast "0.1.2" - -engine.io-client@~3.4.0: - version "3.4.0" - resolved "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.4.0.tgz" - integrity sha512-a4J5QO2k99CM2a0b12IznnyQndoEvtA4UAldhGzKqnHf42I3Qs2W5SPnDvatZRcMaNZs4IevVicBPayxYt6FwA== - dependencies: - component-emitter "1.2.1" - component-inherit "0.0.3" - debug "~4.1.0" - engine.io-parser "~2.2.0" - has-cors "1.1.0" - indexof "0.0.1" - parseqs "0.0.5" - parseuri "0.0.5" - ws "~6.1.0" - xmlhttprequest-ssl "~1.5.4" - yeast "0.1.2" - -engine.io-parser@~2.1.0, engine.io-parser@~2.1.1: - version "2.1.3" - resolved "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.1.3.tgz" - integrity sha512-6HXPre2O4Houl7c4g7Ic/XzPnHBvaEmN90vtRO9uLmwtRqQmTOw0QMevL1TOfL2Cpu1VzsaTmMotQgMdkzGkVA== - dependencies: - after "0.8.2" - arraybuffer.slice "~0.0.7" - base64-arraybuffer "0.1.5" - blob "0.0.5" - has-binary2 "~1.0.2" - -engine.io-parser@~2.2.0: - version "2.2.0" - resolved "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.0.tgz" - integrity sha512-6I3qD9iUxotsC5HEMuuGsKA0cXerGz+4uGcXQEkfBidgKf0amsjrrtwcbwK/nzpZBxclXlV7gGl9dgWvu4LF6w== - dependencies: - after "0.8.2" - arraybuffer.slice "~0.0.7" - base64-arraybuffer "0.1.5" - blob "0.0.5" - has-binary2 "~1.0.2" - -engine.io@~3.2.0: - version "3.2.1" - resolved "https://registry.npmjs.org/engine.io/-/engine.io-3.2.1.tgz" - integrity sha512-+VlKzHzMhaU+GsCIg4AoXF1UdDFjHHwMmMKqMJNDNLlUlejz58FCy4LBqB2YVJskHGYl06BatYWKP2TVdVXE5w== - dependencies: - accepts "~1.3.4" - base64id "1.0.0" - cookie "0.3.1" - debug "~3.1.0" - engine.io-parser "~2.1.0" - ws "~3.3.1" - -enhanced-resolve@4.1.0, enhanced-resolve@^4.1.0: - version "4.1.0" - resolved "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz" - integrity sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng== - dependencies: - graceful-fs "^4.1.2" - memory-fs "^0.4.0" - tapable "^1.0.0" - -entities@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz" - integrity sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw== - -env-paths@^2.2.0: - version "2.2.0" - resolved "https://registry.npmjs.org/env-paths/-/env-paths-2.2.0.tgz" - integrity sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA== - -errno@^0.1.3, errno@~0.1.7: - version "0.1.7" - resolved "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz" - integrity sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg== - dependencies: - prr "~1.0.1" - -error-ex@^1.2.0, error-ex@^1.3.1: - version "1.3.2" - resolved "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz" - integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== - dependencies: - is-arrayish "^0.2.1" - -es-abstract@^1.12.0, es-abstract@^1.13.0, es-abstract@^1.5.1: - version "1.14.2" - resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.14.2.tgz" - integrity sha512-DgoQmbpFNOofkjJtKwr87Ma5EW4Dc8fWhD0R+ndq7Oc456ivUfGOOP6oAZTTKl5/CcNMP+EN+e3/iUzgE0veZg== - dependencies: - es-to-primitive "^1.2.0" - function-bind "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.0" - is-callable "^1.1.4" - is-regex "^1.0.4" - object-inspect "^1.6.0" - object-keys "^1.1.1" - string.prototype.trimleft "^2.0.0" - string.prototype.trimright "^2.0.0" - -es-to-primitive@^1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz" - integrity sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg== - dependencies: - is-callable "^1.1.4" - is-date-object "^1.0.1" - is-symbol "^1.0.2" - -es5-ext@^0.10.35, es5-ext@^0.10.46, es5-ext@^0.10.50, es5-ext@^0.10.51, es5-ext@~0.10.14: - version "0.10.51" - resolved "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.51.tgz" - integrity sha512-oRpWzM2WcLHVKpnrcyB7OW8j/s67Ba04JCm0WnNv3RiABSvs7mrQlutB8DBv793gKcp0XENR8Il8WxGTlZ73gQ== - dependencies: - es6-iterator "~2.0.3" - es6-symbol "~3.1.1" - next-tick "^1.0.0" - -es6-error@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" - integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== - -es6-iterator@^2.0.1, es6-iterator@^2.0.3, es6-iterator@~2.0.1, es6-iterator@~2.0.3: - version "2.0.3" - resolved "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz" - integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c= - dependencies: - d "1" - es5-ext "^0.10.35" - es6-symbol "^3.1.1" - -es6-map@^0.1.3: - version "0.1.5" - resolved "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz" - integrity sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA= - dependencies: - d "1" - es5-ext "~0.10.14" - es6-iterator "~2.0.1" - es6-set "~0.1.5" - es6-symbol "~3.1.1" - event-emitter "~0.3.5" - -es6-set@~0.1.5: - version "0.1.5" - resolved "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz" - integrity sha1-0rPsXU2ADO2BjbU40ol02wpzzLE= - dependencies: - d "1" - es5-ext "~0.10.14" - es6-iterator "~2.0.1" - es6-symbol "3.1.1" - event-emitter "~0.3.5" - -es6-symbol@3.1.1: - version "3.1.1" - resolved "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz" - integrity sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc= - dependencies: - d "1" - es5-ext "~0.10.14" - -es6-symbol@^3.1.1, es6-symbol@~3.1.1: - version "3.1.2" - resolved "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.2.tgz" - integrity sha512-/ZypxQsArlv+KHpGvng52/Iz8by3EQPxhmbuz8yFG89N/caTFBSbcXONDw0aMjy827gQg26XAjP4uXFvnfINmQ== - dependencies: - d "^1.0.1" - es5-ext "^0.10.51" - -es6-templates@^0.2.3: - version "0.2.3" - resolved "https://registry.npmjs.org/es6-templates/-/es6-templates-0.2.3.tgz" - integrity sha1-XLmsn7He1usSOTQrgdeSu7QHjuQ= - dependencies: - recast "~0.11.12" - through "~2.3.6" - -es6-weak-map@^2.0.1: - version "2.0.3" - resolved "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz" - integrity sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA== - dependencies: - d "1" - es5-ext "^0.10.46" - es6-iterator "^2.0.3" - es6-symbol "^3.1.1" - -escape-html@~1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz" - integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= - -escape-string-regexp@^1.0.0, escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: - version "1.0.5" - resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" - integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= - -escape-string-regexp@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" - integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== - -escodegen@^1.11.0: - version "1.12.0" - resolved "https://registry.npmjs.org/escodegen/-/escodegen-1.12.0.tgz" - integrity sha512-TuA+EhsanGcme5T3R0L80u4t8CpbXQjegRmf7+FPTJrtCTErXFeelblRgHQa1FofEzqYYJmJ/OqjTwREp9qgmg== - dependencies: - esprima "^3.1.3" - estraverse "^4.2.0" - esutils "^2.0.2" - optionator "^0.8.1" - optionalDependencies: - source-map "~0.6.1" - -escope@^3.6.0: - version "3.6.0" - resolved "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz" - integrity sha1-4Bl16BJ4GhY6ba392AOY3GTIicM= - dependencies: - es6-map "^0.1.3" - es6-weak-map "^2.0.1" - esrecurse "^4.1.0" - estraverse "^4.1.1" - -eslint-scope@^4.0.3: - version "4.0.3" - resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz" - integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg== - dependencies: - esrecurse "^4.1.0" - estraverse "^4.1.1" - -eslint-utils@^1.3.1: - version "1.4.2" - resolved "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.2.tgz" - integrity sha512-eAZS2sEUMlIeCjBeubdj45dmBHQwPHWyBcT1VSYB7o9x9WRRqKxyUoiXlRjyAwzN7YEzHJlYg0NmzDRWx6GP4Q== - dependencies: - eslint-visitor-keys "^1.0.0" - -eslint-visitor-keys@^1.0.0: - version "1.1.0" - resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz" - integrity sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A== - -eslint@^2.7.0: - version "2.13.1" - resolved "https://registry.npmjs.org/eslint/-/eslint-2.13.1.tgz" - integrity sha1-5MyPoPAJ+4KaquI4VaKTYL4fbBE= - dependencies: - chalk "^1.1.3" - concat-stream "^1.4.6" - debug "^2.1.1" - doctrine "^1.2.2" - es6-map "^0.1.3" - escope "^3.6.0" - espree "^3.1.6" - estraverse "^4.2.0" - esutils "^2.0.2" - file-entry-cache "^1.1.1" - glob "^7.0.3" - globals "^9.2.0" - ignore "^3.1.2" - imurmurhash "^0.1.4" - inquirer "^0.12.0" - is-my-json-valid "^2.10.0" - is-resolvable "^1.0.0" - js-yaml "^3.5.1" - json-stable-stringify "^1.0.0" - levn "^0.3.0" - lodash "^4.0.0" - mkdirp "^0.5.0" - optionator "^0.8.1" - path-is-absolute "^1.0.0" - path-is-inside "^1.0.1" - pluralize "^1.2.1" - progress "^1.1.8" - require-uncached "^1.0.2" - shelljs "^0.6.0" - strip-json-comments "~1.0.1" - table "^3.7.8" - text-table "~0.2.0" - user-home "^2.0.0" - -eslint@^5.9.0: - version "5.16.0" - resolved "https://registry.npmjs.org/eslint/-/eslint-5.16.0.tgz" - integrity sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg== - dependencies: - "@babel/code-frame" "^7.0.0" - ajv "^6.9.1" - chalk "^2.1.0" - cross-spawn "^6.0.5" - debug "^4.0.1" - doctrine "^3.0.0" - eslint-scope "^4.0.3" - eslint-utils "^1.3.1" - eslint-visitor-keys "^1.0.0" - espree "^5.0.1" - esquery "^1.0.1" - esutils "^2.0.2" - file-entry-cache "^5.0.1" - functional-red-black-tree "^1.0.1" - glob "^7.1.2" - globals "^11.7.0" - ignore "^4.0.6" - import-fresh "^3.0.0" - imurmurhash "^0.1.4" - inquirer "^6.2.2" - js-yaml "^3.13.0" - json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.3.0" - lodash "^4.17.11" - minimatch "^3.0.4" - mkdirp "^0.5.1" - natural-compare "^1.4.0" - optionator "^0.8.2" - path-is-inside "^1.0.2" - progress "^2.0.0" - regexpp "^2.0.1" - semver "^5.5.1" - strip-ansi "^4.0.0" - strip-json-comments "^2.0.1" - table "^5.2.3" - text-table "^0.2.0" - -espree@^3.1.6: - version "3.5.4" - resolved "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz" - integrity sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A== - dependencies: - acorn "^5.5.0" - acorn-jsx "^3.0.0" - -espree@^5.0.1: - version "5.0.1" - resolved "https://registry.npmjs.org/espree/-/espree-5.0.1.tgz" - integrity sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A== - dependencies: - acorn "^6.0.7" - acorn-jsx "^5.0.0" - eslint-visitor-keys "^1.0.0" - -esprima@^3.1.3, esprima@~3.1.0: - version "3.1.3" - resolved "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz" - integrity sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM= - -esprima@^4.0.0: - version "4.0.1" - resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz" - integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== - -esquery@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz" - integrity sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA== - dependencies: - estraverse "^4.0.0" - -esrecurse@^4.1.0, esrecurse@^4.2.1: - version "4.2.1" - resolved "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz" - integrity sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ== - dependencies: - estraverse "^4.1.0" - -estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: - version "4.3.0" - resolved "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz" - integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== - -esutils@^2.0.2: - version "2.0.3" - resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz" - integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== - -etag@1.8.1, etag@^1.8.1, etag@~1.8.1: - version "1.8.1" - resolved "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz" - integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= - -event-emitter@~0.3.5: - version "0.3.5" - resolved "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz" - integrity sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk= - dependencies: - d "1" - es5-ext "~0.10.14" - -eventemitter3@^4.0.0: - version "4.0.4" - resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.4.tgz" - integrity sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ== - -events@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/events/-/events-3.0.0.tgz" - integrity sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA== - -evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz" - integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== - dependencies: - md5.js "^1.3.4" - safe-buffer "^5.1.1" - -exec-buffer@^3.0.0: - version "3.2.0" - resolved "https://registry.npmjs.org/exec-buffer/-/exec-buffer-3.2.0.tgz" - integrity sha512-wsiD+2Tp6BWHoVv3B+5Dcx6E7u5zky+hUwOHjuH2hKSLR3dvRmX8fk8UD8uqQixHs4Wk6eDmiegVrMPjKj7wpA== - dependencies: - execa "^0.7.0" - p-finally "^1.0.0" - pify "^3.0.0" - rimraf "^2.5.4" - tempfile "^2.0.0" - -execa@^0.10.0: - version "0.10.0" - resolved "https://registry.npmjs.org/execa/-/execa-0.10.0.tgz" - integrity sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw== - dependencies: - cross-spawn "^6.0.0" - get-stream "^3.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" - -execa@^0.7.0: - version "0.7.0" - resolved "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz" - integrity sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c= - dependencies: - cross-spawn "^5.0.1" - get-stream "^3.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" - -execa@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz" - integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== - dependencies: - cross-spawn "^6.0.0" - get-stream "^4.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" - -execa@^4.0.0: - version "4.0.2" - resolved "https://registry.npmjs.org/execa/-/execa-4.0.2.tgz" - integrity sha512-QI2zLa6CjGWdiQsmSkZoGtDx2N+cQIGb3yNolGTdjSQzydzLgYYf8LRuagp7S7fPimjcrzUDSUFd/MgzELMi4Q== - dependencies: - cross-spawn "^7.0.0" - get-stream "^5.0.0" - human-signals "^1.1.1" - is-stream "^2.0.0" - merge-stream "^2.0.0" - npm-run-path "^4.0.0" - onetime "^5.1.0" - signal-exit "^3.0.2" - strip-final-newline "^2.0.0" - -execa@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/execa/-/execa-5.0.0.tgz" - integrity sha512-ov6w/2LCiuyO4RLYGdpFGjkcs0wMTgGE8PrkTHikeUy5iJekXyPIKUjifk5CsE0pt7sMCrMZ3YNqoCj6idQOnQ== - dependencies: - cross-spawn "^7.0.3" - get-stream "^6.0.0" - human-signals "^2.1.0" - is-stream "^2.0.0" - merge-stream "^2.0.0" - npm-run-path "^4.0.1" - onetime "^5.1.2" - signal-exit "^3.0.3" - strip-final-newline "^2.0.0" - -executable@^4.1.0: - version "4.1.1" - resolved "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz" - integrity sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg== - dependencies: - pify "^2.2.0" - -exif-parser@^0.1.12: - version "0.1.12" - resolved "https://registry.npmjs.org/exif-parser/-/exif-parser-0.1.12.tgz" - integrity sha1-WKnS1ywCwfbwKg70qRZicrd2CSI= - -exit-hook@^1.0.0: - version "1.1.1" - resolved "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz" - integrity sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g= - -expand-brackets@^2.1.4: - version "2.1.4" - resolved "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz" - integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= - dependencies: - debug "^2.3.3" - define-property "^0.2.5" - extend-shallow "^2.0.1" - posix-character-classes "^0.1.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - -expand-tilde@^2.0.0, expand-tilde@^2.0.2: - version "2.0.2" - resolved "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz" - integrity sha1-l+gBqgUt8CRU3kawK/YhZCzchQI= - dependencies: - homedir-polyfill "^1.0.1" - -ext-list@^2.0.0: - version "2.2.2" - resolved "https://registry.npmjs.org/ext-list/-/ext-list-2.2.2.tgz" - integrity sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA== - dependencies: - mime-db "^1.28.0" - -ext-name@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/ext-name/-/ext-name-5.0.0.tgz" - integrity sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ== - dependencies: - ext-list "^2.0.0" - sort-keys-length "^1.0.0" - -extend-shallow@^1.1.2: - version "1.1.4" - resolved "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz" - integrity sha1-Gda/lN/AnXa6cR85uHLSH/TdkHE= - dependencies: - kind-of "^1.1.0" - -extend-shallow@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz" - integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= - dependencies: - is-extendable "^0.1.0" - -extend-shallow@^3.0.0, extend-shallow@^3.0.2: - version "3.0.2" - resolved "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz" - integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= - dependencies: - assign-symbols "^1.0.0" - is-extendable "^1.0.1" - -extend@^3.0.0, extend@~3.0.2: - version "3.0.2" - resolved "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz" - integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== - -external-editor@^3.0.3: - version "3.1.0" - resolved "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz" - integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== - dependencies: - chardet "^0.7.0" - iconv-lite "^0.4.24" - tmp "^0.0.33" - -extglob@^2.0.4: - version "2.0.4" - resolved "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz" - integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== - dependencies: - array-unique "^0.3.2" - define-property "^1.0.0" - expand-brackets "^2.1.4" - extend-shallow "^2.0.1" - fragment-cache "^0.2.1" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - -extract-zip@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a" - integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg== - dependencies: - debug "^4.1.1" - get-stream "^5.1.0" - yauzl "^2.10.0" - optionalDependencies: - "@types/yauzl" "^2.9.1" - -extsprintf@1.3.0, extsprintf@^1.2.0: - version "1.3.0" - resolved "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz" - integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= - -eyes@0.1.x: - version "0.1.8" - resolved "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz" - integrity sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A= - -fancy-log@^1.1.0, fancy-log@^1.2.0, fancy-log@^1.3.2, fancy-log@^1.3.3: - version "1.3.3" - resolved "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz" - integrity sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw== - dependencies: - ansi-gray "^0.1.1" - color-support "^1.1.3" - parse-node-version "^1.0.0" - time-stamp "^1.0.0" - -fast-deep-equal@^3.1.1: - version "3.1.1" - resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz" - integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA== - -fast-glob@^3.0.3: - version "3.2.2" - resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.2.tgz" - integrity sha512-UDV82o4uQyljznxwMxyVRJgZZt3O5wENYojjzbaGEGZgeOxkLFf+V4cnUD+krzb2F72E18RhamkMZ7AdeggF7A== - 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" - -fast-json-stable-stringify@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz" - integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I= - -fast-levenshtein@~2.0.4: - version "2.0.6" - resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz" - integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= - -fastdom@^1.0.9: - version "1.0.9" - resolved "https://registry.npmjs.org/fastdom/-/fastdom-1.0.9.tgz" - integrity sha512-SSp4fbVzu8JkkG01NUX+0iOwe9M5PN3MGIQ84txLf4TkkJG4q30khkzumKgi4hUqO1+jX6wLHfnCPoZ6eSZ6Tg== - dependencies: - strictdom "^1.0.1" - -faster.js@^1.1.0: - version "1.1.1" - resolved "https://registry.npmjs.org/faster.js/-/faster.js-1.1.1.tgz" - integrity sha512-vPThNSLL/E1f7cLHd9yuayxZR82o/Iic4S5ZY45iY5AgBLNIlr3b3c+VpDjoYqjY9a9C/FQVUQy9oTILVP7X0g== - -fastparse@^1.1.1: - version "1.1.2" - resolved "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz" - integrity sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ== - -fastq@^1.6.0: - version "1.8.0" - resolved "https://registry.npmjs.org/fastq/-/fastq-1.8.0.tgz" - integrity sha512-SMIZoZdLh/fgofivvIkmknUXyPnvxRE3DhtZ5Me3Mrsk5gyPL42F0xr51TdRXskBxHfMp+07bcYzfsYEsSQA9Q== - dependencies: - reusify "^1.0.4" - -faye-websocket@~0.7.2: - version "0.7.3" - resolved "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.7.3.tgz" - integrity sha1-zEB0x/Sk39A69U3WXDVLE1EyzhE= - dependencies: - websocket-driver ">=0.3.6" - -fd-slicer@~1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz" - integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4= - dependencies: - pend "~1.2.0" - -figgy-pudding@^3.5.1: - version "3.5.1" - resolved "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz" - integrity sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w== - -figures@^1.3.5: - version "1.7.0" - resolved "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz" - integrity sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4= - dependencies: - escape-string-regexp "^1.0.5" - object-assign "^4.1.0" - -figures@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz" - integrity sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI= - dependencies: - escape-string-regexp "^1.0.5" - -file-entry-cache@^1.1.1: - version "1.3.1" - resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-1.3.1.tgz" - integrity sha1-RMYepgeuS+nBQC9B9EJwy/4zT/g= - dependencies: - flat-cache "^1.2.1" - object-assign "^4.0.1" - -file-entry-cache@^5.0.1: - 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== - dependencies: - flat-cache "^2.0.1" - -file-loader@^0.8.1: - version "0.8.5" - resolved "https://registry.npmjs.org/file-loader/-/file-loader-0.8.5.tgz" - integrity sha1-knXQMf54DyfUf19K8CvUNxPMFRs= - dependencies: - loader-utils "~0.2.5" - -file-type@5.2.0, file-type@^5.2.0: - version "5.2.0" - resolved "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz" - integrity sha1-LdvqfHP/42No365J3DOMBYwritY= - -file-type@^10.4.0: - version "10.11.0" - resolved "https://registry.npmjs.org/file-type/-/file-type-10.11.0.tgz" - integrity sha512-uzk64HRpUZyTGZtVuvrjP0FYxzQrBf4rojot6J65YMEbwBLB0CWm0CLojVpwpmFmxcE/lkvYICgfcGozbBq6rw== - -file-type@^12.0.0: - version "12.4.2" - resolved "https://registry.npmjs.org/file-type/-/file-type-12.4.2.tgz" - integrity sha512-UssQP5ZgIOKelfsaB5CuGAL+Y+q7EmONuiwF3N5HAH0t27rvrttgi6Ra9k/+DVaY9UF6+ybxu5pOXLUdA8N7Vg== - -file-type@^3.8.0: - version "3.9.0" - resolved "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz" - integrity sha1-JXoHg4TR24CHvESdEH1SpSZyuek= - -file-type@^4.2.0: - version "4.4.0" - resolved "https://registry.npmjs.org/file-type/-/file-type-4.4.0.tgz" - integrity sha1-G2AOX8ofvcboDApwxxyNul95BsU= - -file-type@^6.1.0: - version "6.2.0" - resolved "https://registry.npmjs.org/file-type/-/file-type-6.2.0.tgz" - integrity sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg== - -file-type@^8.1.0: - version "8.1.0" - resolved "https://registry.npmjs.org/file-type/-/file-type-8.1.0.tgz" - integrity sha512-qyQ0pzAy78gVoJsmYeNgl8uH8yKhr1lVhW7JbzJmnlRi0I4R2eEDEJZVKG8agpDnLpacwNbDhLNG/LMdxHD2YQ== - -file-type@^9.0.0: - version "9.0.0" - resolved "https://registry.npmjs.org/file-type/-/file-type-9.0.0.tgz" - integrity sha512-Qe/5NJrgIOlwijpq3B7BEpzPFcgzggOTagZmkXQY4LA6bsXKTUstK7Wp12lEJ/mLKTpvIZxmIuRcLYWT6ov9lw== - -file-uri-to-path@1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz" - integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== - -filename-reserved-regex@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz" - integrity sha1-q/c9+rc10EVECr/qLZHzieu/oik= - -filenamify@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/filenamify/-/filenamify-2.1.0.tgz" - integrity sha512-ICw7NTT6RsDp2rnYKVd8Fu4cr6ITzGy3+u4vUujPkabyaz+03F24NWEX7fs5fp+kBonlaqPH8fAO2NM+SXt/JA== - dependencies: - filename-reserved-regex "^2.0.0" - strip-outer "^1.0.0" - trim-repeated "^1.0.0" - -filenamify@^4.1.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/filenamify/-/filenamify-4.3.0.tgz#62391cb58f02b09971c9d4f9d63b3cf9aba03106" - integrity sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg== - dependencies: - filename-reserved-regex "^2.0.0" - strip-outer "^1.0.1" - trim-repeated "^1.0.0" - -fill-range@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz" - integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= - dependencies: - extend-shallow "^2.0.1" - is-number "^3.0.0" - repeat-string "^1.6.1" - to-regex-range "^2.1.0" - -fill-range@^7.0.1: - version "7.0.1" - resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz" - integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== - dependencies: - to-regex-range "^5.0.1" - -finalhandler@1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz" - integrity sha1-zgtoVbRYU+eRsvzGgARtiCU91/U= - dependencies: - debug "2.6.9" - encodeurl "~1.0.1" - escape-html "~1.0.3" - on-finished "~2.3.0" - parseurl "~1.3.2" - statuses "~1.3.1" - unpipe "~1.0.0" - -find-cache-dir@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz" - integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ== - dependencies: - commondir "^1.0.1" - make-dir "^2.0.0" - pkg-dir "^3.0.0" - -find-index@^0.1.1: - version "0.1.1" - resolved "https://registry.npmjs.org/find-index/-/find-index-0.1.1.tgz" - integrity sha1-Z101iyyjiS15Whq0cjL4tuLg3eQ= - -find-up@^1.0.0: - version "1.1.2" - resolved "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz" - integrity sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8= - dependencies: - path-exists "^2.0.0" - pinkie-promise "^2.0.0" - -find-up@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz" - integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= - dependencies: - locate-path "^2.0.0" - -find-up@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz" - integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== - dependencies: - locate-path "^3.0.0" - -find-up@^4.1.0: - version "4.1.0" - resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz" - integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== - dependencies: - locate-path "^5.0.0" - path-exists "^4.0.0" - -find-versions@^3.0.0: - version "3.1.0" - resolved "https://registry.npmjs.org/find-versions/-/find-versions-3.1.0.tgz" - integrity sha512-NCTfNiVzeE/xL+roNDffGuRbrWI6atI18lTJ22vKp7rs2OhYzMK3W1dIdO2TUndH/QMcacM4d1uWwgcZcHK69Q== - dependencies: - array-uniq "^2.1.0" - semver-regex "^2.0.0" - -findup-sync@3.0.0, findup-sync@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz" - integrity sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg== - dependencies: - detect-file "^1.0.0" - is-glob "^4.0.0" - micromatch "^3.0.4" - resolve-dir "^1.0.1" - -findup-sync@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz" - integrity sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw= - dependencies: - detect-file "^1.0.0" - is-glob "^3.1.0" - micromatch "^3.0.4" - resolve-dir "^1.0.1" - -findup-sync@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/findup-sync/-/findup-sync-4.0.0.tgz" - integrity sha512-6jvvn/12IC4quLBL1KNokxC7wWTvYncaVUYSoxWw7YykPLuRrnv4qdHcSOywOI5RpkOVGeQRtWM8/q+G6W6qfQ== - dependencies: - detect-file "^1.0.0" - is-glob "^4.0.0" - micromatch "^4.0.2" - resolve-dir "^1.0.1" - -fined@^1.0.1: - version "1.2.0" - resolved "https://registry.npmjs.org/fined/-/fined-1.2.0.tgz" - integrity sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng== - dependencies: - expand-tilde "^2.0.2" - is-plain-object "^2.0.3" - object.defaults "^1.1.0" - object.pick "^1.2.0" - parse-filepath "^1.0.1" - -first-chunk-stream@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-1.0.0.tgz" - integrity sha1-Wb+1DNkF9g18OUzT2ayqtOatk04= - -flagged-respawn@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz" - integrity sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q== - -flat-cache@^1.2.1: - version "1.3.4" - resolved "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.4.tgz" - integrity sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg== - dependencies: - circular-json "^0.3.1" - graceful-fs "^4.1.2" - rimraf "~2.6.2" - write "^0.2.1" - -flat-cache@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz" - integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA== - dependencies: - flatted "^2.0.0" - rimraf "2.6.3" - write "1.0.3" - -flatted@^2.0.0, flatted@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/flatted/-/flatted-2.0.1.tgz" - integrity sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg== - -flatten@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/flatten/-/flatten-1.0.2.tgz" - integrity sha1-2uRqnXj74lKSJYzB54CkHZXAN4I= - -flora-colossus@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/flora-colossus/-/flora-colossus-1.0.1.tgz" - integrity sha512-d+9na7t9FyH8gBJoNDSi28mE4NgQVGGvxQ4aHtFRetjyh5SXjuus+V5EZaxFmFdXVemSOrx0lsgEl/ZMjnOWJA== - dependencies: - debug "^4.1.1" - fs-extra "^7.0.0" - -fluent-ffmpeg@^2.1.2: - version "2.1.2" - resolved "https://registry.npmjs.org/fluent-ffmpeg/-/fluent-ffmpeg-2.1.2.tgz" - integrity sha1-yVLeIkD4EuvaCqgAbXd27irPfXQ= - dependencies: - async ">=0.2.9" - which "^1.1.1" - -flush-write-stream@^1.0.0, flush-write-stream@^1.0.2: - version "1.1.1" - resolved "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz" - integrity sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w== - dependencies: - inherits "^2.0.3" - readable-stream "^2.3.6" - -follow-redirects@1.5.10: - version "1.5.10" - resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz" - integrity sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ== - dependencies: - debug "=3.1.0" - -follow-redirects@^1.0.0: - version "1.12.1" - resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.12.1.tgz" - integrity sha512-tmRv0AVuR7ZyouUHLeNSiO6pqulF7dYa3s19c6t+wz9LD69/uSzdMxJ2S91nTI9U3rt/IldxpzMOFejp6f0hjg== - -for-each@^0.3.3: - version "0.3.3" - resolved "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz" - integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== - dependencies: - is-callable "^1.1.3" - -for-in@^1.0.1, for-in@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz" - integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= - -for-own@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz" - integrity sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs= - dependencies: - for-in "^1.0.1" - -forever-agent@~0.6.1: - version "0.6.1" - resolved "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz" - integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= - -fork-stream@^0.0.4: - version "0.0.4" - resolved "https://registry.npmjs.org/fork-stream/-/fork-stream-0.0.4.tgz" - integrity sha1-24Sfznf2cIpfjzhq5TOgkHtUrnA= - -form-data@~2.3.2: - version "2.3.3" - resolved "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz" - integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.6" - mime-types "^2.1.12" - -fragment-cache@^0.2.1: - version "0.2.1" - resolved "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz" - integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= - dependencies: - map-cache "^0.2.2" - -fresh@0.5.2, fresh@^0.5.2: - version "0.5.2" - resolved "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz" - integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= - -from2@^2.1.0, from2@^2.1.1: - version "2.3.0" - resolved "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz" - integrity sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8= - dependencies: - inherits "^2.0.1" - readable-stream "^2.0.0" - -front-matter@2.1.2: - version "2.1.2" - resolved "https://registry.npmjs.org/front-matter/-/front-matter-2.1.2.tgz" - integrity sha1-91mDufL0E75ljJPf172M5AePXNs= - dependencies: - js-yaml "^3.4.6" - -fs-constants@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz" - integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== - -fs-extra@3.0.1, fs-extra@^3.0.1: - version "3.0.1" - resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-3.0.1.tgz" - integrity sha1-N5TzeMWLNC6n27sjCVEJxLO2IpE= - dependencies: - graceful-fs "^4.1.2" - jsonfile "^3.0.0" - universalify "^0.1.0" - -fs-extra@^10.0.0: - version "10.0.1" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.0.1.tgz#27de43b4320e833f6867cc044bfce29fdf0ef3b8" - integrity sha512-NbdoVMZso2Lsrn/QwLXOy6rm0ufY2zEOKCDzJR/0kBsb0E6qed0P3iYK+Ath3BfvXEeu4JhEtXLgILx5psUfag== - dependencies: - graceful-fs "^4.2.0" - jsonfile "^6.0.1" - universalify "^2.0.0" - -fs-extra@^4.0.0: - version "4.0.3" - resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz" - integrity sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg== - dependencies: - graceful-fs "^4.1.2" - jsonfile "^4.0.0" - universalify "^0.1.0" - -fs-extra@^7.0.0: - version "7.0.1" - resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz" - integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw== - dependencies: - graceful-fs "^4.1.2" - jsonfile "^4.0.0" - universalify "^0.1.0" - -fs-extra@^8.1.0: - version "8.1.0" - resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz" - integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== - dependencies: - graceful-fs "^4.2.0" - jsonfile "^4.0.0" - universalify "^0.1.0" - -fs-extra@^9.0.0, fs-extra@^9.0.1: - version "9.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" - integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== - dependencies: - at-least-node "^1.0.0" - graceful-fs "^4.2.0" - jsonfile "^6.0.1" - universalify "^2.0.0" - -fs-mkdirp-stream@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz" - integrity sha1-C3gV/DIBxqaeFNuYzgmMFpNSWes= - dependencies: - graceful-fs "^4.1.11" - through2 "^2.0.3" - -fs-write-stream-atomic@^1.0.8: - version "1.0.10" - resolved "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz" - integrity sha1-tH31NJPvkR33VzHnCp3tAYnbQMk= - dependencies: - graceful-fs "^4.1.2" - iferr "^0.1.5" - imurmurhash "^0.1.4" - readable-stream "1 || 2" - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" - integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= - -fsevents@^1.2.7: - version "1.2.13" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.13.tgz#f325cb0455592428bcf11b383370ef70e3bfcc38" - integrity sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw== - dependencies: - bindings "^1.5.0" - nan "^2.12.1" - -fsevents@~2.1.2: - version "2.1.3" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" - integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== - -function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz" - integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== - -functional-red-black-tree@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz" - integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= - -galactus@^0.2.1: - version "0.2.1" - resolved "https://registry.npmjs.org/galactus/-/galactus-0.2.1.tgz" - integrity sha1-y+0tIKQMH1Z5o1kI4rlBVzPnjbk= - dependencies: - debug "^3.1.0" - flora-colossus "^1.0.0" - fs-extra "^4.0.0" - -gaze@^0.5.1: - version "0.5.2" - resolved "https://registry.npmjs.org/gaze/-/gaze-0.5.2.tgz" - integrity sha1-QLcJU30k0dRXZ9takIaJ3+aaxE8= - dependencies: - globule "~0.1.0" - -generate-function@^2.0.0: - version "2.3.1" - resolved "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz" - integrity sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ== - dependencies: - is-property "^1.0.2" - -generate-object-property@^1.1.0: - version "1.2.0" - resolved "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz" - integrity sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA= - dependencies: - is-property "^1.0.0" - -gensync@^1.0.0-beta.1: - version "1.0.0-beta.1" - resolved "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz" - integrity sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg== - -get-caller-file@^1.0.1: - version "1.0.3" - resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz" - integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== - -get-caller-file@^2.0.1: - version "2.0.5" - resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz" - integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== - -get-package-info@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/get-package-info/-/get-package-info-1.0.0.tgz" - integrity sha1-ZDJ5ZWPigRPNlHTbvQAFKYWkmZw= - dependencies: - bluebird "^3.1.1" - debug "^2.2.0" - lodash.get "^4.0.0" - read-pkg-up "^2.0.0" - -get-proxy@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/get-proxy/-/get-proxy-2.1.0.tgz" - integrity sha512-zmZIaQTWnNQb4R4fJUEp/FC51eZsc6EkErspy3xtIYStaq8EB/hDIWipxsal+E8rz0qD7f2sL/NA9Xee4RInJw== - dependencies: - npm-conf "^1.1.0" - -get-stdin@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz" - integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4= - -get-stream@3.0.0, get-stream@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz" - integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ= - -get-stream@^2.2.0: - version "2.3.1" - resolved "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz" - integrity sha1-Xzj5PzRgCWZu4BUKBUFn+Rvdld4= - dependencies: - object-assign "^4.0.1" - pinkie-promise "^2.0.0" - -get-stream@^4.0.0, get-stream@^4.1.0: - version "4.1.0" - resolved "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz" - integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== - dependencies: - pump "^3.0.0" - -get-stream@^5.0.0, get-stream@^5.1.0: - version "5.1.0" - resolved "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz" - integrity sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw== - dependencies: - pump "^3.0.0" - -get-stream@^6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/get-stream/-/get-stream-6.0.0.tgz" - integrity sha512-A1B3Bh1UmL0bidM/YX2NsCOTnGJePL9rO/M+Mw3m9f2gUpfokS0hi5Eah0WSUEWZdZhIZtMjkIYS7mDfOqNHbg== - -get-value@^2.0.3, get-value@^2.0.6: - version "2.0.6" - resolved "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz" - integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= - -getpass@^0.1.1: - version "0.1.7" - resolved "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz" - integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= - dependencies: - assert-plus "^1.0.0" - -gifsicle@^5.0.0, gifsicle@^5.2.0: - version "5.2.0" - resolved "https://registry.npmjs.org/gifsicle/-/gifsicle-5.2.0.tgz" - integrity sha512-vOIS3j0XoTCxq9pkGj43gEix82RkI5FveNgaFZutjbaui/HH+4fR8Y56dwXDuxYo8hR4xOo6/j2h1WHoQW6XLw== - dependencies: - bin-build "^3.0.0" - bin-wrapper "^4.0.0" - execa "^5.0.0" - logalot "^2.0.0" - -glob-all@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/glob-all/-/glob-all-3.1.0.tgz" - integrity sha1-iRPd+17hrHgSZWJBsD1SF8ZLAqs= - dependencies: - glob "^7.0.5" - yargs "~1.2.6" - -glob-parent@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz" - integrity sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4= - dependencies: - is-glob "^3.1.0" - path-dirname "^1.0.0" - -glob-parent@^5.1.0, glob-parent@~5.1.0: - 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== - dependencies: - is-glob "^4.0.1" - -glob-stream@^3.1.5: - version "3.1.18" - resolved "https://registry.npmjs.org/glob-stream/-/glob-stream-3.1.18.tgz" - integrity sha1-kXCl8St5Awb9/lmPMT+PeVT9FDs= - dependencies: - glob "^4.3.1" - glob2base "^0.0.12" - minimatch "^2.0.1" - ordered-read-streams "^0.1.0" - through2 "^0.6.1" - unique-stream "^1.0.0" - -glob-stream@^6.1.0: - version "6.1.0" - resolved "https://registry.npmjs.org/glob-stream/-/glob-stream-6.1.0.tgz" - integrity sha1-cEXJlBOz65SIjYOrRtC0BMx73eQ= - dependencies: - extend "^3.0.0" - glob "^7.1.1" - glob-parent "^3.1.0" - is-negated-glob "^1.0.0" - ordered-read-streams "^1.0.0" - pumpify "^1.3.5" - readable-stream "^2.1.5" - remove-trailing-separator "^1.0.1" - to-absolute-glob "^2.0.0" - unique-stream "^2.0.2" - -glob-watcher@^0.0.6: - version "0.0.6" - resolved "https://registry.npmjs.org/glob-watcher/-/glob-watcher-0.0.6.tgz" - integrity sha1-uVtKjfdLOcgymLDAXJeLTZo7cQs= - dependencies: - gaze "^0.5.1" - -glob-watcher@^5.0.3: - version "5.0.3" - resolved "https://registry.npmjs.org/glob-watcher/-/glob-watcher-5.0.3.tgz" - integrity sha512-8tWsULNEPHKQ2MR4zXuzSmqbdyV5PtwwCaWSGQ1WwHsJ07ilNeN1JB8ntxhckbnpSHaf9dXFUHzIWvm1I13dsg== - dependencies: - anymatch "^2.0.0" - async-done "^1.2.0" - chokidar "^2.0.0" - is-negated-glob "^1.0.0" - just-debounce "^1.0.0" - object.defaults "^1.1.0" - -glob2base@^0.0.12: - version "0.0.12" - resolved "https://registry.npmjs.org/glob2base/-/glob2base-0.0.12.tgz" - integrity sha1-nUGbPijxLoOjYhZKJ3BVkiycDVY= - dependencies: - find-index "^0.1.1" - -glob@^4.3.1: - version "4.5.3" - resolved "https://registry.npmjs.org/glob/-/glob-4.5.3.tgz" - integrity sha1-xstz0yJsHv7wTePFbQEvAzd+4V8= - dependencies: - inflight "^1.0.4" - inherits "2" - minimatch "^2.0.1" - once "^1.3.0" - -glob@^6.0.4: - version "6.0.4" - resolved "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz" - integrity sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI= - dependencies: - inflight "^1.0.4" - inherits "2" - minimatch "2 || 3" - once "^1.3.0" - path-is-absolute "^1.0.0" - -glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.0.6, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@~7.1.1: - version "7.1.4" - resolved "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz" - integrity sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A== - 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" - -glob@^7.1.6: - version "7.2.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" - integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== - 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" - -glob@~3.1.21: - version "3.1.21" - resolved "https://registry.npmjs.org/glob/-/glob-3.1.21.tgz" - integrity sha1-0p4KBV3qUTj00H7UDomC6DwgZs0= - dependencies: - graceful-fs "~1.2.0" - inherits "1" - minimatch "~0.2.11" - -glob@~3.2.6: - version "3.2.11" - resolved "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz" - integrity sha1-Spc/Y1uRkPcV0QmH1cAP0oFevj0= - dependencies: - inherits "2" - minimatch "0.3" - -global-agent@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/global-agent/-/global-agent-3.0.0.tgz#ae7cd31bd3583b93c5a16437a1afe27cc33a1ab6" - integrity sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q== - dependencies: - boolean "^3.0.1" - es6-error "^4.1.1" - matcher "^3.0.0" - roarr "^2.15.3" - semver "^7.3.2" - serialize-error "^7.0.1" - -global-modules@2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz" - integrity sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A== - dependencies: - global-prefix "^3.0.0" - -global-modules@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz" - integrity sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg== - dependencies: - global-prefix "^1.0.1" - is-windows "^1.0.1" - resolve-dir "^1.0.0" - -global-prefix@^1.0.1: - version "1.0.2" - resolved "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz" - integrity sha1-2/dDxsFJklk8ZVVoy2btMsASLr4= - dependencies: - expand-tilde "^2.0.2" - homedir-polyfill "^1.0.1" - ini "^1.3.4" - is-windows "^1.0.1" - which "^1.2.14" - -global-prefix@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz" - integrity sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg== - dependencies: - ini "^1.3.5" - kind-of "^6.0.2" - which "^1.3.1" - -global-tunnel-ng@^2.7.1: - version "2.7.1" - resolved "https://registry.yarnpkg.com/global-tunnel-ng/-/global-tunnel-ng-2.7.1.tgz#d03b5102dfde3a69914f5ee7d86761ca35d57d8f" - integrity sha512-4s+DyciWBV0eK148wqXxcmVAbFVPqtc3sEtUE/GTQfuU80rySLcMhUmHKSHI7/LDj8q0gDYI1lIhRRB7ieRAqg== - dependencies: - encodeurl "^1.0.2" - lodash "^4.17.10" - npm-conf "^1.1.3" - tunnel "^0.0.6" - -global@~4.3.0: - version "4.3.2" - resolved "https://registry.npmjs.org/global/-/global-4.3.2.tgz" - integrity sha1-52mJJopsdMOJCLEwWxD8DjlOnQ8= - dependencies: - min-document "^2.19.0" - process "~0.5.1" - -globals@^11.1.0, globals@^11.7.0: - version "11.12.0" - resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz" - integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== - -globals@^9.18.0, globals@^9.2.0: - version "9.18.0" - resolved "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz" - integrity sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ== - -globalthis@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.2.tgz#2a235d34f4d8036219f7e34929b5de9e18166b8b" - integrity sha512-ZQnSFO1la8P7auIOQECnm0sSuoMeaSq0EEdXMBFF2QJO4uNcwbyhSgG3MruWNbFTqCLmxVwGOl7LZ9kASvHdeQ== - dependencies: - define-properties "^1.1.3" - -globby@^10.0.0: - version "10.0.2" - resolved "https://registry.npmjs.org/globby/-/globby-10.0.2.tgz" - integrity sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg== - dependencies: - "@types/glob" "^7.1.1" - array-union "^2.1.0" - dir-glob "^3.0.1" - fast-glob "^3.0.3" - glob "^7.1.3" - ignore "^5.1.1" - merge2 "^1.2.3" - slash "^3.0.0" - -globule@^1.0.0: - version "1.2.1" - resolved "https://registry.npmjs.org/globule/-/globule-1.2.1.tgz" - integrity sha512-g7QtgWF4uYSL5/dn71WxubOrS7JVGCnFPEnoeChJmBnyR9Mw8nGoEwOgJL/RC2Te0WhbsEUCejfH8SZNJ+adYQ== - dependencies: - glob "~7.1.1" - lodash "~4.17.10" - minimatch "~3.0.2" - -globule@~0.1.0: - version "0.1.0" - resolved "https://registry.npmjs.org/globule/-/globule-0.1.0.tgz" - integrity sha1-2cjt3h2nnRJaFRt5UzuXhnY0auU= - dependencies: - glob "~3.1.21" - lodash "~1.0.1" - minimatch "~0.2.11" - -glogg@^1.0.0: - version "1.0.2" - resolved "https://registry.npmjs.org/glogg/-/glogg-1.0.2.tgz" - integrity sha512-5mwUoSuBk44Y4EshyiqcH95ZntbDdTQqA3QYSrxmzj28Ai0vXBGMH1ApSANH14j2sIRtqCEyg6PfsuP7ElOEDA== - dependencies: - sparkles "^1.0.0" - -gonzales-pe-sl@^4.2.3: - version "4.2.3" - resolved "https://registry.npmjs.org/gonzales-pe-sl/-/gonzales-pe-sl-4.2.3.tgz" - integrity sha1-aoaLw4BkXxQf7rBCxvl/zHG1n+Y= - dependencies: - minimist "1.1.x" - -gonzales-pe@^4.2.3: - version "4.2.4" - resolved "https://registry.npmjs.org/gonzales-pe/-/gonzales-pe-4.2.4.tgz" - integrity sha512-v0Ts/8IsSbh9n1OJRnSfa7Nlxi4AkXIsWB6vPept8FDbL4bXn3FNuxjYtO/nmBGu7GDkL9MFeGebeSu6l55EPQ== - dependencies: - minimist "1.1.x" - -got@^7.0.0: - version "7.1.0" - resolved "https://registry.npmjs.org/got/-/got-7.1.0.tgz" - integrity sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw== - dependencies: - decompress-response "^3.2.0" - duplexer3 "^0.1.4" - get-stream "^3.0.0" - is-plain-obj "^1.1.0" - is-retry-allowed "^1.0.0" - is-stream "^1.0.0" - isurl "^1.0.0-alpha5" - lowercase-keys "^1.0.0" - p-cancelable "^0.3.0" - p-timeout "^1.1.1" - safe-buffer "^5.0.1" - timed-out "^4.0.0" - url-parse-lax "^1.0.0" - url-to-options "^1.0.1" - -got@^8.3.1: - version "8.3.2" - resolved "https://registry.npmjs.org/got/-/got-8.3.2.tgz" - integrity sha512-qjUJ5U/hawxosMryILofZCkm3C84PLJS/0grRIpjAwu+Lkxxj5cxeCU25BG0/3mDSpXKTyZr8oh8wIgLaH0QCw== - dependencies: - "@sindresorhus/is" "^0.7.0" - cacheable-request "^2.1.1" - decompress-response "^3.3.0" - duplexer3 "^0.1.4" - get-stream "^3.0.0" - into-stream "^3.1.0" - is-retry-allowed "^1.1.0" - isurl "^1.0.0-alpha5" - lowercase-keys "^1.0.0" - mimic-response "^1.0.0" - p-cancelable "^0.4.0" - p-timeout "^2.0.1" - pify "^3.0.0" - safe-buffer "^5.1.1" - timed-out "^4.0.1" - url-parse-lax "^3.0.0" - url-to-options "^1.0.1" - -got@^9.6.0: - version "9.6.0" - resolved "https://registry.npmjs.org/got/-/got-9.6.0.tgz" - integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q== - dependencies: - "@sindresorhus/is" "^0.14.0" - "@szmarczak/http-timer" "^1.1.2" - cacheable-request "^6.0.0" - decompress-response "^3.3.0" - duplexer3 "^0.1.4" - get-stream "^4.1.0" - lowercase-keys "^1.0.1" - mimic-response "^1.0.1" - p-cancelable "^1.0.0" - to-readable-stream "^1.0.0" - url-parse-lax "^3.0.0" - -graceful-fs@^3.0.0: - version "3.0.12" - resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.12.tgz" - integrity sha512-J55gaCS4iTTJfTXIxSVw3EMQckcqkpdRv3IR7gu6sq0+tbC363Zx6KH/SEwXASK9JRbhyZmVjJEVJIOxYsB3Qg== - dependencies: - natives "^1.1.3" - -graceful-fs@^4.0.0, graceful-fs@^4.1.10, graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2: - version "4.2.2" - resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz" - integrity sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q== - -graceful-fs@~1.2.0: - version "1.2.3" - resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz" - integrity sha1-FaSAaldUfLLS2/J/QuiajDRRs2Q= - -"graceful-readlink@>= 1.0.0": - version "1.0.1" - resolved "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz" - integrity sha1-TK+tdrxi8C+gObL5Tpo906ORpyU= - -gulp-audiosprite@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/gulp-audiosprite/-/gulp-audiosprite-1.1.0.tgz" - integrity sha512-CwSfZjmNPlTyzcAFaE8RiKzh1dQFDLPPZMHshKwvGqNeTB86s30K8hMXGrrjFqHNF9xb0SUnXfbYT32MO4aNog== - dependencies: - audiosprite "*" - through2 "*" - vinyl "*" - -gulp-cache@^1.1.3: - version "1.1.3" - resolved "https://registry.npmjs.org/gulp-cache/-/gulp-cache-1.1.3.tgz" - integrity sha512-NE814LdX1NWQn2sMzn+Rf673o4mqlgg7OyLf92oQ4KEl6DdPfduEGLNH+HexLVcFZXH93DBuxFOvpv4/Js5VaA== - dependencies: - "@babel/runtime" "^7.5.5" - cache-swap "^0.3.0" - core-js "3" - object.pick "^1.3.0" - plugin-error "^1.0.1" - through2 "3.0.1" - vinyl "^2.2.0" - -gulp-cached@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/gulp-cached/-/gulp-cached-1.1.1.tgz" - integrity sha1-/nzU+H83YB5gc8/t7lwr2vi2rM4= - dependencies: - lodash.defaults "^4.2.0" - through2 "^2.0.1" - -gulp-clean@^0.4.0: - version "0.4.0" - resolved "https://registry.npmjs.org/gulp-clean/-/gulp-clean-0.4.0.tgz" - integrity sha512-DARK8rNMo4lHOFLGTiHEJdf19GuoBDHqGUaypz+fOhrvOs3iFO7ntdYtdpNxv+AzSJBx/JfypF0yEj9ks1IStQ== - dependencies: - fancy-log "^1.3.2" - plugin-error "^0.1.2" - rimraf "^2.6.2" - through2 "^2.0.3" - vinyl "^2.1.0" - -gulp-cli@^2.2.0: - version "2.3.0" - resolved "https://registry.npmjs.org/gulp-cli/-/gulp-cli-2.3.0.tgz" - integrity sha512-zzGBl5fHo0EKSXsHzjspp3y5CONegCm8ErO5Qh0UzFzk2y4tMvzLWhoDokADbarfZRL2pGpRp7yt6gfJX4ph7A== - dependencies: - ansi-colors "^1.0.1" - archy "^1.0.0" - array-sort "^1.0.0" - color-support "^1.1.3" - concat-stream "^1.6.0" - copy-props "^2.0.1" - fancy-log "^1.3.2" - gulplog "^1.0.0" - interpret "^1.4.0" - isobject "^3.0.1" - liftoff "^3.1.0" - matchdep "^2.0.0" - mute-stdout "^1.0.0" - pretty-hrtime "^1.0.0" - replace-homedir "^1.0.0" - semver-greatest-satisfied-range "^1.1.0" - v8flags "^3.2.0" - yargs "^7.1.0" - -gulp-dart-sass@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/gulp-dart-sass/-/gulp-dart-sass-1.0.2.tgz" - integrity sha512-8fLttA824mbuc0jRVlGs00zWYZXBckat6INawx5kp66Eqsz5srNWTA51t0mbfB4C8a/a/GZ9muYLwXGklgAHlw== - dependencies: - chalk "^2.3.0" - lodash.clonedeep "^4.3.2" - plugin-error "^1.0.1" - replace-ext "^1.0.0" - sass "^1.26.3" - strip-ansi "^4.0.0" - through2 "^2.0.0" - vinyl-sourcemaps-apply "^0.2.0" - -gulp-dom@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/gulp-dom/-/gulp-dom-1.0.0.tgz" - integrity sha512-hD2w2t3fsjPicX2mT6MFFb+eP3FyCVtEHdejGMMH4+w9EBFxA2xIZadqlzYdAEdE+39dP1aGatuhdHJteUvn1A== - dependencies: - jsdom "12.2.0" - plugin-error "1.0.1" - through2 "2.0.3" - -gulp-flatten@^0.4.0: - version "0.4.0" - resolved "https://registry.npmjs.org/gulp-flatten/-/gulp-flatten-0.4.0.tgz" - integrity sha512-eg4spVTAiv1xXmugyaCxWne1oPtNG0UHEtABx5W8ScLiqAYceyYm6GYA36x0Qh8KOIXmAZV97L2aYGnKREG3Sg== - dependencies: - plugin-error "^0.1.2" - through2 "^2.0.0" - -gulp-fluent-ffmpeg@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/gulp-fluent-ffmpeg/-/gulp-fluent-ffmpeg-2.0.0.tgz" - integrity sha512-pwG6N+NKwLzO/0ybzgcwiADKZ4OzpFjNR4drqCvbvluYcSh/yvsAW7wm63jFzpJIjfFnanYGPNWiUn8+TuTR/g== - dependencies: - concat-stream "^2.0.0" - fluent-ffmpeg "^2.1.2" - plugin-error "^1.0.1" - through2 "^3.0.1" - -gulp-html-beautify@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/gulp-html-beautify/-/gulp-html-beautify-1.0.1.tgz" - integrity sha1-KDTG93ZpYFcm7uVeMgX2MHS3oVI= - dependencies: - js-beautify "^1.5.10" - rcloader "^0.1.4" - through2 "^2.0.0" - -gulp-htmlmin@^5.0.1: - version "5.0.1" - resolved "https://registry.npmjs.org/gulp-htmlmin/-/gulp-htmlmin-5.0.1.tgz" - integrity sha512-ASlyDPZOSKjHYUifYV0rf9JPDflN9IRIb8lw2vRqtYMC4ljU3zAmnnaVXwFQ3H+CfXxZSUesZ2x7jrnPJu93jA== - dependencies: - html-minifier "^3.5.20" - plugin-error "^1.0.1" - through2 "^2.0.3" - -gulp-if@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/gulp-if/-/gulp-if-3.0.0.tgz" - integrity sha512-fCUEngzNiEZEK2YuPm+sdMpO6ukb8+/qzbGfJBXyNOXz85bCG7yBI+pPSl+N90d7gnLvMsarthsAImx0qy7BAw== - dependencies: - gulp-match "^1.1.0" - ternary-stream "^3.0.0" - through2 "^3.0.1" - -gulp-imagemin@^7.1.0: - version "7.1.0" - resolved "https://registry.npmjs.org/gulp-imagemin/-/gulp-imagemin-7.1.0.tgz" - integrity sha512-6xBTNybmPY2YrvrhhlS8Mxi0zn0ypusLon63p9XXxDtIf7U7c6KcViz94K7Skosucr3378A6IY2kJSjJyuwylQ== - dependencies: - chalk "^3.0.0" - fancy-log "^1.3.2" - imagemin "^7.0.0" - plugin-error "^1.0.1" - plur "^3.0.1" - pretty-bytes "^5.3.0" - through2-concurrent "^2.0.0" - optionalDependencies: - imagemin-gifsicle "^7.0.0" - imagemin-mozjpeg "^8.0.0" - imagemin-optipng "^7.0.0" - imagemin-svgo "^7.0.0" - -gulp-load-plugins@^2.0.3: - version "2.0.3" - resolved "https://registry.npmjs.org/gulp-load-plugins/-/gulp-load-plugins-2.0.3.tgz" - integrity sha512-U/1Sml7UbyOu2kH6Fbpo+ka2xyp4DRH6+oDtHgC8oKsnlQRuiBQYQ/LS4k6HxBv1HJlucaNV/SdwZXtLBuvSqg== - dependencies: - array-unique "^0.3.2" - fancy-log "^1.2.0" - findup-sync "^4.0.0" - gulplog "^1.0.0" - has-gulplog "^0.1.0" - micromatch "^4.0.2" - resolve "^1.15.1" - -gulp-match@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/gulp-match/-/gulp-match-1.1.0.tgz" - integrity sha512-DlyVxa1Gj24DitY2OjEsS+X6tDpretuxD6wTfhXE/Rw2hweqc1f6D/XtsJmoiCwLWfXgR87W9ozEityPCVzGtQ== - dependencies: - minimatch "^3.0.3" - -gulp-phonegap-build@^0.1.5: - version "0.1.5" - resolved "https://registry.npmjs.org/gulp-phonegap-build/-/gulp-phonegap-build-0.1.5.tgz" - integrity sha1-NsFF5jzSBHAr4PO5m+GeCWcSvK8= - dependencies: - archiver "~0.11.0" - gulp "~3.8.7" - gulp-util "~3.0.0" - lodash "~2.4.1" - needle "" - read "~1.0.4" - through2 "~0.6.1" - vinyl-buffer "0.0.0" - vinyl-source-stream "^0.1.1" - -gulp-plumber@^1.2.1: - version "1.2.1" - resolved "https://registry.npmjs.org/gulp-plumber/-/gulp-plumber-1.2.1.tgz" - integrity sha512-mctAi9msEAG7XzW5ytDVZ9PxWMzzi1pS2rBH7lA095DhMa6KEXjm+St0GOCc567pJKJ/oCvosVAZEpAey0q2eQ== - dependencies: - chalk "^1.1.3" - fancy-log "^1.3.2" - plugin-error "^0.1.2" - through2 "^2.0.3" - -gulp-pngquant@^1.0.13: - version "1.0.13" - resolved "https://registry.npmjs.org/gulp-pngquant/-/gulp-pngquant-1.0.13.tgz" - integrity sha512-oSo5Rw2Rb10eyGhc8XKbghq6yteMmxvsSAKGOZU0ssbylMHk3WoTWcEpNg0YWMyRjrY913y+B+PA4wHM1AL2wA== - dependencies: - chalk "^3.0.0" - fancy-log "^1.3.3" - plugin-error "^1.0.1" - pngquant-bin "^5.0.2" - through2 "^3.0.1" - -gulp-postcss@^8.0.0: - version "8.0.0" - resolved "https://registry.npmjs.org/gulp-postcss/-/gulp-postcss-8.0.0.tgz" - integrity sha512-Wtl6vH7a+8IS/fU5W9IbOpcaLqKxd5L1DUOzaPmlnCbX1CrG0aWdwVnC3Spn8th0m8D59YbysV5zPUe1n/GJYg== - dependencies: - fancy-log "^1.3.2" - plugin-error "^1.0.1" - postcss "^7.0.2" - postcss-load-config "^2.0.0" - vinyl-sourcemaps-apply "^0.2.1" - -gulp-rename@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/gulp-rename/-/gulp-rename-2.0.0.tgz" - integrity sha512-97Vba4KBzbYmR5VBs9mWmK+HwIf5mj+/zioxfZhOKeXtx5ZjBk57KFlePf5nxq9QsTtFl0ejnHE3zTC9MHXqyQ== - -gulp-sass-lint@^1.4.0: - version "1.4.0" - resolved "https://registry.npmjs.org/gulp-sass-lint/-/gulp-sass-lint-1.4.0.tgz" - integrity sha512-XerYvHx7rznInkedMw5Ayif+p8EhysOVHUBvlgUa0FSl88H2cjNjaRZ3NGn5Efmp+2HxpXp4NHqMIbOSdwef3A== - dependencies: - plugin-error "^0.1.2" - sass-lint "^1.12.0" - through2 "^2.0.2" - -"gulp-sftp@git+https://git@github.com/webksde/gulp-sftp": - version "0.1.6" - resolved "git+https://git@github.com/webksde/gulp-sftp.git#c8dfb20e290477eeed66a867406576d0c3d4fc6b" - dependencies: - async "~0.9.0" - gulp-util "~3.0.0" - object-assign "~0.3.1" - parents "~1.0.0" - ssh2 "~0.6.1" - through2 "~0.4.2" - -gulp-terser@^1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/gulp-terser/-/gulp-terser-1.2.0.tgz" - integrity sha512-lf+jE2DALg2w32p0HRiYMlFYRYelKZPNunHp2pZccCYrrdCLOs0ItbZcN63yr2pbz116IyhUG9mD/QbtRO1FKA== - dependencies: - plugin-error "^1.0.1" - terser "^4.0.0" - through2 "^3.0.1" - vinyl-sourcemaps-apply "^0.2.1" - -gulp-util@^2.2.19: - version "2.2.20" - resolved "https://registry.npmjs.org/gulp-util/-/gulp-util-2.2.20.tgz" - integrity sha1-1xRuVyiRC9jwR6awseVJvCLb1kw= - dependencies: - chalk "^0.5.0" - dateformat "^1.0.7-1.2.3" - lodash._reinterpolate "^2.4.1" - lodash.template "^2.4.1" - minimist "^0.2.0" - multipipe "^0.1.0" - through2 "^0.5.0" - vinyl "^0.2.1" - -gulp-util@^3.0.0, gulp-util@~3.0.0: - version "3.0.8" - resolved "https://registry.npmjs.org/gulp-util/-/gulp-util-3.0.8.tgz" - integrity sha1-AFTh50RQLifATBh8PsxQXdVLu08= - dependencies: - array-differ "^1.0.0" - array-uniq "^1.0.2" - beeper "^1.0.0" - chalk "^1.0.0" - dateformat "^2.0.0" - fancy-log "^1.1.0" - gulplog "^1.0.0" - has-gulplog "^0.1.0" - lodash._reescape "^3.0.0" - lodash._reevaluate "^3.0.0" - lodash._reinterpolate "^3.0.0" - lodash.template "^3.0.0" - minimist "^1.1.0" - multipipe "^0.1.2" - object-assign "^3.0.0" - replace-ext "0.0.1" - through2 "^2.0.0" - vinyl "^0.5.0" - -gulp-webserver@^0.9.1: - version "0.9.1" - resolved "https://registry.npmjs.org/gulp-webserver/-/gulp-webserver-0.9.1.tgz" - integrity sha1-4JmSFl2XxYZWFtZCoWAVKbA2cGQ= - dependencies: - connect "^3.0.1" - connect-livereload "^0.4.0" - gulp-util "^2.2.19" - isarray "0.0.1" - node.extend "^1.0.10" - open "^0.0.5" - proxy-middleware "^0.5.0" - serve-index "^1.1.4" - serve-static "^1.3.0" - through2 "^0.5.1" - tiny-lr "0.1.4" - watch "^0.11.0" - -gulp-yaml@^2.0.4: - version "2.0.4" - resolved "https://registry.npmjs.org/gulp-yaml/-/gulp-yaml-2.0.4.tgz" - integrity sha512-S/9Ib8PO+jGkCvWDwBUkmFkeW7QM0pp4PO8NNrMEfWo5Sk30P+KqpyXc4055L/vOX326T/b9MhM4nw5EenyX9g== - dependencies: - bufferstreams "^2.0.1" - js-yaml "^3.13.1" - object-assign "^4.1.1" - plugin-error "^1.0.1" - replace-ext "^1.0.0" - through2 "^3.0.0" - -gulp@^4.0.2: - version "4.0.2" - resolved "https://registry.npmjs.org/gulp/-/gulp-4.0.2.tgz" - integrity sha512-dvEs27SCZt2ibF29xYgmnwwCYZxdxhQ/+LFWlbAW8y7jt68L/65402Lz3+CKy0Ov4rOs+NERmDq7YlZaDqUIfA== - dependencies: - glob-watcher "^5.0.3" - gulp-cli "^2.2.0" - undertaker "^1.2.1" - vinyl-fs "^3.0.0" - -gulp@~3.8.7: - version "3.8.11" - resolved "https://registry.npmjs.org/gulp/-/gulp-3.8.11.tgz" - integrity sha1-1Vfgpyg+tBNkkZabBJd2eXLx0oo= - dependencies: - archy "^1.0.0" - chalk "^0.5.0" - deprecated "^0.0.1" - gulp-util "^3.0.0" - interpret "^0.3.2" - liftoff "^2.0.1" - minimist "^1.1.0" - orchestrator "^0.3.0" - pretty-hrtime "^0.2.0" - semver "^4.1.0" - tildify "^1.0.0" - v8flags "^2.0.2" - vinyl-fs "^0.3.0" - -gulplog@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/gulplog/-/gulplog-1.0.0.tgz" - integrity sha1-4oxNRdBey77YGDY86PnFkmIp/+U= - dependencies: - glogg "^1.0.0" - -har-schema@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz" - integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= - -har-validator@~5.1.0: - version "5.1.3" - resolved "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz" - integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g== - dependencies: - ajv "^6.5.5" - har-schema "^2.0.0" - -has-ansi@^0.1.0: - version "0.1.0" - resolved "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz" - integrity sha1-hPJlqujA5qiKEtcCKJS3VoiUxi4= - dependencies: - ansi-regex "^0.2.0" - -has-ansi@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz" - integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= - dependencies: - ansi-regex "^2.0.0" - -has-binary2@~1.0.2: - version "1.0.3" - resolved "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz" - integrity sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw== - dependencies: - isarray "2.0.1" - -has-cors@1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz" - integrity sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk= - -has-flag@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz" - integrity sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo= - -has-flag@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz" - integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= - -has-flag@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" - integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== - -has-gulplog@^0.1.0: - version "0.1.0" - resolved "https://registry.npmjs.org/has-gulplog/-/has-gulplog-0.1.0.tgz" - integrity sha1-ZBTIKRNpfaUVkDl9r7EvIpZ4Ec4= - dependencies: - sparkles "^1.0.0" - -has-symbol-support-x@^1.4.1: - version "1.4.2" - resolved "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz" - integrity sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw== - -has-symbols@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz" - integrity sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q= - -has-to-string-tag-x@^1.2.0: - version "1.4.1" - resolved "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz" - integrity sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw== - dependencies: - has-symbol-support-x "^1.4.1" - -has-value@^0.3.1: - version "0.3.1" - resolved "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz" - integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= - dependencies: - get-value "^2.0.3" - has-values "^0.1.4" - isobject "^2.0.0" - -has-value@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz" - integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= - dependencies: - get-value "^2.0.6" - has-values "^1.0.0" - isobject "^3.0.0" - -has-values@^0.1.4: - version "0.1.4" - resolved "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz" - integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= - -has-values@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz" - integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= - dependencies: - is-number "^3.0.0" - kind-of "^4.0.0" - -has@^1.0.0, has@^1.0.1, has@^1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/has/-/has-1.0.3.tgz" - integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== - dependencies: - function-bind "^1.1.1" - -hash-base@^3.0.0: - version "3.0.4" - resolved "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz" - integrity sha1-X8hoaEfs1zSZQDMZprCj8/auSRg= - dependencies: - inherits "^2.0.1" - safe-buffer "^5.0.1" - -hash.js@^1.0.0, hash.js@^1.0.3: - version "1.1.7" - resolved "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz" - integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== - dependencies: - inherits "^2.0.3" - minimalistic-assert "^1.0.1" - -he@1.2.x: - version "1.2.0" - resolved "https://registry.npmjs.org/he/-/he-1.2.0.tgz" - integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== - -hex-color-regex@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz" - integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ== - -hmac-drbg@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz" - integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= - dependencies: - hash.js "^1.0.3" - minimalistic-assert "^1.0.0" - minimalistic-crypto-utils "^1.0.1" - -home-or-tmp@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz" - integrity sha1-42w/LSyufXRqhX440Y1fMqeILbg= - dependencies: - os-homedir "^1.0.0" - os-tmpdir "^1.0.1" - -homedir-polyfill@^1.0.1: - version "1.0.3" - resolved "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz" - integrity sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA== - dependencies: - parse-passwd "^1.0.0" - -hosted-git-info@^2.1.4: - version "2.8.4" - resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.4.tgz" - integrity sha512-pzXIvANXEFrc5oFFXRMkbLPQ2rXRoDERwDLyrcUxGhaZhgP54BBSl9Oheh7Vv0T090cszWBxPjkQQ5Sq1PbBRQ== - -howler@^2.1.2: - version "2.1.2" - resolved "https://registry.npmjs.org/howler/-/howler-2.1.2.tgz" - integrity sha512-oKrTFaVXsDRoB/jik7cEpWKTj7VieoiuzMYJ7E/EU5ayvmpRhumCv3YQ3823zi9VTJkSWAhbryHnlZAionGAJg== - -hsl-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz" - integrity sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4= - -hsla-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/hsla-regex/-/hsla-regex-1.0.0.tgz" - integrity sha1-wc56MWjIxmFAM6S194d/OyJfnDg= - -html-comment-regex@^1.1.0: - version "1.1.2" - resolved "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.2.tgz" - integrity sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ== - -html-encoding-sniffer@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz" - integrity sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw== - dependencies: - whatwg-encoding "^1.0.1" - -html-loader@^0.5.5: - version "0.5.5" - resolved "https://registry.npmjs.org/html-loader/-/html-loader-0.5.5.tgz" - integrity sha512-7hIW7YinOYUpo//kSYcPB6dCKoceKLmOwjEMmhIobHuWGDVl0Nwe4l68mdG/Ru0wcUxQjVMEoZpkalZ/SE7zog== - dependencies: - es6-templates "^0.2.3" - fastparse "^1.1.1" - html-minifier "^3.5.8" - loader-utils "^1.1.0" - object-assign "^4.1.1" - -html-minifier@^3.5.20, html-minifier@^3.5.8: - version "3.5.21" - resolved "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.21.tgz" - integrity sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA== - dependencies: - camel-case "3.0.x" - clean-css "4.2.x" - commander "2.17.x" - he "1.2.x" - param-case "2.1.x" - relateurl "0.2.x" - uglify-js "3.4.x" - -http-cache-semantics@3.8.1: - version "3.8.1" - resolved "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz" - integrity sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w== - -http-cache-semantics@^4.0.0: - version "4.0.3" - resolved "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.0.3.tgz" - integrity sha512-TcIMG3qeVLgDr1TEd2XvHaTnMPwYQUQMIBLy+5pLSDKYFc7UIqj39w8EGzZkaxoLv/l2K8HaI0t5AVA+YYgUew== - -http-errors@1.7.3: - version "1.7.3" - resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz" - integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== - dependencies: - depd "~1.1.2" - inherits "2.0.4" - setprototypeof "1.1.1" - statuses ">= 1.5.0 < 2" - toidentifier "1.0.0" - -http-errors@~1.6.2: - version "1.6.3" - resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz" - integrity sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0= - dependencies: - depd "~1.1.2" - inherits "2.0.3" - setprototypeof "1.1.0" - statuses ">= 1.4.0 < 2" - -"http-parser-js@>=0.4.0 <0.4.11": - version "0.4.10" - resolved "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.10.tgz" - integrity sha1-ksnBN0w1CF912zWexWzCV8u5P6Q= - -http-proxy@^1.18.1: - version "1.18.1" - resolved "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz" - integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== - dependencies: - eventemitter3 "^4.0.0" - follow-redirects "^1.0.0" - requires-port "^1.0.0" - -http-signature@~1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz" - integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= - dependencies: - assert-plus "^1.0.0" - jsprim "^1.2.2" - sshpk "^1.7.0" - -https-browserify@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz" - integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= - -human-signals@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz" - integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== - -human-signals@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz" - integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== - -iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4: - version "0.4.24" - resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz" - integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== - dependencies: - safer-buffer ">= 2.1.2 < 3" - -iconv-lite@0.4.4: - version "0.4.4" - resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.4.tgz" - integrity sha1-6V8uQdsHNfwhZS94J6XuMuY8g6g= - -ieee754@^1.1.4: - version "1.1.13" - resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz" - integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== - -iferr@^0.1.5: - version "0.1.5" - resolved "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz" - integrity sha1-xg7taebY/bazEEofy8ocGS3FtQE= - -ignore-loader@^0.1.2: - version "0.1.2" - resolved "https://registry.npmjs.org/ignore-loader/-/ignore-loader-0.1.2.tgz" - integrity sha1-2B8kA3bQuk8Nd4lyw60lh0EXpGM= - -ignore@^3.1.2: - version "3.3.10" - resolved "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz" - integrity sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug== - -ignore@^4.0.6: - version "4.0.6" - resolved "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz" - integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== - -ignore@^5.1.1: - version "5.1.8" - resolved "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz" - integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== - -imagemin-gifsicle@^7.0.0: - version "7.0.0" - resolved "https://registry.npmjs.org/imagemin-gifsicle/-/imagemin-gifsicle-7.0.0.tgz" - integrity sha512-LaP38xhxAwS3W8PFh4y5iQ6feoTSF+dTAXFRUEYQWYst6Xd+9L/iPk34QGgK/VO/objmIlmq9TStGfVY2IcHIA== - dependencies: - execa "^1.0.0" - gifsicle "^5.0.0" - is-gif "^3.0.0" - -imagemin-jpegtran@^7.0.0: - version "7.0.0" - resolved "https://registry.npmjs.org/imagemin-jpegtran/-/imagemin-jpegtran-7.0.0.tgz" - integrity sha512-MJoyTCW8YjMJf56NorFE41SR/WkaGA3IYk4JgvMlRwguJEEd3PnP9UxA8Y2UWjquz8d+On3Ds/03ZfiiLS8xTQ== - dependencies: - exec-buffer "^3.0.0" - is-jpg "^2.0.0" - jpegtran-bin "^5.0.0" - -imagemin-mozjpeg@^8.0.0: - version "8.0.0" - resolved "https://registry.npmjs.org/imagemin-mozjpeg/-/imagemin-mozjpeg-8.0.0.tgz" - integrity sha512-+EciPiIjCb8JWjQNr1q8sYWYf7GDCNDxPYnkD11TNIjjWNzaV+oTg4DpOPQjl5ZX/KRCPMEgS79zLYAQzLitIA== - dependencies: - execa "^1.0.0" - is-jpg "^2.0.0" - mozjpeg "^6.0.0" - -imagemin-optipng@^7.0.0: - version "7.1.0" - resolved "https://registry.npmjs.org/imagemin-optipng/-/imagemin-optipng-7.1.0.tgz" - integrity sha512-JNORTZ6j6untH7e5gF4aWdhDCxe3ODsSLKs/f7Grewy3ebZpl1ZsU+VUTPY4rzeHgaFA8GSWOoA8V2M3OixWZQ== - dependencies: - exec-buffer "^3.0.0" - is-png "^2.0.0" - optipng-bin "^6.0.0" - -imagemin-pngquant@^9.0.0: - version "9.0.0" - resolved "https://registry.npmjs.org/imagemin-pngquant/-/imagemin-pngquant-9.0.0.tgz" - integrity sha512-9cqnTEaJwAHWUi+8EMTB3NUouWToCWxtL+QnoYr8bfVwuKilHvRVWKsa9lt+0c3aWaGxCAkHs++j8qINvSqomA== - dependencies: - execa "^4.0.0" - is-png "^2.0.0" - is-stream "^2.0.0" - ow "^0.17.0" - pngquant-bin "^6.0.0" - -imagemin-svgo@^7.0.0: - version "7.0.0" - resolved "https://registry.npmjs.org/imagemin-svgo/-/imagemin-svgo-7.0.0.tgz" - integrity sha512-+iGJFaPIMx8TjFW6zN+EkOhlqcemdL7F3N3Y0wODvV2kCUBuUtZK7DRZc1+Zfu4U2W/lTMUyx2G8YMOrZntIWg== - dependencies: - is-svg "^3.0.0" - svgo "^1.0.5" - -imagemin@^7.0.0: - version "7.0.1" - resolved "https://registry.npmjs.org/imagemin/-/imagemin-7.0.1.tgz" - integrity sha512-33AmZ+xjZhg2JMCe+vDf6a9mzWukE7l+wAtesjE7KyteqqKjzxv7aVQeWnul1Ve26mWvEQqyPwl0OctNBfSR9w== - dependencies: - file-type "^12.0.0" - globby "^10.0.0" - graceful-fs "^4.2.2" - junk "^3.1.0" - make-dir "^3.0.0" - p-pipe "^3.0.0" - replace-ext "^1.0.0" - -immutable@^3: - version "3.8.2" - resolved "https://registry.npmjs.org/immutable/-/immutable-3.8.2.tgz" - integrity sha1-wkOZUUVbs5kT2vKBN28VMOEErfM= - -import-cwd@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz" - integrity sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk= - dependencies: - import-from "^2.1.0" - -import-fresh@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz" - integrity sha1-2BNVwVYS04bGH53dOSLUMEgipUY= - dependencies: - caller-path "^2.0.0" - resolve-from "^3.0.0" - -import-fresh@^3.0.0: - version "3.1.0" - resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-3.1.0.tgz" - integrity sha512-PpuksHKGt8rXfWEr9m9EHIpgyyaltBy8+eF6GJM0QCAxMgxCfucMF3mjecK2QsJr0amJW7gTqh5/wht0z2UhEQ== - dependencies: - parent-module "^1.0.0" - resolve-from "^4.0.0" - -import-from@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/import-from/-/import-from-2.1.0.tgz" - integrity sha1-M1238qev/VOqpHHUuAId7ja387E= - dependencies: - resolve-from "^3.0.0" - -import-lazy@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/import-lazy/-/import-lazy-3.1.0.tgz" - integrity sha512-8/gvXvX2JMn0F+CDlSC4l6kOmVaLOO3XLkksI7CI3Ud95KDYJuYur2b9P/PUt/i/pDAMd/DulQsNbbbmRRsDIQ== - -import-local@2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz" - integrity sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ== - dependencies: - pkg-dir "^3.0.0" - resolve-cwd "^2.0.0" - -imurmurhash@^0.1.4: - version "0.1.4" - resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz" - integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= - -indent-string@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz" - integrity sha1-ji1INIdCEhtKghi3oTfppSBJ3IA= - dependencies: - repeating "^2.0.0" - -indexes-of@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz" - integrity sha1-8w9xbI4r00bHtn0985FVZqfAVgc= - -indexof@0.0.1: - version "0.0.1" - resolved "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz" - integrity sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10= - -infer-owner@^1.0.3: - version "1.0.4" - resolved "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz" - integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A== - -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" - integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@1: - version "1.0.2" - resolved "https://registry.npmjs.org/inherits/-/inherits-1.0.2.tgz" - integrity sha1-ykMJ2t7mtUzAuNJH6NfHoJdb3Js= - -inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3, inherits@~2.0.4: - version "2.0.4" - resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -inherits@2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" - integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE= - -inherits@2.0.3: - version "2.0.3" - resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" - integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= - -ini@^1.3.4, ini@^1.3.5: - version "1.3.5" - resolved "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz" - integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== - -inquirer@^0.12.0: - version "0.12.0" - resolved "https://registry.npmjs.org/inquirer/-/inquirer-0.12.0.tgz" - integrity sha1-HvK/1jUE3wvHV4X/+MLEHfEvB34= - dependencies: - ansi-escapes "^1.1.0" - ansi-regex "^2.0.0" - chalk "^1.0.0" - cli-cursor "^1.0.1" - cli-width "^2.0.0" - figures "^1.3.5" - lodash "^4.3.0" - readline2 "^1.0.1" - run-async "^0.1.0" - rx-lite "^3.1.2" - string-width "^1.0.1" - strip-ansi "^3.0.0" - through "^2.3.6" - -inquirer@^6.2.2: - version "6.5.2" - resolved "https://registry.npmjs.org/inquirer/-/inquirer-6.5.2.tgz" - integrity sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ== - dependencies: - ansi-escapes "^3.2.0" - chalk "^2.4.2" - cli-cursor "^2.1.0" - cli-width "^2.0.0" - external-editor "^3.0.3" - figures "^2.0.0" - lodash "^4.17.12" - mute-stream "0.0.7" - run-async "^2.2.0" - rxjs "^6.4.0" - string-width "^2.1.0" - strip-ansi "^5.1.0" - through "^2.3.6" - -interpret@1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz" - integrity sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw== - -interpret@^0.3.2: - version "0.3.10" - resolved "https://registry.npmjs.org/interpret/-/interpret-0.3.10.tgz" - integrity sha1-CIwl3nMcbFsRKpDwBxz69Fnlp7s= - -interpret@^1.4.0: - version "1.4.0" - resolved "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz" - integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== - -into-stream@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/into-stream/-/into-stream-3.1.0.tgz" - integrity sha1-lvsKk2wSur1v8XUqF9BWFqvQlMY= - dependencies: - from2 "^2.1.1" - p-is-promise "^1.1.0" - -invariant@^2.2.2: - version "2.2.4" - resolved "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz" - integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== - dependencies: - loose-envify "^1.0.0" - -invert-kv@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz" - integrity sha1-EEqOSqym09jNFXqO+L+rLXo//bY= - -invert-kv@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz" - integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA== - -irregular-plurals@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-2.0.0.tgz" - integrity sha512-Y75zBYLkh0lJ9qxeHlMjQ7bSbyiSqNW/UOPWDmzC7cXskL1hekSITh1Oc6JV0XCWWZ9DE8VYSB71xocLk3gmGw== - -is-absolute-url@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz" - integrity sha1-UFMN+4T8yap9vnhS6Do3uTufKqY= - -is-absolute@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz" - integrity sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA== - dependencies: - is-relative "^1.0.0" - is-windows "^1.0.1" - -is-accessor-descriptor@^0.1.6: - version "0.1.6" - resolved "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz" - integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= - dependencies: - kind-of "^3.0.2" - -is-accessor-descriptor@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz" - integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== - dependencies: - kind-of "^6.0.0" - -is-arrayish@^0.2.1: - version "0.2.1" - resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz" - integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= - -is-arrayish@^0.3.1: - version "0.3.2" - resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz" - integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== - -is-binary-path@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz" - integrity sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg= - dependencies: - binary-extensions "^1.0.0" - -is-binary-path@~2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz" - integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== - dependencies: - binary-extensions "^2.0.0" - -is-buffer@^1.1.5: - version "1.1.6" - resolved "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz" - integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== - -is-buffer@^2.0.2: - version "2.0.3" - resolved "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz" - integrity sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw== - -is-callable@^1.1.3, is-callable@^1.1.4: - version "1.1.4" - resolved "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz" - integrity sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA== - -is-color-stop@^1.0.0: - version "1.1.0" - resolved "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz" - integrity sha1-z/9HGu5N1cnhWFmPvhKWe1za00U= - dependencies: - css-color-names "^0.0.4" - hex-color-regex "^1.1.0" - hsl-regex "^1.0.0" - hsla-regex "^1.0.0" - rgb-regex "^1.0.1" - rgba-regex "^1.0.0" - -is-data-descriptor@^0.1.4: - version "0.1.4" - resolved "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz" - integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= - dependencies: - kind-of "^3.0.2" - -is-data-descriptor@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz" - integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== - dependencies: - kind-of "^6.0.0" - -is-date-object@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz" - integrity sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY= - -is-descriptor@^0.1.0: - version "0.1.6" - resolved "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz" - integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== - dependencies: - is-accessor-descriptor "^0.1.6" - is-data-descriptor "^0.1.4" - kind-of "^5.0.0" - -is-descriptor@^1.0.0, is-descriptor@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz" - integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== - dependencies: - is-accessor-descriptor "^1.0.0" - is-data-descriptor "^1.0.0" - kind-of "^6.0.2" - -is-directory@^0.3.1: - version "0.3.1" - resolved "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz" - integrity sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE= - -is-docker@^2.0.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" - integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== - -is-extendable@^0.1.0, is-extendable@^0.1.1: - version "0.1.1" - resolved "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz" - integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= - -is-extendable@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz" - integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== - dependencies: - is-plain-object "^2.0.4" - -is-extglob@^2.1.0, is-extglob@^2.1.1: - version "2.1.1" - resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" - integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= - -is-finite@^1.0.0: - version "1.0.2" - resolved "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz" - integrity sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko= - dependencies: - number-is-nan "^1.0.0" - -is-fullwidth-code-point@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz" - integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= - dependencies: - number-is-nan "^1.0.0" - -is-fullwidth-code-point@^2.0.0: - 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= - -is-fullwidth-code-point@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz" - integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== - -is-function@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/is-function/-/is-function-1.0.1.tgz" - integrity sha1-Es+5i2W1fdPRk6MSH19uL0N2ArU= - -is-gif@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/is-gif/-/is-gif-3.0.0.tgz" - integrity sha512-IqJ/jlbw5WJSNfwQ/lHEDXF8rxhRgF6ythk2oiEvhpG29F704eX9NO6TvPfMiq9DrbwgcEDnETYNcZDPewQoVw== - dependencies: - file-type "^10.4.0" - -is-glob@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz" - integrity sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo= - dependencies: - is-extglob "^2.1.0" - -is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz" - integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== - dependencies: - is-extglob "^2.1.1" - -is-jpg@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/is-jpg/-/is-jpg-2.0.0.tgz" - integrity sha1-LhmX+m6RZuqsAkLarkQ0A+TvHZc= - -is-my-ip-valid@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/is-my-ip-valid/-/is-my-ip-valid-1.0.0.tgz" - integrity sha512-gmh/eWXROncUzRnIa1Ubrt5b8ep/MGSnfAUI3aRp+sqTCs1tv1Isl8d8F6JmkN3dXKc3ehZMrtiPN9eL03NuaQ== - -is-my-json-valid@^2.10.0: - version "2.20.0" - resolved "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.20.0.tgz" - integrity sha512-XTHBZSIIxNsIsZXg7XB5l8z/OBFosl1Wao4tXLpeC7eKU4Vm/kdop2azkPqULwnfGQjmeDIyey9g7afMMtdWAA== - dependencies: - generate-function "^2.0.0" - generate-object-property "^1.1.0" - is-my-ip-valid "^1.0.0" - jsonpointer "^4.0.0" - xtend "^4.0.0" - -is-natural-number@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/is-natural-number/-/is-natural-number-4.0.1.tgz" - integrity sha1-q5124dtM7VHjXeDHLr7PCfc0zeg= - -is-negated-glob@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz" - integrity sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI= - -is-number-like@^1.0.3: - version "1.0.8" - resolved "https://registry.npmjs.org/is-number-like/-/is-number-like-1.0.8.tgz" - integrity sha512-6rZi3ezCyFcn5L71ywzz2bS5b2Igl1En3eTlZlvKjpz1n3IZLAYMbKYAIQgFmEu0GENg92ziU/faEOA/aixjbA== - dependencies: - lodash.isfinite "^3.3.2" - -is-number@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz" - integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= - dependencies: - kind-of "^3.0.2" - -is-number@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz" - integrity sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ== - -is-number@^7.0.0: - version "7.0.0" - resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" - integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== - -is-obj@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz" - integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8= - -is-object@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/is-object/-/is-object-1.0.1.tgz" - integrity sha1-iVJojF7C/9awPsyF52ngKQMINHA= - -is-plain-obj@^1.0.0, is-plain-obj@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz" - integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= - -is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: - version "2.0.4" - resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz" - integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== - dependencies: - isobject "^3.0.1" - -is-png@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/is-png/-/is-png-2.0.0.tgz" - integrity sha512-4KPGizaVGj2LK7xwJIz8o5B2ubu1D/vcQsgOGFEDlpcvgZHto4gBnyd0ig7Ws+67ixmwKoNmu0hYnpo6AaKb5g== - -is-promise@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz" - integrity sha1-eaKp7OfwlugPNtKy87wWwf9L8/o= - -is-property@^1.0.0, is-property@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz" - integrity sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ= - -is-regex@^1.0.4: - version "1.0.4" - resolved "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz" - integrity sha1-VRdIm1RwkbCTDglWVM7SXul+lJE= - dependencies: - has "^1.0.1" - -is-relative@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz" - integrity sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA== - dependencies: - is-unc-path "^1.0.0" - -is-resolvable@^1.0.0: - version "1.1.0" - resolved "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz" - integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg== - -is-retry-allowed@^1.0.0, is-retry-allowed@^1.1.0: - version "1.2.0" - resolved "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz" - integrity sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg== - -is-stream@^1.0.0, is-stream@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz" - integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= - -is-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz" - integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== - -is-svg@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/is-svg/-/is-svg-3.0.0.tgz" - integrity sha512-gi4iHK53LR2ujhLVVj+37Ykh9GLqYHX6JOVXbLAucaG/Cqw9xwdFOjDM2qeifLs1sF1npXXFvDu0r5HNgCMrzQ== - dependencies: - html-comment-regex "^1.1.0" - -is-symbol@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz" - integrity sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw== - dependencies: - has-symbols "^1.0.0" - -is-typedarray@~1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz" - integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= - -is-unc-path@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz" - integrity sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ== - dependencies: - unc-path-regex "^0.1.2" - -is-utf8@^0.2.0, is-utf8@^0.2.1: - version "0.2.1" - resolved "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz" - integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= - -is-valid-glob@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-1.0.0.tgz" - integrity sha1-Kb8+/3Ab4tTTFdusw5vDn+j2Aao= - -is-windows@^1.0.1, is-windows@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz" - integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== - -is-wsl@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz" - integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= - -is-wsl@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" - integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== - dependencies: - is-docker "^2.0.0" - -is@^3.2.1: - version "3.3.0" - resolved "https://registry.npmjs.org/is/-/is-3.3.0.tgz" - integrity sha512-nW24QBoPcFGGHJGUwnfpI7Yc5CdqWNdsyHQszVE/z2pKHXzh7FZ5GWhJqSyaQ9wMkQnsTx+kAI8bHlCX4tKdbg== - -isarray@0.0.1: - version "0.0.1" - resolved "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" - integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= - -isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" - integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= - -isarray@2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz" - integrity sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4= - -isbinaryfile@^3.0.2: - version "3.0.3" - resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-3.0.3.tgz#5d6def3edebf6e8ca8cae9c30183a804b5f8be80" - integrity sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw== - dependencies: - buffer-alloc "^1.2.0" - -isbinaryfile@^4.0.8: - version "4.0.10" - resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-4.0.10.tgz#0c5b5e30c2557a2f06febd37b7322946aaee42b3" - integrity sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw== - -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" - integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= - -isobject@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz" - integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= - dependencies: - isarray "1.0.0" - -isobject@^3.0.0, isobject@^3.0.1: - version "3.0.1" - resolved "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz" - integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= - -isstream@0.1.x, isstream@~0.1.2: - version "0.1.2" - resolved "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz" - integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= - -isurl@^1.0.0-alpha5: - version "1.0.0" - resolved "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz" - integrity sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w== - dependencies: - has-to-string-tag-x "^1.2.0" - is-object "^1.0.1" - -jimp@^0.6.1: - version "0.6.8" - resolved "https://registry.npmjs.org/jimp/-/jimp-0.6.8.tgz" - integrity sha512-F7emeG7Hp61IM8VFbNvWENLTuHe0ghizWPuP4JS9ujx2r5mCVYEd/zdaz6M2M42ZdN41blxPajLWl9FXo7Mr2Q== - dependencies: - "@jimp/custom" "^0.6.8" - "@jimp/plugins" "^0.6.8" - "@jimp/types" "^0.6.8" - core-js "^2.5.7" - regenerator-runtime "^0.13.3" - -jpeg-js@^0.3.4: - version "0.3.6" - resolved "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.3.6.tgz" - integrity sha512-MUj2XlMB8kpe+8DJUGH/3UJm4XpI8XEgZQ+CiHDeyrGoKPdW/8FJv6ku+3UiYm5Fz3CWaL+iXmD8Q4Ap6aC1Jw== - -jpegtran-bin@^5.0.0: - version "5.0.1" - resolved "https://registry.npmjs.org/jpegtran-bin/-/jpegtran-bin-5.0.1.tgz" - integrity sha512-xQoXWkIEt4ckmvcHd9xG3RcCIn00sf2TshDyFMOAE+46EspEqwqoPWotVI3e55FGWafMa9cEqaoIyrCeWDnFPw== - dependencies: - bin-build "^3.0.0" - bin-wrapper "^4.0.0" - logalot "^2.0.0" - -js-base64@^2.1.9: - version "2.5.1" - resolved "https://registry.npmjs.org/js-base64/-/js-base64-2.5.1.tgz" - integrity sha512-M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw== - -js-beautify@^1.5.10: - version "1.10.2" - resolved "https://registry.npmjs.org/js-beautify/-/js-beautify-1.10.2.tgz" - integrity sha512-ZtBYyNUYJIsBWERnQP0rPN9KjkrDfJcMjuVGcvXOUJrD1zmOGwhRwQ4msG+HJ+Ni/FA7+sRQEMYVzdTQDvnzvQ== - dependencies: - config-chain "^1.1.12" - editorconfig "^0.15.3" - glob "^7.1.3" - mkdirp "~0.5.1" - nopt "~4.0.1" - -js-levenshtein@^1.1.3: - version "1.1.6" - resolved "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz" - integrity sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g== - -"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz" - integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== - -js-tokens@^3.0.2: - version "3.0.2" - resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz" - integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= - -js-yaml@^3.13.0, js-yaml@^3.13.1, js-yaml@^3.4.6, js-yaml@^3.5.1, js-yaml@^3.5.4: - version "3.13.1" - resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz" - integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - -jsbn@~0.1.0: - version "0.1.1" - resolved "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz" - integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= - -jsdom@12.2.0: - version "12.2.0" - resolved "https://registry.npmjs.org/jsdom/-/jsdom-12.2.0.tgz" - integrity sha512-QPOggIJ8fquWPLaYYMoh+zqUmdphDtu1ju0QGTitZT1Yd8I5qenPpXM1etzUegu3MjVp8XPzgZxdn8Yj7e40ig== - dependencies: - abab "^2.0.0" - acorn "^6.0.2" - acorn-globals "^4.3.0" - array-equal "^1.0.0" - cssom "^0.3.4" - cssstyle "^1.1.1" - data-urls "^1.0.1" - domexception "^1.0.1" - escodegen "^1.11.0" - html-encoding-sniffer "^1.0.2" - nwsapi "^2.0.9" - parse5 "5.1.0" - pn "^1.1.0" - request "^2.88.0" - request-promise-native "^1.0.5" - saxes "^3.1.3" - symbol-tree "^3.2.2" - tough-cookie "^2.4.3" - w3c-hr-time "^1.0.1" - webidl-conversions "^4.0.2" - whatwg-encoding "^1.0.5" - whatwg-mimetype "^2.2.0" - whatwg-url "^7.0.0" - ws "^6.1.0" - xml-name-validator "^3.0.0" - -jsesc@^1.3.0: - version "1.3.0" - resolved "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz" - integrity sha1-RsP+yMGJKxKwgz25vHYiF226s0s= - -jsesc@^2.5.1: - version "2.5.2" - resolved "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz" - integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== - -jsesc@~0.5.0: - version "0.5.0" - resolved "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz" - integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= - -json-buffer@3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz" - integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= - -json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz" - integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== - -json-schema-traverse@^0.4.1: - 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== - -json-schema@0.2.3: - version "0.2.3" - resolved "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz" - integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= - -json-stable-stringify-without-jsonify@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz" - integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= - -json-stable-stringify@^1.0.0, json-stable-stringify@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz" - integrity sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8= - dependencies: - jsonify "~0.0.0" - -json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: - version "5.0.1" - resolved "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" - integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= - -json5@^0.5.0, json5@^0.5.1: - version "0.5.1" - resolved "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz" - integrity sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE= - -json5@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz" - integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== - dependencies: - minimist "^1.2.0" - -json5@^2.1.2: - version "2.1.3" - resolved "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz" - integrity sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA== - dependencies: - minimist "^1.2.5" - -jsonfile@^3.0.0: - version "3.0.1" - resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-3.0.1.tgz" - integrity sha1-pezG9l9T9mLEQVx2daAzHQmS7GY= - optionalDependencies: - graceful-fs "^4.1.6" - -jsonfile@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz" - integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= - optionalDependencies: - graceful-fs "^4.1.6" - -jsonfile@^6.0.1: - version "6.1.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" - integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== - dependencies: - universalify "^2.0.0" - optionalDependencies: - graceful-fs "^4.1.6" - -jsonify@~0.0.0: - version "0.0.0" - resolved "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz" - integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM= - -jsonpointer@^4.0.0: - version "4.0.1" - resolved "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz" - integrity sha1-T9kss04OnbPInIYi7PUfm5eMbLk= - -jsprim@^1.2.2: - version "1.4.1" - resolved "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz" - integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= - dependencies: - assert-plus "1.0.0" - extsprintf "1.3.0" - json-schema "0.2.3" - verror "1.10.0" - -junk@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/junk/-/junk-3.1.0.tgz" - integrity sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ== - -just-debounce@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/just-debounce/-/just-debounce-1.0.0.tgz" - integrity sha1-h/zPrv/AtozRnVX2cilD+SnqNeo= - -keyv@3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/keyv/-/keyv-3.0.0.tgz" - integrity sha512-eguHnq22OE3uVoSYG0LVWNP+4ppamWr9+zWBe1bsNcovIMy6huUJFPgy4mGwCd/rnl3vOLGW1MTlu4c57CT1xA== - dependencies: - json-buffer "3.0.0" - -keyv@^3.0.0: - version "3.1.0" - resolved "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz" - integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA== - dependencies: - json-buffer "3.0.0" - -kind-of@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz" - integrity sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ= - -kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: - version "3.2.2" - resolved "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz" - integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= - dependencies: - is-buffer "^1.1.5" - -kind-of@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz" - integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= - dependencies: - is-buffer "^1.1.5" - -kind-of@^5.0.0, kind-of@^5.0.2: - version "5.1.0" - resolved "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz" - integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== - -kind-of@^6.0.0, kind-of@^6.0.2: - version "6.0.2" - resolved "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz" - integrity sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA== - -known-css-properties@^0.11.0: - version "0.11.0" - resolved "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.11.0.tgz" - integrity sha512-bEZlJzXo5V/ApNNa5z375mJC6Nrz4vG43UgcSCrg2OHC+yuB6j0iDSrY7RQ/+PRofFB03wNIIt9iXIVLr4wc7w== - -known-css-properties@^0.3.0: - version "0.3.0" - resolved "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.3.0.tgz" - integrity sha512-QMQcnKAiQccfQTqtBh/qwquGZ2XK/DXND1jrcN9M8gMMy99Gwla7GQjndVUsEqIaRyP6bsFRuhwRj5poafBGJQ== - -last-run@^1.1.0: - version "1.1.1" - resolved "https://registry.npmjs.org/last-run/-/last-run-1.1.1.tgz" - integrity sha1-RblpQsF7HHnHchmCWbqUO+v4yls= - dependencies: - default-resolution "^2.0.0" - es6-weak-map "^2.0.1" - -lazystream@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz" - integrity sha1-9plf4PggOS9hOWvolGJAe7dxaOQ= - dependencies: - readable-stream "^2.0.5" - -lazystream@~0.1.0: - version "0.1.0" - resolved "https://registry.npmjs.org/lazystream/-/lazystream-0.1.0.tgz" - integrity sha1-GyXWPHcqTCDwpe0KnXf0hLbhaSA= - dependencies: - readable-stream "~1.0.2" - -lcid@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz" - integrity sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU= - dependencies: - invert-kv "^1.0.0" - -lcid@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz" - integrity sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA== - dependencies: - invert-kv "^2.0.0" - -lead@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/lead/-/lead-1.0.0.tgz" - integrity sha1-bxT5mje+Op3XhPVJVpDlkDRm7kI= - dependencies: - flush-write-stream "^1.0.2" - -levn@^0.3.0, levn@~0.3.0: - version "0.3.0" - resolved "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz" - integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= - dependencies: - prelude-ls "~1.1.2" - type-check "~0.3.2" - -liftoff@^2.0.1: - version "2.5.0" - resolved "https://registry.npmjs.org/liftoff/-/liftoff-2.5.0.tgz" - integrity sha1-IAkpG7Mc6oYbvxCnwVooyvdcMew= - dependencies: - extend "^3.0.0" - findup-sync "^2.0.0" - fined "^1.0.1" - flagged-respawn "^1.0.0" - is-plain-object "^2.0.4" - object.map "^1.0.0" - rechoir "^0.6.2" - resolve "^1.1.7" - -liftoff@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/liftoff/-/liftoff-3.1.0.tgz" - integrity sha512-DlIPlJUkCV0Ips2zf2pJP0unEoT1kwYhiiPUGF3s/jtxTCjziNLoiVVh+jqWOWeFi6mmwQ5fNxvAUyPad4Dfog== - dependencies: - extend "^3.0.0" - findup-sync "^3.0.0" - fined "^1.0.1" - flagged-respawn "^1.0.0" - is-plain-object "^2.0.4" - object.map "^1.0.0" - rechoir "^0.6.2" - resolve "^1.1.7" - -limiter@^1.0.5: - version "1.1.4" - resolved "https://registry.npmjs.org/limiter/-/limiter-1.1.4.tgz" - integrity sha512-XCpr5bElgDI65vVgstP8TWjv6/QKWm9GU5UG0Pr5sLQ3QLo8NVKsioe+Jed5/3vFOe3IQuqE7DKwTvKQkjTHvg== - -line-column@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/line-column/-/line-column-1.0.2.tgz" - integrity sha1-0lryk2tvSEkXKzEuR5LR2Ye8NKI= - dependencies: - isarray "^1.0.0" - isobject "^2.0.0" - -load-bmfont@^1.3.1, load-bmfont@^1.4.0: - version "1.4.0" - resolved "https://registry.npmjs.org/load-bmfont/-/load-bmfont-1.4.0.tgz" - integrity sha512-kT63aTAlNhZARowaNYcY29Fn/QYkc52M3l6V1ifRcPewg2lvUZDAj7R6dXjOL9D0sict76op3T5+odumDSF81g== - dependencies: - buffer-equal "0.0.1" - mime "^1.3.4" - parse-bmfont-ascii "^1.0.3" - parse-bmfont-binary "^1.0.5" - parse-bmfont-xml "^1.1.4" - phin "^2.9.1" - xhr "^2.0.1" - xtend "^4.0.0" - -load-json-file@^1.0.0: - version "1.1.0" - resolved "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz" - integrity sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA= - dependencies: - graceful-fs "^4.1.2" - parse-json "^2.2.0" - pify "^2.0.0" - pinkie-promise "^2.0.0" - strip-bom "^2.0.0" - -load-json-file@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz" - integrity sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg= - dependencies: - graceful-fs "^4.1.2" - parse-json "^2.2.0" - pify "^2.0.0" - strip-bom "^3.0.0" - -loader-runner@^2.4.0: - version "2.4.0" - resolved "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz" - integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw== - -loader-utils@1.2.3: - version "1.2.3" - resolved "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz" - integrity sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA== - dependencies: - big.js "^5.2.2" - emojis-list "^2.0.0" - json5 "^1.0.1" - -loader-utils@^0.2.5, loader-utils@~0.2.2, loader-utils@~0.2.3, loader-utils@~0.2.5: - version "0.2.17" - resolved "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz" - integrity sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g= - dependencies: - big.js "^3.1.3" - emojis-list "^2.0.0" - json5 "^0.5.0" - object-assign "^4.0.1" - -loader-utils@^1.0.0, loader-utils@^1.1.0, loader-utils@^1.2.3, loader-utils@^1.4.0: - version "1.4.0" - resolved "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz" - integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA== - dependencies: - big.js "^5.2.2" - emojis-list "^3.0.0" - json5 "^1.0.1" - -loader-utils@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.2.tgz#d6e3b4fb81870721ae4e0868ab11dd638368c129" - integrity sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A== - dependencies: - big.js "^5.2.2" - emojis-list "^3.0.0" - json5 "^2.1.2" - -localtunnel@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/localtunnel/-/localtunnel-2.0.0.tgz" - integrity sha512-g6E0aLgYYDvQDxIjIXkgJo2+pHj3sGg4Wz/XP3h2KtZnRsWPbOQY+hw1H8Z91jep998fkcVE9l+kghO+97vllg== - dependencies: - axios "0.19.0" - debug "4.1.1" - openurl "1.1.1" - yargs "13.3.0" - -locate-path@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz" - integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= - dependencies: - p-locate "^2.0.0" - path-exists "^3.0.0" - -locate-path@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz" - integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== - dependencies: - p-locate "^3.0.0" - path-exists "^3.0.0" - -locate-path@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz" - integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== - dependencies: - p-locate "^4.1.0" - -lodash._basecopy@^3.0.0: - version "3.0.1" - resolved "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz" - integrity sha1-jaDmqHbPNEwK2KVIghEd08XHyjY= - -lodash._basetostring@^3.0.0: - version "3.0.1" - resolved "https://registry.npmjs.org/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz" - integrity sha1-0YYdh3+CSlL2aYMtyvPuFVZqB9U= - -lodash._basevalues@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/lodash._basevalues/-/lodash._basevalues-3.0.0.tgz" - integrity sha1-W3dXYoAr3j0yl1A+JjAIIP32Ybc= - -lodash._escapehtmlchar@~2.4.1: - version "2.4.1" - resolved "https://registry.npmjs.org/lodash._escapehtmlchar/-/lodash._escapehtmlchar-2.4.1.tgz" - integrity sha1-32fDu2t+jh6DGrSL+geVuSr+iZ0= - dependencies: - lodash._htmlescapes "~2.4.1" - -lodash._escapestringchar@~2.4.1: - version "2.4.1" - resolved "https://registry.npmjs.org/lodash._escapestringchar/-/lodash._escapestringchar-2.4.1.tgz" - integrity sha1-7P4iYYoq3lC/7qQ5N+Ud9m8O23I= - -lodash._getnative@^3.0.0: - version "3.9.1" - resolved "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz" - integrity sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U= - -lodash._htmlescapes@~2.4.1: - version "2.4.1" - resolved "https://registry.npmjs.org/lodash._htmlescapes/-/lodash._htmlescapes-2.4.1.tgz" - integrity sha1-MtFL8IRLbeb4tioFG09nwii2JMs= - -lodash._isiterateecall@^3.0.0: - version "3.0.9" - resolved "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz" - integrity sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw= - -lodash._isnative@~2.4.1: - version "2.4.1" - resolved "https://registry.npmjs.org/lodash._isnative/-/lodash._isnative-2.4.1.tgz" - integrity sha1-PqZAS3hKe+g2x7V1gOHN95sUgyw= - -lodash._objecttypes@~2.4.1: - version "2.4.1" - resolved "https://registry.npmjs.org/lodash._objecttypes/-/lodash._objecttypes-2.4.1.tgz" - integrity sha1-fAt/admKH3ZSn4kLDNsbTf7BHBE= - -lodash._reescape@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/lodash._reescape/-/lodash._reescape-3.0.0.tgz" - integrity sha1-Kx1vXf4HyKNVdT5fJ/rH8c3hYWo= - -lodash._reevaluate@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/lodash._reevaluate/-/lodash._reevaluate-3.0.0.tgz" - integrity sha1-WLx0xAZklTrgsSTYBpltrKQx4u0= - -lodash._reinterpolate@^2.4.1, lodash._reinterpolate@~2.4.1: - version "2.4.1" - resolved "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-2.4.1.tgz" - integrity sha1-TxInqlqHEfxjL1sHofRgequLMiI= - -lodash._reinterpolate@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz" - integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= - -lodash._reunescapedhtml@~2.4.1: - version "2.4.1" - resolved "https://registry.npmjs.org/lodash._reunescapedhtml/-/lodash._reunescapedhtml-2.4.1.tgz" - integrity sha1-dHxPxAED6zu4oJduVx96JlnpO6c= - dependencies: - lodash._htmlescapes "~2.4.1" - lodash.keys "~2.4.1" - -lodash._root@^3.0.0: - version "3.0.1" - resolved "https://registry.npmjs.org/lodash._root/-/lodash._root-3.0.1.tgz" - integrity sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI= - -lodash._shimkeys@~2.4.1: - version "2.4.1" - resolved "https://registry.npmjs.org/lodash._shimkeys/-/lodash._shimkeys-2.4.1.tgz" - integrity sha1-bpzJZm/wgfC1psl4uD4kLmlJ0gM= - dependencies: - lodash._objecttypes "~2.4.1" - -lodash.capitalize@^4.1.0: - version "4.2.1" - resolved "https://registry.npmjs.org/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz" - integrity sha1-+CbJtOKoUR2E46yinbBeGk87cqk= - -lodash.clone@^4.3.2: - version "4.5.0" - resolved "https://registry.npmjs.org/lodash.clone/-/lodash.clone-4.5.0.tgz" - integrity sha1-GVhwRQ9aExkkeN9Lw9I9LeoZB7Y= - -lodash.clonedeep@^4.3.2: - version "4.5.0" - resolved "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz" - integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= - -lodash.defaults@^4.2.0: - version "4.2.0" - resolved "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz" - integrity sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw= - -lodash.defaults@~2.4.1: - version "2.4.1" - resolved "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-2.4.1.tgz" - integrity sha1-p+iIXwXmiFEUS24SqPNngCa8TFQ= - dependencies: - lodash._objecttypes "~2.4.1" - lodash.keys "~2.4.1" - -lodash.escape@^3.0.0: - version "3.2.0" - resolved "https://registry.npmjs.org/lodash.escape/-/lodash.escape-3.2.0.tgz" - integrity sha1-mV7g3BjBtIzJLv+ucaEKq1tIdpg= - dependencies: - lodash._root "^3.0.0" - -lodash.escape@~2.4.1: - version "2.4.1" - resolved "https://registry.npmjs.org/lodash.escape/-/lodash.escape-2.4.1.tgz" - integrity sha1-LOEsXghNsKV92l5dHu659dF1o7Q= - dependencies: - lodash._escapehtmlchar "~2.4.1" - lodash._reunescapedhtml "~2.4.1" - lodash.keys "~2.4.1" - -lodash.get@^4.0.0: - version "4.4.2" - resolved "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz" - integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk= - -lodash.isarguments@^3.0.0: - version "3.1.0" - resolved "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz" - integrity sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo= - -lodash.isarray@^3.0.0: - version "3.0.4" - resolved "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz" - integrity sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U= - -lodash.isfinite@^3.3.2: - version "3.3.2" - resolved "https://registry.npmjs.org/lodash.isfinite/-/lodash.isfinite-3.3.2.tgz" - integrity sha1-+4m2WpqAKBgz8LdHizpRBPiY67M= - -lodash.isobject@~2.4.1: - version "2.4.1" - resolved "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-2.4.1.tgz" - integrity sha1-Wi5H/mmVPx7mMafrof5k0tBlWPU= - dependencies: - lodash._objecttypes "~2.4.1" - -lodash.kebabcase@^4.0.0: - version "4.1.1" - resolved "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz" - integrity sha1-hImxyw0p/4gZXM7KRI/21swpXDY= - -lodash.keys@^3.0.0: - version "3.1.2" - resolved "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz" - integrity sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo= - dependencies: - lodash._getnative "^3.0.0" - lodash.isarguments "^3.0.0" - lodash.isarray "^3.0.0" - -lodash.keys@~2.4.1: - version "2.4.1" - resolved "https://registry.npmjs.org/lodash.keys/-/lodash.keys-2.4.1.tgz" - integrity sha1-SN6kbfj/djKxDXBrissmWR4rNyc= - dependencies: - lodash._isnative "~2.4.1" - lodash._shimkeys "~2.4.1" - lodash.isobject "~2.4.1" - -lodash.memoize@^4.1.2: - version "4.1.2" - resolved "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz" - integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= - -lodash.restparam@^3.0.0: - version "3.6.1" - resolved "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz" - integrity sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU= - -lodash.some@^4.2.2: - version "4.6.0" - resolved "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz" - integrity sha1-G7nzFO9ri63tE7VJFpsqlF62jk0= - -lodash.sortby@^4.7.0: - version "4.7.0" - resolved "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz" - integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= - -lodash.template@^2.4.1: - version "2.4.1" - resolved "https://registry.npmjs.org/lodash.template/-/lodash.template-2.4.1.tgz" - integrity sha1-nmEQB+32KRKal0qzxIuBez4c8g0= - dependencies: - lodash._escapestringchar "~2.4.1" - lodash._reinterpolate "~2.4.1" - lodash.defaults "~2.4.1" - lodash.escape "~2.4.1" - lodash.keys "~2.4.1" - lodash.templatesettings "~2.4.1" - lodash.values "~2.4.1" - -lodash.template@^3.0.0: - version "3.6.2" - resolved "https://registry.npmjs.org/lodash.template/-/lodash.template-3.6.2.tgz" - integrity sha1-+M3sxhaaJVvpCYrosMU9N4kx0U8= - dependencies: - lodash._basecopy "^3.0.0" - lodash._basetostring "^3.0.0" - lodash._basevalues "^3.0.0" - lodash._isiterateecall "^3.0.0" - lodash._reinterpolate "^3.0.0" - lodash.escape "^3.0.0" - lodash.keys "^3.0.0" - lodash.restparam "^3.0.0" - lodash.templatesettings "^3.0.0" - -lodash.template@^4.5.0: - version "4.5.0" - resolved "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz" - integrity sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A== - dependencies: - lodash._reinterpolate "^3.0.0" - lodash.templatesettings "^4.0.0" - -lodash.templatesettings@^3.0.0: - version "3.1.1" - resolved "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz" - integrity sha1-+zB4RHU7Zrnxr6VOJix0UwfbqOU= - dependencies: - lodash._reinterpolate "^3.0.0" - lodash.escape "^3.0.0" - -lodash.templatesettings@^4.0.0: - version "4.2.0" - resolved "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz" - integrity sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ== - dependencies: - lodash._reinterpolate "^3.0.0" - -lodash.templatesettings@~2.4.1: - version "2.4.1" - resolved "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-2.4.1.tgz" - integrity sha1-6nbHXRHrhtTb6JqDiTu4YZKaxpk= - dependencies: - lodash._reinterpolate "~2.4.1" - lodash.escape "~2.4.1" - -lodash.uniq@^4.5.0: - version "4.5.0" - resolved "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz" - integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= - -lodash.values@~2.4.1: - version "2.4.1" - resolved "https://registry.npmjs.org/lodash.values/-/lodash.values-2.4.1.tgz" - integrity sha1-q/UUQ2s8twUAFieXjLzzCxKA7qQ= - dependencies: - lodash.keys "~2.4.1" - -lodash@^3.0.1: - version "3.10.1" - resolved "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz" - integrity sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y= - -lodash@^4.0.0, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.4, lodash@^4.3.0, lodash@~4.17.10: - version "4.17.15" - resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz" - integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== - -lodash@~1.0.1: - version "1.0.2" - resolved "https://registry.npmjs.org/lodash/-/lodash-1.0.2.tgz" - integrity sha1-j1dWDIO1n8JwvT1WG2kAQ0MOJVE= - -lodash@~2.4.1: - version "2.4.2" - resolved "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" - integrity sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4= - -logalot@^2.0.0, logalot@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/logalot/-/logalot-2.1.0.tgz" - integrity sha1-X46MkNME7fElMJUaVVSruMXj9VI= - dependencies: - figures "^1.3.5" - squeak "^1.0.0" - -longest@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz" - integrity sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc= - -loose-envify@^1.0.0: - version "1.4.0" - resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz" - integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== - dependencies: - js-tokens "^3.0.0 || ^4.0.0" - -loud-rejection@^1.0.0: - version "1.6.0" - resolved "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz" - integrity sha1-W0b4AUft7leIcPCG0Eghz5mOVR8= - dependencies: - currently-unhandled "^0.4.1" - signal-exit "^3.0.0" - -lower-case@^1.1.1: - version "1.1.4" - resolved "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz" - integrity sha1-miyr0bno4K6ZOkv31YdcOcQujqw= - -lowercase-keys@1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.0.tgz" - integrity sha1-TjNms55/VFfjXxMkvfb4jQv8cwY= - -lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz" - integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== - -lowercase-keys@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz" - integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== - -lpad-align@^1.0.1: - version "1.1.2" - resolved "https://registry.npmjs.org/lpad-align/-/lpad-align-1.1.2.tgz" - integrity sha1-IfYArBwwlcPG5JfuZyce4ISB/p4= - dependencies: - get-stdin "^4.0.1" - indent-string "^2.1.0" - longest "^1.0.0" - meow "^3.3.0" - -lru-cache@2: - version "2.7.3" - resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz" - integrity sha1-bUUk6LlV+V1PW1iFHOId1y+06VI= - -lru-cache@^4.0.1, lru-cache@^4.1.5: - version "4.1.5" - resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz" - integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== - dependencies: - pseudomap "^1.0.2" - yallist "^2.1.2" - -lru-cache@^5.1.1: - version "5.1.1" - resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz" - integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== - dependencies: - yallist "^3.0.2" - -lru-cache@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" - integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== - dependencies: - yallist "^4.0.0" - -lz-string@^1.4.4: - version "1.4.4" - resolved "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz" - integrity sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY= - -make-dir@^1.0.0, make-dir@^1.2.0: - version "1.3.0" - resolved "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz" - integrity sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ== - dependencies: - pify "^3.0.0" - -make-dir@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz" - integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== - dependencies: - pify "^4.0.1" - semver "^5.6.0" - -make-dir@^3.0.0: - version "3.1.0" - resolved "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz" - integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== - dependencies: - semver "^6.0.0" - -make-iterator@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz" - integrity sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw== - dependencies: - kind-of "^6.0.2" - -map-age-cleaner@^0.1.1: - version "0.1.3" - resolved "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz" - integrity sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w== - dependencies: - p-defer "^1.0.0" - -map-cache@^0.2.0, map-cache@^0.2.2: - version "0.2.2" - resolved "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz" - integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= - -map-obj@^1.0.0, map-obj@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz" - integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0= - -map-visit@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz" - integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= - dependencies: - object-visit "^1.0.0" - -markdown-loader@^5.1.0: - version "5.1.0" - resolved "https://registry.npmjs.org/markdown-loader/-/markdown-loader-5.1.0.tgz" - integrity sha512-xtQNozLEL+55ZSPTNwro8epZqf1h7HjAZd/69zNe8lbckDiGVHeLQm849bXzocln2pwRK2A/GrW/7MAmwjcFog== - dependencies: - loader-utils "^1.2.3" - marked "^0.7.0" - -marked@^0.7.0: - version "0.7.0" - resolved "https://registry.npmjs.org/marked/-/marked-0.7.0.tgz" - integrity sha512-c+yYdCZJQrsRjTPhUx7VKkApw9bwDkNbHUKo1ovgcfDjb2kc8rLuRbIFyXL5WOEUwzSSKo3IXpph2K6DqB/KZg== - -matchdep@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/matchdep/-/matchdep-2.0.0.tgz" - integrity sha1-xvNINKDY28OzfCfui7yyfHd1WC4= - dependencies: - findup-sync "^2.0.0" - micromatch "^3.0.4" - resolve "^1.4.0" - stack-trace "0.0.10" - -matcher@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/matcher/-/matcher-3.0.0.tgz#bd9060f4c5b70aa8041ccc6f80368760994f30ca" - integrity sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng== - dependencies: - escape-string-regexp "^4.0.0" - -md5.js@^1.3.4: - version "1.3.5" - resolved "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz" - integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== - dependencies: - hash-base "^3.0.0" - inherits "^2.0.1" - safe-buffer "^5.1.2" - -mdn-data@2.0.4: - version "2.0.4" - resolved "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz" - integrity sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA== - -mdn-data@~1.1.0: - version "1.1.4" - resolved "https://registry.npmjs.org/mdn-data/-/mdn-data-1.1.4.tgz" - integrity sha512-FSYbp3lyKjyj3E7fMl6rYvUdX0FBXaluGqlFoYESWQlyUTq8R+wp0rkFxoYFqZlHCvsUXGjyJmLQSnXToYhOSA== - -media-typer@0.3.0: - version "0.3.0" - resolved "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz" - integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= - -mem@^4.0.0: - version "4.3.0" - resolved "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz" - integrity sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w== - dependencies: - map-age-cleaner "^0.1.1" - mimic-fn "^2.0.0" - p-is-promise "^2.0.0" - -memory-fs@^0.4.0, memory-fs@^0.4.1: - version "0.4.1" - resolved "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz" - integrity sha1-OpoguEYlI+RHz7x+i7gO1me/xVI= - dependencies: - errno "^0.1.3" - readable-stream "^2.0.1" - -meow@^3.3.0: - version "3.7.0" - resolved "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz" - integrity sha1-cstmi0JSKCkKu/qFaJJYcwioAfs= - dependencies: - camelcase-keys "^2.0.0" - decamelize "^1.1.2" - loud-rejection "^1.0.0" - map-obj "^1.0.1" - minimist "^1.1.3" - normalize-package-data "^2.3.4" - object-assign "^4.0.1" - read-pkg-up "^1.0.1" - redent "^1.0.0" - trim-newlines "^1.0.0" - -merge-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz" - integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== - -merge2@^1.2.3, merge2@^1.3.0: - version "1.3.0" - resolved "https://registry.npmjs.org/merge2/-/merge2-1.3.0.tgz" - integrity sha512-2j4DAdlBOkiSZIsaXk4mTE3sRS02yBHAtfy127xRV3bQUFqXkjHCHLW6Scv7DwNRbIWNHH8zpnz9zMaKXIdvYw== - -merge@^1.2.0: - version "1.2.1" - resolved "https://registry.npmjs.org/merge/-/merge-1.2.1.tgz" - integrity sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ== - -micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4: - version "3.1.10" - resolved "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz" - integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== - dependencies: - arr-diff "^4.0.0" - array-unique "^0.3.2" - braces "^2.3.1" - define-property "^2.0.2" - extend-shallow "^3.0.2" - extglob "^2.0.4" - fragment-cache "^0.2.1" - kind-of "^6.0.2" - nanomatch "^1.2.9" - object.pick "^1.3.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.2" - -micromatch@^4.0.2: - version "4.0.2" - resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz" - integrity sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q== - dependencies: - braces "^3.0.1" - picomatch "^2.0.5" - -miller-rabin@^4.0.0: - version "4.0.1" - resolved "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz" - integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA== - dependencies: - bn.js "^4.0.0" - brorand "^1.0.1" - -mime-db@1.40.0: - version "1.40.0" - resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz" - integrity sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA== - -mime-db@^1.28.0: - version "1.41.0" - resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.41.0.tgz" - integrity sha512-B5gxBI+2K431XW8C2rcc/lhppbuji67nf9v39eH8pkWoZDxnAL0PxdpH32KYRScniF8qDHBDlI+ipgg5WrCUYw== - -mime-db@~1.12.0: - version "1.12.0" - resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.12.0.tgz" - integrity sha1-PQxjGA9FjrENMlqqN9fFiuMS6dc= - -mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24: - version "2.1.24" - resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz" - integrity sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ== - dependencies: - mime-db "1.40.0" - -mime-types@~2.0.9: - version "2.0.14" - resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.0.14.tgz" - integrity sha1-MQ4VnbI+B3+Lsit0jav6SVcUCqY= - dependencies: - mime-db "~1.12.0" - -mime@1.4.1: - version "1.4.1" - resolved "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz" - integrity sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ== - -mime@^1.3.4: - version "1.6.0" - resolved "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz" - integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== - -mime@^2.4.0: - version "2.4.4" - resolved "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz" - integrity sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA== - -mimic-fn@^1.0.0: - version "1.2.0" - resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz" - integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== - -mimic-fn@^2.0.0, mimic-fn@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz" - integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== - -mimic-response@^1.0.0, mimic-response@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz" - integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== - -min-document@^2.19.0: - version "2.19.0" - resolved "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz" - integrity sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU= - dependencies: - dom-walk "^0.1.0" - -min-indent@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz" - integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== - -minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz" - integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== - -minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz" - integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= - -minimatch@0.3: - version "0.3.0" - resolved "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz" - integrity sha1-J12O2qxPG7MyZHIInnlJyDlGmd0= - dependencies: - lru-cache "2" - sigmund "~1.0.0" - -"minimatch@2 || 3", minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4, minimatch@~3.0.2: - version "3.0.4" - resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== - dependencies: - brace-expansion "^1.1.7" - -minimatch@^2.0.1: - version "2.0.10" - resolved "https://registry.npmjs.org/minimatch/-/minimatch-2.0.10.tgz" - integrity sha1-jQh8OcazjAAbl/ynzm0OHoCvusc= - dependencies: - brace-expansion "^1.0.0" - -minimatch@~0.2.11: - version "0.2.14" - resolved "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz" - integrity sha1-x054BXT2PG+aCQ6Q775u9TpqdWo= - dependencies: - lru-cache "2" - sigmund "~1.0.0" - -minimist@0.0.8: - version "0.0.8" - resolved "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" - integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= - -minimist@1.1.x: - version "1.1.3" - resolved "https://registry.npmjs.org/minimist/-/minimist-1.1.3.tgz" - integrity sha1-O+39kaktOQFvz6ocaB6Pqhoe/ag= - -minimist@^0.1.0: - version "0.1.0" - resolved "https://registry.npmjs.org/minimist/-/minimist-0.1.0.tgz" - integrity sha1-md9lelJXTCHJBXSX33QnkLK0wN4= - -minimist@^0.2.0: - version "0.2.0" - resolved "https://registry.npmjs.org/minimist/-/minimist-0.2.0.tgz" - integrity sha1-Tf/lJdriuGTGbC4jxicdev3s784= - -minimist@^1.1.0, minimist@^1.1.3, minimist@^1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" - integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= - -minimist@^1.2.5: - version "1.2.5" - resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz" - integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== - -minimist@^1.2.6: - version "1.2.6" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" - integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== - -minimist@~0.0.1: - version "0.0.10" - resolved "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz" - integrity sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8= - -mississippi@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz" - integrity sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA== - dependencies: - concat-stream "^1.5.0" - duplexify "^3.4.2" - end-of-stream "^1.1.0" - flush-write-stream "^1.0.0" - from2 "^2.1.0" - parallel-transform "^1.1.0" - pump "^3.0.0" - pumpify "^1.3.3" - stream-each "^1.1.0" - through2 "^2.0.0" - -mitt@^1.1.3: - version "1.1.3" - resolved "https://registry.npmjs.org/mitt/-/mitt-1.1.3.tgz" - integrity sha512-mUDCnVNsAi+eD6qA0HkRkwYczbLHJ49z17BGe2PYRhZL4wpZUFZGJHU7/5tmvohoma+Hdn0Vh/oJTiPEmgSruA== - -mixin-deep@^1.2.0: - version "1.3.2" - resolved "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz" - integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== - dependencies: - for-in "^1.0.2" - is-extendable "^1.0.1" - -mkdirp@0.5.1: - version "0.5.1" - resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz" - integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= - dependencies: - minimist "0.0.8" - -mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@~0.5.1: - version "0.5.5" - resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz" - integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== - dependencies: - minimist "^1.2.5" - -move-concurrently@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz" - integrity sha1-viwAX9oy4LKa8fBdfEszIUxwH5I= - dependencies: - aproba "^1.1.1" - copy-concurrently "^1.0.0" - fs-write-stream-atomic "^1.0.8" - mkdirp "^0.5.1" - rimraf "^2.5.4" - run-queue "^1.0.3" - -mozjpeg@^6.0.0: - version "6.0.1" - resolved "https://registry.npmjs.org/mozjpeg/-/mozjpeg-6.0.1.tgz" - integrity sha512-9Z59pJMi8ni+IUvSH5xQwK5tNLw7p3dwDNCZ3o1xE+of3G5Hc/yOz6Ue/YuLiBXU3ZB5oaHPURyPdqfBX/QYJA== - dependencies: - bin-build "^3.0.0" - bin-wrapper "^4.0.0" - logalot "^2.1.0" - -ms@2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" - integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= - -ms@2.1.2, ms@^2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - -multipipe@^0.1.0, multipipe@^0.1.2: - version "0.1.2" - resolved "https://registry.npmjs.org/multipipe/-/multipipe-0.1.2.tgz" - integrity sha1-Ko8t33Du1WTf8tV/HhoTfZ8FB4s= - dependencies: - duplexer2 "0.0.2" - -mute-stdout@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/mute-stdout/-/mute-stdout-1.0.1.tgz" - integrity sha512-kDcwXR4PS7caBpuRYYBUz9iVixUk3anO3f5OYFiIPwK/20vCzKCHyKoulbiDY1S53zD2bxUpxN/IJ+TnXjfvxg== - -mute-stream@0.0.5: - version "0.0.5" - resolved "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.5.tgz" - integrity sha1-j7+rsKmKJT0xhDMfno3rc3L6xsA= - -mute-stream@0.0.7, mute-stream@~0.0.4: - version "0.0.7" - resolved "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz" - integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= - -nan@^2.12.1: - version "2.14.2" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19" - integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ== - -nanoid@^3.1.16: - version "3.1.16" - resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.1.16.tgz" - integrity sha512-+AK8MN0WHji40lj8AEuwLOvLSbWYApQpre/aFJZD71r43wVRLrOYS4FmJOPQYon1TqB462RzrrxlfA74XRES8w== - -nanomatch@^1.2.9: - version "1.2.13" - resolved "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz" - integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== - dependencies: - arr-diff "^4.0.0" - array-unique "^0.3.2" - define-property "^2.0.2" - extend-shallow "^3.0.2" - fragment-cache "^0.2.1" - is-windows "^1.0.2" - kind-of "^6.0.2" - object.pick "^1.3.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - -natives@^1.1.3: - version "1.1.6" - resolved "https://registry.npmjs.org/natives/-/natives-1.1.6.tgz" - integrity sha512-6+TDFewD4yxY14ptjKaS63GVdtKiES1pTPyxn9Jb0rBqPMZ7VcCiooEhPNsr+mqHtMGxa/5c/HhcC4uPEUw/nA== - -natural-compare@^1.4.0: - version "1.4.0" - resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" - integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= - -needle@: - version "2.4.0" - resolved "https://registry.npmjs.org/needle/-/needle-2.4.0.tgz" - integrity sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg== - dependencies: - debug "^3.2.6" - iconv-lite "^0.4.4" - sax "^1.2.4" - -negotiator@0.6.2: - version "0.6.2" - resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz" - integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== - -neo-async@^2.5.0, neo-async@^2.6.1: - version "2.6.1" - resolved "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz" - integrity sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw== - -next-tick@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz" - integrity sha1-yobR/ogoFpsBICCOPchCS524NCw= - -nice-try@^1.0.4: - version "1.0.5" - resolved "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz" - integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== - -no-case@^2.2.0: - version "2.3.2" - resolved "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz" - integrity sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ== - dependencies: - lower-case "^1.1.1" - -node-libs-browser@^2.2.1: - version "2.2.1" - resolved "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz" - integrity sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q== - dependencies: - assert "^1.1.1" - browserify-zlib "^0.2.0" - buffer "^4.3.0" - console-browserify "^1.1.0" - constants-browserify "^1.0.0" - crypto-browserify "^3.11.0" - domain-browser "^1.1.1" - events "^3.0.0" - https-browserify "^1.0.0" - os-browserify "^0.3.0" - path-browserify "0.0.1" - process "^0.11.10" - punycode "^1.2.4" - querystring-es3 "^0.2.0" - readable-stream "^2.3.3" - stream-browserify "^2.0.1" - stream-http "^2.7.2" - string_decoder "^1.0.0" - timers-browserify "^2.0.4" - tty-browserify "0.0.0" - url "^0.11.0" - util "^0.11.0" - vm-browserify "^1.0.1" - -node-releases@^1.1.29: - version "1.1.32" - resolved "https://registry.npmjs.org/node-releases/-/node-releases-1.1.32.tgz" - integrity sha512-VhVknkitq8dqtWoluagsGPn3dxTvN9fwgR59fV3D7sLBHe0JfDramsMI8n8mY//ccq/Kkrf8ZRHRpsyVZ3qw1A== - dependencies: - semver "^5.3.0" - -node-sri@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/node-sri/-/node-sri-1.1.1.tgz" - integrity sha1-BBCW0rEfIytl3txMOuHLYrq7VLA= - -node.extend@^1.0.10: - version "1.1.8" - resolved "https://registry.npmjs.org/node.extend/-/node.extend-1.1.8.tgz" - integrity sha512-L/dvEBwyg3UowwqOUTyDsGBU6kjBQOpOhshio9V3i3BMPv5YUb9+mWNN8MK0IbWqT0AqaTSONZf0aTuMMahWgA== - dependencies: - has "^1.0.3" - is "^3.2.1" - -nopt@~4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz" - integrity sha1-0NRoWv1UFRk8jHUFYC0NF81kR00= - dependencies: - abbrev "1" - osenv "^0.1.4" - -normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: - version "2.5.0" - resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz" - integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== - dependencies: - hosted-git-info "^2.1.4" - resolve "^1.10.0" - semver "2 || 3 || 4 || 5" - validate-npm-package-license "^3.0.1" - -normalize-path@^2.1.1: - version "2.1.1" - resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz" - integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= - dependencies: - remove-trailing-separator "^1.0.1" - -normalize-path@^3.0.0, normalize-path@~3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz" - integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== - -normalize-range@^0.1.2: - version "0.1.2" - resolved "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz" - integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI= - -normalize-url@2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/normalize-url/-/normalize-url-2.0.1.tgz" - integrity sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw== - dependencies: - prepend-http "^2.0.0" - query-string "^5.0.1" - sort-keys "^2.0.0" - -normalize-url@^3.0.0: - version "3.3.0" - resolved "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz" - integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg== - -normalize-url@^4.1.0: - version "4.4.1" - resolved "https://registry.npmjs.org/normalize-url/-/normalize-url-4.4.1.tgz" - integrity sha512-rjH3yRt0Ssx19mUwS0hrDUOdG9VI+oRLpLHJ7tXRdjcuQ7v7wo6qPvOZppHRrqfslTKr0L2yBhjj4UXd7c3cQg== - -now-and-later@^2.0.0: - version "2.0.1" - resolved "https://registry.npmjs.org/now-and-later/-/now-and-later-2.0.1.tgz" - integrity sha512-KGvQ0cB70AQfg107Xvs/Fbu+dGmZoTRJp2TaPwcwQm3/7PteUyN2BCgk8KBMPGBUXZdVwyWS8fDCGFygBm19UQ== - dependencies: - once "^1.3.2" - -npm-conf@^1.1.0, npm-conf@^1.1.3: - version "1.1.3" - resolved "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.3.tgz" - integrity sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw== - dependencies: - config-chain "^1.1.11" - pify "^3.0.0" - -npm-run-path@^2.0.0: - version "2.0.2" - resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz" - integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= - dependencies: - path-key "^2.0.0" - -npm-run-path@^4.0.0, npm-run-path@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz" - integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== - dependencies: - path-key "^3.0.0" - -nth-check@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz" - integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg== - dependencies: - boolbase "~1.0.0" - -num2fraction@^1.2.2: - version "1.2.2" - resolved "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz" - integrity sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4= - -number-is-nan@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz" - integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= - -nwsapi@^2.0.9: - version "2.1.4" - resolved "https://registry.npmjs.org/nwsapi/-/nwsapi-2.1.4.tgz" - integrity sha512-iGfd9Y6SFdTNldEy2L0GUhcarIutFmk+MPWIn9dmj8NMIup03G08uUF2KGbbmv/Ux4RT0VZJoP/sVbWA6d/VIw== - -oauth-sign@~0.9.0: - version "0.9.0" - resolved "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz" - integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== - -object-assign@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz" - integrity sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I= - -object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: - version "4.1.1" - resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" - integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= - -object-assign@~0.3.1: - version "0.3.1" - resolved "https://registry.npmjs.org/object-assign/-/object-assign-0.3.1.tgz" - integrity sha1-Bg4qKifXwNd+x3t48Rqkf9iACNI= - -object-component@0.0.3: - version "0.0.3" - resolved "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz" - integrity sha1-8MaapQ78lbhmwYb0AKM3acsvEpE= - -object-copy@^0.1.0: - version "0.1.0" - resolved "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz" - integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= - dependencies: - copy-descriptor "^0.1.0" - define-property "^0.2.5" - kind-of "^3.0.3" - -object-inspect@^1.6.0: - version "1.6.0" - resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.6.0.tgz" - integrity sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ== - -object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz" - integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== - -object-keys@~0.4.0: - version "0.4.0" - resolved "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz" - integrity sha1-KKaq50KN0sOpLz2V8hM13SBOAzY= - -object-path@^0.9.0: - version "0.9.2" - resolved "https://registry.npmjs.org/object-path/-/object-path-0.9.2.tgz" - integrity sha1-D9mnT8X60a45aLWGvaXGMr1sBaU= - -object-visit@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz" - integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= - dependencies: - isobject "^3.0.0" - -object.assign@^4.0.4, object.assign@^4.1.0: - version "4.1.0" - resolved "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz" - integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w== - dependencies: - define-properties "^1.1.2" - function-bind "^1.1.1" - has-symbols "^1.0.0" - object-keys "^1.0.11" - -object.defaults@^1.0.0, object.defaults@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz" - integrity sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8= - dependencies: - array-each "^1.0.1" - array-slice "^1.0.0" - for-own "^1.0.0" - isobject "^3.0.0" - -object.getownpropertydescriptors@^2.0.3: - version "2.0.3" - resolved "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz" - integrity sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY= - dependencies: - define-properties "^1.1.2" - es-abstract "^1.5.1" - -object.map@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz" - integrity sha1-z4Plncj8wK1fQlDh94s7gb2AHTc= - dependencies: - for-own "^1.0.0" - make-iterator "^1.0.0" - -object.pick@^1.2.0, object.pick@^1.3.0: - version "1.3.0" - resolved "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz" - integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= - dependencies: - isobject "^3.0.1" - -object.reduce@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/object.reduce/-/object.reduce-1.0.1.tgz" - integrity sha1-b+NI8qx/oPlcpiEiZZkJaCW7A60= - dependencies: - for-own "^1.0.0" - make-iterator "^1.0.0" - -object.values@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/object.values/-/object.values-1.1.0.tgz" - integrity sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg== - dependencies: - define-properties "^1.1.3" - es-abstract "^1.12.0" - function-bind "^1.1.1" - has "^1.0.3" - -omggif@^1.0.9: - version "1.0.10" - resolved "https://registry.npmjs.org/omggif/-/omggif-1.0.10.tgz" - integrity sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw== - -on-finished@2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.1.0.tgz" - integrity sha1-DFOfCSkej/rd4MiiWFD7LO3HAi0= - dependencies: - ee-first "1.0.5" - -on-finished@~2.3.0: - version "2.3.0" - resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz" - integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= - dependencies: - ee-first "1.1.1" - -once@^1.3.0, once@^1.3.1, once@^1.3.2, once@^1.4.0: - version "1.4.0" - resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= - dependencies: - wrappy "1" - -once@~1.3.0: - version "1.3.3" - resolved "https://registry.npmjs.org/once/-/once-1.3.3.tgz" - integrity sha1-suJhVXzkwxTsgwTz+oJmPkKXyiA= - dependencies: - wrappy "1" - -onetime@^1.0.0: - version "1.1.0" - resolved "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz" - integrity sha1-ofeDj4MUxRbwXs78vEzP4EtO14k= - -onetime@^2.0.0: - version "2.0.1" - resolved "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz" - integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ= - dependencies: - mimic-fn "^1.0.0" - -onetime@^5.1.0, onetime@^5.1.2: - version "5.1.2" - resolved "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz" - integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== - dependencies: - mimic-fn "^2.1.0" - -open@^0.0.5: - version "0.0.5" - resolved "https://registry.npmjs.org/open/-/open-0.0.5.tgz" - integrity sha1-QsPhjslUZra/DcQvOilFw/DK2Pw= - -openurl@1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/openurl/-/openurl-1.1.1.tgz" - integrity sha1-OHW0sO96UsFW8NtB1GCduw+Us4c= - -opn@5.3.0: - version "5.3.0" - resolved "https://registry.npmjs.org/opn/-/opn-5.3.0.tgz" - integrity sha512-bYJHo/LOmoTd+pfiYhfZDnf9zekVJrY+cnS2a5F2x+w5ppvTqObojTP7WiFG+kVZs9Inw+qQ/lw7TroWwhdd2g== - dependencies: - is-wsl "^1.1.0" - -optimist@~0.6.1: - version "0.6.1" - resolved "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz" - integrity sha1-2j6nRob6IaGaERwybpDrFaAZZoY= - dependencies: - minimist "~0.0.1" - wordwrap "~0.0.2" - -optionator@^0.8.1, optionator@^0.8.2: - version "0.8.2" - resolved "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz" - integrity sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q= - dependencies: - deep-is "~0.1.3" - fast-levenshtein "~2.0.4" - levn "~0.3.0" - prelude-ls "~1.1.2" - type-check "~0.3.2" - wordwrap "~1.0.0" - -optipng-bin@^6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/optipng-bin/-/optipng-bin-6.0.0.tgz" - integrity sha512-95bB4y8IaTsa/8x6QH4bLUuyvyOoGBCLDA7wOgDL8UFqJpSUh1Hob8JRJhit+wC1ZLN3tQ7mFt7KuBj0x8F2Wg== - dependencies: - bin-build "^3.0.0" - bin-wrapper "^4.0.0" - logalot "^2.0.0" - -orchestrator@^0.3.0: - version "0.3.8" - resolved "https://registry.npmjs.org/orchestrator/-/orchestrator-0.3.8.tgz" - integrity sha1-FOfp4nZPcxX7rBhOUGx6pt+UrX4= - dependencies: - end-of-stream "~0.1.5" - sequencify "~0.0.7" - stream-consume "~0.1.0" - -ordered-read-streams@^0.1.0: - version "0.1.0" - resolved "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-0.1.0.tgz" - integrity sha1-/VZamvjrRHO6abbtijQ1LLVS8SY= - -ordered-read-streams@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz" - integrity sha1-d8DLN8QVJdZBZtmQ/61+xqDhNj4= - dependencies: - readable-stream "^2.0.1" - -os-browserify@^0.3.0: - version "0.3.0" - resolved "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz" - integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc= - -os-filter-obj@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/os-filter-obj/-/os-filter-obj-2.0.0.tgz" - integrity sha512-uksVLsqG3pVdzzPvmAHpBK0wKxYItuzZr7SziusRPoz67tGV8rL1szZ6IdeUrbqLjGDwApBtN29eEE3IqGHOjg== - dependencies: - arch "^2.1.0" - -os-homedir@^1.0.0: - version "1.0.2" - resolved "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz" - integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= - -os-locale@^1.4.0: - version "1.4.0" - resolved "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz" - integrity sha1-IPnxeuKe00XoveWDsT0gCYA8FNk= - dependencies: - lcid "^1.0.0" - -os-locale@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz" - integrity sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q== - dependencies: - execa "^1.0.0" - lcid "^2.0.0" - mem "^4.0.0" - -os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" - integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= - -osenv@^0.1.4: - version "0.1.5" - resolved "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz" - integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== - dependencies: - os-homedir "^1.0.0" - os-tmpdir "^1.0.0" - -ow@^0.17.0: - version "0.17.0" - resolved "https://registry.npmjs.org/ow/-/ow-0.17.0.tgz" - integrity sha512-i3keDzDQP5lWIe4oODyDFey1qVrq2hXKTuTH2VpqwpYtzPiKZt2ziRI4NBQmgW40AnV5Euz17OyWweCb+bNEQA== - dependencies: - type-fest "^0.11.0" - -p-cancelable@^0.3.0: - version "0.3.0" - resolved "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.3.0.tgz" - integrity sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw== - -p-cancelable@^0.4.0: - version "0.4.1" - resolved "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.4.1.tgz" - integrity sha512-HNa1A8LvB1kie7cERyy21VNeHb2CWJJYqyyC2o3klWFfMGlFmWv2Z7sFgZH8ZiaYL95ydToKTFVXgMV/Os0bBQ== - -p-cancelable@^1.0.0: - version "1.1.0" - resolved "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz" - integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== - -p-defer@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz" - integrity sha1-n26xgvbJqozXQwBKfU+WsZaw+ww= - -p-event@^1.0.0: - version "1.3.0" - resolved "https://registry.npmjs.org/p-event/-/p-event-1.3.0.tgz" - integrity sha1-jmtPT2XHK8W2/ii3XtqHT5akoIU= - dependencies: - p-timeout "^1.1.1" - -p-event@^2.1.0: - version "2.3.1" - resolved "https://registry.npmjs.org/p-event/-/p-event-2.3.1.tgz" - integrity sha512-NQCqOFhbpVTMX4qMe8PF8lbGtzZ+LCiN7pcNrb/413Na7+TRoe1xkKUzuWa/YEJdGQ0FvKtj35EEbDoVPO2kbA== - dependencies: - p-timeout "^2.0.1" - -p-finally@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz" - integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= - -p-is-promise@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz" - integrity sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4= - -p-is-promise@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz" - integrity sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg== - -p-limit@^1.1.0: - version "1.3.0" - resolved "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz" - integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== - dependencies: - p-try "^1.0.0" - -p-limit@^2.0.0, p-limit@^2.2.0: - version "2.2.1" - resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz" - integrity sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg== - dependencies: - p-try "^2.0.0" - -p-locate@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz" - integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= - dependencies: - p-limit "^1.1.0" - -p-locate@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz" - integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== - dependencies: - p-limit "^2.0.0" - -p-locate@^4.1.0: - version "4.1.0" - resolved "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz" - integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== - dependencies: - p-limit "^2.2.0" - -p-map-series@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/p-map-series/-/p-map-series-1.0.0.tgz" - integrity sha1-v5j+V1cFZYqeE1G++4WuTB8Hvco= - dependencies: - p-reduce "^1.0.0" - -p-pipe@^3.0.0: - version "3.1.0" - resolved "https://registry.npmjs.org/p-pipe/-/p-pipe-3.1.0.tgz" - integrity sha512-08pj8ATpzMR0Y80x50yJHn37NF6vjrqHutASaX5LiH5npS9XPvrUmscd9MF5R4fuYRHOxQR1FfMIlF7AzwoPqw== - -p-reduce@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/p-reduce/-/p-reduce-1.0.0.tgz" - integrity sha1-GMKw3ZNqRpClKfgjH1ig/bakffo= - -p-timeout@^1.1.1: - version "1.2.1" - resolved "https://registry.npmjs.org/p-timeout/-/p-timeout-1.2.1.tgz" - integrity sha1-XrOzU7f86Z8QGhA4iAuwVOu+o4Y= - dependencies: - p-finally "^1.0.0" - -p-timeout@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/p-timeout/-/p-timeout-2.0.1.tgz" - integrity sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA== - dependencies: - p-finally "^1.0.0" - -p-try@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz" - integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= - -p-try@^2.0.0: - version "2.2.0" - resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz" - integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== - -pako@^1.0.5, pako@~1.0.5: - version "1.0.10" - resolved "https://registry.npmjs.org/pako/-/pako-1.0.10.tgz" - integrity sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw== - -parallel-transform@^1.1.0: - version "1.2.0" - resolved "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz" - integrity sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg== - dependencies: - cyclist "^1.0.1" - inherits "^2.0.3" - readable-stream "^2.1.5" - -param-case@2.1.x: - version "2.1.1" - resolved "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz" - integrity sha1-35T9jPZTHs915r75oIWPvHK+Ikc= - dependencies: - no-case "^2.2.0" - -parent-module@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz" - integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== - dependencies: - callsites "^3.0.0" - -parents@~1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/parents/-/parents-1.0.1.tgz" - integrity sha1-/t1NK/GTp3dF/nHjcdc8MwfZx1E= - dependencies: - path-platform "~0.11.15" - -parse-asn1@^5.0.0: - version "5.1.5" - resolved "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz" - integrity sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ== - dependencies: - asn1.js "^4.0.0" - browserify-aes "^1.0.0" - create-hash "^1.1.0" - evp_bytestokey "^1.0.0" - pbkdf2 "^3.0.3" - safe-buffer "^5.1.1" - -parse-author@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/parse-author/-/parse-author-2.0.0.tgz" - integrity sha1-00YL8d3Q367tQtp1QkLmX7aEqB8= - dependencies: - author-regex "^1.0.0" - -parse-bmfont-ascii@^1.0.3: - version "1.0.6" - resolved "https://registry.npmjs.org/parse-bmfont-ascii/-/parse-bmfont-ascii-1.0.6.tgz" - integrity sha1-Eaw8P/WPfCAgqyJ2kHkQjU36AoU= - -parse-bmfont-binary@^1.0.5: - version "1.0.6" - resolved "https://registry.npmjs.org/parse-bmfont-binary/-/parse-bmfont-binary-1.0.6.tgz" - integrity sha1-0Di0dtPp3Z2x4RoLDlOiJ5K2kAY= - -parse-bmfont-xml@^1.1.4: - version "1.1.4" - resolved "https://registry.npmjs.org/parse-bmfont-xml/-/parse-bmfont-xml-1.1.4.tgz" - integrity sha512-bjnliEOmGv3y1aMEfREMBJ9tfL3WR0i0CKPj61DnSLaoxWR3nLrsQrEbCId/8rF4NyRF0cCqisSVXyQYWM+mCQ== - dependencies: - xml-parse-from-string "^1.0.0" - xml2js "^0.4.5" - -parse-filepath@^1.0.1: - version "1.0.2" - resolved "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz" - integrity sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE= - dependencies: - is-absolute "^1.0.0" - map-cache "^0.2.0" - path-root "^0.1.1" - -parse-headers@^2.0.0: - version "2.0.2" - resolved "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.2.tgz" - integrity sha512-/LypJhzFmyBIDYP9aDVgeyEb5sQfbfY5mnDq4hVhlQ69js87wXfmEI5V3xI6vvXasqebp0oCytYFLxsBVfCzSg== - dependencies: - for-each "^0.3.3" - string.prototype.trim "^1.1.2" - -parse-json@^2.2.0: - version "2.2.0" - resolved "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz" - integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck= - dependencies: - error-ex "^1.2.0" - -parse-json@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz" - integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= - dependencies: - error-ex "^1.3.1" - json-parse-better-errors "^1.0.1" - -parse-node-version@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz" - integrity sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA== - -parse-passwd@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz" - integrity sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY= - -parse5@5.1.0: - version "5.1.0" - resolved "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz" - integrity sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ== - -parseqs@0.0.5: - version "0.0.5" - resolved "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz" - integrity sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0= - dependencies: - better-assert "~1.0.0" - -parseuri@0.0.5: - version "0.0.5" - resolved "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz" - integrity sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo= - dependencies: - better-assert "~1.0.0" - -parseurl@~1.3.0, parseurl@~1.3.2: - version "1.3.3" - resolved "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz" - integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== - -pascalcase@^0.1.1: - version "0.1.1" - resolved "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz" - integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= - -path-browserify@0.0.1: - version "0.0.1" - resolved "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz" - integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ== - -path-dirname@^1.0.0: - version "1.0.2" - resolved "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz" - integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA= - -path-exists@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz" - integrity sha1-D+tsZPD8UY2adU3V77YscCJ2H0s= - dependencies: - pinkie-promise "^2.0.0" - -path-exists@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz" - integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= - -path-exists@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz" - integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== - -path-is-absolute@^1.0.0, path-is-absolute@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" - integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= - -path-is-inside@^1.0.1, path-is-inside@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz" - integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= - -path-key@^2.0.0, path-key@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz" - integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= - -path-key@^3.0.0, path-key@^3.1.0: - version "3.1.1" - resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" - integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== - -path-parse@^1.0.6: - version "1.0.6" - resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz" - integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== - -path-platform@~0.11.15: - version "0.11.15" - resolved "https://registry.npmjs.org/path-platform/-/path-platform-0.11.15.tgz" - integrity sha1-6GQhf3TDaFDwhSt43Hv31KVyG/I= - -path-root-regex@^0.1.0: - version "0.1.2" - resolved "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz" - integrity sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0= - -path-root@^0.1.1: - version "0.1.1" - resolved "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz" - integrity sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc= - dependencies: - path-root-regex "^0.1.0" - -path-starts-with@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/path-starts-with/-/path-starts-with-2.0.0.tgz" - integrity sha512-3UHTHbJz5+NLkPafFR+2ycJOjoc4WV2e9qCZCnm71zHiWaFrm1XniLVTkZXvaRgxr1xFh9JsTdicpH2yM03nLA== - -path-type@^1.0.0: - version "1.1.0" - resolved "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz" - integrity sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE= - dependencies: - graceful-fs "^4.1.2" - pify "^2.0.0" - pinkie-promise "^2.0.0" - -path-type@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz" - integrity sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM= - dependencies: - pify "^2.0.0" - -path-type@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz" - integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== - -pbkdf2@^3.0.3: - version "3.0.17" - resolved "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz" - integrity sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA== - dependencies: - create-hash "^1.1.2" - create-hmac "^1.1.4" - ripemd160 "^2.0.1" - safe-buffer "^5.0.1" - sha.js "^2.4.8" - -pend@~1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz" - integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= - -performance-now@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz" - integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= - -phin@^2.9.1: - version "2.9.3" - resolved "https://registry.npmjs.org/phin/-/phin-2.9.3.tgz" - integrity sha512-CzFr90qM24ju5f88quFC/6qohjC144rehe5n6DH900lgXmUe86+xCKc10ev56gRKC4/BkHUoG4uSiQgBiIXwDA== - -phonegap-plugin-mobile-accessibility@^1.0.5: - version "1.0.5" - resolved "https://registry.npmjs.org/phonegap-plugin-mobile-accessibility/-/phonegap-plugin-mobile-accessibility-1.0.5.tgz" - integrity sha1-lah1TRJ1CLxuGuJZpTznZYNurAM= - -picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.2.1: - version "2.2.2" - resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz" - integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== - -pify@^2.0.0, pify@^2.2.0, pify@^2.3.0: - version "2.3.0" - resolved "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz" - integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= - -pify@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz" - integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= - -pify@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz" - integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== - -pinkie-promise@^2.0.0: - version "2.0.1" - resolved "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz" - integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= - dependencies: - pinkie "^2.0.0" - -pinkie@^2.0.0: - version "2.0.4" - resolved "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz" - integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= - -pixelmatch@^4.0.2: - version "4.0.2" - resolved "https://registry.npmjs.org/pixelmatch/-/pixelmatch-4.0.2.tgz" - integrity sha1-j0fc7FARtHe2fbA8JDvB8wheiFQ= - dependencies: - pngjs "^3.0.0" - -pkg-dir@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz" - integrity sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw== - dependencies: - find-up "^3.0.0" - -pkginfo@0.3.x: - version "0.3.1" - resolved "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.1.tgz" - integrity sha1-Wyn2qB9wcXFC4J52W76rl7T4HiE= - -plist@^3.0.0: - version "3.0.1" - resolved "https://registry.npmjs.org/plist/-/plist-3.0.1.tgz" - integrity sha512-GpgvHHocGRyQm74b6FWEZZVRroHKE1I0/BTjAmySaohK+cUn+hZpbqXkc3KWgW3gQYkqcQej35FohcT0FRlkRQ== - dependencies: - base64-js "^1.2.3" - xmlbuilder "^9.0.7" - xmldom "0.1.x" - -plist@^3.0.1, plist@^3.0.5: - version "3.0.5" - resolved "https://registry.yarnpkg.com/plist/-/plist-3.0.5.tgz#2cbeb52d10e3cdccccf0c11a63a85d830970a987" - integrity sha512-83vX4eYdQp3vP9SxuYgEM/G/pJQqLUz/V/xzPrzruLs7fz7jxGQ1msZ/mg1nwZxUSuOp4sb+/bEIbRrbzZRxDA== - dependencies: - base64-js "^1.5.1" - xmlbuilder "^9.0.7" - -plugin-error@1.0.1, plugin-error@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz" - integrity sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA== - dependencies: - ansi-colors "^1.0.1" - arr-diff "^4.0.0" - arr-union "^3.1.0" - extend-shallow "^3.0.2" - -plugin-error@^0.1.2: - version "0.1.2" - resolved "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz" - integrity sha1-O5uzM1zPAPQl4HQ34ZJ2ln2kes4= - dependencies: - ansi-cyan "^0.1.1" - ansi-red "^0.1.1" - arr-diff "^1.0.1" - arr-union "^2.0.1" - extend-shallow "^1.1.2" - -plur@^3.0.1: - version "3.1.1" - resolved "https://registry.npmjs.org/plur/-/plur-3.1.1.tgz" - integrity sha512-t1Ax8KUvV3FFII8ltczPn2tJdjqbd1sIzu6t4JL7nQ3EyeL/lTrj5PWKb06ic5/6XYDr65rQ4uzQEGN70/6X5w== - dependencies: - irregular-plurals "^2.0.0" - -pluralize@^1.2.1: - version "1.2.1" - resolved "https://registry.npmjs.org/pluralize/-/pluralize-1.2.1.tgz" - integrity sha1-0aIUg/0iu0HlihL6NCGCMUCJfEU= - -pn@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz" - integrity sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA== - -pngjs@^3.0.0, pngjs@^3.3.3: - version "3.4.0" - resolved "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz" - integrity sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w== - -pngquant-bin@^5.0.2: - version "5.0.2" - resolved "https://registry.npmjs.org/pngquant-bin/-/pngquant-bin-5.0.2.tgz" - integrity sha512-OLdT+4JZx5BqE1CFJkrvomYV0aSsv6x2Bba+aWaVc0PMfWlE+ZByNKYAdKeIqsM4uvW1HOSEHnf8KcOnykPNxA== - dependencies: - bin-build "^3.0.0" - bin-wrapper "^4.0.1" - execa "^0.10.0" - logalot "^2.0.0" - -pngquant-bin@^6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/pngquant-bin/-/pngquant-bin-6.0.0.tgz" - integrity sha512-oXWAS9MQ9iiDAJRdAZ9KO1mC5UwhzKkJsmetiu0iqIjJuW7JsuLhmc4JdRm7uJkIWRzIAou/Vq2VcjfJwz30Ow== - dependencies: - bin-build "^3.0.0" - bin-wrapper "^4.0.1" - execa "^4.0.0" - logalot "^2.0.0" - -portscanner@2.1.1: - version "2.1.1" - resolved "https://registry.npmjs.org/portscanner/-/portscanner-2.1.1.tgz" - integrity sha1-6rtAnk3iSVD1oqUW01rnaTQ/u5Y= - dependencies: - async "1.5.2" - is-number-like "^1.0.3" - -posix-character-classes@^0.1.0: - version "0.1.1" - resolved "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz" - integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= - -postcss-assets@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/postcss-assets/-/postcss-assets-5.0.0.tgz" - integrity sha1-9yHQfTOWBftYQU6fac8FQBxU5wk= - dependencies: - assets "^3.0.0" - bluebird "^3.5.0" - postcss "^6.0.10" - postcss-functions "^3.0.0" - -postcss-attribute-case-insensitive@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-4.0.1.tgz" - integrity sha512-L2YKB3vF4PetdTIthQVeT+7YiSzMoNMLLYxPXXppOOP7NoazEAy45sh2LvJ8leCQjfBcfkYQs8TtCcQjeZTp8A== - dependencies: - postcss "^7.0.2" - postcss-selector-parser "^5.0.0" - -postcss-calc@^7.0.1: - version "7.0.1" - resolved "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.1.tgz" - integrity sha512-oXqx0m6tb4N3JGdmeMSc/i91KppbYsFZKdH0xMOqK8V1rJlzrKlTdokz8ozUXLVejydRN6u2IddxpcijRj2FqQ== - dependencies: - css-unit-converter "^1.1.1" - postcss "^7.0.5" - postcss-selector-parser "^5.0.0-rc.4" - postcss-value-parser "^3.3.1" - -postcss-color-functional-notation@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-2.0.1.tgz" - integrity sha512-ZBARCypjEDofW4P6IdPVTLhDNXPRn8T2s1zHbZidW6rPaaZvcnCS2soYFIQJrMZSxiePJ2XIYTlcb2ztr/eT2g== - dependencies: - postcss "^7.0.2" - postcss-values-parser "^2.0.0" - -postcss-color-gray@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/postcss-color-gray/-/postcss-color-gray-5.0.0.tgz" - integrity sha512-q6BuRnAGKM/ZRpfDascZlIZPjvwsRye7UDNalqVz3s7GDxMtqPY6+Q871liNxsonUw8oC61OG+PSaysYpl1bnw== - dependencies: - "@csstools/convert-colors" "^1.4.0" - postcss "^7.0.5" - postcss-values-parser "^2.0.0" - -postcss-color-hex-alpha@^5.0.3: - version "5.0.3" - resolved "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-5.0.3.tgz" - integrity sha512-PF4GDel8q3kkreVXKLAGNpHKilXsZ6xuu+mOQMHWHLPNyjiUBOr75sp5ZKJfmv1MCus5/DWUGcK9hm6qHEnXYw== - dependencies: - postcss "^7.0.14" - postcss-values-parser "^2.0.1" - -postcss-color-mod-function@^3.0.3: - version "3.0.3" - resolved "https://registry.npmjs.org/postcss-color-mod-function/-/postcss-color-mod-function-3.0.3.tgz" - integrity sha512-YP4VG+xufxaVtzV6ZmhEtc+/aTXH3d0JLpnYfxqTvwZPbJhWqp8bSY3nfNzNRFLgB4XSaBA82OE4VjOOKpCdVQ== - dependencies: - "@csstools/convert-colors" "^1.4.0" - postcss "^7.0.2" - postcss-values-parser "^2.0.0" - -postcss-color-rebeccapurple@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-4.0.1.tgz" - integrity sha512-aAe3OhkS6qJXBbqzvZth2Au4V3KieR5sRQ4ptb2b2O8wgvB3SJBsdG+jsn2BZbbwekDG8nTfcCNKcSfe/lEy8g== - dependencies: - postcss "^7.0.2" - postcss-values-parser "^2.0.0" - -postcss-colormin@^4.0.3: - version "4.0.3" - resolved "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-4.0.3.tgz" - integrity sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw== - dependencies: - browserslist "^4.0.0" - color "^3.0.0" - has "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-convert-values@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz" - integrity sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ== - dependencies: - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-critical-split@^2.5.3: - version "2.5.3" - resolved "https://registry.npmjs.org/postcss-critical-split/-/postcss-critical-split-2.5.3.tgz" - integrity sha512-FDG+evU4RBGM9/LQ5nCktzFKjYH2O/SLollJwtrdGagXXbMvk620Bc9o8WpqHJnu573uxVkx9lhob1HZvSWhZg== - dependencies: - merge "^1.2.0" - postcss "^6.0.1" - -postcss-custom-media@^7.0.8: - version "7.0.8" - resolved "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-7.0.8.tgz" - integrity sha512-c9s5iX0Ge15o00HKbuRuTqNndsJUbaXdiNsksnVH8H4gdc+zbLzr/UasOwNG6CTDpLFekVY4672eWdiiWu2GUg== - dependencies: - postcss "^7.0.14" - -postcss-custom-properties@^8.0.11: - version "8.0.11" - resolved "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-8.0.11.tgz" - integrity sha512-nm+o0eLdYqdnJ5abAJeXp4CEU1c1k+eB2yMCvhgzsds/e0umabFrN6HoTy/8Q4K5ilxERdl/JD1LO5ANoYBeMA== - dependencies: - postcss "^7.0.17" - postcss-values-parser "^2.0.1" - -postcss-custom-selectors@^5.1.2: - version "5.1.2" - resolved "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-5.1.2.tgz" - integrity sha512-DSGDhqinCqXqlS4R7KGxL1OSycd1lydugJ1ky4iRXPHdBRiozyMHrdu0H3o7qNOCiZwySZTUI5MV0T8QhCLu+w== - dependencies: - postcss "^7.0.2" - postcss-selector-parser "^5.0.0-rc.3" - -postcss-dir-pseudo-class@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-5.0.0.tgz" - integrity sha512-3pm4oq8HYWMZePJY+5ANriPs3P07q+LW6FAdTlkFH2XqDdP4HeeJYMOzn0HYLhRSjBO3fhiqSwwU9xEULSrPgw== - dependencies: - postcss "^7.0.2" - postcss-selector-parser "^5.0.0-rc.3" - -postcss-discard-comments@^4.0.2: - version "4.0.2" - resolved "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz" - integrity sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg== - dependencies: - postcss "^7.0.0" - -postcss-discard-duplicates@^4.0.2: - version "4.0.2" - resolved "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz" - integrity sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ== - dependencies: - postcss "^7.0.0" - -postcss-discard-empty@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz" - integrity sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w== - dependencies: - postcss "^7.0.0" - -postcss-discard-overridden@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz" - integrity sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg== - dependencies: - postcss "^7.0.0" - -postcss-discard-unused@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-4.0.1.tgz" - integrity sha512-/3vq4LU0bLH2Lj4NYN7BTf2caly0flUB7Xtrk9a5K3yLuXMkHMqMO/x3sDq8W2b1eQFSCyY0IVz2L+0HP8kUUA== - dependencies: - postcss "^7.0.0" - postcss-selector-parser "^3.0.0" - uniqs "^2.0.0" - -postcss-double-position-gradients@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-1.0.0.tgz" - integrity sha512-G+nV8EnQq25fOI8CH/B6krEohGWnF5+3A6H/+JEpOncu5dCnkS1QQ6+ct3Jkaepw1NGVqqOZH6lqrm244mCftA== - dependencies: - postcss "^7.0.5" - postcss-values-parser "^2.0.0" - -postcss-env-function@^2.0.2: - version "2.0.2" - resolved "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-2.0.2.tgz" - integrity sha512-rwac4BuZlITeUbiBq60h/xbLzXY43qOsIErngWa4l7Mt+RaSkT7QBjXVGTcBHupykkblHMDrBFh30zchYPaOUw== - dependencies: - postcss "^7.0.2" - postcss-values-parser "^2.0.0" - -postcss-focus-visible@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-4.0.0.tgz" - integrity sha512-Z5CkWBw0+idJHSV6+Bgf2peDOFf/x4o+vX/pwcNYrWpXFrSfTkQ3JQ1ojrq9yS+upnAlNRHeg8uEwFTgorjI8g== - dependencies: - postcss "^7.0.2" - -postcss-focus-within@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-3.0.0.tgz" - integrity sha512-W0APui8jQeBKbCGZudW37EeMCjDeVxKgiYfIIEo8Bdh5SpB9sxds/Iq8SEuzS0Q4YFOlG7EPFulbbxujpkrV2w== - dependencies: - postcss "^7.0.2" - -postcss-font-variant@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-4.0.0.tgz" - integrity sha512-M8BFYKOvCrI2aITzDad7kWuXXTm0YhGdP9Q8HanmN4EF1Hmcgs1KK5rSHylt/lUJe8yLxiSwWAHdScoEiIxztg== - dependencies: - postcss "^7.0.2" - -postcss-functions@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/postcss-functions/-/postcss-functions-3.0.0.tgz" - integrity sha1-DpTQFERwCkgd4g3k1V+yZAVkJQ4= - dependencies: - glob "^7.1.2" - object-assign "^4.1.1" - postcss "^6.0.9" - postcss-value-parser "^3.3.0" - -postcss-gap-properties@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-2.0.0.tgz" - integrity sha512-QZSqDaMgXCHuHTEzMsS2KfVDOq7ZFiknSpkrPJY6jmxbugUPTuSzs/vuE5I3zv0WAS+3vhrlqhijiprnuQfzmg== - dependencies: - postcss "^7.0.2" - -postcss-image-set-function@^3.0.1: - version "3.0.1" - resolved "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-3.0.1.tgz" - integrity sha512-oPTcFFip5LZy8Y/whto91L9xdRHCWEMs3e1MdJxhgt4jy2WYXfhkng59fH5qLXSCPN8k4n94p1Czrfe5IOkKUw== - dependencies: - postcss "^7.0.2" - postcss-values-parser "^2.0.0" - -postcss-initial@^3.0.0: - version "3.0.1" - resolved "https://registry.npmjs.org/postcss-initial/-/postcss-initial-3.0.1.tgz" - integrity sha512-I2Sz83ZSHybMNh02xQDK609lZ1/QOyYeuizCjzEhlMgeV/HcDJapQiH4yTqLjZss0X6/6VvKFXUeObaHpJoINw== - dependencies: - lodash.template "^4.5.0" - postcss "^7.0.2" - -postcss-lab-function@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-2.0.1.tgz" - integrity sha512-whLy1IeZKY+3fYdqQFuDBf8Auw+qFuVnChWjmxm/UhHWqNHZx+B99EwxTvGYmUBqe3Fjxs4L1BoZTJmPu6usVg== - dependencies: - "@csstools/convert-colors" "^1.4.0" - postcss "^7.0.2" - postcss-values-parser "^2.0.0" - -postcss-load-config@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.1.0.tgz" - integrity sha512-4pV3JJVPLd5+RueiVVB+gFOAa7GWc25XQcMp86Zexzke69mKf6Nx9LRcQywdz7yZI9n1udOxmLuAwTBypypF8Q== - dependencies: - cosmiconfig "^5.0.0" - import-cwd "^2.0.0" - -postcss-logical@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/postcss-logical/-/postcss-logical-3.0.0.tgz" - integrity sha512-1SUKdJc2vuMOmeItqGuNaC+N8MzBWFWEkAnRnLpFYj1tGGa7NqyVBujfRtgNa2gXR+6RkGUiB2O5Vmh7E2RmiA== - dependencies: - postcss "^7.0.2" - -postcss-media-minmax@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-4.0.0.tgz" - integrity sha512-fo9moya6qyxsjbFAYl97qKO9gyre3qvbMnkOZeZwlsW6XYFsvs2DMGDlchVLfAd8LHPZDxivu/+qW2SMQeTHBw== - dependencies: - postcss "^7.0.2" - -postcss-merge-idents@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-4.0.1.tgz" - integrity sha512-43S/VNdF6II0NZ31YxcvNYq4gfURlPAAsJW/z84avBXQCaP4I4qRHUH18slW/SOlJbcxxCobflPNUApYDddS7A== - dependencies: - cssnano-util-same-parent "^4.0.0" - has "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-merge-longhand@^4.0.11: - version "4.0.11" - resolved "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz" - integrity sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw== - dependencies: - css-color-names "0.0.4" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - stylehacks "^4.0.0" - -postcss-merge-rules@^4.0.3: - version "4.0.3" - resolved "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz" - integrity sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ== - dependencies: - browserslist "^4.0.0" - caniuse-api "^3.0.0" - cssnano-util-same-parent "^4.0.0" - postcss "^7.0.0" - postcss-selector-parser "^3.0.0" - vendors "^1.0.0" - -postcss-minify-font-values@^4.0.2: - version "4.0.2" - resolved "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz" - integrity sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg== - dependencies: - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-minify-gradients@^4.0.2: - version "4.0.2" - resolved "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz" - integrity sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q== - dependencies: - cssnano-util-get-arguments "^4.0.0" - is-color-stop "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-minify-params@^4.0.2: - version "4.0.2" - resolved "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz" - integrity sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg== - dependencies: - alphanum-sort "^1.0.0" - browserslist "^4.0.0" - cssnano-util-get-arguments "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - uniqs "^2.0.0" - -postcss-minify-selectors@^4.0.2: - version "4.0.2" - resolved "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz" - integrity sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g== - dependencies: - alphanum-sort "^1.0.0" - has "^1.0.0" - postcss "^7.0.0" - postcss-selector-parser "^3.0.0" - -postcss-nesting@^7.0.0: - version "7.0.1" - resolved "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-7.0.1.tgz" - integrity sha512-FrorPb0H3nuVq0Sff7W2rnc3SmIcruVC6YwpcS+k687VxyxO33iE1amna7wHuRVzM8vfiYofXSBHNAZ3QhLvYg== - dependencies: - postcss "^7.0.2" - -postcss-normalize-charset@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz" - integrity sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g== - dependencies: - postcss "^7.0.0" - -postcss-normalize-display-values@^4.0.2: - version "4.0.2" - resolved "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz" - integrity sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ== - dependencies: - cssnano-util-get-match "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-positions@^4.0.2: - version "4.0.2" - resolved "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz" - integrity sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA== - dependencies: - cssnano-util-get-arguments "^4.0.0" - has "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-repeat-style@^4.0.2: - version "4.0.2" - resolved "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz" - integrity sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q== - dependencies: - cssnano-util-get-arguments "^4.0.0" - cssnano-util-get-match "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-string@^4.0.2: - version "4.0.2" - resolved "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz" - integrity sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA== - dependencies: - has "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-timing-functions@^4.0.2: - version "4.0.2" - resolved "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz" - integrity sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A== - dependencies: - cssnano-util-get-match "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-unicode@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz" - integrity sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg== - dependencies: - browserslist "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-url@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz" - integrity sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA== - dependencies: - is-absolute-url "^2.0.0" - normalize-url "^3.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-whitespace@^4.0.2: - version "4.0.2" - resolved "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz" - integrity sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA== - dependencies: - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-ordered-values@^4.1.2: - version "4.1.2" - resolved "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz" - integrity sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw== - dependencies: - cssnano-util-get-arguments "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-overflow-shorthand@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-2.0.0.tgz" - integrity sha512-aK0fHc9CBNx8jbzMYhshZcEv8LtYnBIRYQD5i7w/K/wS9c2+0NSR6B3OVMu5y0hBHYLcMGjfU+dmWYNKH0I85g== - dependencies: - postcss "^7.0.2" - -postcss-page-break@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-2.0.0.tgz" - integrity sha512-tkpTSrLpfLfD9HvgOlJuigLuk39wVTbbd8RKcy8/ugV2bNBUW3xU+AIqyxhDrQr1VUj1RmyJrBn1YWrqUm9zAQ== - dependencies: - postcss "^7.0.2" - -postcss-place@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/postcss-place/-/postcss-place-4.0.1.tgz" - integrity sha512-Zb6byCSLkgRKLODj/5mQugyuj9bvAAw9LqJJjgwz5cYryGeXfFZfSXoP1UfveccFmeq0b/2xxwcTEVScnqGxBg== - dependencies: - postcss "^7.0.2" - postcss-values-parser "^2.0.0" - -postcss-preset-env@^6.5.0: - version "6.7.0" - resolved "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-6.7.0.tgz" - integrity sha512-eU4/K5xzSFwUFJ8hTdTQzo2RBLbDVt83QZrAvI07TULOkmyQlnYlpwep+2yIK+K+0KlZO4BvFcleOCCcUtwchg== - dependencies: - autoprefixer "^9.6.1" - browserslist "^4.6.4" - caniuse-lite "^1.0.30000981" - css-blank-pseudo "^0.1.4" - css-has-pseudo "^0.10.0" - css-prefers-color-scheme "^3.1.1" - cssdb "^4.4.0" - postcss "^7.0.17" - postcss-attribute-case-insensitive "^4.0.1" - postcss-color-functional-notation "^2.0.1" - postcss-color-gray "^5.0.0" - postcss-color-hex-alpha "^5.0.3" - postcss-color-mod-function "^3.0.3" - postcss-color-rebeccapurple "^4.0.1" - postcss-custom-media "^7.0.8" - postcss-custom-properties "^8.0.11" - postcss-custom-selectors "^5.1.2" - postcss-dir-pseudo-class "^5.0.0" - postcss-double-position-gradients "^1.0.0" - postcss-env-function "^2.0.2" - postcss-focus-visible "^4.0.0" - postcss-focus-within "^3.0.0" - postcss-font-variant "^4.0.0" - postcss-gap-properties "^2.0.0" - postcss-image-set-function "^3.0.1" - postcss-initial "^3.0.0" - postcss-lab-function "^2.0.1" - postcss-logical "^3.0.0" - postcss-media-minmax "^4.0.0" - postcss-nesting "^7.0.0" - postcss-overflow-shorthand "^2.0.0" - postcss-page-break "^2.0.0" - postcss-place "^4.0.1" - postcss-pseudo-class-any-link "^6.0.0" - postcss-replace-overflow-wrap "^3.0.0" - postcss-selector-matches "^4.0.0" - postcss-selector-not "^4.0.0" - -postcss-pseudo-class-any-link@^6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-6.0.0.tgz" - integrity sha512-lgXW9sYJdLqtmw23otOzrtbDXofUdfYzNm4PIpNE322/swES3VU9XlXHeJS46zT2onFO7V1QFdD4Q9LiZj8mew== - dependencies: - postcss "^7.0.2" - postcss-selector-parser "^5.0.0-rc.3" - -postcss-reduce-idents@^4.0.2: - version "4.0.2" - resolved "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-4.0.2.tgz" - integrity sha512-Tz70Ri10TclPoCtFfftjFVddx3fZGUkr0dEDbIEfbYhFUOFQZZ77TEqRrU0e6TvAvF+Wa5VVzYTpFpq0uwFFzw== - dependencies: - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-reduce-initial@^4.0.3: - version "4.0.3" - resolved "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz" - integrity sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA== - dependencies: - browserslist "^4.0.0" - caniuse-api "^3.0.0" - has "^1.0.0" - postcss "^7.0.0" - -postcss-reduce-transforms@^4.0.2: - version "4.0.2" - resolved "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz" - integrity sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg== - dependencies: - cssnano-util-get-match "^4.0.0" - has "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-replace-overflow-wrap@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-3.0.0.tgz" - integrity sha512-2T5hcEHArDT6X9+9dVSPQdo7QHzG4XKclFT8rU5TzJPDN7RIRTbO9c4drUISOVemLj03aezStHCR2AIcr8XLpw== - dependencies: - postcss "^7.0.2" - -postcss-round-subpixels@^1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/postcss-round-subpixels/-/postcss-round-subpixels-1.2.0.tgz" - integrity sha1-4h1qxZUuGF+b3ACLlPAE/lCdChE= - dependencies: - postcss "^5.0.2" - postcss-value-parser "^3.1.2" - -postcss-selector-matches@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/postcss-selector-matches/-/postcss-selector-matches-4.0.0.tgz" - integrity sha512-LgsHwQR/EsRYSqlwdGzeaPKVT0Ml7LAT6E75T8W8xLJY62CE4S/l03BWIt3jT8Taq22kXP08s2SfTSzaraoPww== - dependencies: - balanced-match "^1.0.0" - postcss "^7.0.2" - -postcss-selector-not@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-4.0.0.tgz" - integrity sha512-W+bkBZRhqJaYN8XAnbbZPLWMvZD1wKTu0UxtFKdhtGjWYmxhkUneoeOhRJKdAE5V7ZTlnbHfCR+6bNwK9e1dTQ== - dependencies: - balanced-match "^1.0.0" - postcss "^7.0.2" - -postcss-selector-parser@^3.0.0: - version "3.1.1" - resolved "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz" - integrity sha1-T4dfSvsMllc9XPTXQBGu4lCn6GU= - dependencies: - dot-prop "^4.1.1" - indexes-of "^1.0.1" - uniq "^1.0.1" - -postcss-selector-parser@^5.0.0, postcss-selector-parser@^5.0.0-rc.3, postcss-selector-parser@^5.0.0-rc.4: - version "5.0.0" - resolved "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz" - integrity sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ== - dependencies: - cssesc "^2.0.0" - indexes-of "^1.0.1" - uniq "^1.0.1" - -postcss-svgo@^4.0.2: - version "4.0.2" - resolved "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-4.0.2.tgz" - integrity sha512-C6wyjo3VwFm0QgBy+Fu7gCYOkCmgmClghO+pjcxvrcBKtiKt0uCF+hvbMO1fyv5BMImRK90SMb+dwUnfbGd+jw== - dependencies: - is-svg "^3.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - svgo "^1.0.0" - -postcss-unique-selectors@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz" - integrity sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg== - dependencies: - alphanum-sort "^1.0.0" - postcss "^7.0.0" - uniqs "^2.0.0" - -postcss-unprefix@^2.1.3: - version "2.1.4" - resolved "https://registry.npmjs.org/postcss-unprefix/-/postcss-unprefix-2.1.4.tgz" - integrity sha512-s+muBiGIMx3RvgPTtPBnSrfvIBHJ2Zx16QZf/VDB/sAxdYP6FIzci8d1gLh0+9psu5W6zVtCbU5micNt6Zh3cg== - dependencies: - autoprefixer "^9.4.3" - known-css-properties "^0.11.0" - normalize-range "^0.1.2" - postcss-selector-parser "^5.0.0" - postcss-value-parser "^3.3.1" - pseudo-classes "^1.0.0" - pseudo-elements "^1.1.0" - -postcss-value-parser@^3.0.0, postcss-value-parser@^3.1.2, postcss-value-parser@^3.3.0, postcss-value-parser@^3.3.1: - version "3.3.1" - resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz" - integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ== - -postcss-value-parser@^4.0.0: - version "4.0.2" - resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.0.2.tgz" - integrity sha512-LmeoohTpp/K4UiyQCwuGWlONxXamGzCMtFxLq4W1nZVGIQLYvMCJx3yAF9qyyuFpflABI9yVdtJAqbihOsCsJQ== - -postcss-values-parser@^2.0.0, postcss-values-parser@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/postcss-values-parser/-/postcss-values-parser-2.0.1.tgz" - integrity sha512-2tLuBsA6P4rYTNKCXYG/71C7j1pU6pK503suYOmn4xYrQIzW+opD+7FAFNuGSdZC/3Qfy334QbeMu7MEb8gOxg== - dependencies: - flatten "^1.0.2" - indexes-of "^1.0.1" - uniq "^1.0.1" - -postcss-zindex@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-4.0.1.tgz" - integrity sha512-d/8BlQcUdEugZNRM9AdCA2V4fqREUtn/wcixLN3L6ITgc2P/FMcVVYz8QZkhItWT9NB5qr8wuN2dJCE4/+dlrA== - dependencies: - has "^1.0.0" - postcss "^7.0.0" - uniqs "^2.0.0" - -postcss@>=5.0.0: - version "8.1.7" - resolved "https://registry.npmjs.org/postcss/-/postcss-8.1.7.tgz" - integrity sha512-llCQW1Pz4MOPwbZLmOddGM9eIJ8Bh7SZ2Oj5sxZva77uVaotYDsYTch1WBTNu7fUY0fpWp0fdt7uW40D4sRiiQ== - dependencies: - colorette "^1.2.1" - line-column "^1.0.2" - nanoid "^3.1.16" - source-map "^0.6.1" - -postcss@^5.0.2: - version "5.2.18" - resolved "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz" - integrity sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg== - dependencies: - chalk "^1.1.3" - js-base64 "^2.1.9" - source-map "^0.5.6" - supports-color "^3.2.3" - -postcss@^6.0.1, postcss@^6.0.10, postcss@^6.0.9: - version "6.0.23" - resolved "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz" - integrity sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag== - dependencies: - chalk "^2.4.1" - source-map "^0.6.1" - supports-color "^5.4.0" - -postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.17, postcss@^7.0.2, postcss@^7.0.5, postcss@^7.0.6: - version "7.0.18" - resolved "https://registry.npmjs.org/postcss/-/postcss-7.0.18.tgz" - integrity sha512-/7g1QXXgegpF+9GJj4iN7ChGF40sYuGYJ8WZu8DZWnmhQ/G36hfdk3q9LBJmoK+lZ+yzZ5KYpOoxq7LF1BxE8g== - dependencies: - chalk "^2.4.2" - source-map "^0.6.1" - supports-color "^6.1.0" - -prelude-ls@~1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz" - integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= - -prepend-http@^1.0.1: - version "1.0.4" - resolved "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz" - integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw= - -prepend-http@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz" - integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= - -pretty-bytes@^5.3.0: - version "5.3.0" - resolved "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.3.0.tgz" - integrity sha512-hjGrh+P926p4R4WbaB6OckyRtO0F0/lQBiT+0gnxjV+5kjPBrfVBFCsCLbMqVQeydvIoouYTCmmEURiH3R1Bdg== - -pretty-hrtime@^0.2.0: - version "0.2.2" - resolved "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-0.2.2.tgz" - integrity sha1-1P2INR46R0H4Fzr31qS4RvmJXAA= - -pretty-hrtime@^1.0.0: - version "1.0.3" - resolved "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz" - integrity sha1-t+PqQkNaTJsnWdmeDyAesZWALuE= - -private@^0.1.6, private@^0.1.8, private@~0.1.5: - version "0.1.8" - resolved "https://registry.npmjs.org/private/-/private-0.1.8.tgz" - integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg== - -process-nextick-args@^2.0.0, process-nextick-args@~2.0.0: - version "2.0.1" - resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz" - integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== - -process@^0.11.10: - version "0.11.10" - resolved "https://registry.npmjs.org/process/-/process-0.11.10.tgz" - integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= - -process@~0.5.1: - version "0.5.2" - resolved "https://registry.npmjs.org/process/-/process-0.5.2.tgz" - integrity sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8= - -progress@^1.1.8: - version "1.1.8" - resolved "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz" - integrity sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74= - -progress@^2.0.0, progress@^2.0.3: - version "2.0.3" - resolved "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz" - integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== - -promise-inflight@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz" - integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM= - -promise-polyfill@^8.1.0: - version "8.1.3" - resolved "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-8.1.3.tgz" - integrity sha512-MG5r82wBzh7pSKDRa9y+vllNHz3e3d4CNj1PQE4BQYxLme0gKYYBm9YENq+UkEikyZ0XbiGWxYlVw3Rl9O/U8g== - -proto-list@~1.2.1: - version "1.2.4" - resolved "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz" - integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk= - -proxy-middleware@^0.5.0: - version "0.5.1" - resolved "https://registry.npmjs.org/proxy-middleware/-/proxy-middleware-0.5.1.tgz" - integrity sha1-2iTV1Ywd3xPa0jfH7KUDhJ6uqQM= - -prr@~1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz" - integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY= - -pseudo-classes@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/pseudo-classes/-/pseudo-classes-1.0.0.tgz" - integrity sha1-YKabZzlcNv8RnE0chuGYF4Uga5Y= - -pseudo-elements@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/pseudo-elements/-/pseudo-elements-1.1.0.tgz" - integrity sha1-m6bdisPOHz19NtQ1WqPijQg5Hyg= - -pseudomap@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz" - integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= - -psl@^1.1.24, psl@^1.1.28: - version "1.4.0" - resolved "https://registry.npmjs.org/psl/-/psl-1.4.0.tgz" - integrity sha512-HZzqCGPecFLyoRj5HLfuDSKYTJkAfB5thKBIkRHtGjWwY7p1dAyveIbXIq4tO0KYfDF2tHqPUgY9SDnGm00uFw== - -public-encrypt@^4.0.0: - version "4.0.3" - resolved "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz" - integrity sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q== - dependencies: - bn.js "^4.1.0" - browserify-rsa "^4.0.0" - create-hash "^1.1.0" - parse-asn1 "^5.0.0" - randombytes "^2.0.1" - safe-buffer "^5.1.2" - -pump@^2.0.0: - version "2.0.1" - resolved "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz" - integrity sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA== - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - -pump@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz" - integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - -pumpify@^1.3.3, pumpify@^1.3.5: - version "1.5.1" - resolved "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz" - integrity sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ== - dependencies: - duplexify "^3.6.0" - inherits "^2.0.3" - pump "^2.0.0" - -punycode@1.3.2: - version "1.3.2" - resolved "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz" - integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= - -punycode@^1.2.4, punycode@^1.4.1: - version "1.4.1" - resolved "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz" - integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= - -punycode@^2.1.0, punycode@^2.1.1: - version "2.1.1" - resolved "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz" - integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== - -q@^1.1.2: - version "1.5.1" - resolved "https://registry.npmjs.org/q/-/q-1.5.1.tgz" - integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= - -qs@2.2.4: - version "2.2.4" - resolved "https://registry.npmjs.org/qs/-/qs-2.2.4.tgz" - integrity sha1-Lp+800tUDjQhySTs0B6QqpdTGcg= - -qs@6.2.3: - version "6.2.3" - resolved "https://registry.npmjs.org/qs/-/qs-6.2.3.tgz" - integrity sha1-HPyyXBCpsrSDBT/zn138kjOQjP4= - -qs@~2.2.3: - version "2.2.5" - resolved "https://registry.npmjs.org/qs/-/qs-2.2.5.tgz" - integrity sha1-EIirr53MCuWuRbcJ5sa1iIsjkjw= - -qs@~6.5.2: - version "6.5.2" - resolved "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz" - integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== - -query-string@^5.0.1: - version "5.1.1" - resolved "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz" - integrity sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw== - dependencies: - decode-uri-component "^0.2.0" - object-assign "^4.1.0" - strict-uri-encode "^1.0.0" - -query-string@^6.8.1: - version "6.8.3" - resolved "https://registry.npmjs.org/query-string/-/query-string-6.8.3.tgz" - integrity sha512-llcxWccnyaWlODe7A9hRjkvdCKamEKTh+wH8ITdTc3OhchaqUZteiSCX/2ablWHVrkVIe04dntnaZJ7BdyW0lQ== - dependencies: - decode-uri-component "^0.2.0" - split-on-first "^1.0.0" - strict-uri-encode "^2.0.0" - -querystring-es3@^0.2.0: - version "0.2.1" - resolved "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz" - integrity sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM= - -querystring@0.2.0: - version "0.2.0" - resolved "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz" - integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= - -randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz" - integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== - dependencies: - safe-buffer "^5.1.0" - -randomfill@^1.0.3: - version "1.0.4" - resolved "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz" - integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw== - dependencies: - randombytes "^2.0.5" - safe-buffer "^5.1.0" - -range-parser@~1.2.0: - version "1.2.1" - resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz" - integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== - -raw-body@1.3.0: - version "1.3.0" - resolved "https://registry.npmjs.org/raw-body/-/raw-body-1.3.0.tgz" - integrity sha1-l4IwoValVI9C7vFN4i0PT2EAg9E= - dependencies: - bytes "1" - iconv-lite "0.4.4" - -raw-body@^2.3.2: - version "2.4.1" - resolved "https://registry.npmjs.org/raw-body/-/raw-body-2.4.1.tgz" - integrity sha512-9WmIKF6mkvA0SLmA2Knm9+qj89e+j1zqgyn8aXGd7+nAduPoqgI9lO57SAZNn/Byzo5P7JhXTyg9PzaJbH73bA== - dependencies: - bytes "3.1.0" - http-errors "1.7.3" - iconv-lite "0.4.24" - unpipe "1.0.0" - -raw-loader@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-4.0.2.tgz#1aac6b7d1ad1501e66efdac1522c73e59a584eb6" - integrity sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA== - dependencies: - loader-utils "^2.0.0" - schema-utils "^3.0.0" - -rcedit@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/rcedit/-/rcedit-3.0.1.tgz#ae21b43e49c075f4d84df1929832a12c302f3c90" - integrity sha512-XM0Jv40/y4hVAqj/MO70o/IWs4uOsaSoo2mLyk3klFDW+SStLnCtzuQu+1OBTIMGlM8CvaK9ftlYCp6DJ+cMsw== - dependencies: - cross-spawn-windows-exe "^1.1.0" - -rcfinder@^0.1.6: - version "0.1.9" - resolved "https://registry.npmjs.org/rcfinder/-/rcfinder-0.1.9.tgz" - integrity sha1-8+gPOH3fmugK4wpBADKWQuroERU= - dependencies: - lodash.clonedeep "^4.3.2" - -rcloader@^0.1.4: - version "0.1.4" - resolved "https://registry.npmjs.org/rcloader/-/rcloader-0.1.4.tgz" - integrity sha1-0MkC8ERJg6LuWmkHk3xqecpwRQk= - dependencies: - lodash "^3.0.1" - rcfinder "^0.1.6" - -read-pkg-up@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz" - integrity sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI= - dependencies: - find-up "^1.0.0" - read-pkg "^1.0.0" - -read-pkg-up@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz" - integrity sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4= - dependencies: - find-up "^2.0.0" - read-pkg "^2.0.0" - -read-pkg@^1.0.0: - version "1.1.0" - resolved "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz" - integrity sha1-9f+qXs0pyzHAR0vKfXVra7KePyg= - dependencies: - load-json-file "^1.0.0" - normalize-package-data "^2.3.2" - path-type "^1.0.0" - -read-pkg@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz" - integrity sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg= - dependencies: - load-json-file "^2.0.0" - normalize-package-data "^2.3.2" - path-type "^2.0.0" - -read@~1.0.4: - version "1.0.7" - resolved "https://registry.npmjs.org/read/-/read-1.0.7.tgz" - integrity sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ= - dependencies: - mute-stream "~0.0.4" - -"readable-stream@1 || 2", "readable-stream@2 || 3", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.0, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6: - version "2.3.6" - resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz" - integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.1.1" - util-deprecate "~1.0.1" - -"readable-stream@>=1.0.33-1 <1.1.0-0", readable-stream@^1.0.27-1, readable-stream@~1.0.17, readable-stream@~1.0.2, readable-stream@~1.0.24, readable-stream@~1.0.26: - version "1.0.34" - resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz" - integrity sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw= - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "0.0.1" - string_decoder "~0.10.x" - -readable-stream@^3.0.2, readable-stream@^3.1.1, readable-stream@^3.5.0: - version "3.6.0" - resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz" - integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== - dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" - -readable-stream@~1.1.9: - version "1.1.14" - resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz" - integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk= - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "0.0.1" - string_decoder "~0.10.x" - -readdirp@^2.2.1: - version "2.2.1" - resolved "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz" - integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ== - dependencies: - graceful-fs "^4.1.11" - micromatch "^3.1.10" - readable-stream "^2.0.2" - -readdirp@~3.4.0: - version "3.4.0" - resolved "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz" - integrity sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ== - dependencies: - picomatch "^2.2.1" - -readline2@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/readline2/-/readline2-1.0.1.tgz" - integrity sha1-QQWWCP/BVHV7cV2ZidGZ/783LjU= - dependencies: - code-point-at "^1.0.0" - is-fullwidth-code-point "^1.0.0" - mute-stream "0.0.5" - -recast@~0.11.12: - version "0.11.23" - resolved "https://registry.npmjs.org/recast/-/recast-0.11.23.tgz" - integrity sha1-RR/TAEqx5N+bTktmN2sqIZEkYtM= - dependencies: - ast-types "0.9.6" - esprima "~3.1.0" - private "~0.1.5" - source-map "~0.5.0" - -rechoir@^0.6.2: - version "0.6.2" - resolved "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz" - integrity sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q= - dependencies: - resolve "^1.1.6" - -redent@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz" - integrity sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94= - dependencies: - indent-string "^2.1.0" - strip-indent "^1.0.1" - -regenerate-unicode-properties@^8.1.0: - version "8.1.0" - resolved "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz" - integrity sha512-LGZzkgtLY79GeXLm8Dp0BVLdQlWICzBnJz/ipWUgo59qBaZ+BHtq51P2q1uVZlppMuUAT37SDk39qUbjTWB7bA== - dependencies: - regenerate "^1.4.0" - -regenerate@^1.4.0: - version "1.4.0" - resolved "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz" - integrity sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg== - -regenerator-runtime@^0.11.0: - version "0.11.1" - resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz" - integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== - -regenerator-runtime@^0.13.3: - version "0.13.3" - resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz" - integrity sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw== - -regenerator-runtime@^0.13.4: - version "0.13.5" - resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz" - integrity sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA== - -regenerator-transform@^0.14.0: - version "0.14.1" - resolved "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.1.tgz" - integrity sha512-flVuee02C3FKRISbxhXl9mGzdbWUVHubl1SMaknjxkFB1/iqpJhArQUvRxOOPEc/9tAiX0BaQ28FJH10E4isSQ== - dependencies: - private "^0.1.6" - -regex-not@^1.0.0, regex-not@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz" - integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== - dependencies: - extend-shallow "^3.0.2" - safe-regex "^1.1.0" - -regexp-tree@^0.1.13: - version "0.1.13" - resolved "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.13.tgz" - integrity sha512-hwdV/GQY5F8ReLZWO+W1SRoN5YfpOKY6852+tBFcma72DKBIcHjPRIlIvQN35bCOljuAfP2G2iB0FC/w236mUw== - -regexpp@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz" - integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== - -regexpu-core@^4.5.4: - version "4.6.0" - resolved "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.6.0.tgz" - integrity sha512-YlVaefl8P5BnFYOITTNzDvan1ulLOiXJzCNZxduTIosN17b87h3bvG9yHMoHaRuo88H4mQ06Aodj5VtYGGGiTg== - dependencies: - regenerate "^1.4.0" - regenerate-unicode-properties "^8.1.0" - regjsgen "^0.5.0" - regjsparser "^0.6.0" - unicode-match-property-ecmascript "^1.0.4" - unicode-match-property-value-ecmascript "^1.1.0" - -regjsgen@^0.5.0: - version "0.5.0" - resolved "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.0.tgz" - integrity sha512-RnIrLhrXCX5ow/E5/Mh2O4e/oa1/jW0eaBKTSy3LaCj+M3Bqvm97GWDp2yUtzIs4LEn65zR2yiYGFqb2ApnzDA== - -regjsparser@^0.6.0: - version "0.6.0" - resolved "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.0.tgz" - integrity sha512-RQ7YyokLiQBomUJuUG8iGVvkgOLxwyZM8k6d3q5SAXpg4r5TZJZigKFvC6PpD+qQ98bCDC5YelPeA3EucDoNeQ== - dependencies: - jsesc "~0.5.0" - -relateurl@0.2.x: - version "0.2.7" - resolved "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz" - integrity sha1-VNvzd+UUQKypCkzSdGANP/LYiKk= - -remove-bom-buffer@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz" - integrity sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ== - dependencies: - is-buffer "^1.1.5" - is-utf8 "^0.2.1" - -remove-bom-stream@^1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/remove-bom-stream/-/remove-bom-stream-1.2.0.tgz" - integrity sha1-BfGlk/FuQuH7kOv1nejlaVJflSM= - dependencies: - remove-bom-buffer "^3.0.0" - safe-buffer "^5.1.0" - through2 "^2.0.3" - -remove-trailing-separator@^1.0.1, remove-trailing-separator@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz" - integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= - -repeat-element@^1.1.2: - version "1.1.3" - resolved "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz" - integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g== - -repeat-string@^1.6.1: - version "1.6.1" - resolved "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz" - integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= - -repeating@^2.0.0: - version "2.0.1" - resolved "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz" - integrity sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo= - dependencies: - is-finite "^1.0.0" - -replace-ext@0.0.1: - version "0.0.1" - resolved "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz" - integrity sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ= - -replace-ext@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz" - integrity sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs= - -replace-homedir@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/replace-homedir/-/replace-homedir-1.0.0.tgz" - integrity sha1-6H9tUTuSjd6AgmDBK+f+xv9ueYw= - dependencies: - homedir-polyfill "^1.0.1" - is-absolute "^1.0.0" - remove-trailing-separator "^1.1.0" - -request-promise-core@1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.2.tgz" - integrity sha512-UHYyq1MO8GsefGEt7EprS8UrXsm1TxEvFUX1IMTuSLU2Rh7fTIdFtl8xD7JiEYiWU2dl+NYAjCTksTehQUxPag== - dependencies: - lodash "^4.17.11" - -request-promise-native@^1.0.5: - version "1.0.7" - resolved "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.7.tgz" - integrity sha512-rIMnbBdgNViL37nZ1b3L/VfPOpSi0TqVDQPAvO6U14lMzOLrt5nilxCQqtDKhZeDiW0/hkCXGoQjhgJd/tCh6w== - dependencies: - request-promise-core "1.1.2" - stealthy-require "^1.1.1" - tough-cookie "^2.3.3" - -request@^2.88.0: - version "2.88.0" - resolved "https://registry.npmjs.org/request/-/request-2.88.0.tgz" - integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg== - dependencies: - aws-sign2 "~0.7.0" - aws4 "^1.8.0" - caseless "~0.12.0" - combined-stream "~1.0.6" - extend "~3.0.2" - forever-agent "~0.6.1" - form-data "~2.3.2" - har-validator "~5.1.0" - http-signature "~1.2.0" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.19" - oauth-sign "~0.9.0" - performance-now "^2.1.0" - qs "~6.5.2" - safe-buffer "^5.1.2" - tough-cookie "~2.4.3" - tunnel-agent "^0.6.0" - uuid "^3.3.2" - -require-directory@^2.1.1: - version "2.1.1" - resolved "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz" - integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= - -require-main-filename@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz" - integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE= - -require-main-filename@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz" - integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== - -require-uncached@^1.0.2: - version "1.0.3" - resolved "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz" - integrity sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM= - dependencies: - caller-path "^0.1.0" - resolve-from "^1.0.0" - -requires-port@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz" - integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= - -resolve-cwd@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz" - integrity sha1-AKn3OHVW4nA46uIyyqNypqWbZlo= - dependencies: - resolve-from "^3.0.0" - -resolve-dir@^1.0.0, resolve-dir@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz" - integrity sha1-eaQGRMNivoLybv/nOcm7U4IEb0M= - dependencies: - expand-tilde "^2.0.0" - global-modules "^1.0.0" - -resolve-from@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz" - integrity sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY= - -resolve-from@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz" - integrity sha1-six699nWiBvItuZTM17rywoYh0g= - -resolve-from@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz" - integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== - -resolve-options@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/resolve-options/-/resolve-options-1.1.0.tgz" - integrity sha1-MrueOcBtZzONyTeMDW1gdFZq0TE= - dependencies: - value-or-function "^3.0.0" - -resolve-url@^0.2.1: - version "0.2.1" - resolved "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz" - integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= - -resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.15.1, resolve@^1.3.2, resolve@^1.4.0: - version "1.17.0" - resolved "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz" - integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== - dependencies: - path-parse "^1.0.6" - -resp-modifier@6.0.2: - version "6.0.2" - resolved "https://registry.npmjs.org/resp-modifier/-/resp-modifier-6.0.2.tgz" - integrity sha1-sSTeXE+6/LpUH0j/pzlw9KpFa08= - dependencies: - debug "^2.2.0" - minimatch "^3.0.2" - -responselike@1.0.2, responselike@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz" - integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= - dependencies: - lowercase-keys "^1.0.0" - -restore-cursor@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz" - integrity sha1-NGYfRohjJ/7SmRR5FSJS35LapUE= - dependencies: - exit-hook "^1.0.0" - onetime "^1.0.0" - -restore-cursor@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz" - integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368= - dependencies: - onetime "^2.0.0" - signal-exit "^3.0.2" - -ret@~0.1.10: - version "0.1.15" - resolved "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz" - integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== - -reusify@^1.0.4: - version "1.0.4" - resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz" - integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== - -rgb-regex@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz" - integrity sha1-wODWiC3w4jviVKR16O3UGRX+rrE= - -rgba-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz" - integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM= - -rimraf@2.6.3, rimraf@~2.6.2: - version "2.6.3" - resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz" - integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== - dependencies: - glob "^7.1.3" - -rimraf@^2.4.0, rimraf@^2.5.4, rimraf@^2.6.2, rimraf@^2.6.3: - version "2.7.1" - resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz" - integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== - dependencies: - glob "^7.1.3" - -ripemd160@^2.0.0, ripemd160@^2.0.1: - version "2.0.2" - resolved "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz" - integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== - dependencies: - hash-base "^3.0.0" - inherits "^2.0.1" - -roarr@^2.15.3: - version "2.15.4" - resolved "https://registry.yarnpkg.com/roarr/-/roarr-2.15.4.tgz#f5fe795b7b838ccfe35dc608e0282b9eba2e7afd" - integrity sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A== - dependencies: - boolean "^3.0.1" - detect-node "^2.0.4" - globalthis "^1.0.1" - json-stringify-safe "^5.0.1" - semver-compare "^1.0.0" - sprintf-js "^1.1.2" - -run-async@^0.1.0: - version "0.1.0" - resolved "https://registry.npmjs.org/run-async/-/run-async-0.1.0.tgz" - integrity sha1-yK1KXhEGYeQCp9IbUw4AnyX444k= - dependencies: - once "^1.3.0" - -run-async@^2.2.0: - version "2.3.0" - resolved "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz" - integrity sha1-A3GrSuC91yDUFm19/aZP96RFpsA= - dependencies: - is-promise "^2.1.0" - -run-parallel@^1.1.9: - version "1.1.9" - resolved "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz" - integrity sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q== - -run-queue@^1.0.0, run-queue@^1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz" - integrity sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec= - dependencies: - aproba "^1.1.1" - -rusha@^0.8.13: - version "0.8.13" - resolved "https://registry.npmjs.org/rusha/-/rusha-0.8.13.tgz" - integrity sha1-mghOe4YLF7/zAVuSxnpqM2GRUTo= - -rx-lite@^3.1.2: - version "3.1.2" - resolved "https://registry.npmjs.org/rx-lite/-/rx-lite-3.1.2.tgz" - integrity sha1-Gc5QLKVyZl87ZHsQk5+X/RYV8QI= - -rx@4.1.0: - version "4.1.0" - resolved "https://registry.npmjs.org/rx/-/rx-4.1.0.tgz" - integrity sha1-pfE/957zt0D+MKqAP7CfmIBdR4I= - -rxjs@^5.5.6: - version "5.5.12" - resolved "https://registry.npmjs.org/rxjs/-/rxjs-5.5.12.tgz" - integrity sha512-xx2itnL5sBbqeeiVgNPVuQQ1nC8Jp2WfNJhXWHmElW9YmrpS9UVnNzhP3EH3HFqexO5Tlp8GhYY+WEcqcVMvGw== - dependencies: - symbol-observable "1.0.1" - -rxjs@^6.4.0: - version "6.5.3" - resolved "https://registry.npmjs.org/rxjs/-/rxjs-6.5.3.tgz" - integrity sha512-wuYsAYYFdWTAnAaPoKGNhfpWwKZbJW+HgAJ+mImp+Epl7BG8oNWBCTyRM8gba9k4lk8BgWdoYm21Mo/RYhhbgA== - dependencies: - tslib "^1.9.0" - -safe-buffer@5.1.2, safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - -safe-regex@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz" - integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4= - dependencies: - ret "~0.1.10" - -"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: - version "2.1.2" - resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== - -sass-lint@^1.12.0: - version "1.13.1" - resolved "https://registry.npmjs.org/sass-lint/-/sass-lint-1.13.1.tgz" - integrity sha512-DSyah8/MyjzW2BWYmQWekYEKir44BpLqrCFsgs9iaWiVTcwZfwXHF586hh3D1n+/9ihUNMfd8iHAyb9KkGgs7Q== - dependencies: - commander "^2.8.1" - eslint "^2.7.0" - front-matter "2.1.2" - fs-extra "^3.0.1" - glob "^7.0.0" - globule "^1.0.0" - gonzales-pe-sl "^4.2.3" - js-yaml "^3.5.4" - known-css-properties "^0.3.0" - lodash.capitalize "^4.1.0" - lodash.kebabcase "^4.0.0" - merge "^1.2.0" - path-is-absolute "^1.0.0" - util "^0.10.3" - -sass-unused@^0.3.0: - version "0.3.0" - resolved "https://registry.npmjs.org/sass-unused/-/sass-unused-0.3.0.tgz" - integrity sha512-fGNcUpDeSFwnN+BTQ251iM77Py8awPXc96vSE3TpvMcgbC90IrohonRb4oxWX/KzHpezkmUddS8/t04R+yIB8w== - dependencies: - glob "^7.0.5" - gonzales-pe "^4.2.3" - -sass@^1.26.3: - version "1.30.0" - resolved "https://registry.npmjs.org/sass/-/sass-1.30.0.tgz" - integrity sha512-26EUhOXRLaUY7+mWuRFqGeGGNmhB1vblpTENO1Z7mAzzIZeVxZr9EZoaY1kyGLFWdSOZxRMAufiN2mkbO6dAlw== - dependencies: - chokidar ">=2.0.0 <4.0.0" - -sax@>=0.6.0, sax@^1.2.4, sax@~1.2.4: - version "1.2.4" - resolved "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz" - integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== - -saxes@^3.1.3: - version "3.1.11" - resolved "https://registry.npmjs.org/saxes/-/saxes-3.1.11.tgz" - integrity sha512-Ydydq3zC+WYDJK1+gRxRapLIED9PWeSuuS41wqyoRmzvhhh9nc+QQrVMKJYzJFULazeGhzSV0QleN2wD3boh2g== - dependencies: - xmlchars "^2.1.1" - -schema-utils@^0.4.0: - version "0.4.7" - resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz" - integrity sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ== - dependencies: - ajv "^6.1.0" - ajv-keywords "^3.1.0" - -schema-utils@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz" - integrity sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g== - dependencies: - ajv "^6.1.0" - ajv-errors "^1.0.0" - ajv-keywords "^3.1.0" - -schema-utils@^2.6.5: - version "2.6.5" - resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.5.tgz" - integrity sha512-5KXuwKziQrTVHh8j/Uxz+QUbxkaLW9X/86NBlx/gnKgtsZA2GIVMUn17qWhRFwF8jdYb3Dig5hRO/W5mZqy6SQ== - dependencies: - ajv "^6.12.0" - ajv-keywords "^3.4.1" - -schema-utils@^3.0.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.1.1.tgz#bc74c4b6b6995c1d88f76a8b77bea7219e0c8281" - integrity sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw== - dependencies: - "@types/json-schema" "^7.0.8" - ajv "^6.12.5" - ajv-keywords "^3.5.2" - -seek-bzip@^1.0.5: - version "1.0.5" - resolved "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.5.tgz" - integrity sha1-z+kXyz0nS8/6x5J1ivUxc+sfq9w= - dependencies: - commander "~2.8.1" - -semver-compare@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" - integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w= - -semver-greatest-satisfied-range@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-1.1.0.tgz" - integrity sha1-E+jCZYq5aRywzXEJMkAoDTb3els= - dependencies: - sver-compat "^1.5.0" - -semver-regex@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/semver-regex/-/semver-regex-2.0.0.tgz" - integrity sha512-mUdIBBvdn0PLOeP3TEkMH7HHeUP3GjsXCwKarjv/kGmUFOYg1VqEemKhoQpWMu6X2I8kHeuVdGibLGkVK+/5Qw== - -semver-truncate@^1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/semver-truncate/-/semver-truncate-1.1.2.tgz" - integrity sha1-V/Qd5pcHpicJp+AQS6IRcQnqR+g= - dependencies: - semver "^5.3.0" - -"semver@2 || 3 || 4 || 5", semver@^5.1.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0: - version "5.7.1" - resolved "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== - -semver@^4.1.0: - version "4.3.6" - resolved "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz" - integrity sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto= - -semver@^6.0.0, semver@^6.2.0, semver@^6.3.0: - version "6.3.0" - resolved "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== - -semver@^7.1.3, semver@^7.3.2: - version "7.3.7" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" - integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== - dependencies: - lru-cache "^6.0.0" - -send@0.16.2: - version "0.16.2" - resolved "https://registry.npmjs.org/send/-/send-0.16.2.tgz" - integrity sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw== - dependencies: - debug "2.6.9" - depd "~1.1.2" - destroy "~1.0.4" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - fresh "0.5.2" - http-errors "~1.6.2" - mime "1.4.1" - ms "2.0.0" - on-finished "~2.3.0" - range-parser "~1.2.0" - statuses "~1.4.0" - -sequencify@~0.0.7: - version "0.0.7" - resolved "https://registry.npmjs.org/sequencify/-/sequencify-0.0.7.tgz" - integrity sha1-kM/xnQLgcCf9dn9erT57ldHnOAw= - -serialize-error@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-7.0.1.tgz#f1360b0447f61ffb483ec4157c737fab7d778e18" - integrity sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw== - dependencies: - type-fest "^0.13.1" - -serialize-javascript@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-3.1.0.tgz" - integrity sha512-JIJT1DGiWmIKhzRsG91aS6Ze4sFUrYbltlkg2onR5OrnNM02Kl/hnY/T4FN2omvyeBbQmMJv+K4cPOpGzOTFBg== - dependencies: - randombytes "^2.1.0" - -serve-index@1.9.1, serve-index@^1.1.4: - version "1.9.1" - resolved "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz" - integrity sha1-03aNabHn2C5c4FD/9bRTvqEqkjk= - dependencies: - accepts "~1.3.4" - batch "0.6.1" - debug "2.6.9" - escape-html "~1.0.3" - http-errors "~1.6.2" - mime-types "~2.1.17" - parseurl "~1.3.2" - -serve-static@1.13.2, serve-static@^1.3.0: - version "1.13.2" - resolved "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz" - integrity sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw== - dependencies: - encodeurl "~1.0.2" - escape-html "~1.0.3" - parseurl "~1.3.2" - send "0.16.2" - -server-destroy@1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/server-destroy/-/server-destroy-1.0.1.tgz" - integrity sha1-8Tv5KOQrnD55OD5hzDmYtdFObN0= - -set-blocking@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz" - integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= - -set-value@^2.0.0, set-value@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz" - integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== - dependencies: - extend-shallow "^2.0.1" - is-extendable "^0.1.1" - is-plain-object "^2.0.3" - split-string "^3.0.1" - -setimmediate@^1.0.4: - version "1.0.5" - resolved "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz" - integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= - -setprototypeof@1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz" - integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== - -setprototypeof@1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz" - integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== - -sha.js@^2.4.0, sha.js@^2.4.8: - version "2.4.11" - resolved "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz" - integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== - dependencies: - inherits "^2.0.1" - safe-buffer "^5.0.1" - -shebang-command@^1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz" - integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= - dependencies: - shebang-regex "^1.0.0" - -shebang-command@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz" - integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== - dependencies: - shebang-regex "^3.0.0" - -shebang-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz" - integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= - -shebang-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" - integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== - -shelljs@^0.6.0: - version "0.6.1" - resolved "https://registry.npmjs.org/shelljs/-/shelljs-0.6.1.tgz" - integrity sha1-7GIRvtGSBEIIj+D3Cyg3Iy7SyKg= - -sigmund@^1.0.1, sigmund@~1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" - integrity sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA= - -signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3: - version "3.0.3" - resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz" - integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== - -simple-swizzle@^0.2.2: - version "0.2.2" - resolved "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz" - integrity sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo= - dependencies: - is-arrayish "^0.3.1" - -slash@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz" - integrity sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU= - -slash@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz" - integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== - -slice-ansi@0.0.4: - version "0.0.4" - resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz" - integrity sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU= - -slice-ansi@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz" - integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== - dependencies: - ansi-styles "^3.2.0" - astral-regex "^1.0.0" - is-fullwidth-code-point "^2.0.0" - -snapdragon-node@^2.0.1: - version "2.1.1" - resolved "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz" - integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== - dependencies: - define-property "^1.0.0" - isobject "^3.0.0" - snapdragon-util "^3.0.1" - -snapdragon-util@^3.0.1: - version "3.0.1" - resolved "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz" - integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== - dependencies: - kind-of "^3.2.0" - -snapdragon@^0.8.1: - version "0.8.2" - resolved "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz" - integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== - dependencies: - base "^0.11.1" - debug "^2.2.0" - define-property "^0.2.5" - extend-shallow "^2.0.1" - map-cache "^0.2.2" - source-map "^0.5.6" - source-map-resolve "^0.5.0" - use "^3.1.0" - -socket.io-adapter@~1.1.0: - version "1.1.1" - resolved "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.1.tgz" - integrity sha1-KoBeihTWNyEk3ZFZrUUC+MsH8Gs= - -socket.io-client@2.1.1: - version "2.1.1" - resolved "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.1.1.tgz" - integrity sha512-jxnFyhAuFxYfjqIgduQlhzqTcOEQSn+OHKVfAxWaNWa7ecP7xSNk2Dx/3UEsDcY7NcFafxvNvKPmmO7HTwTxGQ== - dependencies: - backo2 "1.0.2" - base64-arraybuffer "0.1.5" - component-bind "1.0.0" - component-emitter "1.2.1" - debug "~3.1.0" - engine.io-client "~3.2.0" - has-binary2 "~1.0.2" - has-cors "1.1.0" - indexof "0.0.1" - object-component "0.0.3" - parseqs "0.0.5" - parseuri "0.0.5" - socket.io-parser "~3.2.0" - to-array "0.1.4" - -socket.io-client@^2.0.4: - version "2.3.0" - resolved "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.3.0.tgz" - integrity sha512-cEQQf24gET3rfhxZ2jJ5xzAOo/xhZwK+mOqtGRg5IowZsMgwvHwnf/mCRapAAkadhM26y+iydgwsXGObBB5ZdA== - dependencies: - backo2 "1.0.2" - base64-arraybuffer "0.1.5" - component-bind "1.0.0" - component-emitter "1.2.1" - debug "~4.1.0" - engine.io-client "~3.4.0" - has-binary2 "~1.0.2" - has-cors "1.1.0" - indexof "0.0.1" - object-component "0.0.3" - parseqs "0.0.5" - parseuri "0.0.5" - socket.io-parser "~3.3.0" - to-array "0.1.4" - -socket.io-parser@~3.2.0: - version "3.2.0" - resolved "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.2.0.tgz" - integrity sha512-FYiBx7rc/KORMJlgsXysflWx/RIvtqZbyGLlHZvjfmPTPeuD/I8MaW7cfFrj5tRltICJdgwflhfZ3NVVbVLFQA== - dependencies: - component-emitter "1.2.1" - debug "~3.1.0" - isarray "2.0.1" - -socket.io-parser@~3.3.0: - version "3.3.0" - resolved "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.0.tgz" - integrity sha512-hczmV6bDgdaEbVqhAeVMM/jfUfzuEZHsQg6eOmLgJht6G3mPKMxYm75w2+qhAQZ+4X+1+ATZ+QFKeOZD5riHng== - dependencies: - component-emitter "1.2.1" - debug "~3.1.0" - isarray "2.0.1" - -socket.io@2.1.1: - version "2.1.1" - resolved "https://registry.npmjs.org/socket.io/-/socket.io-2.1.1.tgz" - integrity sha512-rORqq9c+7W0DAK3cleWNSyfv/qKXV99hV4tZe+gGLfBECw3XEhBy7x85F3wypA9688LKjtwO9pX9L33/xQI8yA== - dependencies: - debug "~3.1.0" - engine.io "~3.2.0" - has-binary2 "~1.0.2" - socket.io-adapter "~1.1.0" - socket.io-client "2.1.1" - socket.io-parser "~3.2.0" - -sort-keys-length@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/sort-keys-length/-/sort-keys-length-1.0.1.tgz" - integrity sha1-nLb09OnkgVWmqgZx7dM2/xR5oYg= - dependencies: - sort-keys "^1.0.0" - -sort-keys@^1.0.0: - version "1.1.2" - resolved "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz" - integrity sha1-RBttTTRnmPG05J6JIK37oOVD+a0= - dependencies: - is-plain-obj "^1.0.0" - -sort-keys@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/sort-keys/-/sort-keys-2.0.0.tgz" - integrity sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg= - dependencies: - is-plain-obj "^1.0.0" - -source-list-map@^2.0.0: - version "2.0.1" - resolved "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz" - integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== - -source-map-resolve@^0.5.0: - version "0.5.2" - resolved "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz" - integrity sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA== - dependencies: - atob "^2.1.1" - decode-uri-component "^0.2.0" - resolve-url "^0.2.1" - source-map-url "^0.4.0" - urix "^0.1.0" - -source-map-support@^0.4.15: - version "0.4.18" - resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz" - integrity sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA== - dependencies: - source-map "^0.5.6" - -source-map-support@~0.5.12: - version "0.5.13" - resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz" - integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - -source-map-url@^0.4.0: - version "0.4.0" - resolved "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz" - integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= - -source-map@^0.5.0, source-map@^0.5.1, source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7, source-map@~0.5.0: - version "0.5.7" - resolved "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz" - integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= - -source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: - version "0.6.1" - resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== - -source-map@~0.1.38: - version "0.1.43" - resolved "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz" - integrity sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y= - dependencies: - amdefine ">=0.0.4" - -sparkles@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/sparkles/-/sparkles-1.0.1.tgz" - integrity sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw== - -spdx-correct@^3.0.0: - version "3.1.0" - resolved "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz" - integrity sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q== - dependencies: - spdx-expression-parse "^3.0.0" - spdx-license-ids "^3.0.0" - -spdx-exceptions@^2.1.0: - version "2.2.0" - resolved "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz" - integrity sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA== - -spdx-expression-parse@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz" - integrity sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg== - dependencies: - spdx-exceptions "^2.1.0" - spdx-license-ids "^3.0.0" - -spdx-license-ids@^3.0.0: - version "3.0.5" - resolved "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz" - integrity sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q== - -split-on-first@^1.0.0: - version "1.1.0" - resolved "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz" - integrity sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw== - -split-string@^3.0.1, split-string@^3.0.2: - version "3.1.0" - resolved "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz" - integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== - dependencies: - extend-shallow "^3.0.0" - -sprintf-js@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.2.tgz#da1765262bf8c0f571749f2ad6c26300207ae673" - integrity sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug== - -sprintf-js@~1.0.2: - version "1.0.3" - resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz" - integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= - -squeak@^1.0.0: - version "1.3.0" - resolved "https://registry.npmjs.org/squeak/-/squeak-1.3.0.tgz" - integrity sha1-MwRQN7ZDiLVnZ0uEMiplIQc5FsM= - dependencies: - chalk "^1.0.0" - console-stream "^0.1.1" - lpad-align "^1.0.1" - -ssh2-streams@~0.2.0: - version "0.2.1" - resolved "https://registry.npmjs.org/ssh2-streams/-/ssh2-streams-0.2.1.tgz" - integrity sha512-3zCOsmunh1JWgPshfhKmBCL3lUtHPoh+a/cyQ49Ft0Q0aF7xgN06b76L+oKtFi0fgO57FLjFztb1GlJcEZ4a3Q== - dependencies: - asn1 "~0.2.0" - semver "^5.1.0" - streamsearch "~0.1.2" - -ssh2@~0.6.1: - version "0.6.2" - resolved "https://registry.npmjs.org/ssh2/-/ssh2-0.6.2.tgz" - integrity sha512-DJ+dOhXEEsmNpcQTI0x69FS++JH6qqL/ltEHf01pI1SSLMAcmD+hL4jRwvHjPwynPsmSUbHJ/WIZYzROfqZWjA== - dependencies: - ssh2-streams "~0.2.0" - -sshpk@^1.7.0: - version "1.16.1" - resolved "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz" - integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== - dependencies: - asn1 "~0.2.3" - assert-plus "^1.0.0" - bcrypt-pbkdf "^1.0.0" - dashdash "^1.12.0" - ecc-jsbn "~0.1.1" - getpass "^0.1.1" - jsbn "~0.1.0" - safer-buffer "^2.0.2" - tweetnacl "~0.14.0" - -ssri@^6.0.1: - version "6.0.1" - resolved "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz" - integrity sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA== - dependencies: - figgy-pudding "^3.5.1" - -stable@^0.1.8: - version "0.1.8" - resolved "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz" - integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== - -stack-trace@0.0.10, stack-trace@0.0.x: - version "0.0.10" - resolved "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz" - integrity sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA= - -static-extend@^0.1.1: - version "0.1.2" - resolved "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz" - integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= - dependencies: - define-property "^0.2.5" - object-copy "^0.1.0" - -"statuses@>= 1.4.0 < 2", statuses@~1.4.0: - version "1.4.0" - resolved "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz" - integrity sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew== - -"statuses@>= 1.5.0 < 2": - version "1.5.0" - resolved "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz" - integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= - -statuses@~1.3.1: - version "1.3.1" - resolved "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz" - integrity sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4= - -stealthy-require@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz" - integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks= - -stream-browserify@^2.0.1: - version "2.0.2" - resolved "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz" - integrity sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg== - dependencies: - inherits "~2.0.1" - readable-stream "^2.0.2" - -stream-browserify@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz" - integrity sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA== - dependencies: - inherits "~2.0.4" - readable-stream "^3.5.0" - -stream-consume@~0.1.0: - version "0.1.1" - resolved "https://registry.npmjs.org/stream-consume/-/stream-consume-0.1.1.tgz" - integrity sha512-tNa3hzgkjEP7XbCkbRXe1jpg+ievoa0O4SCFlMOYEscGSS4JJsckGL8swUyAa/ApGU3Ae4t6Honor4HhL+tRyg== - -stream-each@^1.1.0: - version "1.2.3" - resolved "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz" - integrity sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw== - dependencies: - end-of-stream "^1.1.0" - stream-shift "^1.0.0" - -stream-exhaust@^1.0.1: - version "1.0.2" - resolved "https://registry.npmjs.org/stream-exhaust/-/stream-exhaust-1.0.2.tgz" - integrity sha512-b/qaq/GlBK5xaq1yrK9/zFcyRSTNxmcZwFLGSTG0mXgZl/4Z6GgiyYOXOvY7N3eEvFRAG1bkDRz5EPGSvPYQlw== - -stream-http@^2.7.2: - version "2.8.3" - resolved "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz" - integrity sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw== - dependencies: - builtin-status-codes "^3.0.0" - inherits "^2.0.1" - readable-stream "^2.3.6" - to-arraybuffer "^1.0.0" - xtend "^4.0.0" - -stream-shift@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz" - integrity sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI= - -stream-throttle@^0.1.3: - version "0.1.3" - resolved "https://registry.npmjs.org/stream-throttle/-/stream-throttle-0.1.3.tgz" - integrity sha1-rdV8jXzHOoFjDTHNVdOWHPr7qcM= - dependencies: - commander "^2.2.0" - limiter "^1.0.5" - -streamsearch@~0.1.2: - version "0.1.2" - resolved "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz" - integrity sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo= - -strict-uri-encode@^1.0.0: - version "1.1.0" - resolved "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz" - integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM= - -strict-uri-encode@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz" - integrity sha1-ucczDHBChi9rFC3CdLvMWGbONUY= - -strictdom@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/strictdom/-/strictdom-1.0.1.tgz" - integrity sha1-GJ3pFkn3PUTVm4Qy76aO+dJllGA= - -string-replace-webpack-plugin@^0.1.3: - version "0.1.3" - resolved "https://registry.npmjs.org/string-replace-webpack-plugin/-/string-replace-webpack-plugin-0.1.3.tgz" - integrity sha1-c8ZX51nWbP6Arh4M8JGqJW0OcVw= - dependencies: - async "~0.2.10" - loader-utils "~0.2.3" - optionalDependencies: - css-loader "^0.9.1" - file-loader "^0.8.1" - style-loader "^0.8.3" - -string-width@^1.0.1, string-width@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz" - integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= - dependencies: - code-point-at "^1.0.0" - is-fullwidth-code-point "^1.0.0" - strip-ansi "^3.0.0" - -string-width@^2.0.0, string-width@^2.1.0: - version "2.1.1" - resolved "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz" - integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== - dependencies: - is-fullwidth-code-point "^2.0.0" - strip-ansi "^4.0.0" - -string-width@^3.0.0, string-width@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz" - integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== - dependencies: - emoji-regex "^7.0.1" - is-fullwidth-code-point "^2.0.0" - strip-ansi "^5.1.0" - -string-width@^4.1.0, string-width@^4.2.0: - version "4.2.0" - resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz" - integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.0" - -string.prototype.trim@^1.1.2: - version "1.2.0" - resolved "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.0.tgz" - integrity sha512-9EIjYD/WdlvLpn987+ctkLf0FfvBefOCuiEr2henD8X+7jfwPnyvTdmW8OJhj5p+M0/96mBdynLWkxUr+rHlpg== - dependencies: - define-properties "^1.1.3" - es-abstract "^1.13.0" - function-bind "^1.1.1" - -string.prototype.trimleft@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz" - integrity sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw== - dependencies: - define-properties "^1.1.3" - function-bind "^1.1.1" - -string.prototype.trimright@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz" - integrity sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg== - dependencies: - define-properties "^1.1.3" - function-bind "^1.1.1" - -string_decoder@^1.0.0, string_decoder@^1.1.1, string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - -string_decoder@~0.10.x: - version "0.10.31" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" - integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= - -strip-ansi@^0.3.0: - version "0.3.0" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz" - integrity sha1-JfSOoiynkYfzF0pNuHWTR7sSYiA= - dependencies: - ansi-regex "^0.2.1" - -strip-ansi@^3.0.0, strip-ansi@^3.0.1: - version "3.0.1" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz" - integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= - dependencies: - ansi-regex "^2.0.0" - -strip-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz" - integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= - dependencies: - ansi-regex "^3.0.0" - -strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: - version "5.2.0" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz" - integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== - dependencies: - ansi-regex "^4.1.0" - -strip-ansi@^6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz" - integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== - dependencies: - ansi-regex "^5.0.0" - -strip-bom@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-1.0.0.tgz" - integrity sha1-hbiGLzhEtabV7IRnqTWYFzo295Q= - dependencies: - first-chunk-stream "^1.0.0" - is-utf8 "^0.2.0" - -strip-bom@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz" - integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4= - dependencies: - is-utf8 "^0.2.0" - -strip-bom@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz" - integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= - -strip-dirs@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/strip-dirs/-/strip-dirs-2.1.0.tgz" - integrity sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g== - dependencies: - is-natural-number "^4.0.1" - -strip-eof@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz" - integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= - -strip-final-newline@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz" - integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== - -strip-indent@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz" - integrity sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI= - dependencies: - get-stdin "^4.0.1" - -strip-indent@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz" - integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ== - dependencies: - min-indent "^1.0.0" - -strip-json-comments@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz" - integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= - -strip-json-comments@^3.0.1: - version "3.0.1" - resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz" - integrity sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw== - -strip-json-comments@~1.0.1: - version "1.0.4" - resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz" - integrity sha1-HhX7ysl9Pumb8tc7TGVrCCu6+5E= - -strip-outer@^1.0.0, strip-outer@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz" - integrity sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg== - dependencies: - escape-string-regexp "^1.0.2" - -style-loader@^0.8.3: - version "0.8.3" - resolved "https://registry.npmjs.org/style-loader/-/style-loader-0.8.3.tgz" - integrity sha1-9Pkut9tjdodI8VBlzWcA9aHIU1c= - dependencies: - loader-utils "^0.2.5" - -stylehacks@^4.0.0: - version "4.0.3" - resolved "https://registry.npmjs.org/stylehacks/-/stylehacks-4.0.3.tgz" - integrity sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g== - dependencies: - browserslist "^4.0.0" - postcss "^7.0.0" - postcss-selector-parser "^3.0.0" - -sumchecker@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/sumchecker/-/sumchecker-3.0.1.tgz#6377e996795abb0b6d348e9b3e1dfb24345a8e42" - integrity sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg== - dependencies: - debug "^4.1.0" - -supports-color@6.1.0, supports-color@^6.1.0: - version "6.1.0" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz" - integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== - dependencies: - has-flag "^3.0.0" - -supports-color@^0.2.0: - version "0.2.0" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz" - integrity sha1-2S3iaU6z9nMjlz1649i1W0wiGQo= - -supports-color@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz" - integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= - -supports-color@^3.2.3: - version "3.2.3" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz" - integrity sha1-ZawFBLOVQXHYpklGsq48u4pfVPY= - dependencies: - has-flag "^1.0.0" - -supports-color@^5.3.0, supports-color@^5.4.0, supports-color@^5.5.0: - version "5.5.0" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz" - integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== - dependencies: - has-flag "^3.0.0" - -supports-color@^7.1.0: - version "7.1.0" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz" - integrity sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g== - dependencies: - has-flag "^4.0.0" - -sver-compat@^1.5.0: - version "1.5.0" - resolved "https://registry.npmjs.org/sver-compat/-/sver-compat-1.5.0.tgz" - integrity sha1-PPh9/rTQe0o/FIJ7wYaz/QxkXNg= - dependencies: - es6-iterator "^2.0.1" - es6-symbol "^3.1.1" - -svgo@^1.0.0, svgo@^1.0.5: - version "1.3.0" - resolved "https://registry.npmjs.org/svgo/-/svgo-1.3.0.tgz" - integrity sha512-MLfUA6O+qauLDbym+mMZgtXCGRfIxyQoeH6IKVcFslyODEe/ElJNwr0FohQ3xG4C6HK6bk3KYPPXwHVJk3V5NQ== - dependencies: - chalk "^2.4.1" - coa "^2.0.2" - css-select "^2.0.0" - css-select-base-adapter "^0.1.1" - css-tree "1.0.0-alpha.33" - csso "^3.5.1" - js-yaml "^3.13.1" - mkdirp "~0.5.1" - object.values "^1.1.0" - sax "~1.2.4" - stable "^0.1.8" - unquote "~1.1.1" - util.promisify "~1.0.0" - -symbol-observable@1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz" - integrity sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ= - -symbol-tree@^3.2.2: - version "3.2.4" - resolved "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz" - integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== - -table@^3.7.8: - version "3.8.3" - resolved "https://registry.npmjs.org/table/-/table-3.8.3.tgz" - integrity sha1-K7xULw/amGGnVdOUf+/Ys/UThV8= - dependencies: - ajv "^4.7.0" - ajv-keywords "^1.0.0" - chalk "^1.1.1" - lodash "^4.0.0" - slice-ansi "0.0.4" - string-width "^2.0.0" - -table@^5.2.3: - version "5.4.6" - resolved "https://registry.npmjs.org/table/-/table-5.4.6.tgz" - integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug== - dependencies: - ajv "^6.10.2" - lodash "^4.17.14" - slice-ansi "^2.1.0" - string-width "^3.0.0" - -tapable@^1.0.0, tapable@^1.1.3: - version "1.1.3" - resolved "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz" - integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== - -tar-stream@^1.5.2: - version "1.6.2" - resolved "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz" - integrity sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A== - dependencies: - bl "^1.0.0" - buffer-alloc "^1.2.0" - end-of-stream "^1.0.0" - fs-constants "^1.0.0" - readable-stream "^2.3.0" - to-buffer "^1.1.1" - xtend "^4.0.0" - -tar-stream@~0.4.0: - version "0.4.7" - resolved "https://registry.npmjs.org/tar-stream/-/tar-stream-0.4.7.tgz" - integrity sha1-Hx0s6evHtCdlJDyg6PG3v9oKrc0= - dependencies: - bl "^0.9.0" - end-of-stream "^1.0.0" - readable-stream "^1.0.27-1" - xtend "^4.0.0" - -temp-dir@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz" - integrity sha1-CnwOom06Oa+n4OvqnB/AvE2qAR0= - -tempfile@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/tempfile/-/tempfile-2.0.0.tgz" - integrity sha1-awRGhWqbERTRhW/8vlCczLCXcmU= - dependencies: - temp-dir "^1.0.0" - uuid "^3.0.1" - -ternary-stream@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/ternary-stream/-/ternary-stream-3.0.0.tgz" - integrity sha512-oIzdi+UL/JdktkT+7KU5tSIQjj8pbShj3OASuvDEhm0NT5lppsm7aXWAmAq4/QMaBIyfuEcNLbAQA+HpaISobQ== - dependencies: - duplexify "^4.1.1" - fork-stream "^0.0.4" - merge-stream "^2.0.0" - through2 "^3.0.1" - -terser-webpack-plugin@^1.1.0, terser-webpack-plugin@^1.4.3: - version "1.4.4" - resolved "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.4.tgz" - integrity sha512-U4mACBHIegmfoEe5fdongHESNJWqsGU+W0S/9+BmYGVQDw1+c2Ow05TpMhxjPK1sRb7cuYq1BPl1e5YHJMTCqA== - dependencies: - cacache "^12.0.2" - find-cache-dir "^2.1.0" - is-wsl "^1.1.0" - schema-utils "^1.0.0" - serialize-javascript "^3.1.0" - source-map "^0.6.1" - terser "^4.1.2" - webpack-sources "^1.4.0" - worker-farm "^1.7.0" - -terser@^4.0.0, terser@^4.1.2: - version "4.3.1" - resolved "https://registry.npmjs.org/terser/-/terser-4.3.1.tgz" - integrity sha512-pnzH6dnFEsR2aa2SJaKb1uSCl3QmIsJ8dEkj0Fky+2AwMMcC9doMqLOQIH6wVTEKaVfKVvLSk5qxPBEZT9mywg== - dependencies: - commander "^2.20.0" - source-map "~0.6.1" - source-map-support "~0.5.12" - -text-table@^0.2.0, text-table@~0.2.0: - version "0.2.0" - resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz" - integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= - -tfunk@^3.0.1: - version "3.1.0" - resolved "https://registry.npmjs.org/tfunk/-/tfunk-3.1.0.tgz" - integrity sha1-OORBT8ZJd9h6/apy+sttKfgve1s= - dependencies: - chalk "^1.1.1" - object-path "^0.9.0" - -through2-concurrent@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/through2-concurrent/-/through2-concurrent-2.0.0.tgz" - integrity sha512-R5/jLkfMvdmDD+seLwN7vB+mhbqzWop5fAjx5IX8/yQq7VhBhzDmhXgaHAOnhnWkCpRMM7gToYHycB0CS/pd+A== - dependencies: - through2 "^2.0.0" - -through2-filter@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/through2-filter/-/through2-filter-3.0.0.tgz" - integrity sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA== - dependencies: - through2 "~2.0.0" - xtend "~4.0.0" - -through2@*, through2@3.0.1, through2@^3.0.0, through2@^3.0.1: - version "3.0.1" - resolved "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz" - integrity sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww== - dependencies: - readable-stream "2 || 3" - -through2@2.0.3: - version "2.0.3" - resolved "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz" - integrity sha1-AARWmzfHx0ujnEPzzteNGtlBQL4= - dependencies: - readable-stream "^2.1.5" - xtend "~4.0.1" - -through2@^0.4.1, through2@~0.4.2: - version "0.4.2" - resolved "https://registry.npmjs.org/through2/-/through2-0.4.2.tgz" - integrity sha1-2/WGYDEVHsg1K7bE22SiKSqEC5s= - dependencies: - readable-stream "~1.0.17" - xtend "~2.1.1" - -through2@^0.5.0, through2@^0.5.1: - version "0.5.1" - resolved "https://registry.npmjs.org/through2/-/through2-0.5.1.tgz" - integrity sha1-390BLrnHAOIyP9M084rGIqs3Lac= - dependencies: - readable-stream "~1.0.17" - xtend "~3.0.0" - -through2@^0.6.1, through2@~0.6.1: - version "0.6.5" - resolved "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz" - integrity sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg= - dependencies: - readable-stream ">=1.0.33-1 <1.1.0-0" - xtend ">=4.0.0 <4.1.0-0" - -through2@^2.0.0, through2@^2.0.1, through2@^2.0.2, through2@^2.0.3, through2@~2.0.0: - version "2.0.5" - resolved "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz" - integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== - dependencies: - readable-stream "~2.3.6" - xtend "~4.0.1" - -through2@~0.3.0: - version "0.3.0" - resolved "https://registry.npmjs.org/through2/-/through2-0.3.0.tgz" - integrity sha1-LR0oyNHa+NnFy3jwppNDxrhkLZc= - dependencies: - readable-stream "~1.0.17" - xtend "~2.1.1" - -through@^2.3.6, through@^2.3.8, through@~2.3.6: - version "2.3.8" - resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz" - integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= - -tildify@^1.0.0: - version "1.2.0" - resolved "https://registry.npmjs.org/tildify/-/tildify-1.2.0.tgz" - integrity sha1-3OwD9V3Km3qj5bBPIYF+tW5jWIo= - dependencies: - os-homedir "^1.0.0" - -time-stamp@^1.0.0: - version "1.1.0" - resolved "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz" - integrity sha1-dkpaEa9QVhkhsTPztE5hhofg9cM= - -timed-out@^4.0.0, timed-out@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz" - integrity sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8= - -timers-browserify@^2.0.4: - version "2.0.11" - resolved "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.11.tgz" - integrity sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ== - dependencies: - setimmediate "^1.0.4" - -timm@^1.6.1: - version "1.6.2" - resolved "https://registry.npmjs.org/timm/-/timm-1.6.2.tgz" - integrity sha512-IH3DYDL1wMUwmIlVmMrmesw5lZD6N+ZOAFWEyLrtpoL9Bcrs9u7M/vyOnHzDD2SMs4irLkVjqxZbHrXStS/Nmw== - -timsort@^0.3.0: - version "0.3.0" - resolved "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz" - integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q= - -tiny-lr@0.1.4: - version "0.1.4" - resolved "https://registry.npmjs.org/tiny-lr/-/tiny-lr-0.1.4.tgz" - integrity sha1-bkHX5n39CHjl4LN+N6BtZ+MJ/00= - dependencies: - body-parser "~1.8.0" - debug "~0.8.1" - faye-websocket "~0.7.2" - parseurl "~1.3.0" - qs "~2.2.3" - -tinycolor2@^1.4.1: - version "1.4.1" - resolved "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.1.tgz" - integrity sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g= - -tmp@^0.0.33: - version "0.0.33" - resolved "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz" - integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== - dependencies: - os-tmpdir "~1.0.2" - -to-absolute-glob@^2.0.0: - version "2.0.2" - resolved "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz" - integrity sha1-GGX0PZ50sIItufFFt4z/fQ98hJs= - dependencies: - is-absolute "^1.0.0" - is-negated-glob "^1.0.0" - -to-array@0.1.4: - version "0.1.4" - resolved "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz" - integrity sha1-F+bBH3PdTz10zaek/zI46a2b+JA= - -to-arraybuffer@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz" - integrity sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M= - -to-buffer@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz" - integrity sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg== - -to-fast-properties@^1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz" - integrity sha1-uDVx+k2MJbguIxsG46MFXeTKGkc= - -to-fast-properties@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz" - integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= - -to-object-path@^0.3.0: - version "0.3.0" - resolved "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz" - integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= - dependencies: - kind-of "^3.0.2" - -to-readable-stream@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz" - integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q== - -to-regex-range@^2.1.0: - version "2.1.1" - resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz" - integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= - dependencies: - is-number "^3.0.0" - repeat-string "^1.6.1" - -to-regex-range@^5.0.1: - 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== - dependencies: - is-number "^7.0.0" - -to-regex@^3.0.1, to-regex@^3.0.2: - version "3.0.2" - resolved "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz" - integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== - dependencies: - define-property "^2.0.2" - extend-shallow "^3.0.2" - regex-not "^1.0.2" - safe-regex "^1.1.0" - -to-through@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/to-through/-/to-through-2.0.0.tgz" - integrity sha1-/JKtq6ByZHvAtn1rA2ZKoZUJOvY= - dependencies: - through2 "^2.0.3" - -tobspr-osx-sign@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/tobspr-osx-sign/-/tobspr-osx-sign-1.0.1.tgz#5cca32185d813357b556a6a839305558656c45d5" - integrity sha512-jXSw9n/ivAnHwwpImvHnTkhbeI06ZDvLKLP3rryZLBoAt1nfljoIEgdPz7vNlOUBGwVEYOl2VauViNOmZPNZ7A== - dependencies: - compare-version "^0.1.2" - debug "^4.3.4" - fs-extra "^10.0.0" - isbinaryfile "^4.0.8" - minimist "^1.2.6" - plist "^3.0.5" - -toidentifier@1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz" - integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== - -tough-cookie@^2.3.3, tough-cookie@^2.4.3: - version "2.5.0" - resolved "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz" - integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== - dependencies: - psl "^1.1.28" - punycode "^2.1.1" - -tough-cookie@~2.4.3: - version "2.4.3" - resolved "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz" - integrity sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ== - dependencies: - psl "^1.1.24" - punycode "^1.4.1" - -tr46@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz" - integrity sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk= - dependencies: - punycode "^2.1.0" - -trim-newlines@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz" - integrity sha1-WIeWa7WCpFA6QetST301ARgVphM= - -trim-repeated@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz" - integrity sha1-42RqLqTokTEr9+rObPsFOAvAHCE= - dependencies: - escape-string-regexp "^1.0.2" - -trim-right@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz" - integrity sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM= - -trim@^0.0.1: - version "0.0.1" - resolved "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz" - integrity sha1-WFhUf2spB1fulczMZm+1AITEYN0= - -tslib@^1.9.0: - version "1.10.0" - resolved "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz" - integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== - -tty-browserify@0.0.0: - version "0.0.0" - resolved "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz" - integrity sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY= - -tunnel-agent@^0.6.0: - version "0.6.0" - resolved "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz" - integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= - dependencies: - safe-buffer "^5.0.1" - -tunnel@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c" - integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg== - -tweetnacl@^0.14.3, tweetnacl@~0.14.0: - version "0.14.5" - resolved "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz" - integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= - -type-check@~0.3.2: - version "0.3.2" - resolved "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz" - integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= - dependencies: - prelude-ls "~1.1.2" - -type-fest@^0.11.0: - version "0.11.0" - resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz" - integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ== - -type-fest@^0.13.1: - version "0.13.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.13.1.tgz#0172cb5bce80b0bd542ea348db50c7e21834d934" - integrity sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg== - -type-is@~1.5.1: - version "1.5.7" - resolved "https://registry.npmjs.org/type-is/-/type-is-1.5.7.tgz" - integrity sha1-uTaKWTzG730GReeLL0xky+zQXpA= - dependencies: - media-typer "0.3.0" - mime-types "~2.0.9" - -type@^1.0.1: - version "1.2.0" - resolved "https://registry.npmjs.org/type/-/type-1.2.0.tgz" - integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== - -typedarray@^0.0.6: - version "0.0.6" - resolved "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz" - integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= - -ua-parser-js@^0.7.18: - version "0.7.21" - resolved "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.21.tgz" - integrity sha512-+O8/qh/Qj8CgC6eYBVBykMrNtp5Gebn4dlGD/kKXVkJNDwyrAwSIqwz8CDf+tsAIWVycKcku6gIXJ0qwx/ZXaQ== - -uglify-js@3.4.x: - version "3.4.10" - resolved "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.10.tgz" - integrity sha512-Y2VsbPVs0FIshJztycsO2SfPk7/KAF/T72qzv9u5EpQ4kB2hQoHlhNQTsNyy6ul7lQtqJN/AoWeS23OzEiEFxw== - dependencies: - commander "~2.19.0" - source-map "~0.6.1" - -uglify-template-string-loader@^1.1.0: - version "1.1.1" - resolved "https://registry.npmjs.org/uglify-template-string-loader/-/uglify-template-string-loader-1.1.1.tgz" - integrity sha512-EHJx8m0aIHlwX5xlJd2xPYIFvLrPkVK5X8zpVxSNTmu7KoT2eSg1TNlwZS+JS65+dwJXC4rC5mc+F4UVe2rckw== - -ultron@~1.1.0: - version "1.1.1" - resolved "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz" - integrity sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og== - -unbzip2-stream@^1.0.9: - version "1.3.3" - resolved "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.3.3.tgz" - integrity sha512-fUlAF7U9Ah1Q6EieQ4x4zLNejrRvDWUYmxXUpN3uziFYCHapjWFaCAnreY9bGgxzaMCFAPPpYNng57CypwJVhg== - dependencies: - buffer "^5.2.1" - through "^2.3.8" - -unc-path-regex@^0.1.2: - version "0.1.2" - resolved "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz" - integrity sha1-5z3T17DXxe2G+6xrCufYxqadUPo= - -underscore@~1.8.3: - version "1.8.3" - resolved "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz" - integrity sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI= - -undertaker-registry@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/undertaker-registry/-/undertaker-registry-1.0.1.tgz" - integrity sha1-XkvaMI5KiirlhPm5pDWaSZglzFA= - -undertaker@^1.2.1: - version "1.2.1" - resolved "https://registry.npmjs.org/undertaker/-/undertaker-1.2.1.tgz" - integrity sha512-71WxIzDkgYk9ZS+spIB8iZXchFhAdEo2YU8xYqBYJ39DIUIqziK78ftm26eecoIY49X0J2MLhG4hr18Yp6/CMA== - dependencies: - arr-flatten "^1.0.1" - arr-map "^2.0.0" - bach "^1.0.0" - collection-map "^1.0.0" - es6-weak-map "^2.0.1" - last-run "^1.1.0" - object.defaults "^1.0.0" - object.reduce "^1.0.0" - undertaker-registry "^1.0.0" - -unicode-canonical-property-names-ecmascript@^1.0.4: - version "1.0.4" - resolved "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz" - integrity sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ== - -unicode-match-property-ecmascript@^1.0.4: - version "1.0.4" - resolved "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz" - integrity sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg== - dependencies: - unicode-canonical-property-names-ecmascript "^1.0.4" - unicode-property-aliases-ecmascript "^1.0.4" - -unicode-match-property-value-ecmascript@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.1.0.tgz" - integrity sha512-hDTHvaBk3RmFzvSl0UVrUmC3PuW9wKVnpoUDYH0JDkSIovzw+J5viQmeYHxVSBptubnr7PbH2e0fnpDRQnQl5g== - -unicode-property-aliases-ecmascript@^1.0.4: - version "1.0.5" - resolved "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.5.tgz" - integrity sha512-L5RAqCfXqAwR3RriF8pM0lU0w4Ryf/GgzONwi6KnL1taJQa7x1TCxdJnILX59WIGOwR57IVxn7Nej0fz1Ny6fw== - -union-value@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz" - integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== - dependencies: - arr-union "^3.1.0" - get-value "^2.0.6" - is-extendable "^0.1.1" - set-value "^2.0.1" - -uniq@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz" - integrity sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8= - -uniqs@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz" - integrity sha1-/+3ks2slKQaW5uFl1KWe25mOawI= - -unique-filename@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz" - integrity sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ== - dependencies: - unique-slug "^2.0.0" - -unique-slug@^2.0.0: - version "2.0.2" - resolved "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz" - integrity sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w== - dependencies: - imurmurhash "^0.1.4" - -unique-stream@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/unique-stream/-/unique-stream-1.0.0.tgz" - integrity sha1-1ZpKdUJ0R9mqbJHnAmP40mpLEEs= - -unique-stream@^2.0.2: - version "2.3.1" - resolved "https://registry.npmjs.org/unique-stream/-/unique-stream-2.3.1.tgz" - integrity sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A== - dependencies: - json-stable-stringify-without-jsonify "^1.0.1" - through2-filter "^3.0.0" - -universalify@^0.1.0: - version "0.1.2" - resolved "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz" - integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== - -universalify@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" - integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== - -unpipe@1.0.0, unpipe@~1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz" - integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= - -unquote@~1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz" - integrity sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ= - -unset-value@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz" - integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= - dependencies: - has-value "^0.3.1" - isobject "^3.0.0" - -unused-files-webpack-plugin@^3.4.0: - version "3.4.0" - resolved "https://registry.npmjs.org/unused-files-webpack-plugin/-/unused-files-webpack-plugin-3.4.0.tgz" - integrity sha512-cmukKOBdIqaM1pqThY0+jp+mYgCVyzrD8uRbKEucQwIGZcLIRn+gSRiQ7uLjcDd3Zba9NUxVGyYa7lWM4UCGeg== - dependencies: - babel-runtime "^7.0.0-beta.3" - glob-all "^3.1.0" - semver "^5.5.0" - util.promisify "^1.0.0" - warning "^3.0.0" - -upath@^1.1.1: - version "1.2.0" - resolved "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz" - integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== - -upper-case@^1.1.1: - version "1.1.3" - resolved "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz" - integrity sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg= - -uri-js@^4.2.2: - version "4.2.2" - resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz" - integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ== - dependencies: - punycode "^2.1.0" - -urix@^0.1.0: - version "0.1.0" - resolved "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz" - integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= - -url-parse-lax@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz" - integrity sha1-evjzA2Rem9eaJy56FKxovAYJ2nM= - dependencies: - prepend-http "^1.0.1" - -url-parse-lax@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz" - integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww= - dependencies: - prepend-http "^2.0.0" - -url-to-options@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz" - integrity sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k= - -url@^0.11.0: - version "0.11.0" - resolved "https://registry.npmjs.org/url/-/url-0.11.0.tgz" - integrity sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE= - dependencies: - punycode "1.3.2" - querystring "0.2.0" - -use@^3.1.0: - version "3.1.1" - resolved "https://registry.npmjs.org/use/-/use-3.1.1.tgz" - integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== - -user-home@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/user-home/-/user-home-1.1.1.tgz" - integrity sha1-K1viOjK2Onyd640PKNSFcko98ZA= - -user-home@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/user-home/-/user-home-2.0.0.tgz" - integrity sha1-nHC/2Babwdy/SGBODwS4tJzenp8= - dependencies: - os-homedir "^1.0.0" - -utif@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/utif/-/utif-2.0.1.tgz" - integrity sha512-Z/S1fNKCicQTf375lIP9G8Sa1H/phcysstNrrSdZKj1f9g58J4NMgb5IgiEZN9/nLMPDwF0W7hdOe9Qq2IYoLg== - dependencies: - pako "^1.0.5" - -util-deprecate@^1.0.1, util-deprecate@~1.0.1: - version "1.0.2" - resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" - integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= - -util.promisify@^1.0.0, util.promisify@~1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz" - integrity sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA== - dependencies: - define-properties "^1.1.2" - object.getownpropertydescriptors "^2.0.3" - -util@0.10.3: - version "0.10.3" - resolved "https://registry.npmjs.org/util/-/util-0.10.3.tgz" - integrity sha1-evsa/lCAUkZInj23/g7TeTNqwPk= - dependencies: - inherits "2.0.1" - -util@^0.10.3: - version "0.10.4" - resolved "https://registry.npmjs.org/util/-/util-0.10.4.tgz" - integrity sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A== - dependencies: - inherits "2.0.3" - -util@^0.11.0: - version "0.11.1" - resolved "https://registry.npmjs.org/util/-/util-0.11.1.tgz" - integrity sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ== - dependencies: - inherits "2.0.3" - -utils-merge@1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz" - integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= - -uuid@^3.0.1, uuid@^3.3.2: - version "3.3.3" - resolved "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz" - integrity sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ== - -v8-compile-cache@2.0.3: - version "2.0.3" - resolved "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.0.3.tgz" - integrity sha512-CNmdbwQMBjwr9Gsmohvm0pbL954tJrNzf6gWL3K+QMQf00PF7ERGrEiLgjuU3mKreLC2MeGhUsNV9ybTbLgd3w== - -v8flags@^2.0.2: - version "2.1.1" - resolved "https://registry.npmjs.org/v8flags/-/v8flags-2.1.1.tgz" - integrity sha1-qrGh+jDUX4jdMhFIh1rALAtV5bQ= - dependencies: - user-home "^1.1.1" - -v8flags@^3.2.0: - version "3.2.0" - resolved "https://registry.npmjs.org/v8flags/-/v8flags-3.2.0.tgz" - integrity sha512-mH8etigqMfiGWdeXpaaqGfs6BndypxusHHcv2qSHyZkGEznCd/qAXCWWRzeowtL54147cktFOC4P5y+kl8d8Jg== - dependencies: - homedir-polyfill "^1.0.1" - -validate-npm-package-license@^3.0.1: - version "3.0.4" - resolved "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz" - integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== - dependencies: - spdx-correct "^3.0.0" - spdx-expression-parse "^3.0.0" - -value-or-function@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/value-or-function/-/value-or-function-3.0.0.tgz" - integrity sha1-HCQ6ULWVwb5Up1S/7OhWO5/42BM= - -vendors@^1.0.0: - version "1.0.3" - resolved "https://registry.npmjs.org/vendors/-/vendors-1.0.3.tgz" - integrity sha512-fOi47nsJP5Wqefa43kyWSg80qF+Q3XA6MUkgi7Hp1HQaKDQW4cQrK2D0P7mmbFtsV1N89am55Yru/nyEwRubcw== - -verror@1.10.0: - version "1.10.0" - resolved "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz" - integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= - dependencies: - assert-plus "^1.0.0" - core-util-is "1.0.2" - extsprintf "^1.2.0" - -vinyl-buffer@0.0.0: - version "0.0.0" - resolved "https://registry.npmjs.org/vinyl-buffer/-/vinyl-buffer-0.0.0.tgz" - integrity sha1-0ZeoJLrcsRzM+WQ6yRviTUPtqNs= - dependencies: - bl "^0.7.0" - through2 "^0.4.1" - -vinyl-fs@^0.3.0: - version "0.3.14" - resolved "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-0.3.14.tgz" - integrity sha1-mmhRzhysHBzqX+hsCTHWIMLPqeY= - dependencies: - defaults "^1.0.0" - glob-stream "^3.1.5" - glob-watcher "^0.0.6" - graceful-fs "^3.0.0" - mkdirp "^0.5.0" - strip-bom "^1.0.0" - through2 "^0.6.1" - vinyl "^0.4.0" - -vinyl-fs@^3.0.0: - version "3.0.3" - resolved "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-3.0.3.tgz" - integrity sha512-vIu34EkyNyJxmP0jscNzWBSygh7VWhqun6RmqVfXePrOwi9lhvRs//dOaGOTRUQr4tx7/zd26Tk5WeSVZitgng== - dependencies: - fs-mkdirp-stream "^1.0.0" - glob-stream "^6.1.0" - graceful-fs "^4.0.0" - is-valid-glob "^1.0.0" - lazystream "^1.0.0" - lead "^1.0.0" - object.assign "^4.0.4" - pumpify "^1.3.5" - readable-stream "^2.3.3" - remove-bom-buffer "^3.0.0" - remove-bom-stream "^1.2.0" - resolve-options "^1.1.0" - through2 "^2.0.0" - to-through "^2.0.0" - value-or-function "^3.0.0" - vinyl "^2.0.0" - vinyl-sourcemap "^1.1.0" - -vinyl-source-stream@^0.1.1: - version "0.1.1" - resolved "https://registry.npmjs.org/vinyl-source-stream/-/vinyl-source-stream-0.1.1.tgz" - integrity sha1-pTpPIaB6I0aV4EwnA/nxtbkIRZU= - dependencies: - through2 "~0.3.0" - vinyl "~0.2.2" - -vinyl-sourcemap@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/vinyl-sourcemap/-/vinyl-sourcemap-1.1.0.tgz" - integrity sha1-kqgAWTo4cDqM2xHYswCtS+Y7PhY= - dependencies: - append-buffer "^1.0.2" - convert-source-map "^1.5.0" - graceful-fs "^4.1.6" - normalize-path "^2.1.1" - now-and-later "^2.0.0" - remove-bom-buffer "^3.0.0" - vinyl "^2.0.0" - -vinyl-sourcemaps-apply@^0.2.0, vinyl-sourcemaps-apply@^0.2.1: - version "0.2.1" - resolved "https://registry.npmjs.org/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz" - integrity sha1-q2VJ1h0XLCsbh75cUI0jnI74dwU= - dependencies: - source-map "^0.5.1" - -vinyl@*, vinyl@^2.0.0, vinyl@^2.1.0, vinyl@^2.2.0: - version "2.2.0" - resolved "https://registry.npmjs.org/vinyl/-/vinyl-2.2.0.tgz" - integrity sha512-MBH+yP0kC/GQ5GwBqrTPTzEfiiLjta7hTtvQtbxBgTeSXsmKQRQecjibMbxIXzVT3Y9KJK+drOz1/k+vsu8Nkg== - dependencies: - clone "^2.1.1" - clone-buffer "^1.0.0" - clone-stats "^1.0.0" - cloneable-readable "^1.0.0" - remove-trailing-separator "^1.0.1" - replace-ext "^1.0.0" - -vinyl@^0.2.1, vinyl@~0.2.2: - version "0.2.3" - resolved "https://registry.npmjs.org/vinyl/-/vinyl-0.2.3.tgz" - integrity sha1-vKk4IJWC7FpJrVOKAPofEl5RMlI= - dependencies: - clone-stats "~0.0.1" - -vinyl@^0.4.0: - version "0.4.6" - resolved "https://registry.npmjs.org/vinyl/-/vinyl-0.4.6.tgz" - integrity sha1-LzVsh6VQolVGHza76ypbqL94SEc= - dependencies: - clone "^0.2.0" - clone-stats "^0.0.1" - -vinyl@^0.5.0: - version "0.5.3" - resolved "https://registry.npmjs.org/vinyl/-/vinyl-0.5.3.tgz" - integrity sha1-sEVbOPxeDPMNQyUTLkYZcMIJHN4= - dependencies: - clone "^1.0.0" - clone-stats "^0.0.1" - replace-ext "0.0.1" - -vm-browserify@^1.0.1: - version "1.1.0" - resolved "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.0.tgz" - integrity sha512-iq+S7vZJE60yejDYM0ek6zg308+UZsdtPExWP9VZoCFCz1zkJoXFnAX7aZfd/ZwrkidzdUZL0C/ryW+JwAiIGw== - -w3c-hr-time@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz" - integrity sha1-gqwr/2PZUOqeMYmlimViX+3xkEU= - dependencies: - browser-process-hrtime "^0.1.2" - -warning@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz" - integrity sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w= - dependencies: - loose-envify "^1.0.0" - -watch@^0.11.0: - version "0.11.0" - resolved "https://registry.npmjs.org/watch/-/watch-0.11.0.tgz" - integrity sha1-6NugkbdFZ5mjr1eXi5hud+EyBAY= - -watchpack-chokidar2@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz#38500072ee6ece66f3769936950ea1771be1c957" - integrity sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww== - dependencies: - chokidar "^2.1.8" - -watchpack@^1.6.1: - version "1.7.2" - resolved "https://registry.npmjs.org/watchpack/-/watchpack-1.7.2.tgz" - integrity sha512-ymVbbQP40MFTp+cNMvpyBpBtygHnPzPkHqoIwRRj/0B8KhqQwV8LaKjtbaxF2lK4vl8zN9wCxS46IFCU5K4W0g== - dependencies: - graceful-fs "^4.1.2" - neo-async "^2.5.0" - optionalDependencies: - chokidar "^3.4.0" - watchpack-chokidar2 "^2.0.0" - -webidl-conversions@^4.0.2: - version "4.0.2" - resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz" - integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== - -webpack-cli@^3.1.0: - version "3.3.9" - resolved "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.3.9.tgz" - integrity sha512-xwnSxWl8nZtBl/AFJCOn9pG7s5CYUYdZxmmukv+fAHLcBIHM36dImfpQg3WfShZXeArkWlf6QRw24Klcsv8a5A== - dependencies: - chalk "2.4.2" - cross-spawn "6.0.5" - enhanced-resolve "4.1.0" - findup-sync "3.0.0" - global-modules "2.0.0" - import-local "2.0.0" - interpret "1.2.0" - loader-utils "1.2.3" - supports-color "6.1.0" - v8-compile-cache "2.0.3" - yargs "13.2.4" - -webpack-deep-scope-plugin@^1.6.0: - version "1.6.2" - resolved "https://registry.npmjs.org/webpack-deep-scope-plugin/-/webpack-deep-scope-plugin-1.6.2.tgz" - integrity sha512-S5ZM1i7oTIVPIS1z/Fu41tqFzaXpy8vZnwEDC9I7NLj5XD8GGrDZbDXtG5FCGkHPGxtAzF4O21DKZZ76vpBGzw== - dependencies: - deep-scope-analyser "^1.7.0" - -webpack-plugin-replace@^1.1.1: - version "1.2.0" - resolved "https://registry.npmjs.org/webpack-plugin-replace/-/webpack-plugin-replace-1.2.0.tgz" - integrity sha512-1HA3etHpJW55qonJqv84o5w5GY7iqF8fqSHpTWdNwarj1llkkt4jT4QSvYs1hoaU8Lu5akDnq/spHHO5mXwo1w== - -webpack-sources@^1.4.0, webpack-sources@^1.4.1: - version "1.4.3" - resolved "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz" - integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ== - dependencies: - source-list-map "^2.0.0" - source-map "~0.6.1" - -webpack-stream@^5.2.1: - version "5.2.1" - resolved "https://registry.npmjs.org/webpack-stream/-/webpack-stream-5.2.1.tgz" - integrity sha512-WvyVU0K1/VB1NZ7JfsaemVdG0PXAQUqbjUNW4A58th4pULvKMQxG+y33HXTL02JvD56ko2Cub+E2NyPwrLBT/A== - dependencies: - fancy-log "^1.3.3" - lodash.clone "^4.3.2" - lodash.some "^4.2.2" - memory-fs "^0.4.1" - plugin-error "^1.0.1" - supports-color "^5.5.0" - through "^2.3.8" - vinyl "^2.1.0" - webpack "^4.26.1" - -webpack-strip-block@^0.2.0: - version "0.2.0" - resolved "https://registry.npmjs.org/webpack-strip-block/-/webpack-strip-block-0.2.0.tgz" - integrity sha1-xg1KcD4O7uiJXn8avptf6RRoFHA= - dependencies: - loader-utils "^1.1.0" - -webpack@^4.26.1, webpack@^4.43.0: - version "4.43.0" - resolved "https://registry.npmjs.org/webpack/-/webpack-4.43.0.tgz" - integrity sha512-GW1LjnPipFW2Y78OOab8NJlCflB7EFskMih2AHdvjbpKMeDJqEgSx24cXXXiPS65+WSwVyxtDsJH6jGX2czy+g== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-module-context" "1.9.0" - "@webassemblyjs/wasm-edit" "1.9.0" - "@webassemblyjs/wasm-parser" "1.9.0" - acorn "^6.4.1" - ajv "^6.10.2" - ajv-keywords "^3.4.1" - chrome-trace-event "^1.0.2" - enhanced-resolve "^4.1.0" - eslint-scope "^4.0.3" - json-parse-better-errors "^1.0.2" - loader-runner "^2.4.0" - loader-utils "^1.2.3" - memory-fs "^0.4.1" - micromatch "^3.1.10" - mkdirp "^0.5.3" - neo-async "^2.6.1" - node-libs-browser "^2.2.1" - schema-utils "^1.0.0" - tapable "^1.1.3" - terser-webpack-plugin "^1.4.3" - watchpack "^1.6.1" - webpack-sources "^1.4.1" - -websocket-driver@>=0.3.6: - version "0.7.3" - resolved "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.3.tgz" - integrity sha512-bpxWlvbbB459Mlipc5GBzzZwhoZgGEZLuqPaR0INBGnPAY1vdBX6hPnoFXiw+3yWxDuHyQjO2oXTMyS8A5haFg== - dependencies: - http-parser-js ">=0.4.0 <0.4.11" - safe-buffer ">=5.1.0" - websocket-extensions ">=0.1.1" - -websocket-extensions@>=0.1.1: - version "0.1.3" - resolved "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz" - integrity sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg== - -whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.5: - version "1.0.5" - resolved "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz" - integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== - dependencies: - iconv-lite "0.4.24" - -whatwg-fetch@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz" - integrity sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q== - -whatwg-mimetype@^2.2.0: - version "2.3.0" - resolved "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz" - integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== - -whatwg-url@^7.0.0: - version "7.0.0" - resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.0.0.tgz" - integrity sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ== - dependencies: - lodash.sortby "^4.7.0" - tr46 "^1.0.1" - webidl-conversions "^4.0.2" - -which-module@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz" - integrity sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8= - -which-module@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz" - integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= - -which@^1.1.1, which@^1.2.14, which@^1.2.9, which@^1.3.1: - version "1.3.1" - resolved "https://registry.npmjs.org/which/-/which-1.3.1.tgz" - integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== - dependencies: - isexe "^2.0.0" - -which@^2.0.1, which@^2.0.2: - version "2.0.2" - resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz" - integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== - dependencies: - isexe "^2.0.0" - -winston@~1.0.0: - version "1.0.2" - resolved "https://registry.npmjs.org/winston/-/winston-1.0.2.tgz" - integrity sha1-NRxY4jI/ikyimkUZWqmqO0w1128= - dependencies: - async "~1.0.0" - colors "1.0.x" - cycle "1.0.x" - eyes "0.1.x" - isstream "0.1.x" - pkginfo "0.3.x" - stack-trace "0.0.x" - -wordwrap@~0.0.2: - version "0.0.3" - resolved "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz" - integrity sha1-o9XabNXAvAAI03I0u68b7WMFkQc= - -wordwrap@~1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz" - integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= - -worker-farm@^1.7.0: - version "1.7.0" - resolved "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz" - integrity sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw== - dependencies: - errno "~0.1.7" - -worker-loader@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/worker-loader/-/worker-loader-2.0.0.tgz" - integrity sha512-tnvNp4K3KQOpfRnD20m8xltE3eWh89Ye+5oj7wXEEHKac1P4oZ6p9oTj8/8ExqoSBnk9nu5Pr4nKfQ1hn2APJw== - dependencies: - loader-utils "^1.0.0" - schema-utils "^0.4.0" - -wrap-ansi@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz" - integrity sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU= - dependencies: - string-width "^1.0.1" - strip-ansi "^3.0.1" - -wrap-ansi@^5.1.0: - version "5.1.0" - resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz" - integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q== - dependencies: - ansi-styles "^3.2.0" - string-width "^3.0.0" - strip-ansi "^5.0.0" - -wrap-ansi@^6.2.0: - version "6.2.0" - resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz" - integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrappy@1: - version "1.0.2" - resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= - -write@1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/write/-/write-1.0.3.tgz" - integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== - dependencies: - mkdirp "^0.5.1" - -write@^0.2.1: - version "0.2.1" - resolved "https://registry.npmjs.org/write/-/write-0.2.1.tgz" - integrity sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c= - dependencies: - mkdirp "^0.5.1" - -ws@^6.1.0: - version "6.2.1" - resolved "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz" - integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA== - dependencies: - async-limiter "~1.0.0" - -ws@~3.3.1: - version "3.3.3" - resolved "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz" - integrity sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA== - dependencies: - async-limiter "~1.0.0" - safe-buffer "~5.1.0" - ultron "~1.1.0" - -ws@~6.1.0: - version "6.1.4" - resolved "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz" - integrity sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA== - dependencies: - async-limiter "~1.0.0" - -xhr@^2.0.1: - version "2.5.0" - resolved "https://registry.npmjs.org/xhr/-/xhr-2.5.0.tgz" - integrity sha512-4nlO/14t3BNUZRXIXfXe+3N6w3s1KoxcJUUURctd64BLRe67E4gRwp4PjywtDY72fXpZ1y6Ch0VZQRY/gMPzzQ== - dependencies: - global "~4.3.0" - is-function "^1.0.1" - parse-headers "^2.0.0" - xtend "^4.0.0" - -xml-name-validator@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz" - integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== - -xml-parse-from-string@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/xml-parse-from-string/-/xml-parse-from-string-1.0.1.tgz" - integrity sha1-qQKekp09vN7RafPG4oI42VpdWig= - -xml2js@^0.4.5: - version "0.4.22" - resolved "https://registry.npmjs.org/xml2js/-/xml2js-0.4.22.tgz" - integrity sha512-MWTbxAQqclRSTnehWWe5nMKzI3VmJ8ltiJEco8akcC6j3miOhjjfzKum5sId+CWhfxdOs/1xauYr8/ZDBtQiRw== - dependencies: - sax ">=0.6.0" - util.promisify "~1.0.0" - xmlbuilder "~11.0.0" - -xmlbuilder@^9.0.7: - version "9.0.7" - resolved "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz" - integrity sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0= - -xmlbuilder@~11.0.0: - version "11.0.1" - resolved "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz" - integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== - -xmlchars@^2.1.1: - version "2.2.0" - resolved "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz" - integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== - -xmldom@0.1.x: - version "0.1.27" - resolved "https://registry.npmjs.org/xmldom/-/xmldom-0.1.27.tgz" - integrity sha1-1QH5ezvbQDr4757MIFcxh6rawOk= - -xmlhttprequest-ssl@~1.5.4: - version "1.5.5" - resolved "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz" - integrity sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4= - -"xtend@>=4.0.0 <4.1.0-0", xtend@^4.0.0, xtend@~4.0.0, xtend@~4.0.1: - version "4.0.2" - resolved "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz" - integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== - -xtend@~2.1.1: - version "2.1.2" - resolved "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz" - integrity sha1-bv7MKk2tjmlixJAbM3znuoe10os= - dependencies: - object-keys "~0.4.0" - -xtend@~3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/xtend/-/xtend-3.0.0.tgz" - integrity sha1-XM50B7r2Qsunvs2laBEcST9ZZlo= - -y18n@^3.2.1: - version "3.2.1" - resolved "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz" - integrity sha1-bRX7qITAhnnA136I53WegR4H+kE= - -y18n@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz" - integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== - -yallist@^2.1.2: - version "2.1.2" - resolved "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz" - integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= - -yallist@^3.0.2: - version "3.0.3" - resolved "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz" - integrity sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A== - -yallist@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" - integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== - -yaml-loader@^0.6.0: - version "0.6.0" - resolved "https://registry.npmjs.org/yaml-loader/-/yaml-loader-0.6.0.tgz" - integrity sha512-1bNiLelumURyj+zvVHOv8Y3dpCri0F2S+DCcmps0pA1zWRLjS+FhZQg4o3aUUDYESh73+pKZNI18bj7stpReow== - dependencies: - loader-utils "^1.4.0" - yaml "^1.8.3" - -yaml@^1.10.0, yaml@^1.8.3: - version "1.10.0" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.0.tgz#3b593add944876077d4d683fee01081bd9fff31e" - integrity sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg== - -yargs-parser@5.0.0-security.0: - version "5.0.0-security.0" - resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0-security.0.tgz" - integrity sha512-T69y4Ps64LNesYxeYGYPvfoMTt/7y1XtfpIslUeK4um+9Hu7hlGoRtaDLvdXb7+/tfq4opVa2HRY5xGip022rQ== - dependencies: - camelcase "^3.0.0" - object.assign "^4.1.0" - -yargs-parser@^13.1.0, yargs-parser@^13.1.1: - version "13.1.1" - resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz" - integrity sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ== - dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" - -yargs-parser@^18.1.2: - version "18.1.3" - resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz" - integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== - dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" - -yargs-parser@^20.0.0: - version "20.2.9" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" - integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== - -yargs@13.2.4: - version "13.2.4" - resolved "https://registry.npmjs.org/yargs/-/yargs-13.2.4.tgz" - integrity sha512-HG/DWAJa1PAnHT9JAhNa8AbAv3FPaiLzioSjCcmuXXhP8MlpHO5vwls4g4j6n30Z74GVQj8Xa62dWVx1QCGklg== - dependencies: - cliui "^5.0.0" - find-up "^3.0.0" - get-caller-file "^2.0.1" - os-locale "^3.1.0" - require-directory "^2.1.1" - require-main-filename "^2.0.0" - set-blocking "^2.0.0" - string-width "^3.0.0" - which-module "^2.0.0" - y18n "^4.0.0" - yargs-parser "^13.1.0" - -yargs@13.3.0: - version "13.3.0" - resolved "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz" - integrity sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA== - dependencies: - cliui "^5.0.0" - find-up "^3.0.0" - get-caller-file "^2.0.1" - require-directory "^2.1.1" - require-main-filename "^2.0.0" - set-blocking "^2.0.0" - string-width "^3.0.0" - which-module "^2.0.0" - y18n "^4.0.0" - yargs-parser "^13.1.1" - -yargs@^15.4.1: - version "15.4.1" - resolved "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz" - integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== - dependencies: - cliui "^6.0.0" - decamelize "^1.2.0" - find-up "^4.1.0" - get-caller-file "^2.0.1" - require-directory "^2.1.1" - require-main-filename "^2.0.0" - set-blocking "^2.0.0" - string-width "^4.2.0" - which-module "^2.0.0" - y18n "^4.0.0" - yargs-parser "^18.1.2" - -yargs@^7.1.0: - version "7.1.1" - resolved "https://registry.npmjs.org/yargs/-/yargs-7.1.1.tgz" - integrity sha512-huO4Fr1f9PmiJJdll5kwoS2e4GqzGSsMT3PPMpOwoVkOK8ckqAewMTZyA6LXVQWflleb/Z8oPBEvNsMft0XE+g== - dependencies: - camelcase "^3.0.0" - cliui "^3.2.0" - decamelize "^1.1.1" - get-caller-file "^1.0.1" - os-locale "^1.4.0" - read-pkg-up "^1.0.1" - require-directory "^2.1.1" - require-main-filename "^1.0.1" - set-blocking "^2.0.0" - string-width "^1.0.2" - which-module "^1.0.0" - y18n "^3.2.1" - yargs-parser "5.0.0-security.0" - -yargs@~1.2.6: - version "1.2.6" - resolved "https://registry.npmjs.org/yargs/-/yargs-1.2.6.tgz" - integrity sha1-nHtKgv1dWVsr8Xq23MQxNUMv40s= - dependencies: - minimist "^0.1.0" - -yauzl@^2.10.0, yauzl@^2.4.2: - version "2.10.0" - resolved "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz" - integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk= - dependencies: - buffer-crc32 "~0.2.3" - fd-slicer "~1.1.0" - -yeast@0.1.2: - version "0.1.2" - resolved "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz" - integrity sha1-AI4G2AlDIMNy28L47XagymyKxBk= - -zip-stream@~0.4.0: - version "0.4.1" - resolved "https://registry.npmjs.org/zip-stream/-/zip-stream-0.4.1.tgz" - integrity sha1-TqeVqM4Z6fq0mjHR0IdyFBWfA6M= - dependencies: - compress-commons "~0.1.0" - lodash "~2.4.1" - readable-stream "~1.0.26" diff --git a/mod_examples/README.md b/mod_examples/README.md deleted file mode 100644 index 5086bbdb..00000000 --- a/mod_examples/README.md +++ /dev/null @@ -1,58 +0,0 @@ -# shapez.io Modding - -## General Instructions - -Currently there are two options to develop mods for shapez.io: - -1. Writing single file mods, which doesn't require any additional tools and can be loaded directly in the game -2. Using the [create-shapezio-mod](https://www.npmjs.com/package/create-shapezio-mod) package. This package is still in development but allows you to pack multiple files and images into a single mod file, so you don't have to base64 encode your images etc. - -## Mod Developer Discord - -A great place to get help with mod development is the official [shapez.io modloader discord](https://discord.gg/xq5v8uyMue). - -## Setting up your development environment - -The simplest way of developing mods is by just creating a `mymod.js` file and putting it in the `mods/` folder of the standalone (You can find the `mods/` folder by clicking "Open Mods Folder" in the shapez Standalone, be sure to select the 1.5.0-modloader branch on Steam). - -You can then add `--dev` to the launch options on Steam. This adds an application menu where you can click "Restart" to reload your mod, and will also show the developer console where you can see any potential errors. - -## Getting started - -To get into shapez.io modding, I highly recommend checking out all of the examples in this folder. Here's a list of examples and what features of the modloader they show: - -| Example | Description | Demonstrates | -| ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | -| [base.js](base.js) | The most basic mod | Base structure of a mod | -| [class_extensions.js](class_extensions.js) | Shows how to extend multiple methods of one class at once, useful for overriding a lot of methods | Overriding and extending builtin methods | -| [custom_css.js](custom_css.js) | Modifies the Main Menu State look | Modifying the UI styles with CSS | -| [replace_builtin_sprites.js](replace_builtin_sprites.js) | Replaces all color sprites with icons | Replacing builtin sprites | -| [translations.js](translations.js) | Shows how to replace and add new translations in multiple languages | Adding and replacing translations | -| [add_building_basic.js](add_building_basic.js) | Shows how to add a new building | Registering a new building | -| [add_building_flipper.js](add_building_flipper.js) | Adds a "flipper" building which mirrors shapes from top to bottom | Registering a new building, Adding a custom shape and item processing operation (flip) | -| [custom_drawing.js](custom_drawing.js) | Displays a a small indicator on every item processing building whether it is currently working | Adding a new GameSystem and drawing overlays | -| [custom_keybinding.js](custom_keybinding.js) | Adds a new customizable ingame keybinding (Shift+F) | Adding a new keybinding | -| [custom_sub_shapes.js](custom_sub_shapes.js) | Adds a new type of sub-shape (Line) | Adding a new sub shape and drawing it, making it spawn on the map, modifying the builtin levels | -| [modify_theme.js](modify_theme.js) | Modifies the default game themes | Modifying the builtin themes | -| [custom_theme.js](custom_theme.js) | Adds a new UI and map theme | Adding a new game theme | -| [mod_settings.js](mod_settings.js) | Shows a dialog counting how often the mod has been launched | Reading and storing mod settings | -| [storing_data_in_savegame.js](storing_data_in_savegame.js) | Shows how to store custom (structured) data in the savegame | Storing custom data in savegame | -| [modify_existing_building.js](modify_existing_building.js) | Makes the rotator building always unlocked and adds a new statistic to the building panel | Modifying a builtin building, replacing builtin methods | -| [modify_ui.js](modify_ui.js) | Shows how to add custom UI elements to builtin game states (the Main Menu in this case) | Extending builtin UI states, Adding CSS | -| [pasting.js](pasting.js) | Shows a dialog when pasting text in the game | Listening to paste events | -| [smooth_zooming.js](smooth_zooming.js) | Allows to smoothly zoom in and out | Keybindings, overriding methods | -| [sandbox.js](sandbox.js) | Makes blueprints free and always unlocked | Overriding builtin methods | - -### Advanced Examples - -| Example | Description | Demonstrates | -| ------------------------------------------------ | ---------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [notification_blocks.js](notification_blocks.js) | Adds a notification block building, which shows a user defined notification when receiving a truthy signal | Adding a new Component, Adding a new GameSystem, Working with wire networks, Adding a new building, Adding a new HUD part, Using Input Dialogs, Adding Translations | -| [usage_statistics.js](usage_statistics.js) | Displays a percentage on every building showing its utilization | Adding a new component, Adding a new GameSystem, Drawing within a GameSystem, Modifying builtin buildings, Adding custom game logic | -| [new_item_type.js](new_item_type.js) | Adds a new type of items to the map (fluids) | Adding a new item type, modifying map generation | -| [buildings_have_cost.js](buildings_have_cost.js) | Adds a new currency, and belts cost 1 of that currency | Extending and replacing builtin methods, Adding CSS and custom sprites | -| [mirrored_cutter.js](mirrored_cutter.js) | Adds a mirrored variant of the cutter | Adding a new variant to existing buildings | - -### Creating new sprites - -If you want to add new buildings and create sprites for them, you can download the original Photoshop PSD files here: https://static.shapez.io/building-psds.zip diff --git a/mod_examples/add_building_basic.js b/mod_examples/add_building_basic.js deleted file mode 100644 index 6b92e769..00000000 --- a/mod_examples/add_building_basic.js +++ /dev/null @@ -1,67 +0,0 @@ -// @ts-nocheck -const METADATA = { - website: "https://tobspr.io", - author: "tobspr", - name: "Mod Example: Add new basic building", - version: "1", - id: "add-building-basic", - description: "Shows how to add a new basic building", - minimumGameVersion: ">=1.5.0", -}; - -class MetaDemoModBuilding extends shapez.ModMetaBuilding { - constructor() { - super("demoModBuilding"); - } - - static getAllVariantCombinations() { - return [ - { - variant: shapez.defaultBuildingVariant, - name: "A test name", - description: "A test building", - - regularImageBase64: RESOURCES["demoBuilding.png"], - blueprintImageBase64: RESOURCES["demoBuildingBlueprint.png"], - tutorialImageBase64: RESOURCES["demoBuildingBlueprint.png"], - }, - ]; - } - - getSilhouetteColor() { - return "red"; - } - - setupEntityComponents(entity) { - // Here you can add components, for example an ItemProcessorComponent. - // To get an idea what you can do with the builtin components, have a look - // at the builtin buildings in - } -} - -class Mod extends shapez.Mod { - init() { - // Register the new building - this.modInterface.registerNewBuilding({ - metaClass: MetaDemoModBuilding, - buildingIconBase64: RESOURCES["demoBuilding.png"], - }); - - // Add it to the regular toolbar - this.modInterface.addNewBuildingToToolbar({ - toolbar: "regular", - location: "primary", - metaClass: MetaDemoModBuilding, - }); - } -} - -//////////////////////////////////////////////////////////////////////// - -const RESOURCES = { - "demoBuilding.png": - "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAYAAABccqhmAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAHYgAAB2IBOHqZ2wAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAABLLSURBVHic7d17sFbVecfx75FjRBAOYKKCJiKCcklEovF+CxoxJkZDp7E2jZpGHWMd0/SinXQ6NcYxSTOd2oqmGptEp60dI1FDlAgOtfGSKArxQkRBoXgFRAG5Ch76x/Oeejy823PO3mutZ+/9/j4zz6B7Zq/9vOvd+zn73Ze1QEREREREREREREREREREREREREREREREREQqoc07AQliBDAG2A/YF/gIsA+wFzAUGAZ0NP5718Y6HcAujf/eAGwDOoF1jWVrgdXAGuCNxr9rgBXAMmB5Yz2pMBWA6mgHDgQ+DkxqxDjswO9wymk1VgheBJ4Gnmr8u9wpH+knFYDyGgccAXyqEVOA3V0z6rt1WCF4Ani4Ea+6ZiRNqQCUxwTgJOAE4ERgpGs24S0HHsKKwVzgBddsBFAB8DQIOAY4AzgT2N83neReBO5vxGx0PUFawAjgfGAWsAXYoWAHsBG4C/hjYI+8nStSRnsA5wL3YVfavQ+2sscm4A7gD7GzJJHKaQNOBm7BTm29D6qqxnrgRuDw/nW/iI9hwEXAIvwPnrrFIuAK7GeUSKlMxv7ab8b/QKl7bACuw56JEHF1HHZBrxP/A6PV4t1G3x/b67ckElAbcBawAP+DQGHxEHDqB31pIkW1Yffs5+O/wyuaxyPYxVeRoKYCj+O/gyv6FvcDRzf9JkX6YSJwO/47tCJfzAIO2OlbFenFcOCfge3478SKYrG18V0OQaQXA4CLsfffvXdcRdh4GXvUWKSpyegCXyvEr4DRiDTsij1hthX/nVORJjY2vvMBSEs7EngW/x1S4ROPYiMrSYtpx/4CvIP/Tqjwjc3YvtA1PqLU3Hh0T1+xc9yLDaQqNfYV9HquIjtWAp9Hamcgdi/YewdTlD86sX2lHamFA7Hhqr13LEW1Yi7wYaTSTgRW4b8zKaoZL2F3iqSC/go9yqsoHpup8ROEdXwQYgBwA/AtdGtHimsHpmOvgz/gm4r0Zg/gl/j/1VDUM37Ce3MrSsmMBBbiv5Mo6h1z8ZuLMbi6zAw0GvtixjrnIa1hITANmxy10upQACYAc7CpsUVSWQycArzinUgRVb9Idjg2IKQOfkltPHZRcH/nPAqp8hnAYdhp/3DvRKSlrcAGIl3qnUgeVS0Ah2IDP+7pnYgINtrQ8dgU6JVSxQJwCDAPHfxSLkuBE4DXvBPpj6oVgDHYb/6R3omINPEc9vj5Su9E+qpKFwFHYr/5dfBLWR2MjStQmecEqlIAOoD7sDMAkTL7JPAzKvLEYBXeBWgH7kYzvUh1HIjdmr7bO5HeVKEAzADO9k5CpJ+mYDMY/9o7kSr7S/yf/VYo8kYnJX+VuMx3AU7BJnGowlmKSJYt2O3B+d6JNFPWAjAa6zANySR1sAJ7bL10Lw+V8S7A7sBMdPBLfXwMuI0Sns2W8QzgVmz47ljeAh4D1kfchlTPUGz8v2ERt/FdbKQqyXAecS/K/Bz7okWaGQrcRdyLgp9L9mkq5mDgbeJ1/pvo4JfedWBnibH2w5XA3sk+TUV8iPjDec1J9mmk6uYQd1+8h5L8/C7LRcArsVd8Y3o7cvtSH7H3ldOBSyNvozKOIs34/TNTfSCpvJnE3x83U4Kpyb3nPxsE3ILv7ZF9gWMCtPM68GCT5ftgg0VI/z2I9WtPxxNmJt9H8BvTbyDwY2zfe9cpB3c/IH6l7e0MYHqg9mdntH9aws9Ytzgto09nB2p/ekb7Kc4AuuIbGTkk4XkN4BM4f3iRErgGx9fcvQrALsCNVOSdaZGIBgHXe23cqwB8Hb3fL9LlNOAcjw17FIARwLcdthtb1oXMstxqraKsvivdM/UB/AAYnHqjHjvn1dRzRN+DMpaPT5pFvRzcz+VVti/w16k3mroATAIuTLzNVPZn59O4IdjPHcnnEqwPuzsHe7uuji4n8UxDqZ8DuNZhmyndCkzF7l/vA1wMHOCaUbWNBZ4E/hV7HuB44HzPhCLbHfgeTtcDYjsZ33vKsZ8DUFQvyvAcQM/oBI7IyCu4lD8Brk64LZGqagOuSrWxVAVgOvbMv4j0bho2w1B0KQpAG/a2n4j03ZUpNpKiAHwBe+xXRPruJOyCclQpCoDGQBPJ5+9jbyB2AfgMCa9oitTMCUS+dha7AFweuX2RuvtmzMZjFoCJ2L1/EcnvD7DJRqOIWQC+QUkGPhSpsAHAZbEaj1UARgB/EqntMusE/hN7hv1K4FXXbOrhVawvL8H6ttM1Gx9/SsWGtP8L/B/z7BkpHgX+ao+2hwPLSvDZqxrLGn3Y3fkB2y/jo8BZcVFGroXEOgP4WqR2y+xl4Cc9lr0FzHDIpS5mYH3Y3U+xvm41Ud6ijVEAjsMuALaaZzOWL06aRb1k9V1WX9fZ4cCU0I3GKAB1fd+/N1lDO7fskM8BqE/f74LQDYYuAIOx2xYiEt6XsTEDggldAM7CYVwzkRbRQfZcCbmELgC1HMlEpETODtlYyAIwAnv2X0TiOYOAZ9khC8BZ2DTfIhLPIOBzoRoLWQDODNiWiGQL9jMgVAEYBJwSqC0R+WDTgN1CNBSqAEzDioCIxDcYe+CusFAFINhvEhHpk8+GaCRUAdB7/yJplaYAjANGB2hHRPpuIgGmEQtRAHTxT8RH4eduQhSA6EMXi0hThS8EhigAxwZoQ0T6r/CxV7QAjAFGFk1CRHIZS8Hjr2gB0F//9+zdz+XSu6ydW3903nNMkZWLFoCjC65fJx+n+fDNX0idSI18scmy8cCk1ImUmGsB+GTB9etkV+AX2KyuuwKjgGvJHnhSencGcBN2qjsEu/c9C2j3TKpkDiuycpGOHIAm/expIvAANoqr5kQI40Jad5i5vpiM7Ws78qxc5AzgIPT8fxYd/JLKMGC/vCsXKQCTC6wrIuHkPhMvUgBacehvkTLK/ce46E8AEfGX+65IkQJwcIF1RSScMXlXzFsA2rC3AEXE3wF5V8xbAPZG4/+LlMXe5Lwjl7cA5L7tICLBtZFzbIC8BeCjOdcTkThy/QzQGUB4bwBzgae8E6mRp7A+fcM7kRLL9Uc5bwHQ21jN3YYNj3Yqdm/2dGCrZ0IVtxXrw8lYn47G+lh29uE8K+UtAB/JuV6drcemb97YbdlsYIZPOrUwA+vDLhuxPl7vk06pJS0AuTZWc08Bm5os/03qRGqkWd9tQj+vmtkzz0oqAOFsyFi+MWO59C6r77L6upUlPQMYlnM9EYkjaQHQQ0Ai5TI0z0p5C4DGARApl1yTheoMQKQekhaAgTnXE5E4khaAATnXE5E4khYAjXknUi7JCoAOfpHyyXVchpgbUET8bcuzUp4CsCPvxkQkmmQFAGBLzvVEJI6kBaDZSy8i4ifXa+d5C8DmnOuJSBy5XpHOWwD0PrZIuazLs1LeAvBmzvVEJI6kZwAqADvLej9CL07ll9WneyTNohp0BuDsEJq/I3FE6kRq5OgmywZhfS3vtzLPSnkLwKqc69VZB3AD7y8CJwKX+aRTC5cCn+32/4OBm8n57nvN5SoA7Tk39krO9eruq8A0YD42W8uR6NHpInYD7sXGAFwJTEHD0WV5Pc9KKgDhjQLO9E6iZnTK37tcBSDvTwAVAJFyWZFnpbwFINfGRCSaZXlWKnIRMNdtBxEJbg2JbwMCvFBgXREJJ9dffyhWAJ4vsK6IhLMk74pFCsBzBdYVkXAW5V2xSAF4usC6IhLOM3lXLFIANEGjSDm4FIAX0CSNIt424HQRsBP9DOhpKXA69qz6OOBHvunUwo+wvhyK9e1S33RKZwF2LOZSdFTgRwuuXyfbgTOA2cDb2I56ETDLM6mKm4X14VKsT2djfbzdM6mSKXQMqgCE8wywuMnyO1MnUiM/b7JsMQWuetfQ/CIrFy0Avy24fp1kvYzxWtIs6kV92jvXArAcfRkiXl7CjsHcQswM9D8B2hCR/nugaAMhCsB/B2hDRPqv8B/fEAVgXoA2RKT/SlEAlqLxAURSe4EAz0SEmh343kDtiEjfzA7RSKgCcE+gdkSkb0pVAOah+QJFUtlMgDsAEK4AbEIXA0VSuY9AM3SHKgAAMwO2JSLZ7gjVUMgCcCfwTsD2RGRnW4FfhmosZAFYC8wN2J6I7GwuAUfkDlkAAG4P3J6IvN9/hGwsdAGYib23LSLhrQd+EbLB0AVgIwEvUIjI+9xOoKv/XUIXAICfRmizCgZkLI/Rx60iq0+zltfdraEbjLFzPkhrjtt2UMbyg5NmUS/jM5ZPSJpFOTwLPBS60RgFYAfwwwjtlt3+wDk9lg0BLnHIpS4uBYb3WHY+sF/6VNzdgB1bQbWHbrDhx8BVwOBI7ZfVrcBU7CxoH+Bi4ADXjKptNDbW4k3YhLTHAX/kmZCTTQS++t8lVgFYC/wX8LVI7ZdVO3BBIySMUcCV3kk4+3fgrRgNx7xAdR0RTllEWswO4NpYjccsAE8CcyK2L9IKZmEXAKOIfYvqHyK3L1J3/xiz8dgFYB7weORtiNTVo8CvY24gxUMq1yTYhkgdfTv2BlIUgLsoOHuJSAt6AvhV7I2kKAA7gKsTbEekTv6OBHfRUj2nPgudBYj01cMEGvSzN6kKwA7g8kTbEqm6K1JtKOWbag9Q//kDtgM3A+dhX+Iy33RqYRnWl+dhfbvdN53o7sTOAGppEvYF7nCIrEFLpwfcRrOXgZY4fd46xJJGH3Z3TsD2p9PcTKfPu43sNyCjSP2u+iKsitfRcuC2Hsvext7iknyuZ+cRpm4D/tchlxSuBxan3KDHYBXfAt5w2G5sSzKWP5c0i3p5vp/Lq2wVDi89eRSAN4G/ddhubO9mLO9MmkW9ZPVdVl9X2d9gb9Em5TVc1c3YY44iAo8At3hs2KsAdAJfp/5XdEV68w5wIU5nip4DVi4Evu+4fZEy+C7we6+NxxoRqK++A3wRmOiYw6PAlwK083rG8t8Far8V/S5j+TXYsHNFef8M/T1WAFraUaR5NkCTl0pfpXgO4B3g8FQfKEsZxqz/LfA97yREErsKjZXx/3bFTsdiVlwNTyZ9NYe4++Ij+P/8Lp2x2FNfsTr9TWBosk8jVTUMux8faz9cBxyY7NNUzLnErbx3AR3JPo1UTQc2+WbMfbBUF4TbvBNo4ibsvmgs67CfG8HmWJda6ACOJO4fiB9SspmiylgABmK/kaZ4JyIS0ALgWGCLdyLdlbEAgP1Gms/O88KJVNFq4FOU8C3GMtwGbOYF7LeSHhWWqtuG7culO/ih3POsv4jNh3a6dyIiBVwG/Mw7iSxlLgAAjwEjKcETUyI5XIc98FNaZb0G0N0A7NHMM70TEemHe7B9ttRjF1ShAAAMAu4HjvZORKQPngBOBDZ6J9KbqhQAgL2w0VLHeici8gGeB47HhvgqvbLeBWhmFTANeMU7EZEMK4DPUJGDH6pVAMDuDHwaeM07EZEeVmN/oFZ4J9IfVSsAYKPvTgPWeCci0rAaOJnEQ3qHUMUCAPA0cCoqAuJvFTAV2ycrp0oXAZuZgN0dGOWdiLSkVcApVPTgh+qeAXR5Fqu+ujAoqb2E3eqr7MEP1S8AYDPvnAAs9U5EWsZi4Dgq+Ju/pzoUALC7A8dibxCKxPQ49genUlf7s9SlAID9Hvs09gimSAz3YVf7V3snItnasZFXYg7rpGi9+BfK//KcdPNn2LvY3juOotqxDXulVyroFGw0YO+dSFHN6LrHLxX2MeLPOaCoXywARiO1MBAbbdh7p1JUI27C9hmpmXOJO/mIotqxFjgbqbWx2HyE3jubolzxGDAGaQntwBXYDK3eO57CN7Zhk9N+CGk5h2IXe7x3QoVPPAMchrS03YDvoLOBVoqt2Gi9uyHScCi6NtAK8RAwEZEmdgEuwJ719t5RFWFjNXAR1R8DQxIYgb1PoEeJqx/vAP+E5pmUHCYAs/DfiRX5YhYwfqdvVaSfpqLHiasUD2IDdogE9XlsQAjvHVzRPBY0viORaNqwOd90RlCeeBibRVoX+CSpk4B7gU78D4JWi85G35/Uy3ckEt0ngBuBDfgfGHWPjdgdGl3ck9IZBvw5Nlqs94FSt1gEfBO7RStSam3YKMU3A+vwP3iqGuuBf0PTwkuFDQK+DNwNbMH/oCp7bAHuBL4E7J6jv0VKqwP4CnAXul7QPTYAd2ADtgzL3bsiFTIQOA2YgU1s4n0Qpo4lwA3Y7TsNv+VE903L4wBsYpOu2Nc3neBewu7XzwPmAstdsxFABaDMPgoc1YgjgUOAIa4Z9d064ElgIfAb7MB/2TUjaUoFoDrasLOEQ4BJ2H3wcdgYh3s65bQWeB6boPU57PbnQmAZdpovJacCUA8jsDOG/YBR2M+HvRrLu2I4Nt5BR2Od7q/HdmJ/tWn8uw27BbcOe49+TSNexybFXIGd0r8V6wOJiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiEg3/weXg5P4WBwXrQAAAABJRU5ErkJggg==", - - "demoBuildingBlueprint.png": - "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAYAAAD0eNT6AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAACAASURBVHic7d13gFxlvcbx5z0zW5NND4RAOgEBaYIFQQVEeoeNBb0qKqiIFMVCc1AR8V5BQVRQREFaIh3BClwURCkiXDokAUJI22Sz2c22mfO7f4QkW2Z3Z7ac95yZ7+cP2J3Tnt3MznnmPWfOkQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8Mn5DgCgMDtdsWJ0ZVV2YtYqJgUWTnJmE82CieZsopMmmdxE52yimZyZquRUK0mysE5yaZOc5MZJkkKrlNMokyRTi5zr2DCvNZqTOblOmZolKVS4XrJ2mUzONTjTqlBqkHMNkhqUCxuc08qgQqtSTUHDU2dNafHx+wFQHAoAEAN7XLl6bJjunGmhzZQFMwNplslmmTRD0iSTJjqp2jYuYJuXtTzrs24PWu/5rJ9le83Tey7Ls3CXb9sk1yDTylD2mpwtCkItkrPFCsJFuWzt4pe/PLEpz6YBRIgCAETkndesmGJZ21nObW+yWWZupqSZJpvp5Cbk30F338smpABsesDyTH3rq9UyLZa0yJwWW2iLU9LznWH66UWnb7k8TywAw4wCAAyzPa60isCt3s5StoezcMfQaSeZ9pC01cZ5LM+et8wKQH8/wxpJz5rpcZmeMenZtmo9sfTkqevzxAUwSBQAYAje9Zumicq17yXZrqZgVzPbWdJcmVIb5si3+6QA9FyggJ8ha9LLTnoqNHvKOT3ZkbV/LDlz2uo8iwIoAAUAKMLeV6+c2hnY3i4M9gmd7S3T7pKCjdN779woAD2nDvZnyL9+WyjpISn4eyrnHnr5jCnPyrl8qwPQAwUA6MO+GUu3zly1q8z2cRbsHco+IGkLST12bptRALo/MPIFoNdjK0Knfznp8SDU38NxHX9f/OlZbXlWD5Q9CgCwkZl7929WvcPJHWyhO8gUvstJVZsmd5uXAhDLAtB7/W2S/mVyf1Rg97566tQnGSEANqAAoKzt9cu1E5TKflCmA8zZYZK2ljbufPrY+b71DQWgj+W6PBCDAtDjcVvppAdC6e4wtLs5hwDljAKA8pKx4N0zV+2eMneAOR0g0wdMqug5GwWg7wybJiWzAHR9KGfSkzL3FwXu7tfWbPWwMi7Ms0qgJFEAUPJ2mm+VY1pXHRiYqw+lQyRNLmznQwHIl2HTpOQXgJ4/xwpJ9zppwei1a//8TGanjjyrB0oGBQAlqX6+pd5sW7NXGIb1kj4qabJU7M6HApAvw6ZJpVcAuj7eaNJdgbkFk0e9+YfHT96zM8+mgESjAKB0ZCzYZ/aa90pWb6E+bLIte85CAei5/n5ylHcB6GqNme4O5RZsRRlACaEAINkyFuwzY9X7lXLzzNxx6vYxvT52XN2+oAD0mYMC0GtbJi2XdEsQhDe/3jjt75wzgCSjACCR9r565VSXTn1CspMlzcr/gk0B6DoHBaD3g8UWgB5zvCGn3+ZyqZ+9+dWpr+aJAsQaBQDJkbFgn1kN+wfOnRSajpGU3jiJAtDf+ikAm78d1gKw8atQsvtkwVVTRi+7nUMESAoKAGJv3982bJM1d4Iz+6Kk6VLvHSgFoL/1UwA2fzsiBaDrw2+adG0uCK9afsaMhXniAbFBAUAs7TTfKie1rT5Ksv8K5Q6RNt5cZwMKQB/L5V0/BWDztyNeADb+L5TZfc4F1zlnC5acOa01T1TAKwoAYmXfG5om5XK5LzgLT5G0pVTIzoEC0P/6KQCbv42sAHThljsX/kSVwc/e+NI2DXkiA15QABAL+1zfODtludPM3Gcl1XZ99acA9Ld+CkDebH3m81EANj3WLqf5Lkx9b+lXpz6fZxYgUhQAeLXf9Y17hBaeJtPHJKU2v3hTAPItTQHoa/151hW/ArDxi9BJ98jCHy89a8Zf8swKRIICgMhlMhbcP3vNYXL6hqT3dp1GAej+DQVg8xwlVAC6fvG4Obts2brpNyjjsnkWA0YMBQCR2Wv+6zWVHaM/K9npzjS7/xdJCkC+pSkAfa0/z7qSUQA2fvuKnC6tXpe7enFmVluexYFhRwHAiNvjSqsYM3rNp810vt663e7AL94UgHxLUwD6Wn+edSWrAGy0xGT/U9McXkkRwEijAGDE7HGlVYypbfyoZOeb05xuEykAfS/b5RsKwOY5yqQAbHjM6TWZXbi8efqvODSAkUIBwLDLZCx4cO7q42S60OTmSoW8oG56+K0vKAD5lqYA9LX+POtKcgHY+B+nxZJdtHz69Ks1z+XyzAoMGgUAw8fM7X/D2uNN+rYsfFu3Sb3mpQD0uWyXbygAm+coywKw2XNm9v0VM6dfTxHAcKEAYFh88IY1R5vp25J23vDi1c/O860HKAB9LNvlGwrA5jnKvABsfPwpF+i85V+ZfmeexYCiUAAwJAfctGoHywWXmtxBGx+jAHT/ggLQfSoFoL9EheUy6QEXBKev+Mo2/8mzOFAQCgAG5YO/aZqodO58SadYz+v0SxQACgAFYMBcgy8Abwkld706Os9acc7s5XlWA/SLAoCi7HGlVYwb3fhFSRdIGiv1fp2iAHT/ggLQfSoFoL9Eg8lljTJ9f2xNx49e/vLc9jyrA/KiAKBgB1zfeIA5+7GkHft78aYAdP+CAtB9KgWgv0SDybXp0ZcknbPyazMW5Fkl0AsFAAPa77qm7VOp8BLJDi3kxZsC0P0LCkD3qRSA/hINJlevZH9NhcEZy78x7ek8qwY2oQCgT/vOXzE6la34rpNOkZSWCnvxpgB0/4IC0H0qBaC/RIPJlW8N6jSzy1NB9fnLz5rSkm8GIPAdAPH0wRsaDk5nK/7PSafprZ0/gMSokHNn5qz96S2+/9pBA8+OcsQIALo57PrG8e1B+H1n7qRC310xAtDf+hkB6DmVEYD+Eg0mV94RgJ6bWVCZqvji0q9OXZV3ZpQlRgCwyYduaqzvCPSCkzvJdxYAw6q+I9f5wqQfvMrfNjZhBAD60I0rp5qruMKZjt7wiBX17ooRgP7WzwhAz6mMAPSXaDC5ChoB2PSlk7s3m8p+ofGrs1/NuyDKBiMA5czMHXhT40nOKp7fvPMHUMpMdkgql3p24sWvfl3zLTXwEihVjACUqUNuapyTM10naS8pz7sbRgB6TWEEoMc8jAD0njcBIwA9PJwN9Ym135yxMO9KUNIYAShDH7qpsT4nPaa3dv4AytZ7U4GenMC5AWWJEYAycsD81WPTobsilDuh/3dvjAAwAtB3hk2TGAHoPW/yRgC6/s39LluZOrnpzGmr864QJYcRgDJx0M1r9ktZ8H8md4LvLABi6fh0Z/jkuB8s+oDvIIgGIwAlbt/7LV29rPFcc+5cvXXXvoHfvTECwAhA3xk2TWIEoPe8yR4B2PyQ2eWr29efpcxOHXlXjpJAAShhB89v2t7C8HpJe3R9nAKggV8UKQAUgH63VdIFYKPHgjB3wqqz57yYdwNIPA4BlKhDbm78nMLwCfXY+QNAIUzaMxeknphw0eITfWfByGAEoMTse41VV9es/Ymkz0iFv7tgBCDf+hkByJdh0yRGAHrPW0IjAD1+rutq2is/vzQzdX3ejSGRGAEoIQfe2DCturbpQbkNO38AGBZOn2it7vj72O8snuU7CoYPBaBEHHbzmv1SQfoxSe/0nQVASdrdpfXo2IsWHug7CIYHBSDpzNwhNzd+PVTwZ0lb+I4DoKRNdAruGXfRoowyxv4j4TgHIMGOvGNlXWdb1TWSHbfxsU1HFQc6Fsk5AAWsn3MA8mXYNIlzAHrPW6rnAOSdxe5Su/6rMTOrMW8AxB4NLqEOnt+0fWdb5SNdd/4AEB13hKrcP8df+NrbfSfB4FAAEuiwBWuOTZk9JmlH31kAlLXtLAgfHve9RdxNNIEoAAlz6Py1p5kFC0wa7TsLAEiqM+duHXfRoozvICgO5wAkRP18S7W4ph/LdIqkAY5/cg4A5wD0XopzAHo/yDkAQzkHIM+8Tr9Y27boi8rsl80/J+KEEYAEOPDaZaNa1HT7pp0/AMSR6XNjK2fePSHz0hjfUTAwCkDMHXRLy1YV1TX/K+lw31kAYEBOB+Uq03+bcPFL2/iOgv5RAGLssJvWvT2dyz4irucPIFl2yeXSj4y7cNFuvoOgbxSAmDrs5sYDFIR/lzTddxYAKJZJW4dOD465cPEhvrMgPwpADB02v+kzcu5eSWN9ZwGAIaiTszvqLlz0Kd9B0BsFIGYOn7/2S3L2C0lp31kAYBhUOKdf1X1v8Zm+g6A7CkCMHLag6etyulx8PBNAaXFO9sO6Cxd933cQbEYBiIkjFqz9tpP44wBQspzT1+u+t+hi3zmwAe80fTNzh9/SfKnMTnvrgUFcZKXnfFwIiAsB9V6KCwH1fpALAQ3zhYA2h+oj1YbHnennTZ0zT1HGhX3MhghQADyqn2+pVrfuKkknbn6UAqAey1MA+ls/BSBvtj7zUQD6ni+6ArDh/3Z9c8ern+Kqgf5wCMCTk660ija37kZ12/kDQHlw5k4YUzHzel35WIXvLOWKAuDBIfdY1Zvj182XVO87CwD4YtK8ulUTbtMlr9f4zlKOKAAR2/caq063NN8lJ26fCQByh41en71dmUXVvpOUGwpAhE660irq6tbNl7MP+c4CALHhdODoCt2uy16q8h2lnFAAIlI/31JLJzVdK+kI31kAIIYOGr0ufYMy93MRtIhQAKJg5tpS637uzH3EdxQAiLFj69Izf6mMsW+KAL/kkWbmjrq16SfO9FnfUQAg7szpk6MrFl3mO0c5oACMsKNuXXeRmfui7xwAkBzulFEXLrrEd4pSRwEYQUcuWJsx09d95wCA5LEzai9ceI7vFKWMAjBCjvzd2tPk3Ld85wCApHLSd0dduPAs3zlKFQVgBBx1S9NnJXep7xwAkHimi0dduOhTvmOUIgrAMDvylnX7SbpC3GcBAIaDk9lVoy5cyPVThhkFYBgd8bt1OznZrZIqfWcBgBJSIdPvRl24cBffQUoJBWCYHHtLy1aB0z2SxvnOAgAlaIzM7qnJvLSN7yClggIwDOrnrxidU/h7SdN9ZwGA0uW2dungDv33slG+k5QCCsAQ1c+3VGdQ81tJu/vOAgClz72jtn39zZpvKd9Jko4CMESdqeYfyeko3zkAoIwcVvvSwit8h0g6CsAQHHVr81clfcl3DgAoO+ZOrvnOotN8x0gyCsAgHXPrumOd2cW+cwBAuXLOLqn97iJGYAeJAjAIR97RtL1J14jfHwD4FEj228rvvbqj7yBJxA6sSEfesbIuyLnbZBrjOwsAQKNTudyt47//yljfQZKGAlAMM5fOVl8jaQffUQAAm2zf1uF+IzOuwFoECkARjrmt+WxzOs53DgBAD05H1Vy4mBsHFYECUKDjbmk+QNIFvnMAAPpgdtGoCxYd5DtGUlAACnDsLa0zQmc3SuLCEwAQX0EY2A3V31k8y3eQJKAADOBT11i1uewtkib5zgIAGNAEp/AWXfJ6je8gcZf2HSDu1o5tuULSHr5zDFbKSe+cWqGdJ6c0oTpQOpCskAX7mKmgZfPM2Wu5wldUzKzdFhrUct222c8ahvz7GWDZYf395JnDit5Mkdvse6bBbdOG9Bwa/HY3Lzyk5fNuf4A19v3PNowZuk/sDE3LmnP6x+tt+vPCVmXDIW7Qn91rWjqvbJX+y3eQOOOMyX4cc2vT5yR3Vc/HbdN/ejzW5Ssb6I+3zxdgG2D9mx/I98dsXSZuNyGls95Tq23qGOgBUJyXGjp18t2r9OSyjk2P5Xtdk/LU/X4Kk/X6oq/Xsvwvknnn7SuXuU+3nT/r131EKXsUgD7U37F221wu+LdJo3tOS0IBmDs+pf/ef5SqUvwTAxic9Z2mw29YtqkEJK4ASC2Wsne0nz3nxT7ilDXeGuZx0pVWkculrleenX8SpJx01rtr2fkDGJLaCqerjpikdHL3FKNcTjfoyscqfAeJo+T+s46ghskt35HsXb5zDNY7t6rQtDH80wIYum0nVGj/WYk+n26P6uUTzvcdIo7YS/Rw/O1N7zNnX/WdYyjePplPKwIYPu+dVu07wtA4O7v6Oy/t6ztG3FAAujj6tjXjQnO/VcI/7z+umn9WAMNni1GJfkmUpEAKrht70avjfQeJE/YUXQQu/TNJ033nGCqO/AMYTkFpvKhs09aZ7fWprnJGAXjL8bc3f1qmj/jOAQAYGU46vvrbCz/uO0dcUAAkHXdL22wz/ch3DgDAyDLZz6oyL2/rO0cclH0ByGQssKDzt5LG+M4CABhxo+XcNcpY2e//yv4X8PSuLac6ub185wAARMRpn6pg4ed9x/CtrO8FUH9X6/QwG3536FfYTrY7XuzQ0yuzXR4Z8Er4/V7FK9+VDAdYRbfHDp5dqb22Lvy6Hfe+0q6Hl3QWtIVCr0JWyHXYC/v95LmGvfq+clnv1fRxT4WBLiM/hOvI99xG/p+g9wr7vjJlH1MHuhLcEJ5DfU7M9/scxO9q/1nVqt+xdoC5NvvrwjbNf7Yl78r7/70NwPr+9+nv9/CeaVX63DvqCt1Kqbqo5rsv39V67rav+w7iS1kXgDCb+4kSerW/4fTC6qwefL3rDrTvS3kWsgPd8KLax46r+ybyrn+nSamiCsDLa3K6/9WO7g/28cJY8GVI8+wZel7KubDfT+EFIP+logcuAAOvP0+h6/NS1IUVgOIvV53nqyJ/huIuh91XvkEWgB7b2mp0IKnwArBwTVZ3vdiaN2CfP0PBuQovABu/rEyXxmn9QzQmDN3PJR3mO4gvZXsI4Ljbmj8u6QjfOQAA3hxaecHLH/YdwpeyLADH3No0UdIPfecAAPjlnPtJXeaFSb5z+FCWBSAVBD+WtIXvHAAA7ya1p9I/8B3Ch7IrAPV3NB8s6QTfOQAA8eBMn666YOGHfOeIWlkVgCPuslqTXeE7BwAgXszZVfrvZaN854hSWRWAymzL92Rutu8cAIDYmVnZ2pzxHSJKZVMA6m9ft6NzOsV3DgBAXLnTK7/90k6+U0SlbAqA5C5RmV/3AADQr7RZcKnvEFEpiwJw/J0tR5l0kO8cKEwuLG7+bG5kcgB96SzyOdoZlvfVRpPESR+quuDlQ3zniELJF4D6+VZpoZXlRzySasX64l5di50fGKql64prnW8UOT/8MrlLdOVjhV+ONKFKvgCoquU0J23nOwYK9+ib2YKvhd6eM/17Wb77AAAj5x9L2tWWLexZGpr0wOK2EU6EYfa2qmXjv+g7xEgr6QJwzK3rtpDpHN85UJzXm3L6y6KOgWeUdPOzbWrpZHgV0VrXHuqXTzQXNO+tz63X4sbswDMiVkz6lkr8CoElXQAqAl0oaazvHCjepf9s0Yur+3/RfOSNTl3zn9aIEgHd/fAfTQO+s39qeYfOuW9NRIkwzMZXKH2B7xAjqWQLQP1tzbuZ3Kd958DgtHSaTv3jOi14rk3tOes17ZdPtuob961TlsP/8CQbmj59xyr9z8NNWtfe/YnYljVd9fg6HXvzSjV3MEKVWE4nV2YW7uI7xkgp3Y/FBfqRTCnfMTB4bVnTTx5br1882aodJ6U1tsqpoTXUcw1ZdTCiihjIhtKP/tmknz62TrtNqdQWtYHWtIV6fGmHWgs8RwCxlrLALpX0Qd9BRkJJFoB5d7UcbTn7gO8cGB5tWdMTnOiHGOvImf71Rrs2nr3Krr+EmPZPZV45PJeZc7fvKMOt5A4BZDIWWGgZ3zkAAKUhcPqeMlZy+8uS+4Ge2715nqRdfecAAJSMnSvdwmN8hxhuJVUA6udbypw733cOAEBpMdkFpTYKUFo/TGXLCTLt4DsHAKDk7FTpFn7Yd4jhVDIFYMO7fy76AwAYGRtGAe4vmZPnS6YAuKr1J4pL/gIARs7cCjftBN8hhktJFID6+VYp6Zu+cwAASl5GmWcqfYcYDiVRAFzl+s9JmuU7BwCg5M2scFWf9B1iOCS+AHzqfquW0zd85wAAlAnTubrspSrfMYYq8QWgdV3r5yRt4zsHAKA8mDS9YnWQ+HvNJLoA1M+3lJmd5jsHAKC8mOzMpF8XINHhXXXrcZLm+M4BACg7c1PulaN8hxiKZBcAszN8ZwAAlCdn+orvDEOR2ALwkdtb3yfpPb5zAADK1t7pb7+0l+8Qg5XYAmAuTHTzAgCUgDA403eEwUpkAfjYXW1zJR3hOwcAoNzZMVWZl7f1nWIwElkAwjB3phKaHQBQUlJZKZGfRkvcTrT+D2snmNMnfOcAAECSnHSiMi9M8p2jWIkrAK4zfaqkUb5zAADwltpAqc/7DlGsRBWA+vlW6Uxf8J0D0QqctO34lPbcqkKzxqUUON+JgO4CJ71tUoXeN71KO06uUIrnaNlx0ilJu0lQou5r7Kpbj5a0pe8ciEZFIH10pxodv0O1xlZtfkVd3Rrq+mfa9Lvn2hSax4AoeyknnfiOOn1hz9GaXJva9Pjq1lC/eKJZP310nbI8ScvFlJSrPiIn3eI7SKESNQLgnH3OdwZEoyrl9D8H1Okzu9V02/lL0oSaQKfuWasL9x3NOy14kw6kq46YqPPfP7bbzl/a8Bz9+t5jdONxk1SV5klaNixZ+6jEFID6O1tnybS/7xyIxqnvrNVuW1b0O88+0yr1yV1qIkoEdHf6e8bowDn9P//eO61KmQ+MjSgRYuBDyiya6TtEoRJTAFIKP6sE5cXgTRkd6LBtC7vT5kd3qlZtBe+wEK26qkAn7VFX0Lwn7DJaW9elBp4RpSBIK3ei7xCFSsQOdd/7LS3pU75zIBrvnlpR8Il+1Wmn3bdM1KksKAF7bVOlmgKH9lNO2n9W9QgnQlyYdKLmWyIaXyIKwFbN6w+XNNV3DkRjyqjinpZbjkrE3xpKyFaji3vOTRtLSS0jW6eeXXiI7xCFSEQBCE2JOrECQ5Mu8lmZZv+PiFUW+Zyr5LOrZcY+6ztBIWJfAE64Z/02TjrIdw4AAAp0mDLPx37UOvYFIJezz0jiPR4AICnSgVKf8pxhQLEuAJmMBTIl5oxKAAAkycl9RhmL9T421uGe3b35A5Km+84BAECRZksL3+s7RH9iXQACBfN8ZwAAYDACWaz3YbEtAPXzLSWnY33nAABgUEz1qo/vNQFiWwDSVS37S9rCdw4AAAZpSnqnl9/nO0RfYlsAQudiPXQCAMBAQim2+7JYFoCTHrMKJx3jOwcAAEPjjlfm/lheCjKWBWDt0vUHSJroOwcAAENimpzW9H19x8gnlgXABfEdMgEAoBhhTD8NELsCUD/fKp3pSN85AAAYJscr80yl7xA9xa4ApGvWHyRpgu8cAAAMC9P4tKr29x2jp9gVAEnH+w4AAMBwCs3qfWfoKVYFILPhuskH+84BAMDwcodKFqv7QseqALy4R+ue4uI/AIDSM0Xnvbyb7xBdxaoAhLJDfGcAAGAkBCnFah8XqwIghv8BACXKycVqHxebAlD/h7UTnPRO3zkAABgJZtpLmUXjfOfYKDYFIN1RcZCk2N41CQCAIUqnlD3Ad4iNYlMA5CxWQyMAAAw3i9Gh7ngUADMn6SDfMQAAGFnx+ThgLArAx36//h2StvSdAwCAEWW2lTKLdvYdQ4pJAQgtiNVHIwAAGDFhLhb7vFgUACc70HcGAACi4IJ4HPL2XgAOuceqxMf/AADl4z1xuDug9wIwobN1T0nVvnMAABAJU42U9n5ZYO8FQIH29h0BAIBIhYH3fZ/3AhCKAgAAKC8uBm9+/RYAM+ekvbxmAAAgevv4DuC1AHz8zvbtJE32mQEAgMiZttS5L83xGcFrAbCUeR8CAQDAi7Tzug/0WwAUvtfn9hFPrdni5m/psJEJAvShucjn3LqOcISSIMkCC8u3AMj8th/E06trcyM6PzBUL63uLGr+FxuKmx/lwVyZjgD8161NEyVt72v7iK9H3ujUugLfYS1pyunZVUUOGQBD9MSbHVrcWNjzrrEt1P2L20Y4ERJqR2WemeBr494KQC6d2ktSLO6IhHhp6TRd9e/1A84XmvSjR9cr5AgAIhaa9K0HGgt67l34t7VFHzJAmTA5qfo9vjbvrQBYEOzua9uIvztfbNfVT7aqr5fNzlC6+B8t+ucbDK3Cj/sWtelrf16jzlz+Z6lJuvihJt3wdEu0wZAsudDbFQHTvjYss1jcDhHxde3Trfrn0k7N27Fau22Z1tiqQA2toR5d2qkbn2nT600c+4dfNz/TokeXtuvkPer0/hnV2nLUhufoP5a066rHm/XU8g7fERFzQWA7+zpF1F8BkHbxuG0kxAsNWX37b829HjdGVBETr6zJ6mt/WbPhG+v2P2BAZs7bvtDLIYD6+VYjaVsf2wYAIEa2U2aRlxvieSkAqVHrd5KU8rFtAABiJK1cdgcfG/ZTACxg+B8AAEmS83JOnJcCEIoTAAEAkKQgCMunADjjBEAAACTJQj8nAvq6DsDbPW0XAIB4cX7eFEdeAD7+h5at5LRF1NsFACCmpijzcuT7xcgLgHWmOP4PAEBXYfQnAkZeAEIXcgMgAAC6CbeLeouRFwDnNDPqbQIAEGvOzYx6k9GfBGgUAAAAurOZUW/RRwGYFfk2AQCIM7PI943RFwAOAQAA0IMr7QJQ/+fVYyWNj3KbAAAkwCR97fm6KDcYaQGo6Khk+B8AgHxq0zOi3FykBSAI3cwotwcAQGKE0Z4HEGkBCIPoj3EAAJAMbmaUW4u0ALiQEwABAMjLRftRwGg/BcAnAAAAyC/ijwJG/THA6RFvDwCAhHCRngSYjnJjEncBRHECJ71raoV2n1Khukqnte2mx97s1GNLO2W+wwGSnKT3z6jWPtOrNK460Nq2UA8vadcDi9qU40mK4kyOcmORFgAnTeTvAYXadnxK571vtGaOTXV7/GM7VeuFhqwyf2vRkqacp3SANGtcWlccniHuIgAAIABJREFUNlE7b1HR7fHP71mnFxo6dco9q/Xcyk5P6ZBAk6LcWGSHAE68w+pMqopqe0i22eNSuuygMb12/httPzGtnx5cpymjor+YJSBJ24xJ69YPb9Fr57/R9hMrdNuHJ2v7ifmnA3nUKLO0NqqNRfbqub6ybWJU20KyOUnf3HuURlW4fucbXx3ozPeMiiYU0MP3DxinSbX9v4TWVQa6/NAJCvp/KgObda6PbF8ZWQGoCMNIhzaQXDtvkdZ2Ewo7OrXX1hWaWscoAKI1c1xaH5hRXdC8O02u0J5TGfxEgQKLbF8Z2StnzowRABRkly2KOzXl7ZOjPpcV5W7PqZVFzf/OIudHGUtFt6+M7q2TpRgBQEHGVhU3Xjq2ihEARKvY59xAhwqATXJB6Y0AKGAEACPDcXwVESv2mL4TT1IUyJXgCIDjEAAAAAMpwREAOQ4BAADQrxIcAZATIwAAAPTHuRIsAKEbE9m2AABIItP4qDYVWQEwGR+EBQCgP6aaqDYV3UmAjssAAwDQL1eCBUBOXAkDAID+ld69AGQUAAAABlCCBYA7AQIAMJDSOwTgJO6JCQBA/wq7y9QwiPBTAIwAAAAwgFRUG4pyBIACAABA/0qvAJgcJwECANC/0isAklEAAADoXzqqDUX5KQAKAAAA/SvFEQAAABAXURaAjgi3BQBAEuWi2lCEBcBRAAAA6F82qg1FeClga49sWwAAJFMJjgA4DgEAADCA0isATmIEAACA/pVeATAZIwAAAPSvLaoNcRIgAADx0RrVhjgHAACA+Fgf1YaiOwfAOAcAAIABlF4BCI0RAAAA+mUleAjAyTECAABAv6z0RgCcs7VRbQsAgERybk1Um4ruY4Cmhqi2BQBAIjkX2b4ywkMAtiqqbQEAkEihlV4BsAhbDQAACRXZm+UIrwNAAcDIMPOdAOUmLPI5Z+JJigKV4iEAhY5DACjI2vbiXizXtIUjlATIb3Vrcc+5Vet5jqJQ0e0rIzwEkGMEAAV5akXht8O2IucHhsOjS9uLek//zzf4FDQKFES3r4zwEEDACAAK8vSKrF5oKGyn/uBrHVrRwrsrRGtJU05/eqWw67U8uaxDjy/lOmgoVAmOAHS2VDMCgIKYpIseblFLZ//vsVauD/Wjf0V2zQygm3Pua9Sy5v7v3LquI9SZf1zDGQAo3Nro9pWRFYAF81yzIrzNIZJtUWNOp/6xSYsa87/APrUiqy/c28SxVXizvDmnY25eoUf7eHf//KpOHX3TSr3Q0BlxMiTYel06LbJLAaej2tBbGiRtHfE2kVCvrMnpxLvX6p1TK7T7lhWqq3Ja3RrqsTc79e9lHPeHf0uaNpSA92xTpfdPr9Lk2pQa20M9/Hq7/ndxm3K89UdxIj1UHnUBWCkKAIoQmvTIG5165A3eRSG+HlnSrkeWtGvjWD/7fQxSpAUgupMAN3gt4u0BAJAQ9mqUW4u0AJjToii3BwBAYjgX6T4y0gLgTIuj3B4AAIlhbnGUm4u4ANjiKLcHAEBiBGHpjgBIxiEAAADyChZHurUoN2apURQAAADyal8c5dYiLQDXH+qaJK2JcpsAACTASmV2ao5yg1F/DFASnwQAAKCHyPeNPgrAYg/bBAAgvlz0+8bIC4AxAgAAQHdWBiMAXAsAAIAePHxMPvoRgCB4IeptAgAQay76fWPkBSDrOp+MepsAAMRap/1f1JuMvAAsOLRupUwrot4uAAAxtVQXzV0Z9UZ9fApAcvaUl+0CABA3Ji/7RE8FwFEAAACQ5Jx72sd2/RQA+flhAQCIm1BWPgUgCENGAAAAkCTzs0/0UgAaUrXPSMr62DYAADGSVWPwvI8NeykA9x7q2p3sJR/bBgAgRp7X5XPbfWw47WOjkiTnnpZpB2/bRyLsMCmt+h2qtduWaY2rDrSmLdTjb3bqxmfa9MqanO94gHacXKHP71GnfaZXaUJNSqtbc3r49Xb94olmPbmsw3c8xJxzeso8bdtbATBzT0s2z9f2EX+f3KVGn961Rq7LY5NqAh00u0oHzKrS5Y+u1y3Pt3nLB3xqt9HK7DtOqS5P0i1GpXT022p11Ntq9YOHmnTZP5v8BUTshaG8nRTv6VMAUujCf/vaNuLvqO2qdGKPnX9XKSed9q5avX96ZaS5gI0O2bZG396v+86/Kyfp63uP0Qk7j4o0FxLGuf/42rS3AhCo82FJoa/tI75GVTidtHvtgPM5SafuWdvnCzAwUtKB9K19x/VZULs6+31jNbqSJynyCtVR8YivjXsrADccPm6Nk3FjIPTynq0rCn7BnDI60E6T/Z3KgvK059QqbV2XKmjecdWB9p9VM8KJkEhOz+j7M9b42ry3AiBJpuAhn9tHPM0cW9gL60YzipwfGKptJxRXOrebSElFb86c132g1wIgEwUAvVQX+VpZU8HwKqJVky7uOTe6wu9LLeIpdFbGBcD5bT8AAHiTzZVvAbjhiOqXnLTMZwYAADxYpu++bZHPAN7HpUzydgYkAAA+OKe/+c7gvQBwHgAAoNyEod/j/1IMCoA5CgAAoMwEKe/7Pu8FINta87ikVt85AACIhFOLlq7xdgXAjbwXgAXzXIdMj/rOAQBAJEz/1FV7dvqO4b0ASJIL3B99ZwAAIApm9gffGaSYFICchbH4ZQAAMOIsFYt9XiwKwM2H1/5b0pu+cwAAMMKW6Duz/893CCkmBUDOmaQ/+Y4BAMAI+4PkzHcIKS4FQJJzLhZDIgAAjBQXk+P/UowKgNTxR0k53ykAABgh2VxQ8VffITaKTQG44fBxayT9y3cOAABGgpN7WJlZjb5zbBSbAiBJzule3xkAABgJFrN9XKwKQC5UbI6NAAAwnELlYrWPi1UB2OGJ2sclrfCdAwCAYbZMmbneL//bVawKQCbjQileQyQAAAyZc7+Py8f/NopVAZAkc1rgOwMAAMMpUDjfd4aeYlcAxkyp/ZOk1b5zAAAwTFZlteQ+3yF6il0BuGpP1ynpdt85AAAYFk63KLNf1neMnmJXACQplN3sOwMAAMMhCBXLfVosC8Dy0aPuE58GAAAkndPy7HNzHvQdI59YFoAH9nNZcRgAAJB47nda4GJ5mftYFgBJcgpjOWQCAEChwpyL7b4stgVg+ydGPyCnN33nAABgUJzeVGrWQ75j9CW2BSCTcaGZbvOdAwCAQZqvDRe4i6XYFgBJMgtiO3QCAEB/wly8P9EW6wKw47+r/y5pse8cAAAU6RV9Z9tHfIfoT6wLQCbjQpl+5TsHAADFMLlfxu3a/z3FugBIUi5wv5IUy49QAACQRzZU57W+Qwwk9gVgwRG1b4g7BAIAkuNuZd621HeIgcS+AEiSTL/wHQHRyRZ5zmxnLtajbChB7UWOSXaEPEfLi0vEPisRBWDZmNp7JL3hOweisayluAawrDm2n7JBiXqjqbj7ury2Nnb3gcHIWZLbcfYffYcoRCIKwAP7uayTfu07B6Lxz6WdKvQNU0un6d/LeXFFtP6xpF3NHYU9SbOhdP+ithFOhLhwsqs1L56X/u0pEQVAkrIKrpbEW70ysKw51N0vtRc073VPt6oty/AqorW+03TFo00FzXv90816Y10i9gcYujCbTl3jO0ShElMAFhxZs8ikv/rOgWj85LH1+veyzn7n+cuiDt34DO+s4MdPH12nO15Y3+88D73Wrgv+d21EiRADf9K5s1/1HaJQiSkAkuQsGSdWYOjac6az/rpOv3yyVY1t3d/hL28J9cNHWvTtvzUXfKgAGG6hSafes1pn39eopT3e4Te0hvr+39fqY7euUjsjVGUkWfuotO8AxQjba+4Iqtcvl7Sl7ywYeZ3hhiH+3/5fq2aOTWlcdaBVraFeX5tjx49YMEnX/qdZ1/2nWdtOSGuLUSk1rA/1YkOn+HBK2Xkzt9Wau3yHKEaiRgAWzHMdznSF7xyIVmjSwsacnljWqdfW5sTrKuLGJL20OquHXmvX86vY+Zcjk/1EJ+/Z/3HLmElUAZCkXFX2CkktvnMAAPCW9WFl9krfIYqVuAKw4OCxqyX9xncOAADe8kudvUOD7xDFSlwBkKRUkPqhuD8AAMC/XCpll/kOMRiJLAA3HF69UNIdvnMAAMqbk25pP2/uK75zDEYiC4AkOXM/9J0BAFDu7FLfCQYrsQXgpqNrH3amf/jOAQAoV+7BzszcR3ynGKzEFgBJCgNGAQAAfpjCRO+DEl0Adnyi5jZJL/vOAQAoOy/mtO3dvkMMRaILQCbjQpl+5DsHAKC8OOmHyrhE36Au0QVAkmrH1l4taYnvHACA8uCk1zonWOKvR5P4AvDr/VybSd/znQMAUCacfVtfnlvYPctjLPEFQJLGL6v9paRFvnMAAEreK51T1l7rO8RwKIkCcNXJrtOcLvSdAwBQ6uyCpN30py8lUQAkSW21v5b0gu8YAICS9WKnLbnRd4jhUjIFYME8l5P0Xd85AAClyUnnK7Nf1neO4VIyBUCSrH3UjXJ6zncOAEDJeabD5izwHWI4lVQB2DAKYBnfOQAApcVJ5yX9c/89lVQBkKT5R4xeIOlJ3zkAACXjiY7z59zuO8RwK7kCIOdMct/yHQMAUBpC03kb9i2lpfQKgKQFR426U6Y/+86B4VGTdtpzqwrtN6NSu26ZVkXKdyKgu8qU07u3rtLh29Von+lVqq1wviNh+Pw1l5lzj+8QIyHtO8BICVN2RhC6J1XCP2Opq047fW73Gh05t0qVqc0vqC2dppueadN1T7cqV3KdHEmSDqQvvWuMTtpjtOoqN7+fas+arn2qRT94aK3Wd/IkTbCsczrDd4iRUpIjAJJ0y5F1z5jsl75zYHBGVThdflCdjn9bdbed/8Zpn9mtRhfvX6d0yT6DEXfpwOmaoybpK3uN6bbzl6SqtNPn3jFat8yb3GsaksT9rOP8OU/7TjFSSvqZ6TrCcySt9p0DxTvz3aO03YT+B2/evXWFTty1JqJEQHdffe8Y7Tuzut95dtmyUhd+cFxEiTDM1nRWtF/gO8RIKukCsGDe2NWOiwMlzrQxKX1wVmVB887bsVqjON6KiNVVBfrM7qMLmvfYHWo1cxxHIpPGOXe+zt6hwXeOkVTSBUCSxi0f9RNxieBEeedWaRW6S69KOe0+pWJE8wA97bVNlarThT1LnaT9BhgpQNzYc+1brr7Sd4qRVvIF4KqTXadz7iu+c6BwW9QW97Qsdn5gqKbWFfdRlGLnh19OqTNK5YY//SmLV84FR436vaQ/+M6BwqSKfFameW1FxCqKfI5WBBymSpC72r8164++Q0ShLAqAJKVCO1NSyTc6AMCgdTjTV32HiErZFICbj617zkyX+84BAIgp06XtmTkv+o4RlbIpAJLUkR51nsm94jsHACB2Xuqoqyzpj/31VFYF4K4j3PogCE+SxKW5AAAbWeD0RZ05rdV3kCiVVQGQpAVH1t0n6TrfOQAAcWFXt5035y++U0St7AqAJOXC8ExJK3znAAB4t6y9ouJrvkP4UJYF4LZjxzRYCd/gAQBQGHPuVH1zxhrfOXwoywIgSbcePfoGZ3an7xwAAG9+33He7N/5DuFLWV+g2lWkT7Vsbj9Jdb6z+LT9hLQ6cl0fsS7/7YPl/XLz9wWcZplvFtOGewEUY9vxKe03I9+9A3pvwfJMyhs1z4N5f86+dNlQ3p+zwNNQey6dL3/e5QrI3/c2u3+R/yfovcK+/j37nDrAv0Ehv6N+t1nAP2xfz9WBNr3D5MLuVbHR7PFpHbFdTd6V9/97G4D1/e/T3+/hHVsVl79ENQVh+HnfIXwq+8tTHXvbulOd3GVSjz8k6/a/bvK9aPR8oRvwBbjP9dsA69/8QF/Zznp3jfafwfXxAQyP+c+06PN3r+qzlPWqVX28PnWbd6AC2MeLZDFlsddbme65vtB+/pyf9xGzLJTtIYCNdvnP6Cvk3EO+cwAAImJ6sD2cfZXvGL6VfQHIZFwYBMEnJDX5zgIAGHFrnQWfVMaFvoP4VvYFQJIWHFmzSKYv+84BABhZJn2xLTNrse8ccUABeMstx47+jUk3+s4BABgxv+04f84NvkPEBQWgC1P2i5K96jvHUHGdYwDDKSyNF5XXqyrSjPR2QQHo4vZjxjcGTp+QlBtw5hhrbCv7Q1sAhtGKlkS/JEpSKAs+sbZML/jTFwpAD787eszfJPcD3zmG4umVif9jBRAjD73e5jvCUH2n7fxZ/+s7RNxQAPJYPW7U+TL3T985BuvRNzv1WhOjAACG7sWGTt2/KNE3yXu0bYs1F/oOEUcUgDwe2M9lwyB7gqR1vrMMRmjS/zyyXm3Z0jhwB8CP9Z2mk+5apWxy3080m3SCTt6z03eQOKIA9OH2Y8a94mSn+84xWC+tyelr97UwEgBgUJ5f1alDr1+mp5Z3+I4yeE5fbD9vzku+Y8RV2V8KeCDH3LruF5I+2/WxuF8KuOvEwEl7bJXWLpPTmlgTKB0U+CmBwi7/XvAK8uUf3JoKX2go4x8D3g1hyL+fAZYd1t9P30/GweaN5jnUfamhPIcGv93NCw91PK2fO1MUusAIZOg+sTM0vbkup4deb9NfF7Yq1/N1KEGXAjZnP287d84X+ogBlfnNgArRVj36S9Vt63aV3Dt9ZxmMnEn/WprVv5ZmNz02cMHo+w+4kD/eDesfuAD0v/6elarHfAUVpH5ecgd68cnzS+q5gy7s91P4zYDyF8Q+MhSUv/schRXQ3tvo8/dYVEnN81WRP0Pxz6F8j+f5fRayo+21rT6en0X8XvurmcXlyr+rLXxHu/GLAf5mk+WfbWPCxI7gRoVDAAO491DXHgbp4ySt9J0FADCgFcrljteX57b7DhJ3FIAC3HF07euSPqKEXx8AAEpc1uQ+3JqZu8R3kCSgABTotmPr7pPsXN85AAB9cPb1tvNmPeA7RlJQAIpw2zF1F0ta4DsHAKAHs9taz5l9qe8YSUIBKIZzls62nijpWd9RAACbvNAahp+Scwk/dzFaFIAiLZi3RXOYsmPl1OQ7CwCUO5PW5RQco8xcXpOLRAEYhDuPGvOCQvukJK6yAwD+5Jx0Qsd5M5/zHSSJKACDdPvxY2535s7ynQMAypXJnbn+3Nl3+c6RVBSAIbjt+NGXSLrcdw4AKDfOuUtbz511me8cSUYBGKLdnh59uqTbfecAgDJyd8vcmYzADhEFYIgyGRdW5EZ/TEru7YMBIDGce3x9Ve1HNM9xYbYhogAMgwXzXGtK7hjJXvWdBQBK2GInO0xnTWnxHaQUUACGya3HjXpTOXeopEbfWQCgBDUptCNbzpm93HeQUkEBGEZ3zKt71oXuGEkJvoE2AMROp5wd23L+nKd9ByklFIBhdnt93QNOOlmJv5smAMSCSfpMyzlz/uo7SKmhAIyA248b82tzdprvHACQeE5ntZw7+zrfMUoRBWCE3HXc2Mtl7nzfOQAguezslnNm/9B3ilJFARhBd9bXfUemi3znAICkcdKFLefM4fVzBFEARtid9WPOlokGCwCFcu4nzefMPtd3jFJHAYjAncfXnSXpF75zAEDcOdNvmjtmcA5VBCgAUXDOqsK6L0juRt9RACC+7JZ12838jDKOO61GgAIQkQXzXG6rhtGflMSdqwCgtzubJ63+KJf4jQ4FIEJXnew6162rmye5P/nOAgCxYfpTc112nk7es9N3lHJCAYjYA592bdXh6COcdJvvLADgnbm7m7M6Sl+e2+47SrmhAHiwYJ7rqLK6eidd6zsLAPjinG5eN3nVscrMavOdpRxRADxZMM/lqqzuRHO62ncWAIie+21Tx+KPM+zvj/MdoOyZuSN+13yJyU5/64ENNxHocSeBXjcWsPw3G7AeE/ucp9/12wDr775Az/k2rL/XGnvN1P/6rfdjXecr6PfTe8lC8m9efz8/Q8G/n3wpeufPt/58SxeXv/sc+dffT44+MmyaVNRzNM9XRf4MxT+H8j2e5/c50L9F3m318fws4veaP9FgcuW/7UjBv+dNXwzwN9t7lj7m7fv1Y+O8zuynTZ2zTuVsf7/SvgOUPefsLumMwxesXSu5b/mOAwAjynRx0zmzv+E7BjgEEBt314/NmMQfBYDS5ZRZd84sXudiggIQI7+vH3Oxk06RxLAYgFJiks5o+uasC3wHwWYUgJi5q37sT83cZyRxYgyAUtAh2Sebzp71I99B0B0FIIbumTfm16HsYEmNvrMAwBCsCcwd3HT27Ot8B0FvFICYunfeuPuCINhH0mLfWQBgEBaFKbd34zkz7/cdBPlRAGLsruPrnjGl9pL0mO8sAFCEf+XM9mr++sznfAdB3ygAMXfPvNHLOtta95V0h+8sADAgp9tHdVTt13LO7OW+o6B/FIAE+NN/TWkZpTHHyeky31kAoC9OdtnatpnHLc1MXe87CwbGlQAT5tD5a0+T9EOZUlwJcKD1cyXArnNwJcDeD3IlwGG7EmDOnDuj6RszLs8/B+KIEYCEuWfe2B/LueMkrfOdBQAkNQXOHcPOP3koAAl0T/2YO2RuT8n+z3cWAOXLSS8E5vZa840Zd/nOguJRABLqno+MebHVdezlnFvgOwuAcuTukMu9e/XZM571nQSDwzkASWfmDl3Q9GUz/bekCs4B6Lp+zgHoOgfnAPR+kHMABnUOQM7kzln7jek/kHN9nBWAJGAEIOmcs3vmjf2xU3CAJD52A2AkrTK5g9d+c8bF7PyTjwJQIu75cN2DSmf3lPSI7ywASo+THlcu3HPtN2f8xXcWDA8KQAm597iJS1zdmH0l43oBAIaPs6tWt7W8t/Hc2a/6joLhwzkAJergG9d8Ss5dbtLontM4B0ADHxflHADOAeh3W2VzDsA6c+6Uxq/P4GY+JYgRgBL1h4+O/7XMdnayh3xnAZA8JvevnKXewc6/dFEAStgfPjp+cduW4/Z1ZhdIyvnOAyARcs7ZxY3jV+7T9M1pL/sOg5HDIYAycchNa98Tyn4raQ6HADTwsCiHADgE0O+2SvQQgOlVSwWfWPO16X/Lu1KUFEYAysS9Hxn7SJDNvUMyhvMA9Oa0ICe3Ozv/8sEIQBn60E2N9c50paTxGx9jBCDf+hkByJdh0yRGAHrPm8QRAFOTc/pSA8f6yw4jAGXozx8Zt0Ap7eHk/u47CwB/THowTOd2YedfnhgBKGdm7sCb135Opv82aUyXCYwAMALACEC/20r4CICpyQKdv3r99MuVcWHeFaDkUQCgg65r2SpMd/5E0rEbHqEAUAD6zrBpEgWg97wJKAAm/V7p9BdWf2Xr1/MuiLJBAcAmB96w+ghzwc8k25oC0HsKBaDHPBSA3vPGuwAsN6evNXxtxrV5F0DZ4RwAbPKnj024q7PN3m6yq5T/dQlA8pik69pzqZ3Y+aMrRgCQ14HXr3l/6PQLSdt1fZwRgK7rz7Ncni8YAeg+lRGA/hINJle/IwALzenkhrO4gQ96YwQAef3phPEP1tS17i6z/5HU6TsPgMI5qcNJ369OB29n54++MAKAAX3wpqbtnOW+K1M9IwBd159nuTxfMALQfSojAP0lGkyuXsn+kg51+rJvzHgmz6qBTSgAKNgB1zceEDq71ElvpwBQACgAeeb1WwBeMGdfWXXWzN/nWSXQC4cAULC/nDDuL+FW43Z35k6WtMp3HgCSpDUyfWPl+uZd2PmjGIwAYFAOmr92QrYz/JakL5qU7jqNEYDuXzAC0H0qIwD9JSoqV1bmfhVk0+cuO3urlXlWA/SLAoAhOfCGVW/LWvoSyQ7Z+BgFoPsXFIDuUykA/SUqNJe7z6V0+vIzpz2dZ3GgIBQADIsP/nb14XLuOybtRgHo/gUFoPtUCkB/iQbM9YSZO2/l16bdk2cxoCgUAAwfM7f/jWsOl7kLzGz3bpN6zUsB6HPZLt9QADbPUdYFwOkZF9oFy86a/js5l28RoGicBIjh45zd97EJd73/pXF7OmfzJL3gOxKQbO55M/vk8unTdl32tRkL2PljODECgBGTyVjw4NzVx8l0YSg3t9tERgD6XrbLN4wAbJ6jzEYAFkt20fLm6b9SxmXzzAIMGQUAI26PK61iTG3jR0Nn33LSbEkUAAoABSBfIqfXzOySusqOn7/85bnteVYDDBsKACJzyD1W1bp6zaclO1Pm5lIA+li2yzcUgM1zlHYBsBecdMmE5uZfP5PZqSPP4sCwowAgcpmMBffPXnOYAvdlmR3QdRoFoPs3FIDNc5RoAXjcnF22bNr06zXP5fIsBowYCgC8et91je8IXHi6TB+VlKYAdP+GArB5jhIqAKGT7pHLXbT0KzMfzjMrEAkKAGLh/dc2znJB7nQz9xlJoygA+ZemAPS1/jzril8BaJHT9a4zuGTp17fmEzLwjgKAWPngb5omdqayn1doX5I0RaIA9L9+CkDebH3m81IAlpm5y1OB/XzJmdNW54kMeEEBQCzVz7fUytaG/ULnTjLpWEmprtMpAH0sl3f9FIDN30ZWAEKZ3edccNWU0ctuf/zkPTvzRAW8ogAg9vb6zaqtU0HwcWf2eUkzJQoABaDn+vOsy08BWGrSdbkwd+Xys2YuyhMPiA0KAJIjY8E+sxr2D5w7KTQdLali4yQKQH/rpwBs/nZECkBOsvtlwVVvrNv6Ni7cg6SgACCR9r1mxZRskP6kFH5OcnMoAP2tnwKw+dthLQCvy+mGMEj9dOlpU1/LEwWINQoAki1jwT6z17zXTPMkO17SVpIoABSAkSkATktl7neh0/ylp099mGvzI8koACgdb5UByeot1DyTTek5CwWg5/r7yUEB2KjBTPeEcguWNk29lyF+lAoKAErTW2UgDMN6SR+WtKVEAei9/n5ylHcBWGPS3YG5BZNHvfkHzuJHKaIAoOTtcaVVVFau/qALrN5Mh0qaQgHoZ9kBMmyaVHoF4E0zu0eBLXi9cdpfeaePUkcBQNnZ+9cNO5nZ4eZ0gEzvN6lMn4/dAAACqUlEQVSy5zwUgL4zbJqU/AKQM+lJmd2tIHXXa1/e6gmO6aOcUABQ1na5dtmo2lxqf2fB4ebsEEnTJApAfxk2TUpmAVjhpP8NpbsD67xz8RmzGvOsAigLFACgi3ddvXzXIEgdbKaDJHu3pNqN0ygAPeZJRgFYL+kROf3RWfiHRadPeyrPIkBZogAAfdg3Y+mWmau3D8Jwb5n2Maf3S5ohiQKQZ+GYFIBlofSYc/p7YO6hwLU8+vKX57bnWT1Q9igAQBH2vnrl1M7A9nZhsE/obG+ZdpcUbJxOAej+wMgXAFso6SEp+Hsq5x56+Ywpz3IcHygMBQAYgn1+2ji+raZjL5cLdpVs19BpZydtJ1N6wxwUgJ5TB/kzZE16QU5PK3RPucA9mcu2/YNj+MDgUQCAYbbHlVYRuNXbhUFuR2e2kwVuD5l2lDR74zwUAPX3M6yR9KyZHpfpGZOe7cjlHl9y5rTWPHEBDBIFAIjI7r96c7JCt0tgbjtTMEuymdpwd8NZkiaVWQFYqVCLnWyRuWCxOS02cy+kKsOnXjx56qo8sQAMMwoAEAM7XbFidKoqnJlWapaZzTKzmYGCWaZwuklbSJooqSYhBWC9pAaZVpjcazItNuUWWaDFFUFqURAGi585ZYvmPJsGECEKAJAQe1y5tLa9wiamsxUTFdrkMLCJzoJJJptoziY6cxNlmiintEnjJFXINFqyGslVd3kPXiNTtdRtp90mudbNj1mbTK0mNUvWKadGZ8pK1hDKNcipwZlWKXQNCtyqwDpXtck1jEu7hsdPnro+wl8LAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEun/Af3OJlev0ZvTAAAAAElFTkSuQmCC", -}; diff --git a/mod_examples/add_building_flipper.js b/mod_examples/add_building_flipper.js deleted file mode 100644 index 03442499..00000000 --- a/mod_examples/add_building_flipper.js +++ /dev/null @@ -1,130 +0,0 @@ -// @ts-nocheck -const METADATA = { - website: "https://tobspr.io", - author: "tobspr", - name: "Mod Example: Add a flipper building", - version: "1", - id: "add-building-extended", - description: - "Shows how to add a new building with logic, in this case it flips/mirrors shapez from top to down", - minimumGameVersion: ">=1.5.0", -}; - -// Declare a new type of item processor -shapez.enumItemProcessorTypes.flipper = "flipper"; - -// For now, flipper always has the same speed -shapez.MOD_ITEM_PROCESSOR_SPEEDS.flipper = () => 10; - -// Declare a handler for the processor so we define the "flip" operation -shapez.MOD_ITEM_PROCESSOR_HANDLERS.flipper = function (payload) { - const shapeDefinition = payload.items.get(0).definition; - - // Flip bottom with top on a new, cloned item (NEVER modify the incoming item!) - const newLayers = shapeDefinition.getClonedLayers(); - newLayers.forEach(layer => { - const tr = layer[shapez.TOP_RIGHT]; - const br = layer[shapez.BOTTOM_RIGHT]; - const bl = layer[shapez.BOTTOM_LEFT]; - const tl = layer[shapez.TOP_LEFT]; - - layer[shapez.BOTTOM_LEFT] = tl; - layer[shapez.BOTTOM_RIGHT] = tr; - - layer[shapez.TOP_LEFT] = bl; - layer[shapez.TOP_RIGHT] = br; - }); - - const newDefinition = new shapez.ShapeDefinition({ layers: newLayers }); - payload.outItems.push({ - item: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(newDefinition), - }); -}; - -// Create the building -class MetaModFlipperBuilding extends shapez.ModMetaBuilding { - constructor() { - super("modFlipperBuilding"); - } - - static getAllVariantCombinations() { - return [ - { - name: "Flipper", - description: "Flipps/Mirrors shapez from top to bottom", - variant: shapez.defaultBuildingVariant, - - regularImageBase64: RESOURCES["flipper.png"], - blueprintImageBase64: RESOURCES["flipper.png"], - tutorialImageBase64: RESOURCES["flipper.png"], - }, - ]; - } - - getSilhouetteColor() { - return "red"; - } - - getAdditionalStatistics(root) { - const speed = root.hubGoals.getProcessorBaseSpeed(shapez.enumItemProcessorTypes.flipper); - return [[shapez.T.ingame.buildingPlacement.infoTexts.speed, shapez.formatItemsPerSecond(speed)]]; - } - - getIsUnlocked(root) { - return true; - } - - setupEntityComponents(entity) { - // Accept shapes from the bottom - entity.addComponent( - new shapez.ItemAcceptorComponent({ - slots: [ - { - pos: new shapez.Vector(0, 0), - direction: shapez.enumDirection.bottom, - filter: "shape", - }, - ], - }) - ); - - // Process those shapes with tye processor type "flipper" (which we added above) - entity.addComponent( - new shapez.ItemProcessorComponent({ - inputsPerCharge: 1, - processorType: shapez.enumItemProcessorTypes.flipper, - }) - ); - - // Eject the result to the top - entity.addComponent( - new shapez.ItemEjectorComponent({ - slots: [{ pos: new shapez.Vector(0, 0), direction: shapez.enumDirection.top }], - }) - ); - } -} - -class Mod extends shapez.Mod { - init() { - // Register the new building - this.modInterface.registerNewBuilding({ - metaClass: MetaModFlipperBuilding, - buildingIconBase64: RESOURCES["flipper.png"], - }); - - // Add it to the regular toolbar - this.modInterface.addNewBuildingToToolbar({ - toolbar: "regular", - location: "primary", - metaClass: MetaModFlipperBuilding, - }); - } -} - -//////////////////////////////////////////////////////////////////////// - -const RESOURCES = { - "flipper.png": - "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAYAAABccqhmAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDcuMS1jMDAwIDc5LmRhYmFjYmIsIDIwMjEvMDQvMTQtMDA6Mzk6NDQgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCAyMy4wIChNYWNpbnRvc2gpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOkZDMkFGQkY5NkUyQTExRUM5QUY0OTQyNUQyODU2NURGIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOkZDMkFGQkZBNkUyQTExRUM5QUY0OTQyNUQyODU2NURGIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6RkMyQUZCRjc2RTJBMTFFQzlBRjQ5NDI1RDI4NTY1REYiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6RkMyQUZCRjg2RTJBMTFFQzlBRjQ5NDI1RDI4NTY1REYiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz43izVKAAATMklEQVR42uzda3QU533H8b/u97tW2tUFjA0YA8YYkMHESgoB14a6iVv7nNTN6eUcjBO3dU7rtGnavmybXnL8MiRv01M7rp3WdmzjxNjIlgQCIRsJkHTACAmJO8LIGDBKDJ3/aEUwF+mZnZnd2d3v55zniMvsXPX8Zp555pnN2LDxGQGQnjLZBQABACANZbML0lKtVZqt8oBV2qzSapUT7BYCAKlrmVWejlb6Odf8+3eiPw9Ew+BHVtnF7qIJgNTx91bptMqfX1f5rzUn+v+d0elBACDJ1VvlNav8wOHnfhD9XD27kCYAktcmqzwS42cnP/f77EauAJCcl/2PuJzHIzQHCAAkn/IYLvunag6Us0sJACTX2d9L32eXEgBIDnOt8j2P5/l3VrmTXZt6uAmY5O5etPgLf9/Ts/u7Pi3qWWtZG6//R2t5HAQCwDMZHBJnrquA663ypOlnI3X1cuzoEdPJn7SW9Qvr5+vsdVeuEAAiOTLxRNrk46jzrRKySi6/H/FRXl4h1dUhuXjxgpz9+GPTj73GnnPtvFVGrDIkv30MW3/+JtUD4G6rPGGVVVZZzu9B4mRmZko4Umf/ORyuk0/GxuTy5cvsmPgokon7KVoevObfO6zSYpUXrRK3dlU8bgLqZekbVumRibvTVP4EC4VqJCcnZ+JSzPqpf0fCrYjWjw+j9WV9KgTApmibcR3HNxhycnKl+roKX20HAq2vAFkXrTc/Fp/vi/kVAF+KnvG/xbEMlnAkYjcBbmwSRNg5wfNUtB49kEwBoJW+LdrmR4AUFhbZN/9uRv+9sKiInRQ8C2XiRuGzyRAA/x697EcA1dVPPbivro7BfwH2Q6v8Z5AD4K9l4okxBFBFRaUUFBROOY3+v06HwPputJ4FLgC0jfIcxyeYtI1fGzZr44fDN94jQKA85+U9AS+OdAaX/cEWqqm92u03nWztFqypZacF2ybxqHcg26OVWej0Q6WlZbJg4QK5beZtUl1dbd+AysrK4tAaeGfLFtmxo8No2tzcXAmFQs4Cw5r+4zOjMj4+bjT98uUr5Ktr1nBgDOg+PXfunIyNnZWhoSHp6+2Vs2fPOp3Nwmi9c93LlrVkqavnctY5vfTXiv/Qw+tk/fr1MmvW7Vabs0Ly8vK47DR08uRJ+cVrrxpPX9/QKPn5Bc4u6TIy7CsG/SU1ceTIiNw5b54U0YswfYWzTnKFhRP3WmbNmiXLmpqkqqpajh09JpcuXXIyK33Ja5dV9ieyCfC0k4nnzJ0rG596ShYsWGD/ksG5zp07jafVCllWFtu7PPRzRUXFvqwXvhi2Wh8m64Wf9c/rAFgkDh5XbLrvPnnsscftS1LEpr+/X7q7zR8Tj9Q1uFpexEG3oK5Xf18fBylGWi++9vVH5b7ljq7IH47Ww4QEwDdMJ5w/f76sXfsgZ32XPugyf11/RWWVFBQUuFqefr7Smo+prq4uDpJLa9asteuLH/XQ6wBYZdrmX7f+9ziyLm1rb5fBwUGzg2q1M8PhsCfLrbXmk2l4c3ZoaNBeT7ij9UXrjZf10OsA0N6DFUZrt3oVl/0uXbx4UVpathpPX1NTK9nZOZ4sW+dT46BbUNdT1xfumgOrv7radPIV4qI3L9YAMHoQoayszLqcWcARdam9rdXRL4++6MNLOr/c3Dxf1hc3d9dd8+3642V99DIAms3a/tztd+vE8eOy08Ed9kik3vN9rvOL1NUZT6/rq+sNd/vcwcmzOd4BYJQ4M2+byZF0qXNXp/G0RcXFUmp+1nBE26Q6f+P17uzk4LnkoP7E/QrA6Mk/3jTjTl9fr/R0dxufMfwezafzN7266Onplt7eXg6iCw7qz8J4B0C1yUT6xBNi17XLvFtNnyxz+sSfUzr/ikrz0YJOui3hqv6E4hkAegowuq3Ps/2xa29vk8OHh8R0P5uO9nOrtjZifFwPHz5sbwdi46D+5EiMg4N4AD+AtBvtvZYW4+knuv3i84JnXY6TbkHdDroFg4sACKC21veNp9WBVFUed/tNR5eny/Vje0AApLWjR486uoOu7/ePd1erLi8cMb/hqNuj2wUCANPocnDjrLi4xMkjo54qLS2V4pISX7YLBEBa2rt3r+zp6TE+C0cS/BJPJw8d6Xbp9oEAwC046TbTUXr5+fkJXV9dvpPRgnQLEgC4hStXrsjIyIjRtBPdfuFArLeuh2l3lW6fbicIAFxneHjYvNLVaqULxje763ro+vixnSAA0igADhtNl5dnXXZXVQdq3XV9dL283E4QAFwB3ISOygvaCEsnowW5AgiWbHZBMDQ2NsrAwYPTTjd4aCDptxNcAeCGijGD7QQBkM5XAGwnCIA0pe3ohoaGlN5G3T7eEEUA4BaWLF3G9oEASFcLFy60KsnSFK38S+3tAwGAKTz00MNSU5ta386r26PbBQIABjZseDJlrgR0O3R7EEw8BxDgK4GGhkZ7AI3pGIEg0Rt+2ubnsp8AgIt7Alr0a6P1CTp9jHb48LAVCMMBrPCN0jij0e7n164+J28MAgGAKWhlmj17tl0A7gEAIAAAEAAACAAABAAAAgAAAQCAAABAAAAgAAAQAAABwC4ACAAABAAAAgAAAQCAAABAAAAgAAAQAAAIAAAEAAACAAABAIAAAEAAACAAABAAAAgAAAQAAAIAAAEAgAAAQAAAIAAAEAAACAAABAAAAgAAAQCAAABAAAAgAAAQAAAIAAAEAIBrZSfLivb398lbmzfLhQsXAruOVVVV8tjjj1s/q285zfj4uLz6yv/JgQMHYpr/E3/8TSkpKUnodiZiG1rff09aW1vjdhwrKyvtP8+cOVNCNTVSXR1K+H5P6wDY09MT6MqvRkdHpXffPmn+8lduOc3p06diqjiT8+/r3Sf3LV+R0O1MxDbEq/JPrqMWde12NjQ0yD2LF8vtt9+RMmGQLfDU8ePH2QkpamRkxC6qqalJVty/MumDgAAAYtDZ2WmX5uZmWb7ifsnNzU3K7eAmIOCCNk1+9sLzVpPhNAEApGvT4L9++lM5dGiAAADSkd6gfuH55+3eKgIASFP/+/OfJ1VzgAAAPKbNAX1WggAA0rQ58Mu3NhMAQLras2ePHD16hAAA0lV7WxsBkG7y8/PZCbDpY8RBvwogADw2Y+ZMdgKu2rd3b6DXL20eBV6zZo3vy9BRY7Nm3c5vfcDo6L57773XaNqxsTEZGBi4OhjIdQDs2ydrH/xdAiDREj2CDomjQ3udHn+vhp/r57UZUFdXTxMASBbz5t0lT//FX9pDgN06deoU9wCAZKMj/B79gz+UwsJCV/M5eeIEAQAkIx3vv3LlSlfzOHv2LAEAJKs7Zs9O2W0jAIBpTPWORwIAAAEApKpkfdsPAQB4YPJFoAQAkGZ0XH/H9u2u5lFeXk4AAMnovZatrh8LLisrIwCAZPP2r35pv/rbrYbGxsBuY9qMBdi5o8OzeWm/cCp3DaX7Jf/AwEHrzN/i2YAg/VoxAiDBtmzZ4um8/uqZ76Tkd8WloiNHjshL//PitNOdOXPGs0o/ac6cOYH+0hC+GShG5859QgAkCR2RF+t3Gbp196JFgd433AMAfKKDiPSLRAkAIA3pIKKgf2cgAQD4dPZffO+SwK8nAQD4YNXq1UnxjcEEAOAxvfN/zz2Lk2JdCQDAQ/oKsa99/dGkWV8CAPCIvn1YXyGWDJf+k3gOAPDozP+NP3oiqSo/AQB4oLm5WZavuD/pKj8BALg8669Zuzaw7/wnAHyid3mDPMAD/h9/fcRXvzsg2aVNAPzDP/4Tv7lwVenn3nmn/WhvKo0B4QoAKc/JdwNOysvPl1AoJHl5eSk99JsAQMqL5bsB0wXPAQAEAIB0RBMgyej31+vXTfspmbu1QACkNH1JpRcvqpyK9m//yZ/+GTubJgDSkX4Rht9XGSAAABAAAAgAAAQAAAIAAAEAgAAAQAAAIAAAEAAACAAABACAFA4AN9+zrq9tDgp9maiOtgsyff/dVEOC3WyDvp7rjtmzHX+uqakppuXpl3S6+d1JdRkbNj7j+DNWuWwyIS/iBNz513/5Zycn8ys0AQAQAAAIAAAEAAACAAABAIAAAAgAdgFAAAAgAAAQAAAIAAAEAAACAAABAIAAAEAAACAAABAAAAgAAAQAAAIAAAEAgAAAQAAAIAAAEAAACAAABAAAAgAAAQDAN9nsgvRz/vx5GT58WIZHhqWxoVEaZ8yQoqIidgwBgFR17Ngx6eraJSPDw3LmzJmr/965c6f9s7KyUhoaG2Xp0mUSiUTYYQQAUsX2bdtk69Z3p5xGQ0FLT3e3rFq1Wu5fuZIdRwAgmZ07d07e2vymHDhwwNHnNCxGrObBQw+vk5KSEnZkCuMmYAqLpfJP0s/p50EAIEkv+2Ot/NeGgM4HBACSyGeffTZtm99Jc0DnBwIASXT299K2be3sVAIAyUDv5G/f7m0AdGzfLqOjo+xcAgBB19Gx3Zf57tjRwc4lABBkH330kez+8EPj6SN19cbT6nw/cnlTEQQAfKRP+pkqL6+Q6uqQlFdU+DJ/EACII73xd9C6AjA66JmZEo7U2X8Oh+vsv5s4ePAgNwQJAASN026/UKhGcnJy7D/rT/27qZatW+kWJAAQJNvazc/KOTm5Un1dha+2AyHXeB7t7W3sdAIAQXD69GlHd/7DkcgNl/wTTQLzEYA7Ojrs5YIAQII56Z4rLCyyb/7djP57oYN3AtAtSAAgwfbv3y/du3cbT19XP3W3X52DbkFdri4fBAASxEm3XEVFpRQUFE45jf6/TufH8kEAwEPaHXdoYMDsIFtt/NqwWRs/HI4Ydwvq8ukWJAAQZ9oNp91xpkI1tVe7/aaTrd2C1vSm6BYkABBn7W3m3XC5ubkSCoUczV+n18/5sT4gAODCyZMnHd2B1yf+MjKcHWadfvJJQRO6PrpeSI8AuGKVcZMJP//8c/awxybf4mtCX/VdVlYe03L0c0VFxb6sF8w4qD+/jtbLuF0BGD0FcuHCBY6ih/r7+6W727zbL1LX4Gp5TkYL6nr19/VxkDzkoP6cincTYJ9RSpw6xVH00AdOuv0qq6SgoMDV8vTzldZ8THV1dXGQPHTqlHGzam+8A8Dors/g4CBH0SP6vL/p/szMypJwOOzJcmut+ej8TAwNDToal4Bp9ufgkKf10csAaDWZqLe3V65cucKRdOnixYvS0mLe7VdTUyvZ2TmeLFvnU+OkW9BaT11fuKP1RuuPl/Ux7lcAY2Nnpa+vl6PpUnub+fHV7jt90YeXdH65uXm+rC9uTuuN1p+gXgHoXccdJhO++867Mj4+zhGN0Ynjx2WngzvskUi9ZGRkeLoOOr9InXm3oK6vrjdio/VF640h7RP+TbwDwK7bJhN98smYvPnGGxzVGHXu6jSetqi4WErLynxZj9LSMnv+xuvd2cnBi5HWF603hra6WZabAHjRdMLe3n3y9tu/4n5ADJeB+mWdpmdpJ6P5YqHzN7266OnpdtKGRdQ7W7bY9cWPeuh1AOhv5mbjM4J1Wfjyyy/RHHCga5d5t5qO4svPL/B1fXT+FZXmowU/YLSgo8v+V195xel7FjZH62HMspYsXe7m83qd8oTpxGdGR2Xv3r1SXFJs31jyuq2aSvS1W6Zn/6ysLJl52yzjUXxu6EtFPj4zanQ1NzY2JplZmTJjxgwO6C3oftQrvZdfetn+RmaH/sYq+xMZALpwHWe6zPQDly5dsp9o6+nukfMXzusekOzsbHu0GoEwQbvRfvbCC8bT6xDe4jh9jbeGjB6nTz89ZzT90OCgLGtqMh6NmOouX75s7btP5eiRI7J794ey+c3N9k+tFw79xCrPuV2fjA0bn3E9D23yWWUhhzf+8vLyZM7ceXENTz1rHdjfH8svLbyhT/4tkhif//fqHsDV3werfJtjkhgTo/3ie+WkywtH6tn5ifNtLyq/VwGg9EGEZzku8VVcXGJ3zyVCaWlp3Jod+IJnxcWDP34FgETbI//B8YnfWThSl9izsB8PHWHaOvaclzP0+rbx92gOxIeO0svPz0/oOujynYwWhCt/68dVth/9Rj+2ygNW2cMx84d2+9V6NNrPLV2PLMPRgoiJ3vBrtsoP/Zi5Xx3HOiZ0UTQM4HWlq9VKlx2QMMq21we++Em0Hvn20kW/nxzR5sAj4uCJQUwtL8+67K6qDlZzxFofXS94ZnO03nxLPLrbn6gAUK9bZV00yf5NDEcR4uZ0VF7Qbrw5HS2Im9oRrR9LovXl9XgsNJ7XkXpP4PvRP+dE7xM0R38usEpNnNcnKQ0eGmAnJDcduqvv+toXvbRvjf78dSJWJlEVTjd2q9w4lDGd+5S+bJWWNNjO37HK+2l8nAM1JDabnRMYrWm0nYwLDwi+GCQ4LltlW4pv47bodoIAwE38iO0DAZC+/tsqm1J02zZFtw8EAKbwtLh8y0sAdUe3CwQADCxOoSuBTdHtAQEAh1cC35TkvTG4Lbr+nPkDjAdvgn9PQIsO+n9Afvvw1JcCuK46/mPyoRYtYxw+AgDe0Mr0RrSoID4wRd8+AQAqG7gHAIAAABBs/y/AAPho4dBfgj+jAAAAAElFTkSuQmCC", -}; diff --git a/mod_examples/base.js b/mod_examples/base.js deleted file mode 100644 index f76ded9b..00000000 --- a/mod_examples/base.js +++ /dev/null @@ -1,20 +0,0 @@ -// @ts-nocheck -const METADATA = { - website: "https://tobspr.io", - author: "tobspr", - name: "Mod Example: Base", - version: "1", - id: "base", - description: "The most basic mod", - minimumGameVersion: ">=1.5.0", - - // You can specify this parameter if savegames will still work - // after your mod has been uninstalled - doesNotAffectSavegame: true, -}; - -class Mod extends shapez.Mod { - init() { - // Start the modding here - } -} diff --git a/mod_examples/buildings_have_cost.js b/mod_examples/buildings_have_cost.js deleted file mode 100644 index 3dae84ae..00000000 --- a/mod_examples/buildings_have_cost.js +++ /dev/null @@ -1,89 +0,0 @@ -// @ts-nocheck -const METADATA = { - website: "https://tobspr.io", - author: "tobspr", - name: "Mod Example: Patch Methods", - version: "1", - id: "patch-methods", - description: "Shows how to patch existing methods to change the game by making the belts cost shapes", - minimumGameVersion: ">=1.5.0", -}; - -class Mod extends shapez.Mod { - init() { - // Define our currency - const CURRENCY = "CyCyCyCy:--------:CuCuCuCu"; - - // Make sure the currency is always pinned - this.modInterface.runAfterMethod(shapez.HUDPinnedShapes, "rerenderFull", function () { - this.internalPinShape({ - key: CURRENCY, - canUnpin: false, - className: "currency", - }); - }); - - // Style it - this.modInterface.registerCss(` - #ingame_HUD_PinnedShapes .shape.currency::after { - content: " "; - position: absolute; - display: inline-block; - width: $scaled(8px); - height: $scaled(8px); - top: $scaled(4px); - left: $scaled(-7px); - background: url('${RESOURCES["currency.png"]}') center center / contain no-repeat; - } - - .currencyIcon { - display: inline-block; - vertical-align: middle; - background: url('${RESOURCES["currency.png"]}') center center / contain no-repeat; - } - - .currencyIcon.small { - width: $scaled(11px); - height: $scaled(11px); - } - `); - - // Make the player start with some currency - this.modInterface.runAfterMethod(shapez.GameCore, "initNewGame", function () { - this.root.hubGoals.storedShapes[CURRENCY] = 100; - }); - - // Make belts have a cost - this.modInterface.replaceMethod(shapez.MetaBeltBuilding, "getAdditionalStatistics", function ( - $original, - [root, variant] - ) { - const oldStats = $original(root, variant); - oldStats.push(["Cost", "1 x "]); - return oldStats; - }); - - // Only allow placing an entity when there is enough currency - this.modInterface.replaceMethod(shapez.GameLogic, "checkCanPlaceEntity", function ( - $original, - [entity, options] - ) { - const storedCurrency = this.root.hubGoals.storedShapes[CURRENCY] || 0; - return storedCurrency > 0 && $original(entity, options); - }); - - // Take shapes when placing a building - this.modInterface.replaceMethod(shapez.GameLogic, "tryPlaceBuilding", function ($original, args) { - const result = $original(...args); - if (result && result.components.Belt) { - this.root.hubGoals.storedShapes[CURRENCY]--; - } - return result; - }); - } -} - -const RESOURCES = { - "currency.png": - "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAACXBIWXMAAAIAAAACAAF+ftPjAAAFwWlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNy4xLWMwMDAgNzkuZGFiYWNiYiwgMjAyMS8wNC8xNC0wMDozOTo0NCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIDIzLjAgKE1hY2ludG9zaCkiIHhtcDpDcmVhdGVEYXRlPSIyMDIyLTAxLTE2VDE2OjAzOjE1KzAxOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAyMi0wMS0xNlQxNjowNDowMyswMTowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyMi0wMS0xNlQxNjowNDowMyswMTowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6M2IxMTM4ZjEtNzdmMi00MzcyLTg4ZDktZTgzN2I4NzlkNGUwIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOmU1ZjZhNTU3LTIyZmEtNDQ3Zi05NDU2LWI3N2ZhNDM4MzRmYSIgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ4bXAuZGlkOmU1ZjZhNTU3LTIyZmEtNDQ3Zi05NDU2LWI3N2ZhNDM4MzRmYSI+IDx4bXBNTTpIaXN0b3J5PiA8cmRmOlNlcT4gPHJkZjpsaSBzdEV2dDphY3Rpb249ImNyZWF0ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6ZTVmNmE1NTctMjJmYS00NDdmLTk0NTYtYjc3ZmE0MzgzNGZhIiBzdEV2dDp3aGVuPSIyMDIyLTAxLTE2VDE2OjAzOjE1KzAxOjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgMjMuMCAoTWFjaW50b3NoKSIvPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6M2IxMTM4ZjEtNzdmMi00MzcyLTg4ZDktZTgzN2I4NzlkNGUwIiBzdEV2dDp3aGVuPSIyMDIyLTAxLTE2VDE2OjA0OjAzKzAxOjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgMjMuMCAoTWFjaW50b3NoKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz5/oDBEAAAJ60lEQVR4nMWbeWxUxxnAf7ten+ADDBhz2BAwGBscMHcLgRIChYooaQmhLSQ0qAInJERVGzV1FdS0pEqrpFSUGlKSkKqiEEqREqAFSsJNucFg7sOG+uBwbIzxbW//+Gx22Z15u37vbfhJT5bfzJuZ79t5M9983/ccaWlphJBoYCSQCTwODAD6AMlAlE/dOqAUuAZcBPKBAuAIUBuqATpCoIABQA4wDhhhU5tHgX1AHqIc27BTAU8hgj9rV4MaNiGK2GFHY04b2pgIbAW2E3rhae1jO7AFmGC1MSsKeBxYCXwJTLM6EBNMB3YBHwNZZhsxq4Ac4CSwwGzHNjIPOAUsNPOwS1eQn6u+n7WUZcDiYBqPdEFmMvTvCgOSoE9n6JEAXTtKmTf1TXC7GooroagcLt6Cy7ehoFTKgiAPyMjP5TXNuJVoF0FfBWQtpQ+whiDeuz6JMHUQDE+BQd0hPjrQE2ru1sK5Mjh2Hbadg8LyoB7bA7yQn0uR901LCshayg8QDccZ9dwrAX40Fsb3h+6GNdtPWRXsuQwfH5RZEoC7QE5+Ln9vu6FTQMA1IBjh46Jg3hhY+xI8l22/8CBtzsqG9fPh5Scg1teMeph4YGXr2A0xVEDWUuYAqzAQPqsnrJgNP3kSEkxO9fYQFwULx8OK52FoL+OqwKpWGbQYGUKPIatrR12FWdnya3TuYDzoUHG3Fpbvhk+PGVarRrbsq6rCsMTERN2DbwFPqAqcDlg8CV6dCDERQY/XdqLCYVw/2VEOF2qrRQAtwDZVoU4BC4HfqAqcDvjFVJgzChyOdo/5AfVNcjU2g9sNLpMWicMB2b1lFu6/Am51tbFAGeA3V1SvQBYy9f0Ic8JrE2Wlt0JxJSzZApduiUKT4+Gt6ZCeZK3dDw/An3ZDc4uy2I28Cqe9b6r0nqPrYOYwmDvayhCFGxVQUAIVNVB+H86UiDKs8sJo+N5QbbEDhWy+CpiIxqQc0hNeecL8VPXGjbXXR0d4GLwyAQb30FbJAcZ73/AV52eqp2Kj4M0pkBBjeYwA1DdCg495e/GmPW13ipGxdozUVnnD+x/vRXAK8Lbqibmj4GnT5y0PFTWw4xx8sF/sfm8KSmH9cTh4Tay+mEhRvJkZlxQHVXVw8n/K4gHAAVq3Re9FcBPwjG/tXgmwbr4YIGapbYTP8sWMLbkb/HNTBsGS6QGtPiWVtTD7Q21/m4DvgucVGIhCeIB5Y60JX1kLv9oCS//dPuFBZsud++b6TYg23K2eBdLAowDlyt83Eb41wNwAQPb5P++BrQXmnh/ZBxItWJmTBsjJVEMOeBQwTlXjyXQ5u5vldImxmRrpguhwuSJdYhN4MyrV2uzrGiuvkYZxIA6RGGC4anCj+5jvvMUt732LwjTrGAkvjoEZQ6BbrBgut+7BhZuw/yocvAr3GwIedoJieApEhEFDs1/RSCDKhUJ4gIxkcWaYpbEZjhapy74/AhZ4zTmXE3p3kmtyOtysgusVctK0SkZ38UqdUO8II5zAYFVJWldr06+xWX5VFT0TjJ9NioORqf5uMzPER0O/rtriTCcwVFUy0KJd3uKGZs3JZOcF2ae/LtL1M3mYk9btwJfUztY6jXRBN80Cuu8yvL5BzgCN/u+m7RjIkuYEUlUlyfHWOg0Pg/EaX4sbOHod5qyBJZvlGFsZsuifoSypTkC51HSLtdap0yGnRyOPcIsbNp+BnHXw03/CP06IuWw3SXpZejoB5bHBjgUoPUn8B777u4rDhfD2Vnh5HXx+OmD1dmEgS5QdsUFDZmbDr2fIKS0YCkrFdH7vP2obwm5CrgAHYvCs/iFMGhjc1trQDJ8cglX7Qj26r0EBbaR1g2UzYdlz8J3BstcHYvV+OHEjtONyAfUo1oH6JnvWAV9GpMh1tlTM3m1nJQ6oorEZ1h6Bob1lJpnFILZY5wSKVSU6K84uMpLhx9+E5c/L3zDNXNx7Bb4yeSRu46ZelmInoLTYS9t5djdLcpzEF14crd4tahrEiWoFA1mKnMAlZclX1jptL5PT1REmBxIBskKRPqp80QmcUJWct8FJ2dwS/K9X2wh1jf733UBni85YA1lOuoAzqpLLt+TAYuVEePyGuMLSk8QxMb6/mMi+lN+XoEZ1vX9ZpxhI1Xt1AnK3VhItNJxxAcdVJWfLJDnBilPkiwtw9Y5cuy6JeZ3SCTJ7yAGlvgnOl8lucF3zygVrO+g4WyY7joZjLqAGiZk95Bipb4JDheYVUN8kgrVR0yAZHoXlkujQtuAZWXtxURKDtMKx60pvEEgCZl3b5qO0uXae9/ffB8u1cuOUlha3sfDhYfDGU9Cvi7n+Qbby7ee0xfvAYwnmqWpcK4cvLeRlmjWkEmLgl9OsB2O+uGD4I+SBJzJUjniG0n1rXbkjtnxUePs679xBvLodIiUaXKtY4X2JjYKnh8DPp8iCaYXKWsj9TL2w4sk2fSg0VgHM9a15r0724jF92zcAR2vY+xuPiQLH9RffXHQ43Kl++L10IFHnd5+R3cKqLwIkHrHvirZ4Ma2hMW8FXAFGoXCRXboti6GZgTkckkXSKwGG9YZpmeL0OOVlgCd2gEUToG+X4HwHgThVDL/foV38Pgce5Iz5WuC/Uz1RXQ/vbLPPW+ObVmOH0G1U1MC727VTH3xk9FXAbjQL4pkSWLEbmtTZF5awy/HR2AzLd8lYNeThs+OpzmB5aFJtNp6Evx4yPb4HZCZL3l9MhESJhvYSA8kqnxyCTcrkHkBk8vtxdWlyC5BMcD+cDnhzKswabv6M3uIWu7/tl49wSfjKCuuPwW+3Gc6mhUjO40PossSOIdmWfgFmN7C31ZLLTjGX6uJwiKET4ZJL5wsIhuYW+OiA+BAN3qT3gXdUBUZ5gpeA+UienR9HiuQQM6QHRD+iXMHKWnhvpyReGFANvIRs835odZ+fy1Ukhq41hjcch1c3aAOPIeXEDVi0XsZgQDWSNK3MEoUATtH8XP6GrAdVujqni2Ug7++EyhAENXypqpOVftGnkK905nmqAgtaZdAS8O3Lz2UtkmCoPRXcq4M1/4XZH8liVKpVl3lKq6TtWavhL/ulTwPuAgtbx25Iez6YiAL+heQSGpLSGb6d4flgwmwWeWWNnOfbPpjQ+Qx82AXMs/WDCZ+G/gC8HsxIIsLE+dGvi3iFUhOhR7yk3fgeruoa4VY1lFSKP/LCTbhyG86UtiuC/Mf8XPXY2q2AACxEYzE+QnLQ2C5GmN2BV+L5bO5R8wEWxmIlNJaPaH0CsNlCO2bZjHzPsKB1LKawIza4B5gBTAY22tBeIDa29jUD2Gu1sVB8PJ2G5+PpkTa1eRjPx9OXbWoTCI0CvIlClJCBuNy8P5/33RxrgRKgELE5TuH5fF5/urfI/wGbHtxP6bdutwAAAABJRU5ErkJggg==", -}; diff --git a/mod_examples/class_extensions.js b/mod_examples/class_extensions.js deleted file mode 100644 index ace5aae9..00000000 --- a/mod_examples/class_extensions.js +++ /dev/null @@ -1,32 +0,0 @@ -// @ts-nocheck -const METADATA = { - website: "https://tobspr.io", - author: "tobspr", - name: "Mod Example: Class Extensions", - version: "1", - id: "class-extensions", - description: "Shows how to extend builtin classes", - minimumGameVersion: ">=1.5.0", -}; - -const BeltExtension = ({ $super, $old }) => ({ - getShowWiresLayerPreview() { - // Access the old method - return !$old.getShowWiresLayerPreview(); - }, - - getIsReplaceable(variant, rotationVariant) { - // Instead of super, use $super - return $super.getIsReplaceable.call(this, variant, rotationVariant); - }, - - getIsRemoveable() { - return false; - }, -}); - -class Mod extends shapez.Mod { - init() { - this.modInterface.extendClass(shapez.MetaBeltBuilding, BeltExtension); - } -} diff --git a/mod_examples/custom_css.js b/mod_examples/custom_css.js deleted file mode 100644 index 0d28fda7..00000000 --- a/mod_examples/custom_css.js +++ /dev/null @@ -1,44 +0,0 @@ -// @ts-nocheck -const METADATA = { - website: "https://tobspr.io", - author: "tobspr", - name: "Mod Example: Add custom CSS", - version: "1", - id: "custom-css", - description: "Shows how to add custom css", - minimumGameVersion: ">=1.5.0", - - // You can specify this parameter if savegames will still work - // after your mod has been uninstalled - doesNotAffectSavegame: true, -}; - -class Mod extends shapez.Mod { - init() { - // Notice that, since the UI is scaled dynamically, every pixel value - // should be wrapped in '$scaled()' (see below) - - this.modInterface.registerCss(` - * { - font-family: "Comic Sans", "Comic Sans MS", "ComicSans", Tahoma !important; - } - - #state_MainMenuState { - background: #9dc499 url('${RESOURCES["cat.png"]}') top left repeat !important; - } - - #state_MainMenuState .fullscreenBackgroundVideo { - display: none !important; - } - - #state_MainMenuState .mainContainer, #state_MainMenuState .modsOverview { - border: $scaled(5px) solid #000 !important; - } - `); - } -} - -const RESOURCES = { - "cat.png": - "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAACXBIWXMAAAGxAAABsQFhmCgOAAAE8mlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNy4xLWMwMDAgNzkuZGFiYWNiYiwgMjAyMS8wNC8xNC0wMDozOTo0NCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIDIzLjAgKE1hY2ludG9zaCkiIHhtcDpDcmVhdGVEYXRlPSIyMDIyLTAxLTE1VDEzOjI3OjU1KzAxOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAyMi0wMS0xNVQxMzozMDowMyswMTowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyMi0wMS0xNVQxMzozMDowMyswMTowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NTI2OGI2OWUtYTQ2Ni00YmNkLWJjNGYtM2VlNmUwOGI2NzA2IiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjUyNjhiNjllLWE0NjYtNGJjZC1iYzRmLTNlZTZlMDhiNjcwNiIgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ4bXAuZGlkOjUyNjhiNjllLWE0NjYtNGJjZC1iYzRmLTNlZTZlMDhiNjcwNiI+IDx4bXBNTTpIaXN0b3J5PiA8cmRmOlNlcT4gPHJkZjpsaSBzdEV2dDphY3Rpb249ImNyZWF0ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6NTI2OGI2OWUtYTQ2Ni00YmNkLWJjNGYtM2VlNmUwOGI2NzA2IiBzdEV2dDp3aGVuPSIyMDIyLTAxLTE1VDEzOjI3OjU1KzAxOjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgMjMuMCAoTWFjaW50b3NoKSIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4V61CfAAAOpUlEQVR4nO2df4wU133AP+/NzM7+vN07DjjggGAgtQvBNjV2oG0SqcKuFavmR0IwjmpRx3+0aSLquk4bRbaIKjlpo5BUdlzHVtwmtes4kkkjWYhUKWlLyw//CI7tmNSODRjzw8DdLXu3uzM7M69/zMH9YHe521tuZz3zkfbY3dl5+915n/m+N29mHkIpRUR4ka0OIKK1RAKEnEiAkBMJEHIiAUJOJEDIiQQIOZEAIScSIOREAoScSICQEwkQciIBQk4kQMiJBAg5kQAhJxIg5EQChJxIgJATCRByIgFCTiRAyIkECDmRACEnEiDkRAKEnEiAkBMJEHIiAUJOJEDIiQQIOXqrA6jKC+uvw+NJYBZCfYMbf7yj1SE1xMF1f4ES9wHvI9nKqp2HWh3SeEQgJ4g4sP6/gd8b9c4P8YytrH621KqQJsW+TQlk5UngM6Pe3ctNO3+/VSHVIqhNwPJxrz+DrOzlwMbelkQzGQ5s7EVW9jK28uHS3xQIgiqAqPLeSvBeZN+6NdMezUTZt24NeC8CK6ssrfabWk5QBajFbKTYw4F1W1sdyCUcWLcVKfYAs1sdymQIZiewPjEQ3+PA+hUcM+7j08+6LY3mR5s0FlS+AWJbS+NokHbLAKPZxoLKLvbe1tmyCPbe1smCyi5gW8timCKBFqA46HH2ZIWhQs2dfC2GcYD9G66ZzrgA2L/hGgzjALC22uKhgsvZkxWKg940BzY5Ai2AVfI3XmnQo1R7Qy5FqP0c3PjJaQvs4MZPItR+YGm1xaPjvfAbgkrb9AGGCi5SAzNR1dkOlPcTDq7/G27c+XeXLD185+/giJuJmTaaNguYA2oO0DP8iVMgTgInUd4phDiBVPtZ9MTRS8o6uP5+FA9RY+exSl69jBU42kYAgMKAi5QCw6x6RCVRfJ2D61agJbchvS+gvNuBxZhmmkR8+GNVB76WX3xfDJftCfjNPa+h1PNo4nkK1suUzz+GEnfWiq9iKQoD7VP5ENyRwAEg23/GwXXGxicEZGfo6EYVCaQEXYdKBZSCZBISiZFKnQoKB6usUyyCd2ladyqK/DmH8ZtT0wWdM3WAPDftzE09kOYS6D5ANZSC8/0unjtqS1+oeM8D2/Zf53K+AM2ofACBTjwOXV2QTvvfMYznKs73u5dUfjvQdgKAv8HzfS4KAYbhV7zj+Avjcb/y9SvYusXj0NkJqRQKQb5vnJBtRFsKAGAkdYQUfrq/QCrl753N2uvrIQQkEqhcDhFrq67UGNpSgI5ZJukUY9vidNpv76cZqWvkrppBvCs57d/dDNpKACEEnXMTxLRxnbB02k/LrUII0vOypHtziOnIPk2kbQSQUtA5L46mnLELEonWVv4o4p0JOhZ1tZUEbSGAEJDrMZHuuMo3DL/dDxBGKkZ6fq7VYUyYthAg1xNHqnEDLFJCR0drAroMZjZOqifT6jAmROAFSM+Mo1FldC2Vmp7efoMkZqbbomMYaAGMpEHcqHIyxTDANKc/oEmSnptFixutDqMugRYglRFUHV4LWLtfEwHJgDcFwRVA1xFVxtyJxa7sKF+T0ROxQGeroAogqp1wAQJzyDcp/IwVyA5LMAUwYsmqAkjpZ4B2Q0pIJgOZBoInwEt/lMR1quf4dtz7LxCPx2B74LZ34AJCJu6tmf6NYPeo6yKl4K3jH2t1GOMJngCoO2ouaqPOX1WE2NjqEMYTMAGExHGvrrpI1wM98DMx1Iag/YhgCfDq5rvxvOoxTSD9DxYV+cKVuzCjL68oladU/lx+c3egbm0LVk6tuJ+quazOjtM34PHIDwq89qYkmU4hsbn7UxprVjbn5719zOGRHxQ4eS6BETNIxh3u/5zBot6G9p/VwP80JbAmEKwMALXv/pXVQ3UcxQM7Bvj10QTf+u4OvvXYDhYu/jD/+IzineNTvya/b8Djy3/fj4hfxaP//DBf/4eH0M1Otj/sULYaygaBusM5WAIo1VVzWY0McOCQzZHjDtffcB1z5s0hm+tg1UdvACH4p+cqVdeZDM/vKTFYVPzux9eQzWXpmTObZSuW4SnJj3Y1UL4Q86YcVBMJVhMAtQfOa2SAI+/51wgc3PcCh18/TCbbwd6f+xn2/b6pB3TkuF/+z3b/B6tWr6JYLHLopVcAePt4AxlABSsDBEsAz6s90lNjbKBS8SthaHCIr9z3IMlU8nKrTIrK8H0JR94+yl/+2V9hxkcG9BynoSYgUBkgWE2A52l1llV9W9dGmgbHGXvFUDw2dQPqlZ9NNyRAoOYPCJYAUtausRoCLJw34oxTcbDKFij/+bLFUxdgdPm2ZVOx/Xbftm1WLW/okP7clINqIkEToPYkUG71e+5uus5k7uyRShoaHKK/rx+7XOD2tVM/d/CHH08QH74XUSlF4XyB/nP9dCRKrF7Z0Pmd41MOqokESwAhztdcViMDxGKC7dtyLP/wyEDRvNmSr96bY3Z37RZloszu1vjqvTl6e0bKWrZUZ/u2HLFYIxlAvDfloJpIsG4O/cXmV7CtFVWXCeHfl1dnQKgw5FGpQFfuynjdN+BhGJBJTaV88TCLv/uFpgU1RYJ1FAAngeoCKOXf+Fnn6pqpVczlaYpYQgUqAwSrCdDFnrrLbbspX+N5YNlQcUYe05YIBS9N0zdNiGBlgHPxb5O0HkKp6nnetv2amsIJtVcOO2z/dh4hdFKZkYtLe7oF926V9HRfwZN1ij6O9u5h0ZX7iskSLAE+8WTZe+HT79hF66qKrXAdhecpPFfheeC6CnXsxMgcHwqU/weAbG+WzOz6V+GeOuNh2y7gEk/G0TS/c3fqrOL8IPR01w+xcLpA/njefyFA+H/8l0IgNIGma0hNInX/ocU0jKSBETN2a5940Kld+vQTiE7g0adu/rwQcj2ojwCzGi0n05Ohc2H9WeM8D/7kSwOc7bOJmTHSmTQA118j2HaXdtnk0n+0n8KpQqMhArwP4lWlvJ0L7/zpI1MpqBkEQAAhjj198xlghv8KRzekbsQEuiGREqQmkFL4z6VApNMQM4bX9mtMSIEWm9hhX9+A4p4v92FZLtnOLPPn6Dz4eW1kGqHL4NouyvO328Xtp/znnuvhOd6Yf52ygz1U8dxyxVMjWffcgi0/nTmNvY+qBKAJUArW3ogQazzFa05Of2N+T+YAtn1tzVU8CxIjY/7KU1iDFoNnBnHKDtneLLpZ+6d15QQPfDHLgzsGUG6ZbXd1XLbyHcshfzyPHtcxMyZm2vQnqJg4D7/1f+/dr/c710jBcpT631ZXPgQiA1Thtc2LKdpv1uoMKgWWMihbCqtgYQ/ajP4dXR/qIj07fdmvKZYVmhSYE7jSfPD0IH1HRk4vCiGIpWOYGZN4Jo7ZUUcIRR9xtZTeJ5pwfrK5BFMAgEN3/BCrvOnCS89TlIZc/1FyUeMGBnXT3zPj2TipGanm34ahYOjcEOV8Gatg4Vhj+3JCChK5BIlO/yE1OXrdP2fJ4y1v76sRXAF+tSmmhlS+mC/HhwouVnnsLFy6LoinDcwZGcwOE32a5+lxbAfrvIVVsCjny2OEEEJgdpikulMkZ6ReF0vy10KLJ7WuQSAFOPHMLfMdxZ9KIb7oeeriwXrMlCRSGomURiw2vIeZJmTGHvq5tsv5I30YSYNkTwdSHzve5ZYdrHwJ66w/sGR2xzCzCbT4WIk8x6N46jyVYoWOD3XV7WTaRZtSf4lSfwl7aGTASkiRV0p9Rxc8Onfz7ncb2yJXjkAJcPSZtddLT3xFIW4HNABNF+WOrBFPpjU0vUZeTyTG3DHsWg4Db55FKYXQBLG0iRY3UJ7CKVWoDFpVizHSJnrCQEiBW65gD1ooVyGEILe0G61Ox3I0ru1SPFckfyLf7zneheNSV6D+zZPqbxdu/vdfTHijXGECNRQsPPmcQmwAJIjdSorb5r2xOpWZmThSs/IBSiUoly++1Eyd3NJujLSJchVWvkzxdIHSmUG/8j2Bd6wDZ988nH3z8I51gCeoDFqUzgxSPF3AypdRrsJIm5OqfAAtppHpyTzV+8ZHu5UUt4HYDUiF2CA8+VzjW6j5BCoDvPv0LZsR6gbH1R5f9Nldv7644Feb0pTVu1ScXN0Ckkn/MQqnaFMpVXBLDmpIo/iyiTqdQpXHVqiIO4jZQyRXWoiUi5bQMRIGerKhm1H/Eyt/M7/97MW24J1/ufW3dM29ByVenL9l9zONFHolCJQAdfnlHy/CLh7GdevXSCzm9wmqDOk578Y485Rf8VJKvOFrDEY/n3mngz5/CiedFIeJGWtY8J3+xguZPgLVBNRlxfffwYxdi67XP5a2bRgYGJk6dhTazArpbJruWd3MmjMy4jxrziy6Z3WTzqbRZk7pUvKfgfuxdql8aCcBAD7y9GESYg5GbH/dz7ku5PNQLI4ZbBNxRWaZwIgZCCHIdGTIdGQQwn8vs0wg4g1lRIXiayzO38KS751ppIBW0T5NwHhe2fwQlv2lmqeOLzA8p+/FaeNLOuyaB8VxnbqkA7e+B4nJnqwT58G7i8VP/HiSKwaC9hUA4Jd3/AGO+30qlbmX/ayUI7OKOhq8noMzwycAZpZh2QBUm5GsPjtB3c/iJ96a7IpBob0FuMCrn91CxdpBxZnYqWTD8B8NTzglnkeJB1jy2MsNrBwoPhgCXODQls/hVr6G48yY8DpS+jJomv9cCIbPO/vPlfIvIlCeQjf+CyH/miWP1++DtBEfLAEu8OqWq/G8bbjerTjO/Mv2E2ohhMLQj6HLn2CIb3L1vx5pbqCt54MpwGh+vjVOp3UPyrsVpeaiVBdKdeCpJMrz87+QDlIUESKPkGeR4gSoPRTnPsrqb7bH/1jeIB98ASLq0l7jABFNJxIg5EQChJxIgJATCRByIgFCTiRAyIkECDmRACEnEiDkRAKEnEiAkBMJEHIiAUJOJEDIiQQIOZEAIScSIOREAoScSICQEwkQciIBQk4kQMiJBAg5kQAhJxIg5EQChJxIgJATCRByIgFCTiRAyPl/nEjnrRV64t8AAAAASUVORK5CYII=", -}; diff --git a/mod_examples/custom_drawing.js b/mod_examples/custom_drawing.js deleted file mode 100644 index 2dccab2d..00000000 --- a/mod_examples/custom_drawing.js +++ /dev/null @@ -1,63 +0,0 @@ -// @ts-nocheck -const METADATA = { - website: "https://tobspr.io", - author: "tobspr", - name: "Mod Example: custom drawing", - version: "1", - id: "base", - description: "Displays an indicator on every item processing building when its working", - minimumGameVersion: ">=1.5.0", - - // You can specify this parameter if savegames will still work - // after your mod has been uninstalled - doesNotAffectSavegame: true, -}; - -class ItemProcessorStatusGameSystem extends shapez.GameSystem { - drawChunk(parameters, chunk) { - const contents = chunk.containedEntitiesByLayer.regular; - for (let i = 0; i < contents.length; ++i) { - const entity = contents[i]; - const processorComp = entity.components.ItemProcessor; - if (!processorComp) { - continue; - } - - const staticComp = entity.components.StaticMapEntity; - - const context = parameters.context; - const center = staticComp.getTileSpaceBounds().getCenter().toWorldSpace(); - - // Culling for better performance - if (parameters.visibleRect.containsCircle(center.x, center.y, 40)) { - // Circle - context.fillStyle = processorComp.ongoingCharges.length === 0 ? "#aaa" : "#53cf47"; - context.strokeStyle = "#000"; - context.lineWidth = 1; - - context.beginCircle(center.x + 5, center.y + 5, 4); - context.fill(); - context.stroke(); - } - } - } -} - -class Mod extends shapez.Mod { - init() { - // Register our game system - this.modInterface.registerGameSystem({ - id: "item_processor_status", - systemClass: ItemProcessorStatusGameSystem, - - // Specify at which point the update method will be called, - // in this case directly before the belt system. You can use - // before: "end" to make it the last system - before: "belt", - - // Specify where our drawChunk method should be called, check out - // map_chunk_view - drawHooks: ["staticAfter"], - }); - } -} diff --git a/mod_examples/custom_keybinding.js b/mod_examples/custom_keybinding.js deleted file mode 100644 index 0109833c..00000000 --- a/mod_examples/custom_keybinding.js +++ /dev/null @@ -1,32 +0,0 @@ -// @ts-nocheck -const METADATA = { - website: "https://tobspr.io", - author: "tobspr", - name: "Mod Example: Custom Keybindings", - version: "1", - id: "base", - description: "Shows how to add a new keybinding", - minimumGameVersion: ">=1.5.0", - - // You can specify this parameter if savegames will still work - // after your mod has been uninstalled - doesNotAffectSavegame: true, -}; - -class Mod extends shapez.Mod { - init() { - // Register keybinding - this.modInterface.registerIngameKeybinding({ - id: "demo_mod_binding", - keyCode: shapez.keyToKeyCode("F"), - translation: "Do something (always with SHIFT)", - modifiers: { - shift: true, - }, - handler: root => { - this.dialogs.showInfo("Mod Message", "It worked!"); - return shapez.STOP_PROPAGATION; - }, - }); - } -} diff --git a/mod_examples/custom_sub_shapes.js b/mod_examples/custom_sub_shapes.js deleted file mode 100644 index 3aea03cf..00000000 --- a/mod_examples/custom_sub_shapes.js +++ /dev/null @@ -1,46 +0,0 @@ -// @ts-nocheck -const METADATA = { - website: "https://tobspr.io", - author: "tobspr", - name: "Mod Example: Custom Sub Shapes", - version: "1", - id: "custom-sub-shapes", - description: "Shows how to add custom sub shapes", - minimumGameVersion: ">=1.5.0", -}; - -class Mod extends shapez.Mod { - init() { - // Add a new type of sub shape ("Line", short code "L") - this.modInterface.registerSubShapeType({ - id: "line", - shortCode: "L", - - // Make it spawn on the map - weightComputation: distanceToOriginInChunks => - Math.round(20 + Math.max(Math.min(distanceToOriginInChunks, 30), 0)), - - // This defines how to draw it - draw: ({ context, quadrantSize, layerScale }) => { - const quadrantHalfSize = quadrantSize / 2; - context.beginPath(); - context.moveTo(-quadrantHalfSize, quadrantHalfSize); - context.arc( - -quadrantHalfSize, - quadrantHalfSize, - quadrantSize * layerScale, - -Math.PI * 0.25, - 0 - ); - context.closePath(); - context.fill(); - context.stroke(); - }, - }); - - // Modify the goal of the first level to add our goal - this.signals.modifyLevelDefinitions.add(definitions => { - definitions[0].shape = "LuLuLuLu"; - }); - } -} diff --git a/mod_examples/custom_theme.js b/mod_examples/custom_theme.js deleted file mode 100644 index a70d949f..00000000 --- a/mod_examples/custom_theme.js +++ /dev/null @@ -1,99 +0,0 @@ -// @ts-nocheck -const METADATA = { - website: "https://tobspr.io", - author: "tobspr", - name: "Mod Example: Custom Game Theme", - version: "1", - id: "custom-theme", - description: "Shows how to add a custom game theme", - minimumGameVersion: ">=1.5.0", - - // You can specify this parameter if savegames will still work - // after your mod has been uninstalled - doesNotAffectSavegame: true, -}; - -class Mod extends shapez.Mod { - init() { - this.modInterface.registerGameTheme({ - id: "my-theme", - name: "My fancy theme", - theme: RESOURCES["my-theme.json"], - }); - } -} - -const RESOURCES = { - "my-theme.json": { - map: { - background: "#abc", - grid: "#ccc", - gridLineWidth: 1, - - selectionOverlay: "rgba(74, 163, 223, 0.7)", - selectionOutline: "rgba(74, 163, 223, 0.5)", - selectionBackground: "rgba(74, 163, 223, 0.2)", - - chunkBorders: "rgba(0, 30, 50, 0.03)", - - directionLock: { - regular: { - color: "rgb(74, 237, 134)", - background: "rgba(74, 237, 134, 0.2)", - }, - wires: { - color: "rgb(74, 237, 134)", - background: "rgba(74, 237, 134, 0.2)", - }, - error: { - color: "rgb(255, 137, 137)", - background: "rgba(255, 137, 137, 0.2)", - }, - }, - - colorBlindPickerTile: "rgba(50, 50, 50, 0.4)", - - resources: { - shape: "#eaebec", - red: "#ffbfc1", - green: "#cbffc4", - blue: "#bfdaff", - }, - - chunkOverview: { - empty: "#a6afbb", - filled: "#c5ccd6", - beltColor: "#777", - }, - - wires: { - overlayColor: "rgba(97, 161, 152, 0.75)", - previewColor: "rgb(97, 161, 152, 0.4)", - highlightColor: "rgba(72, 137, 255, 1)", - }, - - connectedMiners: { - overlay: "rgba(40, 50, 60, 0.5)", - textColor: "#fff", - textColorCapped: "#ef5072", - background: "rgba(40, 50, 60, 0.8)", - }, - - zone: { - borderSolid: "rgba(23, 192, 255, 1)", - outerColor: "rgba(240, 240, 255, 0.5)", - }, - }, - - items: { - outline: "#55575a", - outlineWidth: 0.75, - circleBackground: "rgba(40, 50, 65, 0.1)", - }, - - shapeTooltip: { - background: "#dee1ea", - outline: "#54565e", - }, - }, -}; diff --git a/mod_examples/mirrored_cutter.js b/mod_examples/mirrored_cutter.js deleted file mode 100644 index ae457a8c..00000000 --- a/mod_examples/mirrored_cutter.js +++ /dev/null @@ -1,81 +0,0 @@ -// @ts-nocheck -const METADATA = { - website: "https://tobspr.io", - author: "tobspr", - name: "Mod Example: Mirrored Cutter Variant", - version: "1", - id: "mirrored-cutter", - description: "Shows how to add new variants to existing buildings", - minimumGameVersion: ">=1.5.0", -}; - -class Mod extends shapez.Mod { - init() { - shapez.enumCutterVariants.mirrored = "mirrored"; - - this.modInterface.addVariantToExistingBuilding( - shapez.MetaCutterBuilding, - shapez.enumCutterVariants.mirrored, - { - name: "Cutter (Mirrored)", - description: "A mirrored cutter", - - tutorialImageBase64: RESOURCES["cutter-mirrored.png"], - regularSpriteBase64: RESOURCES["cutter-mirrored.png"], - blueprintSpriteBase64: RESOURCES["cutter-mirrored.png"], - - dimensions: new shapez.Vector(2, 1), - - additionalStatistics(root) { - const speed = root.hubGoals.getProcessorBaseSpeed(shapez.enumItemProcessorTypes.cutter); - return [ - [ - shapez.T.ingame.buildingPlacement.infoTexts.speed, - shapez.formatItemsPerSecond(speed), - ], - ]; - }, - - isUnlocked(root) { - return true; - }, - } - ); - - // Extend instance methods - this.modInterface.extendClass(shapez.MetaCutterBuilding, ({ $old }) => ({ - updateVariants(entity, rotationVariant, variant) { - if (variant === shapez.enumCutterVariants.mirrored) { - entity.components.ItemEjector.setSlots([ - { pos: new shapez.Vector(0, 0), direction: shapez.enumDirection.top }, - { pos: new shapez.Vector(1, 0), direction: shapez.enumDirection.top }, - ]); - entity.components.ItemProcessor.type = shapez.enumItemProcessorTypes.cutter; - entity.components.ItemAcceptor.setSlots([ - { - pos: new shapez.Vector(1, 0), - direction: shapez.enumDirection.bottom, - filter: "shape", - }, - ]); - } else { - // Since we are changing the ItemAcceptor slots, we should reset - // it to the regular slots when we are not using our mirrored variant - entity.components.ItemAcceptor.setSlots([ - { - pos: new shapez.Vector(0, 0), - direction: shapez.enumDirection.bottom, - filter: "shape", - }, - ]); - $old.updateVariants.bind(this)(entity, rotationVariant, variant); - } - }, - })); - } -} - -const RESOURCES = { - "cutter-mirrored.png": - "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAYAAAADACAYAAAAN6LRnAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA4VpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDcuMS1jMDAwIDc5LmRhYmFjYmIsIDIwMjEvMDQvMTQtMDA6Mzk6NDQgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6NGQwZTY5MmYtOTRlNy00MDQyLWFjY2ItNmU3OGEzMGU1N2ZjIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOkIwOTY4MzA0N0I4QTExRUNCQzRERjNBOEI5ODYyRkJBIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOkIwOTY4MzAzN0I4QTExRUNCQzRERjNBOEI5ODYyRkJBIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE5IChXaW5kb3dzKSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOmRmNmU1ODFjLTQ3ZDItYzE0OS05MmQzLWRhZDMyMTg5YTA3MiIgc3RSZWY6ZG9jdW1lbnRJRD0iYWRvYmU6ZG9jaWQ6cGhvdG9zaG9wOjk3MDYxZWYyLTY2ZGMtZjI0Zi1iZTMyLTVhNTdhOWI3YTQzNCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PhdKHucAADjLSURBVHja7H13dFzHee8Ai0Y0ohAkOkASjSQAQgQp9iZZEilZjSq2rNjpyfPLe3byTvLeO5b8zsmLpJM4+eNZjuNEkktiR1aXrEZK7L2InZRIkSgksGgkAZDojcCb3wALLYCZu/di7+7eu/v9zpmDxd7duTOzc+eb7zdfCRsZGWEEz/jTP/++v28Zz8uasbKWl2xenLzs5+XAWOmiX4ZgYVhmDr/8bz+mX0OCCBoCy6GQl+/y8se8JE66No+XdWOvO3n5OS8/4+USDRuB5jDBKMJpCCyF3+flDC9/JXlwJiOBl78c+/wf0NARaA4TSADYF3/Ny694mWHwezG8/HLs+wQCzWGCboT54wwgAPy53fCHvPzChHr+2KR6CASawxaBL88vSAMIPIrZKAdqBlBPCQ0pgeYwQQ/oENhPKCuvkL5/7uzpH/E/0VL1LCyMpaSksplJSSwyMpINDg6yWzdvsra2NjYyMiz7ShQv/8jv9SCNOMFf8Mcc5veggfYB7EoBhQXJ+P93XqT6HR6W/LnzWEzMVDq1r6+PXamtFg+TAn+lqpdAoDnsd3i1yPqSArKDAFjCvrIjruQlJ9g1l/DwcDa/oFD64Lg/QNVVl9jw8DAtQQSaw9bGEBv1fzjJvvKBOG4FAWDVhfROXn6Ply28ZIXaw5Oekan54AAxMTHic40NTlptCDSHrQ2ss/ljZcvYe028vMnLb3j5LGCC2mIDNZeXf+Tl6JhqGXKLf0JCIktNnaXrs/gcPk8g0By2HTJ4+R4vx3j5e17yQl0APM1GHUJC1hbY4Yhg2dk5hr6TnZPLIiLoLJ9Ac9jG+F9ja99ToSoA/mJMFUoI5VmAByciMtKYbskfnCyDDxyBQHPYcpjJy6u8/OEPnnk2pAQAqJ5/DvVfPzk5hSXOnDmt7yYmzmTJKam0+hBoDtsfv3jh+eee9tfNAq13oaMvan0ApmTFJSWssLCIZWSki4kCCwO74cyZ0+yjDz+UXouKimKZWdle1Z+ZmcW6u7rYwEC/9PoDD3ydLa6ooMeLQHPYz4CVU0dHB2tubmaXL19iFy9c0DJ/BeAMd4iX2mAWAHOZhvcgHEgqly5la9asZbGxsbZ+cFpbW9m2rVuV/czJzfNaqOH7Obm5rKa6islMe7du/Zir2dls1qxZjECgOew/oF9JSUmilPDN7F133c327d3LTp06qfoKqPAf8vJHPm9bAMflu0zB+c+YMYN96+mn2b333mf7xR/YuXMHu337tvRaWtps3sc4U+6DetJmz1HuQnbu2E4rGYHmcIARFxfHNt9/P3vssce1Dr8RW+lrwSoAlvHyN/IJEMu+/Z3fZ3l5+UHx4Hx27BirunyZyQVdLJs9J93U+83mD49KaFZXV7NjR4/SakagOWwBgNresuUxoUEp8BfBKgCeVqmSj27ZEjQ0RWNDA9u+/VMNdTdP68efFlBfdo5aHd/Bd1BOJzmPEWgOWwEFhYVszdq1qsuP8FIWjAJgi+zNJZWVQbPzB7Zu26q8Bg/I6Ohon9wX9WZkqH3otm39mFY2As1hi2DVqtUsKSlZdXlNsAmAxWw0ns8EgAvDgW+wYN++vayluVl6zYin5HSRkprKEhPlHpbXrl1je/bsptWNQHPYAnA4HGzZnXeqLm8ONgEglWg4HcfhSDDg/Pnz7MD+/dJrEHTwfPQHsrLVHpaHDh7k7TxnyfHDww0LiRs3boTEQot+njp5UvSb5nBwzGGjKC4uCogGEAgzUOk2H1xYMODmzZua6ik8Hv3l9i4eVP4AXblSo1Cjt/Lr2Vrqp99QVVXFTp44zurr61l//1d24LAIAzW4fv2GoFv49+7Zw06ePMF6e3snUB85OTm8z0tZQUEBzWEbzWFvAP+mtLQ0dv369cmXkv/0z79f+spLL56ffMGMSM6B0AAqZW+mp2cExUONXcnAwID0Gjwd8UP7EwlchYYqLQPauV+xy/MnoMq/8fprQgi4L/4AFseDBw6wV//zN0G1+P/nb37DDh48MGHxB9B/jAPGI1AUB83hwADmtAr47CA4EAJAerKTkGD/MEBnTp9mp0+fkl6LiooWno6BAA7TVId1586eVbbZH3j/d78TC44nXLlyRVh/BAPQj6tXr+haiDE+NIetPYfNArRdBVKCSQA4pA0Jt3d6YvC4WxVq86inZG7A+oj7wqxOZa4HNToQfPsHH7xviMOF/bfdH3S034gdO8YH40Rz2Jpz2ExERUepLvksXnYgzgBw0jUlaEh3d7fyxN8OgIeiKrNRmnBsCewBNxxr4GDT0jLVqgPthq33U099y2/t2b9/n9i5GcUo55tjiq8I4s7gzKG+vk56PScnV/DxcfHxpi2wqnAKWsA4IYzA2rXraA5baA4HAwIhAFqCTQAcO3ZMeChK1bqxSWsF4CHu7OxkPT3dU67V1tSwo3xnunz5cp+3AxYm+/ftU15HpigsNm1trdIHfcf27eybT00/dDqCceHwFbSSFj77bDRRU3FxMVvDF985c7z7HdFu1QKLxOn4XZAmUSow+XglJyez0tIymsMWmMP+xMjIiM9yoAdCn5PauvV0d9vyx2locPIHW8NTMsd8T8npwpMajx3g559/7tM2eLIwwYHf/IIiEVlSFQ6gpqZaPOjTWoR5H995522Pi787vvzyS/bzV15mJ04cn3a/0V60W7WzRX/R7wSNTRC0h5s322kOB3gO+xtDg4NRwSQAWqTquE0FwFYNlV7r4CpQwEFeRqa2h+WtW7d8dv+TJ04oLUzgXJSXN1c83J7CAUznQcfO35s4Mp9s28ZaWloMfw/tVAUxc+e28Rr9V6VIxLid4ONHcziwc9jfGB4ZxkMwI1gEgNS1sKu7y3Y/zN69e9g1xYKgZboWaKRomPLBDPGkDxYZofrxsTpy5LD0GhaZ3Lz8CTtNEQ7AxAcdtI8ZAswI0D4tjQf9c19g0X+Mg2rRPXrkiHLO0Rz2/RwOIGb4Yr0mCmiawK4O9ukyuJxXrIxRZx556r7Dhw/x/p03/Z7nFBY/WvHkPT3oRmgZHPh6C9WBsQpo32TfBhfQrxRJFixPQdbOmeT9SnPYdoghCsgC8LSr03JftwpG3flzNHbXW3k/b5p6z8uXLknfT05JEWGFp/OgHzl8mJ0759maCFEtZfHswb9jFy4riNEyGbDkmey8pRR4vF1on3z8IzXz4GI8VCkSVeNIc9j3czjAMP0swDIaQHd3j21+BaiXql3d6I7VHtZMWgG9RnfX5qnRTU1NrK2tTbr7nz073esHvb29TbMOlY14dHQMmzUrTVpkAsA1Np6A9miZfKI/nhZYWN7ItACMY1NjI81hP89hCwDrtSNINQD7nAFcvHhRLp49HE5ZEVohfS9euGjafVT0C3hm5H325kFHflWk2LMS0B5V3lf0Q3XQ6w6Mi8oqyFs6i+awbWF7ASA/A+ixhwbgdNYrd5twHLKbR/OoFYqc64XJoRm8uRg3BXc+c2aSwQddToOCzzbjkNcUDZG3Q2WhhPajH3qhGp96Zz3NYT/PYYvAVHvcQPzS1+UaQLfSScZKUE0mPKixNg1nDaermUlJioW73qfjFh+fYOhB1zocBeUS6JDKuL+ZydNV41NfV0dz2M9zmASAOcAqL6WBem2gBagevOTkFFvPquQkefvr6uu8rhuem7JDflAcRg8aETBrjkbk2E+2bQ3oOGrdH+3WCPglBcZHRpFBY+7s6KA57Kc5HKwIlK6n8AWwviXQjRutctU+JtrWE0HV/lZFf41AZYkRGTk9owaEzY2Li1fubmECGAjgvqrdNdqrEe5XE6pxutVxi+awn+YwCQBzIaWBZPE9rIbERLlKrjrwswtU7Vf11whUnr/hjulPP4QDUFnp7N61y+824Lgf7isD2on2ThcOxTgN9A/QHPbTHCYB4AcNoLvL+gIgR3HYdK2l2ZQMPYEA2n2tpdlQf41AGUfGi+HCrhjxc1Twpw047qNl8ol2TlfbGf19DI4rzWHT53CwIlCeHvKDYBucAeQo7NG7urpY1eVLIkwxoic6wqdvrRUZpV4sBgcGTOvL7eHbgktuvXGD9fX1GuqvEcTEyHlvmWOWESANIHhwWYA02IAfOXKE3XffJvG/yo8AZxMqC6WhoSHp+5OD1OE+Kpt6tNHbdIWqcdKIHx/wOeyvee2vOUwCwFw0ySdgp+UHDAsJYrMjquVkYAI6nd5bHJSVVyivXbz4hd/6CmegzCzvbcJTUuSHc/0D/V7Xjd01fEhk6v+J48dZRkYGKy9fLNqAuP7IATCBRuFtGDDQDoSEjnJbyM6ePSPuI9dSIjW1FL1QjdOMmBjLzmGrzGuz5jBRQH7QAOzgC4CHf83atSExOZYuW8Z3n0Ne1wMnnVRJULFhvrPt7+/zqu5Rfj1PeR3UjMsD2YydYEZGpggFgdLY2KhJ/aBdqnMK3Ys/H59hiQaABTyc1z0wjZ0zzWFCoAVAk0oFtQOwo6y4446gnhgLFy5kJSUlYmc9YALtpDoEhYmot9CysAGNs2vnDvF6yZJK7x6W8HC2YEGJWJRRkLRdRRNpWSoZgWp8MjMzx3fsNIf9M4dJAJgHaWCW3h77xAO6//4H2KpVq4NyUiyprGQbNm786nfp7fGar1ftvj3F8NFNzaRnsBiFjf2lS5dEdq/z57yLoglHxS++GKUqzp49y67U1ko/F+PBV8EIVOMDasvVpsHBAZrDfpjDwQiLnQHYKycAJlh2drZYXGpra2w/GbBIl5WXs/z8/Anvw8ICD5ARr129AqCvt1fschMSvDPVE3H0c/NEpMyRkake5ds//cSU3L4Iaof4+Qf271e0I1y0w4wMWhiXPkXk0fSMrwTM4MDgtK2MaA6TALCMBtBjIw3AhYLCQlGwUxMJxuvq+SLRyFVz2cHdCBt2M7Pr6jTv0Dt+GgtodFQUmz1nDsvkiwkWlJkzZyo/C6oDO83pLjSwhJk3fz6rkeSdbWlu5AKg2OsxQJwd7IwbGxuk17slGwyEZVY5EPV090wRJgj1sFcj8FxGpjpekVG0NEv3SVzA5E74rYa85Li9mcPSjZwf57U/5zAJAPMwMCYEZk1WsSEEVLlgrQy40aOAW1VhsrXKv/z0p6bd/zvf+Y7vf7QB7x6eyiWVUgGAA9Ub16+xWdP0lHVH6qw0sXPu7NQXJgGahyqQ2JcXv5Byx9evX1fUpY5YaniHxMcDO1YZSktLp1BTKN4GcZvOHJbByvPa2zlsFLt372KHD3nnmd7V1XnEV+0LZNg/21oCTRd25yC9bf/8ggJhYy5DM9/t9vX1mdJOPbH2Td9JechZYAQYh2bF7j85OZnl5k21ehrxUyDFUJ/DgcC7b/82KAWAPB5QVxcLVtgtzK7Z7cf3N2zYKL0Gjra+7oopnqiesm35AlpZy4zA0zgsX7FC+juMsBGaw0HY/qGhwV/4dDxIA/AfYmJiQr79RcXFbMGCheqdb1OjKW1Fvl1VSkWzoZW32PCuSHDvck2ogGtQ8+bNozkcIu3nm4Dzv/rFz/7Gp5qr1TSA7iDWALBDBE+MsAG+dE4xc5cDaxaHI0I4c3nr1OTC+g0b2OXLl6Q29DduXBdZsMyw1sjMzBLzacAEj2MVzMygBU949F8+dyLY8uXL1b+TuWHidc9hf8YOmu689sUc9sfO39eLf6AFQMhpAAAmoK8Puc3ajfpux5zCNm++n33wwfvS64jNU1hU4vXDOppAJpfVVFf5ZKGaToIXFcBNOzXi1q9fv16Z8ET01Y8Lmz/msB3ntbcYHh4+cnto6EhPb8/RN1//j0/9ItCtpgF0dXcxQvADttotLc3s2LFjU67ByqTBWc9y8/K9vg8yRSG5eosiUqQ3QL1mLYTor8q6ZvHixay4pERzQTbD74DgP3AN6u9e++0vfzxh3g8M+D0UayDPABTJ4btpdoQIlt25XJnMGyGWzfISThMLtbmpDlEf6jUD6KcqdDXi9pQvXqy9izPh8Jngf2DBdy+BaAMJAELAAKedTZvvV15vbHCaEsNllKqRJzuHY1BHxy1pUeWodlFLZuy60T/0UwWcl3jyko6KIscmwvQQSApImr27hwRASGHRokXCc3TnWMA2d2ABrq+/yubPL/T6PjiszczMZk7nRJ4dZsdGTY9RD+ozA+ifStCsWrWKFRZq9x0hp+1ysEmwHkgDIAQcsG3PU/D92BBcu9Ziyn2SU1JY4kzvDhLx/eQUc5Kno1+qDU9WVpauaJ0x0TE0gQi21AAQ5QpBQybotzANhImZihsmBCfuve8+9srLL0mtdZDqLyE+QWSp8hbZ2TnsUk8PG5pG/lvstrNNcjBD5FtVCkNQS2vXrfNYB2IOOSIiaPLYHz01V69NcP544fnnxl+/8tKLQakBkBZAGEdaWpowDZVBeMdqUCVGAHvw7Ozp5YjNys4V3/cWLmpLZZoK3j/Fg5YB2meGIvw1gWAXASB3BiNT0JAEKI+ysnLpNWiFTU0NptwHh6oIGmcE+Ly3IatdQD9UOYSLi4tFIhNPMNuqiUACIBC4JhcAPfTLhCjWrlurtGppa21lHR0dptwHqR2jdYYFQPgAfN4MoP3ohwygmJbdeafHOmbMiKWDX0JQCIAW0gAI7kDegE2bNyuvNzjrlGkYjcCVQMaTKafL29cMk0+0u8Gp7e2LJOZagJCg8zFCUGsAPaQBhDRKS8vYqtWrlYuoVsgEI4iJ8Zy6MR2pJmPM4drRbpXwqqysFIHyPAkj7P4JhCDXAOgQONSBsNHI9CQDkr20tt4w5T4iebsiVSTeNyNJDYD2qpLUIEcCTGE9AWEn7B6OmUACwKMG0N1DAoDA2GYNKghhk/v7zUkgk5OTN4VTx/943wygnVphrtetX++xDjieUSpDQrAJAHly+E46AyDAGSqb3XPPvdJrwpSy7qopUT7Bq2dl5Uy6d45431uMJnhRm7CuXrOGpaenaz+kfNdPJp+EYBQAIRkSmqAfsIopUIRDQC5hs6J8ItQyDqCBpORkzdDLRoD2oZ0y5Ofni0ifnhAXG0fRPglBKQDID4DgEXff/TWl2SOSp5s1X6BxxMXFs6zMbFPqw1kW2id98Hh/Vq5a5bEOHECTty8hWAUAQkFMIXIRIdEMUz9CcCA1NVVpGjrqJVzHhk1I9o1Fed78AlOSq6A9mt6+69eLBO9aQBYwu6dgJJAAmJYWEMzJ4QnGsXhxBVtSWSm9Nsg3DA0aIZUDAbRnUBHKurS0lC1YsEDz+2TySQgVAUDnAARdWLFipdJL+ObNdnbr5k1LtBPtQHtkwMGyniif5O1LCBUB0EQaAEEPkpKSNBPINDSo0yr6CyKdJW+HCgj0psfbl5K8EEJcAyBfAMJUgD7ZsHGj9NrtMd49kMD9byvOI1asWMGKioq0H8jwcAr0RvAbrGBeQBqACfiXn/7U6zpycnJYNi85ObmsoKDAsn1dtWo1u3zpspT37+bz5vr1a8LD198QFkmKeQtbf9UZhjvg7etvk8+qqipxkO6sr+d/6+lhIgHg3+dG9mYvnQEEYPc6ugAcZofEIqvaaVsB8BJ+5ZWXpddamptE6GazYvjoQV9fL2tublJe1+vt6+8E73t272aHDh2kyR+ioDMAghRYFLA4WBWIE3Tfpk3SazC9rDPJS1gPPN0Piz/i/WghEAleaPEnWEEASDWAnp5e+nUsIARAD1gVlZVLlclT+vv6WJNG/B1TdzCIS9Qnj0sEKg3nFp4QO8O/1A9+V1r8CVYQAI1yDaCTfh0LoN6k0Mu+wrr1G4TDlAytN66zzk7fziPUj/vIgHYtX77cYx3C5NPP3r5W/10JoSMAyA/AwnBa/FAQuXM3a5iGOp117PZt33iVo15PCV48xRSCkAhEghcnHfYSmDUOgZEfD8bbE06/EEAL5nTkDDMRZeUVPqv73NnTkp1ivQ3GpJxdu3aNHT16ZMq1ocFBLgTqWV7eXPMXUafa76CiooIVl5Rofh+UT6BMPlW/qy/nF4E0ANICCD7B0mXLlDvpjlu3WHtbm6n3Q32oVwY4cUEoeQIleCGQABiFPCooWQIRdGLmzJmaXsLNLU3mTliN+uDtCzNULURFRlGCFwIJANIACGbgFt+Nb9v6sfJ6uofcv0ahVd/ePXs0D59FgpdYCvRGIAGgqQF0UV4Agk58duwo6+/vV2gHSSw5OcXU+6E+1CsDwpmfPXNG+d1AePsSCFYWAJQcnjBtnDt7lh07dkx6TaR7zM7xyX1Rrypt5BkuAL68eHHK+9HRMX739iUQVLBKqiESAISJ2l9XlyiuxO+wlhkN7zAxQUpbWxvbqkH9ZOfk+sySDPWi/tqaaun1vXv3ihhALlNQl7dvX18f6+joYL29PeNCIT4+XhQCIRQFgDRvXk+QCwAEX5OZ48nMMQPZRl9jNIpn3Wgsorr6Uc9aBZ0DIZCZlSUC1qFthw8fUmaPmzUrjS+qCYbagkXZSCIW1I/73JA4g6FdR44cYXcsWcL71MSuX7/OGhsbWZfifABWTBkZGaJvo0H5cpRObnace2RiSgKANIAJu9Mcy9vZZ/tIAGBxvHLlCjt/7iz74osvdH8Ph6ugVmT0ijugKaRnZBpqE+ZbbU0VmzuvgMXF6bfPx33gud4nCQdRXV0tih5A6GFMUFxYsGChMCnNy8tT0k3BOvcIIa4BdAe9BpArIm9avY1mL/znz53jO/fDrL29zSdtxgFrTm6+oYPW4eFh5hzL4Yu/hYXFunMDu+5XdflL0wPQXbjwhSjIH7xi5UpWVlZuilZgh7lH8D3oEDiAQKAwhF22KtA2M/MC1NbUsFdefol9/PFHPlv8xY48PdNwMnXkFhgYy+GLvw2NDYa+LzSO9Eyf9am9vZ1t/fhjMX4Yx2Cfe4TQEgBKPwB/hfQNFBBz34oPIpKXVNxRIXLbupfpArbxv/3tq+LQ1pcQnHxamqHviBy+kwQS/jeaYxj3NXrmYBQYP4wjxjNY5x7Bf7AEBfSDZ569/cLzz4EGmpDGCYs/hIARPtauQmCUkw1sViYcQIqSmSk4ZzNwky+iH334Ibt69YqxiRkRySIjI8ZpmNtDQyLujirdIuCyyjECrRy+eD+Wzz0j3Dvuf/nSRY/tRJ2uCKDD/LODg0NsaEh/PuODBw8wp9PJHvj610WuZLvPPUIIC4AxtEwWAABooGAXAC6V3Ajd4s1u3F84f/688M51UStaQPwcOFbFJySMhkdW8O9YsJEvurOjg3V03Jqw0GrZ5avg1Mjhi/dxHYfCeuHyO6hzE3joS2LiTJaQmCjMWVVtxP1ghQQroVu3bnocNwhVUEIIgaEn54C/5p4Z6UkJoScAQvIgOFjx+eef61r8YdY5K22Obht4LJ4QFCjQELEYXWtpEZsElWeuCsjh6ynzHK7jc7MM5Bh2eR5j7iJzWVJSsq4DaQgKUEgosCxy5Tfu7OxQfgfji3FG/YsWLaKJR7C1BkACIAjgisujtfjD+Qn2/N5w5lj0sNBigWXM2FmRpxy+7sDn4g3mGM7KzkYLvQr5EMeFIgpMTBsbGsad4lRCIJvfE0HxCAS9sFIs2pB0BgtG7N+3T+nIBaSkpLKCwiLTDkyxyIaF6Z/K0BzqDeQMNvr50TaFmxbvB+NUWFQkxk0FjDfGnUCwqwCggHBBgJMnTrCzZ88oF+rMzCzBkQcyDn5zU6PUaQuYr+DC8flmP+UYVgkUjBvGTyVYMO4YfwJBLyx/BtDTTSGhZRilPawFZOXatm2r8npmVrbmLtYfAJ1yQyOH74rly9nVK1ek4SXwPRzk+trUUwups9JYGBeeDU65tQ7GH1Y9s2fPDqm5RyANgBBgHDms9iydMyc94Iv/qFWP5xy++KsCvq9l4ukPYBwxntP5HQgEqwoASgpjY1y9elWYfcoAS5/ZGguWv9CgkcN38eLF4zl88XdxhTxwmfAbcAbeVh7jqco6ht8BvweBYHsNgNJC2gOffy5f/MH1Z2UbjyeE2Dyw94etP0pvb69XXuEIPQHbehngg1DOBYA7ysvLxfsyoB5vQlmgH+iPq2/oJ/prFBhX1VmK6vcgENxhpTMA0gBsCkToRIA36U519hxDzlnd3aO273CGmrzgY7GDjX1a2mwWbSDWD8wkGxucyuuyHL74H+9v//RT6XdQX1xcvFJIyNDf1yf6BgEyecHHwS5MTdE31KsHGFeMr8ycFb/H2rXrPOYmJpAGYAn84JlnYTc4ZVs16h3ZS7+UhYHwAbJDU4cjQhxa6t3xw9SyprpKePnKdvv4DHbely9/qduGHxjkAkC1w161ahUrLCyUXsP7uK5q76AOD+dx9Za3F+1G+2VtQX/Rb/Qf46BXI8D4YpwnA78HhXUg2EYAkBZgX1TzRUsGhDDWY+4JIV9ddVl3eAssltevtbCrV2p10UJwplJpDHEePJBV11FfnA7vZbQP7UR79VJYGIfq6su6DpsxvhhnI78LgWBVASDd1sF0j2BhDaCuTiEA9CViv3q1VnjmGgX48yadtvmqtjQ4ndo796Zmr/qG9qGdRtHHtd46nQH0VG1R/S4EglUFwA25BkAUkFUB/v+mJGxyRGQki5nhOXRCa+sNrw76W29c1xUuRMWFI1WjCojNoxIwerh1tKtV4XOgB9j4YHw8AeMcITlnwe/S2UmbJ4LtNQCyBLLs7l9hV68ngquLylEhMTGR5c+dy/Lz8zUPkq+1NHteJGNmSLlyJFpReQXD+/bGjRsSwRChKy6QVrvQH/QL/UM/VdBLHanGu76etACCGhEWa490u9NLZwCWBRKdSxfcaM9WOrD4UdnlL1myhFUuXTq+8Hd0dIgE61WXL0t3ykO8nggP1kbRMdGsp3tIKgSQB8EdCEkNz2ZVPZ6AfqmoSxwuL1+xYnzhx2ePHz/OTp08Ka0H4+TJ+xjjfcvA70MgWFED2C9788iRw+x3773Hqquqgj5DmN2gypoVFeV5kVRRN0hGg/y37rt+LJYr+Xsqs8vuHs80kKpNEC6Td+fR0dHKQ2k9fetRtAftR9/cd/24H/qmSsKjh+JStcloVjMCaQCBxAHZmzBpg2MLCmyly0rLhONOamoq/YIBhoo+cehIqD6giBiqMssE746Y96dOnZpalw6TzAhFm9z7AHt87P5HNc9eQ/Xoac+i0lLl+UEB77fMg3dgoN/j/Rw6+kYgWFoD+MEzz4IPeEXrM3AQOnz4EPu3f/0Z+/df/YqdOHGc/AQCCUVkSj16mkqbC9dYYFXXzNIMY2NjTYlUOqKw43do1K1axEeGPfdtxOAYEwhW1ACAf+Ll27x41LMbGpyi7Ni+nRUVFbHy8sVs3vz5psVhJ3iGckHTsfCoOHtk+FKlKGxXJJWPiPA8lZUCZ6wPoFEiI6PchE34tBdVVd/a2tQhJNBvI3XpGe+RsfbSM0GwvAYwpgV8yf98l5d+vd+Bw8yFCxfY66+/xn7ykxfZrl07lQd4BHORkCC3YBkc9EzJzFCYiZ4+fZp9+eWXU94/d/Ysq6mpMVTXBCpF0SZQMhACk+tQpZgc8KJvaD/6MRnoL/o93b6pxjuOazRayXkIpAFYDlwI/PKF5587NCYIHuVFdzQxUERHDh8WJT0jQwT1WrSoVNdDRDCO5BS5E1K/Dk4ewgM7U9mOeueOHay1tZVlZmaK3W290yldOAHs2l28vaYA6Je3CWkUQb9M3iWrPGxV9UxctGNFu2QL8/79+9nNW7dYDtJG8ns2Njay05JzDQBtUglZPeON8NZIJYlDbdICCLYQAG6awF9yQfBX/O99vDzFRqkh3bO4ualJFCwmoIjKysoFRRTIbFTBhiws0BL06rDKAW2D5CKqyJpYFFULoztmpXmONwQtUXWYCgEAc0t8xp2HT1EIN9Qz+bOqdjU1NkivQZipBJo7MD566C3VeM+ZM0cIWBxKQwgQCLYQAG6CANvDbShcGPxf/ncNL3/Iy3qjFBEKHGZgiYHzAl9mTQoVpE+ynx9fkHp7dS2S6ekZIlTCdJOsxMTEsNTUWZ41w65OqaaRnp4+viHAIumuKYpQ1lnZ4pzJHagH3suJHhKwo13tba3TtsTB2KnGd/L8VhlCpI0JRwgtEgAE2wmAScKgmv9B+XcuDFbyv0/w8hAv8/XWAZvqY0ePiuKiiBYuXCSsPwjGMbpIZvFFsmHKItlx65aSIhqfgJGRLDcvn12prTFssYKdcW7eXF3URuckW38X3B3AhoZghDaRKszJyZkiAAAILU8CAO1C+2qqL0ujpXr+bj7vo+cDYIyzbOyw+3cJNwgJRBgl7Zcw4fm1a8O5MDjMy//gL2E0vnl4+Par/K+hrBqghz795BP24o//H3vrzTfYpUuXppWYI9SBHLQytPHdrx7AyzUvf54u3wEXYLEzb36Brl0tFj9VMhiEYnD/3OSFtKi4SPo91KdHa0H75s0r0OU85r7zz+fjoTf3sGqcMybRc7dvD9FkJdhXA9CiiOblzd678e5N/5A+J2PFjNjYp8LDHRv01oFFH4s/ClFExlFcXMKOHjky5X14w/ZwjStWR1wgWOIUFpVwodwoFlctk82U1FkiJ67e3SwCsskEO37rySEghkFbuXHu2dk5wvmwa1JQNdSHevWkukTo6MKiYtbS0szaWm8oNxnY9cPyKD0jU3cSHYyvyut43rx5kwTAMDOQm4dAAsBW6Nu9c9tF/reWl7ce2fLNZfxhepir0Jv4gzV3OhQRBAAEAQSCnuBmIasBZGez3Nw8Vlc31YsV0TTnFxTqqgeLXg6vJ2MwU4Rn6OntFtRJGBu19IEgSUxI1HQUmwx8/7oiIicMAqZoC3xxnlx7SUkJO/7ZZ1M+i3ohjPQc0kJYZfCFfc7sOayjs0Ms3LAQgpjD92NnxInwEBEGV2hVtFJYT+F8Y7JwIxCCUgDUXL2GZwm2cANcGwh/753X9vLX2JY++8ST394YF5/wKH/QHmc6HMxcgC/Bjh3bhV8BHJPKF1eIv8SjTsWSykqpAMDutK21lS+U+sN2YBHE51OY96E+GhudyoVP5mwm0zwWLFgoFQCoF/Xn5ubrbg+EFyx7ULwFxlW1+y/lm5YpfWPkFUwIXg3AXRhAx4bpRR8XBhFvvvFrWBHt2nj3ph95SxHBSgR+BTg81mOhESoA3YAdbIfksLWpqYHFxcex6OgYv7YJFjiqYGjFfFefofP3w0FwGf+9ZWabqL89vpUlp/g3LhVs+zGuMsTHx3NNKpcmJSE0BcAkYYCTL5RuLgy6xymiR7+5ZGZS0iMREZH3hIWFFemtD+Z2x49/JgpRRF8B5ph3f+1r7N133pEKUFj5zC8o0kWXmAGYaTZoJIIvk+yQtVBZuVRpt4/74JBXT4pIMwBaC+OpledYdjgexsgRjDBJIw2lznJhMMALTvNuvvfuawf+/Zf/+szPX/7Jyls32x/nD9V/MgPhJwAXRfSTF38swlDAz+B2CPOsoErmz5db5MLGvramSsTt9zVAiyDNpOogGQvk7DlzpNccivg/4NTvvvtr0msi7y+/X48O5zfvF/9BMY6qaKO5eXkiqqgMYURdEkJNA1AIgskU0VYGiuiu+/5hTnrmitjY2MfDwx336q0POzHkKkAJdYro7q/dw2pra6W7UzhEIdl5Xv5cXRm1pgNQMk5nnXJ3DDqn4o47lN9HFjAVkMQFsXxqa6fGI4Lgr62pZtnZuSL8gi+AvMlIMK9a/HE2tXr1avVujwQAgQTAFGHgooh6xiiiK7y8/fCj37gjKSn5UW8oInhhlpaVifwF8QkJITGes2bNYps3388++uhDpSZQXXVZmDrq8eA1IoTBieNgVDnZIyLYunXrNBb/MI++CPdt2sReefklqWMX2lBXd4WldKeyjIwsUxdc5AaGiayWn8r69euV8YtE/w1YTxFIAISiMBi3Ivrdu68f5K+P8/J/Rq2I4h/kwgBWRLrJfqTj271rF9uze7cwOcR5AWISOYL8QVxcUSEydKHfqsW6scHJbra3Ma5xiUPL6QL0y832dtbS0qRML+m+QGrtziN1eN0iPhAE3AcfvK/8DIQQPI/nzMlgSXxB9iYIG84ympsbWY+HtKgruHayYOFCbeEWQY87gQTAtCmiNevu+vvs7DxQRE8ZoYiwSLlTRCULFgiKCHFmghWrVq0Wu/1DBw8qP4NFDXz2jNhYvrCmCicovcIRIY5v3WwXXrCDOs4V1m/YICx/NB8GnTb4sAiCBrB168fKz6BNoKIgmETfkpJ1x+JxeS6jb3ryYVdWVgozXC245zkgEEgAGKSIXnj+uZ5XXnoRFBFMS94fo4hgRbSR764WGaGIkPwbBSktkdoyWCmiDRs2sii+8OzZs1t7TPgi14DirBdhlBGXCYsl4uAgKQsEKLSGQS5Q+vr7WE93j640iS5svOsutoALXS1ghxxpwAnrjiVLRNs++vBDzc9BEMADGAWWQrFxsSKBe2RUlKCIcN/h28PicBdCDUKxt7dHdztwLlHpYfEHoqNIABBIAHiFP/mz7wmKiAsCd4rI8fiT314fHx//sFGKCPHuXRTR3HnzWCkXBPA6jQgiVX3V6tWC4sFuWY+FFBY/IwugFpCAHTt/VY7hCQskX5SNUjWLF1cIIbWN901P0hUILSOCSwvQlEBplXgQbOIh5/OJ6B8CCQDzBME4RcSFgeOtN379CX+9240i+kZ4uGOTEYqoprpalE+2RYvopOWLg4cigpaTlZ0tPKovX7rkl3tih16qkYB98u5/uqGSkaQeoTBOnjghclX7AwhgB1PWJJ3WRjNiKBkSgQSAr4QBtrXYsoIi6nFRRA8+9ER5SmrqFr5DvMsIRYSd5KlTJ0UBRSSsiHhJTJxp63FCX5544kl28cIFtptrPKokMN4CkUlBiSBEtV6AcvLmoBYJZUAz4aD/0MEDwgzWF8B9VqxcqfS1kGtB0bT7J5AA8JMwcFFEYR+8/ybiEJ3EGLtRRA/z/3UbiYMi2rtnD9u3dy/Lz89nZeWLbU8RgbKAo1JVVZXYNV+5Ys5iWVRcLCyscg2GQJicCN4b5OXliVJTU83OnzvPzp8/Z45Q4xoGvM1Rt5HfHjQRpUIlkADwvyCAC+oUimjpspV/N7+geHlsbNzT/OG8X299oIiwq0QBRQRzP5wX5No03gsWMQgyFIRRQEJ0Z309czqdhnbtiHePRRExfWbONK4h4dDXF4mA5s2bL8radWtZPe8XrL/wt6urS3cdiOSJ/s2dO3dKVE89gEYTFxdPeYAJJAACLAwERVRWXtHz/f/2R93HPzvcwv/f5kYRbeAPaZne+kARuXLlwukHJokwKbUrRYRzDtdZBwQdMotdvXplNNJlb6+w/EEETQiNxIQEkWEMC+J0FvzJQogLYp/2LTk5RRT4fwBtbW1CO7hx/Trr6OwUpqSIKAqLoFi+U0ffEF8KDoTeLNywLsLiT56/BBIAFsKP//kXMFgf5IJgEkX0e2vj4xMe4sLgUawbeutrb28X9ND+ffsERYTzAiRnibKpyR8WPdAdiLvT3d3ls7hKME2d4SXvPx3AiQxCG2avA4MDPrkHaB9a/AkkAKwtCMYpIi4MHG+98Zvt/PXepctWvuA1RRS1jZWULBCWN3aliLB4IR0iYgch7LGZAgZ+BoEUkGgDEttEDEQKc1ejeZC1AFNWRGUl2odAAsA+wmDcisidInrgwccWzkpNe8IREbGOL4gVeuuD9+3Zs2dEcVFEMIc0IwGJvxdKHGBGRUYK569BL6OI4rAXi6NVdsYQQqChIOS89Q3AWQacy8jah0ACwN7CYJwi+uiDt5GCCgHo3SmiR8Ak6K3PRRGhQBuAVgDtwE4UERa1uIh4dntoiPVz4SbSKOrcNYMOgYVP1JjXrRU1HRxCQzANjPVNL+016rkcJTx8aeEnkAAILkGAFQ7bwn53iqi0tOJvFywqX8mFwVN8cXvYSJ11dXWifPrJJ0IIQDOAULALXYBFLlYsdLFCGCB3LxbLEf4XqQ5dyU5wYIyY/g5HhG14cLQTQgBlWPRrSCRwd6WydPUP8fwh1Bz4S4u+7REZFRU2pp2GvfD8c2E/eOZZv+fspFlkfWEwgSI6f/70R/z1pw88+NgibykiWA7BggjCQCuMsBWFQbDGU4UwCA+PYgZzwxNshujo6B/+/h/8lx+6v8eFwAH+x1U+IgFAmCwMpBTRlse/tTohIREU0UN8Rz9bb30dHbfYgQP7RXFRRLAimm5YBAKB4BXWjBXgZ7z8VxIABJkgmEARvfPWqzv46/2lpRV/5y1FtG3rVuGgBa9jmJaSRQmBEBB8l5dVvFT48iYkAOwvDEAR9aK4U0SbH3i0eHbanG86IiJWhoeH36m3PjgnnT9/XhS7UkQEQpAAHoT/4ktNgARAcAmDcYpo60fvnuKvv2AmUUTw1kWEUkQqJYqIQPCrJkACgGBIELhTROEuiqikpPRvyxYvWRUfn/CEw+F43EidiNmDsv3TT4kiIhD8iD/5s++teeWlFw+QACBMRxggd8E4RXTx4nlQRNs33//IP82enf6UNxQRYu0jKB0OjxHumUAgTMTGjXeJoge7d+9ihw9Jc0qsZaOWQSQACF4JAxdF1LP14/dO89cXMAe2PPatlQmJiUh6/3W+o9cdSL+zs1MkQUEhiohA8A4uXxbpJR+BBEBoCoKJFNHbr+7irw9m5+T97cpV69d6SxEVFhaJw2MkSCGKiEDQB42NUyIJAIKvhIE7RdT15uv/MU4RJaekroiJmfFEeHj4ar31gSK6cOELUZDovowoIgJBrwpAGgAhoMJgCGu4G0UEK6JfP/rYUysSE2c+ZJQi6nKjiDKzskRQukWLSilLFYEg0wCiSAMgWEMQTKCI3n37t7v560MuiiguLv6xiIiIJ4zsTBobGkTZuWOHSN2IJClEEREIbtt89bPgs6BWJAAInoSBlCK6594HfpQ2O315TMyMbxmhiBDA7cKFC6KAIlq0cKEwKUUmLAIhlBEVrYzSm0ACgGAFYTBOEW3/9CNkPL/Ey2tuFNF9fBeTr7c+UERHjx4VJT0jQxwcE0VECF0NQLnR91nsQxIAhOkIAlBEyGk44E4RRUZFPfPII9+4Ky4+ARTRk8wARdTc1CQKKKLCoiLhX1BQUECpDQmhowGo83TEkwAgWFUYuFNEEW++8euP+esd99z7wD+OUkQxT4WHO9borQ8U0cULF0SJi4tji0pLxXkBUUSEYEe4+gyANACCLYSBlCJ6ZMs3l82cmfRwRETkprCwsLl66+vu7mbHjh4VBRQRTEoXLlokBAOBEGyIJA2AECSCYAJF9N47r+3lr4/wCf5DN4roUWi9euscp4h27hDUUPniCqKICMGlASjm8vDwcAz7KhUskkP1kQAg2EUYDI9N2D53imjj3ZteSJ+TsWJGbCwoog1660PKxEuXLonioohKF5UKDYFAsLUGoE4DF+v22lS7aRIABH8Kg3GKaPfObd38dS0vb5lBEeGMAGcFEAhEERHsCOR7loE/E5EkAAjBJAikFBEvzz7x5Lc3xsUnPBoREYFYRLqjyl27do3t2LGd7dq1U1BD8C3AX9VDRSBYTwNQLsc+s4smAUAItDCYTBFt5a93bbx704+8pYjgTwC/AvgXEEVEsDr4PFfKBtIACKEgDARFxEsPchewMYro4Ue/cUdSUjLXCiLv4epwkd76ent72fHjn4nioohgRRQfH0+DTbCgBiA/A+BznjQAQsgJg3GK6Hfvvn6Qvz7Oy//xliKCFRFiEEEYICYRUUQEq0BjLkaRBkAIVUEgpYjWrLvruezsvBWxoxTRvXrrGxkZYdVVVaK4KKKysjKWkZlJg00IKPimRnUpOjIyKmxwcGCENABCKAuDyRSRk5f3zaCIkK8AeQvgbIYgdQSCv4FcGgr0uy3+pAEQCCqK6PEnv70+Pj4eJqWgiHTbg7a2trLdu3axPbt3E0VECAg6OztUWmujz7QOGnaCzQXBBIrorTd+/Ql/vXvNurv+3luKCCn6kOMYuY6R85hA8CWcTqf0/du3hz5z+5c0AAJBIQzcKSK4zAuK6KFHnixPTk7ZwrWCu8LCwhbpra+/v5+dOnVSFKKICL4GTJdlGBgYOEwaAIFgTBiMU0Tvv/cGnMxOYr6bQRHl5+cLR7OSkhKtgzsCQTfg1Q6tU4b29rYjpAEQCNMTBO4UkcNFEa1aveGFnNz8FbGxcd9wOBz3660PFFFtba0on2wjiohgDg4eOCA9BObzrXrrR+9eIg2AQPBeGNxmo9EURymigwyHax8++NAT5SmpqV5RRCkpKay0rEx4HScmzqTBJujG1atX2IkTx6XXhoYGd0x6izQAAsEEYeCiiMI+eP/NyRTR17kwQLjqZL31tbW1sX1797L9+/YRRUTQjevXr7N333lHaJYy3LzZ/qYv70+zkxDqggBP3hSKaOmylS/MLyheHhsb97Q3FFExFwIwKc3NzaXBJkzZ+b/z9tvCH0WGwcGBn/zu3ddPkQZAIPhHGLhTRN3HPzvcwl9vc1FEDkfE2vDw8Aq99YEiOnvmjCjJyclcKygnioggDnzB+YP2Ue38Obqczrpf+rotJAAIBLkwGMQmbCpF9Htr4+MTHjJKEbW3t49TRHl5eeK8oKRkgVYicEKQAIe7cPJqampmVZcvsYsXL2p5/Y4Kia7Ov965/eOrkkumagBhGhLINPzpn39f8/oPnnnW8j/iC88/59X3y8or6EmwOUARsdEAdFFLl61MHaOInnI4HF+n0TEfCO09ODgw/n90dExI9Luvr/d//+Y/Xn5ZcRkLdrt4YcLabQkBQCDYDa+89GKkSxg88OBjC2elpj3hiIhYZ4QiImjj9u2hCTvlUBAAfPH/n3zx/7nWsPByiwQAgWANQQCVPGpMGIxTRA5HxINhYWFpNELTB3b/0AJCRAB0gfb57au/9GT1089LNwkAAsF6wmCcIiotrUhcsKh8JRcGoIgeptHxbvcfzAIA1j448FVw/lMEBRtNp0oCgECwsDBwUUSRDzz42KIximhNeHj4Ehod44t/sAkAvu42DA0Nfgg7f4mppwpQh0D/jJAAIBDsIQgmUERbHv/W6ri4hOWRkRErwsMdK/l7sTRKE3bDE2ifIBEA/QjpzPt1hvfvMF/0j374/lvnplEPTJT73IQICQACwUbCwOEmDMLxXmRUVFio9D8yMjJMvfAPjozu/m8rTWsdDke7LYXagCmZvGCW3DlJiyABQCDYVBiMU0TMZNtum0PLt6I9RMcElj/IFjNitgAgRzACIQD4kz/7nnA0m0wR0cgQJDv/rsmLv1nwiwbgdSPDaINECAmEjwmCcYqINICQ1QDGw5irPkAaAIEQfA9971iJHNMMUGgHFPwYGfv9QfcMjO38fb47Jw2AQCAQ7CgxQkUDsIOQIhAIBLshnIaAQCAQQhP/X4ABAOKZHP5dp/U5AAAAAElFTkSuQmCC", -}; diff --git a/mod_examples/mod_settings.js b/mod_examples/mod_settings.js deleted file mode 100644 index b87c138b..00000000 --- a/mod_examples/mod_settings.js +++ /dev/null @@ -1,32 +0,0 @@ -// @ts-nocheck -const METADATA = { - website: "https://tobspr.io", - author: "tobspr", - name: "Mod Example: Mod Settings", - version: "1", - id: "mod-settings", - description: "Shows how to add settings to your mod", - minimumGameVersion: ">=1.5.0", - - settings: { - timesLaunched: 0, - }, -}; - -class Mod extends shapez.Mod { - init() { - // Increment the setting every time we launch the mod - this.settings.timesLaunched++; - this.saveSettings(); - - // Show a dialog in the main menu with the settings - this.signals.stateEntered.add(state => { - if (state instanceof shapez.MainMenuState) { - this.dialogs.showInfo( - "Welcome back", - `You have launched this mod ${this.settings.timesLaunched} times` - ); - } - }); - } -} diff --git a/mod_examples/modify_existing_building.js b/mod_examples/modify_existing_building.js deleted file mode 100644 index b09f5a20..00000000 --- a/mod_examples/modify_existing_building.js +++ /dev/null @@ -1,27 +0,0 @@ -// @ts-nocheck -const METADATA = { - website: "https://tobspr.io", - author: "tobspr", - name: "Mod Example: Modify existing building", - version: "1", - id: "modify-existing-building", - description: "Shows how to modify an existing building", - minimumGameVersion: ">=1.5.0", -}; - -class Mod extends shapez.Mod { - init() { - // Make Rotator always unlocked - this.modInterface.replaceMethod(shapez.MetaRotaterBuilding, "getIsUnlocked", function () { - return true; - }); - - // Add some custom stats to the info panel when selecting the building - this.modInterface.replaceMethod(shapez.MetaRotaterBuilding, "getAdditionalStatistics", function ( - root, - variant - ) { - return [["Awesomeness", 5]]; - }); - } -} diff --git a/mod_examples/modify_theme.js b/mod_examples/modify_theme.js deleted file mode 100644 index de7f0ad2..00000000 --- a/mod_examples/modify_theme.js +++ /dev/null @@ -1,24 +0,0 @@ -// @ts-nocheck -const METADATA = { - website: "https://tobspr.io", - author: "tobspr", - name: "Mod Example: Modify Builtin Themes", - version: "1", - id: "modify-theme", - description: "Shows how to modify builtin themes", - minimumGameVersion: ">=1.5.0", - - // You can specify this parameter if savegames will still work - // after your mod has been uninstalled - doesNotAffectSavegame: true, -}; - -class Mod extends shapez.Mod { - init() { - shapez.THEMES.light.map.background = "#eee"; - shapez.THEMES.light.items.outline = "#000"; - - shapez.THEMES.dark.map.background = "#245"; - shapez.THEMES.dark.items.outline = "#fff"; - } -} diff --git a/mod_examples/modify_ui.js b/mod_examples/modify_ui.js deleted file mode 100644 index 4beb403d..00000000 --- a/mod_examples/modify_ui.js +++ /dev/null @@ -1,46 +0,0 @@ -// @ts-nocheck -const METADATA = { - website: "https://tobspr.io", - author: "tobspr", - name: "Mod Example: Modify UI", - version: "1", - id: "modify-ui", - description: "Shows how to modify a builtin game state, in this case the main menu", - minimumGameVersion: ">=1.5.0", - - // You can specify this parameter if savegames will still work - // after your mod has been uninstalled - doesNotAffectSavegame: true, -}; - -class Mod extends shapez.Mod { - init() { - // Add fancy sign to main menu - this.signals.stateEntered.add(state => { - if (state.key === "MainMenuState") { - const element = document.createElement("div"); - element.id = "demo_mod_hello_world_element"; - document.body.appendChild(element); - - const button = document.createElement("button"); - button.classList.add("styledButton"); - button.innerText = "Hello!"; - button.addEventListener("click", () => { - this.dialogs.showInfo("Mod Message", "Button clicked!"); - }); - element.appendChild(button); - } - }); - - this.modInterface.registerCss(` - #demo_mod_hello_world_element { - position: absolute; - top: calc(10px * var(--ui-scale)); - left: calc(10px * var(--ui-scale)); - color: red; - z-index: 0; - } - - `); - } -} diff --git a/mod_examples/new_item_type.js b/mod_examples/new_item_type.js deleted file mode 100644 index 104ef0a0..00000000 --- a/mod_examples/new_item_type.js +++ /dev/null @@ -1,149 +0,0 @@ -// @ts-nocheck -const METADATA = { - website: "https://tobspr.io", - author: "tobspr", - name: "Mod Example: New Item Type (Fluids)", - version: "1", - id: "new-item-type", - description: "Shows how to add a new item type (fluid)", - minimumGameVersion: ">=1.5.0", -}; - -// Define which fluid types there are -const enumFluidType = { - water: "water", - oil: "oil", -}; - -// Define which color they should have on the map -const fluidColors = { - [enumFluidType.water]: "#477be7", - [enumFluidType.oil]: "#bc483a", -}; - -// The fluid item class (also see ColorItem and ShapeItem) -class FluidItem extends shapez.BaseItem { - static getId() { - return "fluid"; - } - - static getSchema() { - return shapez.types.enum(enumFluidType); - } - - serialize() { - return this.fluidType; - } - - deserialize(data) { - this.fluidType = data; - } - - getItemType() { - return "fluid"; - } - - /** - * @returns {string} - */ - getAsCopyableKey() { - return this.fluidType; - } - - /** - * @param {BaseItem} other - */ - equalsImpl(other) { - return this.fluidType === /** @type {FluidItem} */ (other).fluidType; - } - - /** - * @param {enumFluidType} fluidType - */ - constructor(fluidType) { - super(); - this.fluidType = fluidType; - } - - getBackgroundColorAsResource() { - return fluidColors[this.fluidType]; - } - - /** - * Draws the item to a canvas - * @param {CanvasRenderingContext2D} context - * @param {number} size - */ - drawFullSizeOnCanvas(context, size) { - if (!this.cachedSprite) { - this.cachedSprite = shapez.Loader.getSprite(`sprites/fluids/${this.fluidType}.png`); - } - this.cachedSprite.drawCentered(context, size / 2, size / 2, size); - } - - /** - * @param {number} x - * @param {number} y - * @param {number} diameter - * @param {DrawParameters} parameters - */ - drawItemCenteredClipped(x, y, parameters, diameter = shapez.globalConfig.defaultItemDiameter) { - const realDiameter = diameter * 0.6; - if (!this.cachedSprite) { - this.cachedSprite = shapez.Loader.getSprite(`sprites/fluids/${this.fluidType}.png`); - } - this.cachedSprite.drawCachedCentered(parameters, x, y, realDiameter); - } -} - -/** - * Singleton instances. - * - * NOTICE: The game tries to instantiate as few instances as possible. - * Which means that if you have two types of fluids in this case, there should - * ONLY be 2 instances of FluidItem at *any* time. - * - * This works by having a map from fluid type to the FluidItem singleton. - * Additionally, all items are and should be immutable. - * @type {Object} - */ -const FLUID_ITEM_SINGLETONS = {}; - -for (const fluidType in enumFluidType) { - FLUID_ITEM_SINGLETONS[fluidType] = new FluidItem(fluidType); -} - -class Mod extends shapez.Mod { - init() { - // Register the sprites - this.modInterface.registerSprite("sprites/fluids/oil.png", RESOURCES["oil.png"]); - this.modInterface.registerSprite("sprites/fluids/water.png", RESOURCES["water.png"]); - - // Make the item spawn on the map - this.modInterface.runAfterMethod(shapez.MapChunk, "generatePatches", function ({ - rng, - chunkCenter, - distanceToOriginInChunks, - }) { - // Generate a simple patch - // ALWAYS use rng and NEVER use Math.random() otherwise the map will look different - // every time you resume the game - if (rng.next() > 0.8) { - const fluidType = rng.choice(Array.from(Object.keys(enumFluidType))); - this.internalGeneratePatch(rng, 4, FLUID_ITEM_SINGLETONS[fluidType]); - } - }); - - this.modInterface.registerItem(FluidItem, itemData => FLUID_ITEM_SINGLETONS[itemData]); - } -} - -/////////////////////////////////////// - -const RESOURCES = { - "oil.png": - "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAACXBIWXMAAAOxAAADsQH1g+1JAAAE8mlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNy4xLWMwMDAgNzkuZGFiYWNiYiwgMjAyMS8wNC8xNC0wMDozOTo0NCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIDIzLjAgKE1hY2ludG9zaCkiIHhtcDpDcmVhdGVEYXRlPSIyMDIyLTAxLTE3VDEyOjIyOjUxKzAxOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAyMi0wMS0xN1QxMjoyMzozMCswMTowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyMi0wMS0xN1QxMjoyMzozMCswMTowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6Y2EzMGQxMDEtZWU5Yy00Mzc2LTgyOGEtZDM5ZmFkN2ViZTYyIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOmNhMzBkMTAxLWVlOWMtNDM3Ni04MjhhLWQzOWZhZDdlYmU2MiIgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ4bXAuZGlkOmNhMzBkMTAxLWVlOWMtNDM3Ni04MjhhLWQzOWZhZDdlYmU2MiI+IDx4bXBNTTpIaXN0b3J5PiA8cmRmOlNlcT4gPHJkZjpsaSBzdEV2dDphY3Rpb249ImNyZWF0ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6Y2EzMGQxMDEtZWU5Yy00Mzc2LTgyOGEtZDM5ZmFkN2ViZTYyIiBzdEV2dDp3aGVuPSIyMDIyLTAxLTE3VDEyOjIyOjUxKzAxOjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgMjMuMCAoTWFjaW50b3NoKSIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz7eSqt/AAAOOElEQVR4nO2da4wcV5XHf9XV3dPPmZ7padt5yEsyYRIccGzI4rB2snkQQh52ZMIjwSasNkjQC0isFpZskPiyu1KysB94afgACDAQlEhgMklAxKAQO3EAJXZekDX0hrDYjqfG8+rX9KtqP9Q42El3163qek1X/6SWZuTbdY9v/adu3XPPOVfSNI0BwSXktQEDvCVs14UmJyftutQpssC2lc/lwHk2XnsOOHDap2DjtV3hyJEjtlzHNgHYyK3A54E3OdjHGuAi4CMrvx8H/hv4JrDgYL++w29TwJeBe3H25rfjLOCLwDPADS737Sl+EcAo8BTwSY/tWA88BPynx3a4hl8EMA281WsjTuMuAiICPwjgHmCr10a04S4CMB14LYCbgX/12IZuTAEZr41wEq8F8BmP+zdiPfoytG/xUgAS/nz0v5a+FoCrfoCpidyrP+cLitDAXpyIcuNogmxEts2Oiqqxb6HCE0vLCDjCt51utxvkC4prfXnpCDIUwGQ8wkfXDRORJFs7HpZhdy6NBDxuLIKt+YISmprIqbYa4RO8nAIuN2pwbSZh+80/nesyCWSxy0ccM8JjvBTAOUYNMmFnzcuEZSSEFPCZfEG5MV9Qhh01yAP8uBfgGiaeLf9+6od8QXmWlU2kqYncvfZb5S5eCqBu1KCl6R+naFiLhdi48vmnfEHZCnx+aiI3Z6thLuKlAPYDl3Zr8NP5CmlZQnVIBCrQ6i0g5uPAzfmCkp+ayD1oj1Xu4qUADgD/3K3BM+WaS6b0xLnAdL6g3D01kfs3r40xi5cvgQc87NsJ7swXlJu8NsIsnglgaiI3A9gT1uIffpwvKG7HMvSEZwLIF5R7ANvjyDwmDBzMF5T1XhsiiicCyBeUHfh7F7AXRoAn8wVl3GtDRHD9JTBfUP4G2CPaPiWH2JSMMiJ797qiAv9Xa/J8xXDleoqz0EXwtqmJ3KJzlvWOqwLIF5QR4DFAyKOWi8jcvibNBTHvPbHLqsaji1Wm58oIbgpMAPtXRNBw1LgecPvP6mfoe+yGpOSQb24+QCwk8e7RBDeOJc187S3o/g7f4qYAHgAuE2kYkSR251K+ufmnc8NogndlEma+siVfUH7plD294pYAvg9sF2koS/D+8RSXJIccNsk6O7NJrhyJm/nKVfmC8nOn7OkFNwTwdeCDoo1vHE2ybTjmoDn28N5sir9Lm7Lz2nxBecApe6zitAC+AHxUtPFVI3GuGzX1ePUMWYIP5FJcmjL1pNqeLyg/cMomKzgpgC8CnxZtvCUdY2c26XmUqhmiksTuXJqNyaiZr92WLyjfcMomszg13ncD/yLa+KJ4lFuySUejf5xiKCTxoVyai+KmRHBHvqBMOWWTGZwQwHbgs6KNz4mG2ZVLkfbQ0dMrKTnEP6xJc765VcvH8gXlK07ZJIrdo55FT6YQYjwic8faYcZtjPj1ipFwiH9cm+YNQ6Z8a5/IF5QvOGWTCHYLYDcCsX7w17+as6Kr/+afIhuWuX3NMOdETYng0/mCcrdTNhlhtwA+J9IoLOnz5oSPHD01VaPYUllsqZRaKk2LgUJnRWV25VJmn2qfzRcUT5JR7dwLuBAwzKCISBK35VJm35wdo6FpHCrVOFis8VKtQU3VSMshNiSivCMd48K4eZGeF4uwO5fmWyeWWGoJpxPclS8oj09N5B423WEPSHYViZqcnLwD6Lq8CUmwfTTJu32y1q+qGj+cLfGb4nLbf5cl2D6W5Dpzrt9Xeb5S59szRcriIgA94PQ5o0Z2lYixcwowzPTZkopxrcXBdIL7utx80COS954s8+Bc2dL135yIsiuXIh4ytbx9AnvrIXXFTgEYZvpcPyqcieM4z5br/LbU+eafzsPzFX6+ULHUz+bkEO8fTxEW93Gk0EXgSkKinQKYMGqQ89Fy71C5JpxzoAHTc2UeXaxa6uuydIxbsklMPAjWoYvA1N6zFVav96UHNGC+2TL1naYGe+fKPNllyujGlSNx3jOWMpONdAFwEHD0bTmYAtAQjeo5g5qqcf9sicMW8xWuycS5yXxAyeOWOhMkkAIISViOMayoGt9TSrwgHh94BjeMJrje3CroUuBRS50JEEgBAGxIRM3MyWdQbqnsmSnyh6q1UL/tY0muNhdQ8vfAXkudGRBYAfxtKsZkzPr0uthS2aMUebnWNP1dCXhPNsVWc4EvN+NATaXACiAswa25FOt62ItQGi32zBR5pW7uhRJ0J9N7s6YDSv4Lm+sqBVYAAGsjMh9ZO8zaHpanR+tNvnFiCaVhXgSxkMQHzQeU/IfpjroQaAGAHo/woTVpsuHeRTDfNL+2iK8ElGxICIvgSnRnkS0EXgAAE7EIt+VSDPcQlPLnWpNvnViiZM7vD+hb47vMhcHbVrpuIIAVLk5E+cB4ipjVpQHwx+UG354psmyhosVYWGZXLs3ZYrEEAwE4wVtTQ9w6niLaQ2ziC5U6350pUrMggnVRmR1jQj6CgQCcYks6xvvGU4R72LQ6VK7xw9kSTQtb7W8RS4gx3HgTZSCANmwbjrEza8pv/zqeLC7z03nzO4iCN8S2+zYQQAeuHomz3Zzf/nU8slDlaN28o8hNBgLowvWjiZ4CWBqaxq+L/i50NRCAATuzpv32Z/ByzbelAYCBAAyRgFvGU1wxbE0EVqOL3WIgAAFCwPvGk2wxlw0MwJjD9Y57xd/W+YiwJHHreIpNJusWbPRxnQMYCMAUsZDErlyKNwkmgk7EImz2Sf5DJwYCMMmp2kVGIjg7GmZ3Lm0mGtgTBgKwQCYc4o61aW4YTZB8zQZSWIIrhuN8bN1wT7EGbhHo8wJ6ISmH2D6WZOtwnBONJotNlUQoxLqozHhYthxu5jYDAfTIWDjEWNjf83w3BlNAwBkIIOAMBBBwBgIIOAMBBJyBAALOQAABZyCAgBNYR9CJRovfr2T4bkhEWeOj4hVuEkgBvFCp852ZIsWVJI60HOLDa9JcLJ6d0zcEbgqYb6rcN1t69eYDFFsq98+WmDNZNaQfCJQAWho8NF9mpk0i54lGi4fnK46eVexHAiWAI9U6B5c61/g5WFzmxaq1yh+rlcAIoK5p/Ohk9xO/VA1+dLJsKa1rtRIYAfxsvsJfBJI0jtWblmsCrkYCIYBX6i32d3n0v5bHlpZ5xULBh9VIIASwb7FiKm+/1FL5RUCeAn0vgP9dbnCo1D49KxuWO1YGOVSq8dKyv7N67KCvBaChv9lXOrzU7cgm2ZFtnwBaVjUOFpfp99fBvhbAK/Umh8vtl3XnxyJsSkbZnIzyxg5nAjxTrluqALaa6FsBaOj1+tvN/WEJrs3EiUoSEUni6pF424IQSy2V5yu1vn4K9K0A6qrGUx3m/gvj0TP8/hcnolzYIdHj6VKNeh/7BfpWAMWWyp/bVPGU0GsBnX5GYUSS2JwcalsR5OVa84x9g36jbwUw22y1fXSPR+S25djeGI+0PehJA+Ys1P9bLfStAEIdKvyEJdqWghuSpJ4KQ61W+lYAmQ55+bMNlefarAyeq9SZbbT/Sx/1eY5/L/Tt/ywth9pG+TQ0jb1zZQ4sLdPUoKlp7F9a5idzZRptyrqtjcikVvGxtkb0bURQLCSxKTnUdmOn1FK5d7bI9MppYCVVpdOL/iXJoZ6qh/qdvpW2hH5YU6cj21RNX+cvtTrf/HhI4rJ0rKd6gX6nbwUA+jGuN40lLd1ACf1kj34627gdfS0AgCuGY1yTMV/h65pMnMvNneixKrHzHaCMwTl3NVVjyOX5NCxJ7BhLkpFlHpwvG1byjockbhpLcvlwzJPyLoLRSNaOMm2DnQJ4AXh7twbH6k3O8+DE8IgkcU0mzsZklH0LVX5XrVNuqa8GgMqSXvFjQzzKOzNxTw+4PCZWWvZ5u/qzUwD7MRDAi9WGJwI4RS4ic1suRVPTOFZvsbDi4cuEQ5wdlX1R0OlFsZPI9tvVn53vAAeMGjxdqlk6UcNuwpLE+qEwG5NRNiajrB8K++Lml1oqT4sdSmk41qK4KoCj9Sb7FqqWTu3sdzTgl4tVjoodQ+dLAcwCx7o10IDD5RoLAczAMWK+2eLpklDswV+Ak3b1a6cAzgfONurs6kyc0R5O6OpXMmGZq0biIjfkXOANdvVrpwAMz7GZjEd5e6q/PWtWCaEfV9MpPO01+PLMIEOjNiWjfe1X75VYSOISseLSvhSA4UFGncKuBvyVi8SeAL48NGrCqEG7iJsBZyLohLrArv7sFIChdIMYcWMWQX+EbY9SOwVguLwfrP+NEQxAtm0dbacAXjJqcDIgCZe9MCvmIzEca1HsFIChf/qPAci16xXBMVqdewHPdEjTGqCjAc+KjZEvXcGGRr1YrXNIbLMjkBwu10RL1PhSAP8DKN0a1FSN6bkKs4N3gdcx22jx0FxFJCBkBjhiV792h4QZzk3H602+eWLJ92fqusnxesvMmNg2/4P9AnhApNGfak2+dnyRXy1WLR2x3i80NY1HF6t89fgCfxLbBgaYttMGu/MC9qFvVWaNGs43Ve47WWL/0jKbU0NcEIuQCYc6hnH3C1VVY6Gp8odqncPlOscbTdG1P+hju89Oe+wWwFEgD9wn0ljV9CCRo3OD6UCQPPoY24YTYeH3A19y4LpB50voY2srTuUFfArY69C1g8g0+pjajpOJITuBRxy8flB4BNjh1MWdzgx6F/Bdh/voZ/agj6FjuJEa9mHgHhf66TfuAW53uhO3cgPvRH+M2foG26ccRR+rO93ozM36ANPAE+jxbFeihzW9zcX+/cxTwGPAr9D9/LaFfRvhdoGIk8BPVj4AcXRBbEMXxLnAiMs2uc0iemz/fvSbfQCoemWMpAXYFTsgAPUBBnTn/wEw/PfizbscIwAAAABJRU5ErkJggg==", - - "water.png": - "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAACXBIWXMAAATEAAAExAE8zNSDAAAE8mlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNy4xLWMwMDAgNzkuZGFiYWNiYiwgMjAyMS8wNC8xNC0wMDozOTo0NCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIDIzLjAgKE1hY2ludG9zaCkiIHhtcDpDcmVhdGVEYXRlPSIyMDIyLTAxLTE3VDEyOjI0OjA3KzAxOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAyMi0wMS0xN1QxMjoyNTowNiswMTowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyMi0wMS0xN1QxMjoyNTowNiswMTowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NmU3YjM1ZDctOTAyNi00ZjNlLTkxNGItZTc0NjJhMzM3MGE4IiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjZlN2IzNWQ3LTkwMjYtNGYzZS05MTRiLWU3NDYyYTMzNzBhOCIgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ4bXAuZGlkOjZlN2IzNWQ3LTkwMjYtNGYzZS05MTRiLWU3NDYyYTMzNzBhOCI+IDx4bXBNTTpIaXN0b3J5PiA8cmRmOlNlcT4gPHJkZjpsaSBzdEV2dDphY3Rpb249ImNyZWF0ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6NmU3YjM1ZDctOTAyNi00ZjNlLTkxNGItZTc0NjJhMzM3MGE4IiBzdEV2dDp3aGVuPSIyMDIyLTAxLTE3VDEyOjI0OjA3KzAxOjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgMjMuMCAoTWFjaW50b3NoKSIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz5TDuuTAAANuklEQVR4nO2deZAcVR3HP3Ptzs7uzmbvTUKSzZIshByQkJCTkVNBiIhCFC+kKI9ESyjAhFMxCSBYWqKIllIgKGgNSkGhgEewCCGAKIcackAmJJDdJcne2dnZncs/ejfZyV793vTr7sn0p6r/6J7+vffb7W+/7v6933vPlU6ncchf3FY74GAtjgDyHEcAeY4jgDzHEUCe4wggz3EEkOc4AshzHAHkOY4A8hxHAHmOI4A8xxFAnuMIIM9xBJDneFUW3tjYqLJ4q6kHVgBbgPdUVrRr1y5lZSsVwHHGPLQLPrhNGfLb+2hCGNz+Y7p3kjgC0Mf9wOoxfp8CXDGwgdYirAaeU+tW9jjvAGOzFHiTsS/+SNQDzwLrDfbHcBwBjM55wFbg1CzKuA3YZIw7anAEMDILgCcMKusc4CmDyjIcRwDDKQd+BZQaWOYngLUGlmcYjgCG8xhaC2A0d6N9PdiKfBPASUDdGL/fClygsP6fo7UwtuF4/wxcxNHv9jOB6oHj7wIvcvS7fRfas3qDYn/moLUwFyquRzfHqwCuQrvbCkf5fcbAdtXA/gEgboJfoLUwtwIbTapvTI63R4AX7QXuQUa/+CNRA0xW4tHIbEBrcSzHVi1Aw6rN0raRcGgK8BdglmEOjUPJtPOJ7n+JVCIqY/4IWqxhh7FeiXE8tQAPYeLFL6pZQM0Zt1B9xs2AS6aIyWitlaUcFwKIhENPAeeaVZ83UEPN0u+By03xCSEmnHzF+EYjswKLg0Q5L4BIOLQWLdBiCi63j9plG/EUlh05VjH3qxTVSIcOLA0S5bQAIuHQCrQAi2lULriGwoqTMw+63NQs/R7eQI1ssZYFiXJWAJFwqAztU880SqdfRLBh5MbGU1hG7bKNuNw+2eJN/VsGyVkBAL9DC6yYQmHFyVSdft345yy4VraKOVjwKMhJAUTCoVswMZrmKQhSu2y9rru7tGElpdMvkq3K9EdBzgkgEg6djWQUzVc6BY9fMBTvclOz9Ha8gbG6EDKpOv264e8J+jH1UZBTAoiEQ43Ar2Vs3d4i6pbfSc2S28Gl/8+umHM1RbULherSvhQ24CkICnoJaI+C82QMZcgpAQAPAFPFzVxUL7oRX3AaRTXzqZynL8MrMHEpE2Z9Ubw6wBuopXrJbUgGiX4sVakEOSOASDj0NFqPnjBlJ62ieMrZQ/Y/k7E/Ep7CCVSfcaNMdUcI1C2mbOanZExnIyV0cXJCAJFw6EbgYhnb0e74mkU3URCsH9WuetE6PIXZd91XnLpmzHrG4DNZV64D2wsgEg6FgLtkbL1F1dQsvX3EZ77L66d2+Ubc3sCw30qmnktg0nKZKofX4/ZRtXAtEo8CU74GbC2ASDg0Ccm34iMvYmPcxb7SqZQ2ZH6yuTyFVJy6RqbKUfFXzaFk2vmiZo4AgIeBU2QMK+d/i8LK8U2jTVsz9oMNK/EWVY9ytjzls68S+voAKpD820WwrQAi4dAGJD+HSusvIHjiJeOe198ZIX54/5AjLsoaL5epclx8JZMJ1C0WNZN66RXBlgKIhEMr0dKmhCksn0nV6TfoOrf3wOvDbL3FE2Wq1UXJVOEkIOWPAdsJIBIOzUM22FMQpHbZHbg8BbrO7++MZOz7q+fLVKsbf9U8UZP8EkAkHCpGy5KpEDZ2uald8h28xfpDtsne1ox9X6natEBv8URcbqEsvHpAn5olsZUAgDBwhoxh+eyrKKoTM00nYxn7Lo9fpmohJOooVuHHIHYSwM3Ax2UMA5OWU37KlcJ2Ls8xicOphEz1QqTF6+hX4ccgdhHAWcAdMoa+ksnULJZ6X8RTOCFjPxFtkSpHL8m+jmGtzjgcAnoUuQPYRwBywR6vn9rld+L2ybWSvuC0jP2+tp1S5eilr004A/w1FX4MxQ4CuAeQ6jyvXriWgrLp0hX7K2dn7PcefJNUole6vPGINr8sarJFhR9DsVoAlwDfljEsm3kZJVOz6zYvrJyd0Xqkk30c3vu3rMocjXQiRs8+4bkiXlThy1CsFEAQbe4dYfzV86g47RtZO+By+yg+4ayMY507HiOdMn6YYOe7T5Ds7xI1O65bgFuBSaJGHn8ltUvX43J5DHGibOZlDO2pi/c00b7tYUPKHlpmx9vCZb4EKF/TzyoBfByJpt/l9lK7bD0ev3icaDQKJpxI8ZSzMo517Pgt0eZXDCk/nYhxYOttMu8Wyu9+sE4AP5Uxqpj7VfxVc432hcrTvpmZF5BO8eHW79Dbkt1LeCreQ8uWdfS1vyNj/kxWlevECgF8GWgQNSqqXUjZSZ813hu0xJGqRZkp+elkjJYt6+jc+XtkWuL+zj00Pb+G3gNvyLjUBMgPlRbAiuHhPxA18BQEqVl8iwpfjlAy5RzinXtoH/KsTqcStL51P4f3baJi7ld0hZqTsVY6tj9K1+4nZaJ+g2SXjCiA2QJYDVSJGlUuuBaPv1KBO5mUz7madCpOx47HMo73te+kefMN+IonEZi8An/VXLzFdXgKykgn+0jE2ujv2EW05TViH/6bdDqZjRt7gN9kU4AILpWLR48wWfQHCM7EUTx5BbXL7zTMJz10R57m0Bv3kk4qDcOPxgVoE10cQeVk0Wa+A1yI4MV3ef1Uzr9GkTujU9qwksnnP4C/Wrj/PlvWc8zFV42ZAhDuriuf9SW8gVoVvoxLQbCeSWffR92Z91BUMx/JAR4ibAK+q7qSYzHzHWD8JL0hePyVlDWuUuWLbgITlxCYuIREtIVo08vEDr5Ff/deEtGDpOM9uDyFpJIxSKeyqWYf8HWDXBbCLAGcAwhlQpTPvlJ3apcZeAN1BGdcSnDGpcN+a33r/oHPRSm6gM+hzV1oOmY9AoRy29wFQUrrpXJDLKGs8XLRVK9B3gU+hhb2tQRbCiDYcLGt7v7x8BZVD+tU0sEzwEcAY2LOkthSACX1KqfrVYNE1/QMtIifpZghgIVAkd6TfSUnyA6mtJSiukWimUmNgDWfOEMwQwBCd39g4hJVfijF5fZRVHu6qJnl08eb1QLoJoupVSzHXyncU7lMhR8imCEAoeE2BRNmqvJDORL5iabNcjYaZghg2vinHEVkZI/dkPC9XoEbQqgWgBeRkS0uN26v7vdF2yExo4j01KJGoVoAQgsvjTRbR04hNv4frM/KVu6AWEZEdv3o1iPuv+V/sGoBHBY5WeWgDDOQWDiiW4UfIqgWQBoQGnCXjLWOf5JNSfa2iZqoHYyoAzOeQUIpsfHuD1T5oZz44fdFTdSl+ujEDAFsEzm5v2uPKj+U09+1V9Rkuwo/RDBDAEIDHGIH31Llh3Ji4ingpgz+GAszBCA0wPHYiZtyhVSiV2b4t/BwYaMxQwD7BjZdJGPtOdkKRPe/KJoO/irmLVY5KmYFIoSauu73nlPlhzIkfFY+9FsPthRAz/vPywyltox4116ZR5flz38wTwBC49xSiV66doVV+WI4HTselckKzisBbENboFk3ne/8kWRMOLBiOv0du2VmFdkO2CLiZWZnxOMiJ6fiPbS+KTWK3FQOvf4jmbGAv1bgihRmCuARUYPD+zbRs9+UUdJSdO56nNih/8qYCv8vVGGmAP6JNvJViIOv3U2ix/KQ+TD62nbQ9p9fyJhuwwZ9AIOY3R+9TtQg1d9Ny4trSfVb3nF2hES0hQ9fukV2MqnsZ7cyELMF8DggHDDv73qPli3rZLpbDScZa6f5hetJ9B6UMd/esGrzC0b7lA1WZKRcK2MUO/Q/mv9xDcm+ToPd0U/88H6anl9DvFu4128QfevVmYgVAngSuE/GsK99J01//xp97eb3ovYeeJ2mTWuOWWFEiJvtdveDdTlpGxGMCwwS72miadNqOt/5Q7ZDsnWRTsVp++8DNL9wHcm+dtlidjas2iy18plqrBLAh2TRHKZTcVrf+An7N32dWKtQuoEQ0eZX+OC5K+nY/ki2YrNd0z+IFbOEDfIE8EPgetkC+tp20LRpNYG6xUyY9QX81adm71U6RbT5Fdq3/4Y+Y8R1a8Oqzf8woiAVWCkAgBuARUAom0KiLa8SbXkVX8lkSqZ9lMDEpRSWN+pO006nEvS1vU20aSvde/9KsvdQNu4M5dmGVZul1kEwC6sFAPAV4E9A1mPC4of3077tIdq3PYTbV0JheSO+4DR8xZNwF5Rqo3fTKVKJKMn+LhLd++nv3ktf207RhRz08C/g80YXajRmTxM3GouAPwPGr9hoDduBSwFDVqA4XqaJG4vX0CaRyt2U4KP8D7gCgy6+auwiANDy4y4BpHpXbMLLwCeBnMlps5MAAF5Hm1Es93LCtPeYELDbakdEsJsAQFsp60LgNqsdEeAmYCWiYyFtgB0FMMhG4FxsMHpmDN5Gm+nr+1Y7IoudBQDwPHASkh1ICokBVwOzMWlef1XYXQCD3AucBvzSYj/g6DJ3D1rtiBHkigBAe7P+Gtpd9zPAzOSAVuAuYApaUotwToNdsUsgSJZPAxcB56FdHCPZDfwVeBp41uCyhVAZCLJDKDgb/jiwgTYZ1YqB7Uy0lkKEN9BG62wZ2JoN8tHW5LoAhrJ3YHt0YL8cWADMAqYDFWiLVSbRZi5pRZuseQda3F7pIs12RekjwMH+5NJLoIMCHAHkOY4A8hxHAHmOI4A8xxFAnuMIIM9xBJDnOALIcxwB5DmOAPIcRwB5jiOAPMcRQJ7jCCDP+T/8rVBSzB2WowAAAABJRU5ErkJggg==", -}; diff --git a/mod_examples/notification_blocks.js b/mod_examples/notification_blocks.js deleted file mode 100644 index 23f95943..00000000 --- a/mod_examples/notification_blocks.js +++ /dev/null @@ -1,314 +0,0 @@ -// @ts-nocheck -const METADATA = { - website: "https://tobspr.io", - author: "tobspr", - name: "Mod Example: Notification Blocks", - version: "1", - id: "notification-blocks", - description: - "Adds a new building to the wires layer, 'Notification Blocks' which show a custom notification when they get a truthy signal.", - - minimumGameVersion: ">=1.5.0", -}; - -//////////////////////////////////////////////////////////////////////// -// This is the component storing which text the block should show as -// a notification. -class NotificationBlockComponent extends shapez.Component { - static getId() { - return "NotificationBlock"; - } - - static getSchema() { - // Here you define which properties should be saved to the savegame - // and get automatically restored - return { - notificationText: shapez.types.string, - lastStoredInput: shapez.types.bool, - }; - } - - constructor() { - super(); - this.notificationText = "Test"; - this.lastStoredInput = false; - } -} - -//////////////////////////////////////////////////////////////////////// -// The game system to trigger notifications when the signal changes -class NotificationBlocksSystem extends shapez.GameSystemWithFilter { - constructor(root) { - // By specifying the list of components, `this.allEntities` will only - // contain entities which have *all* of the specified components - super(root, [NotificationBlockComponent]); - - // Ask for a notification text once an entity is placed - this.root.signals.entityManuallyPlaced.add(entity => { - const editorHud = this.root.hud.parts.notificationBlockEdit; - if (editorHud) { - editorHud.editNotificationText(entity, { deleteOnCancel: true }); - } - }); - } - - update() { - if (!this.root.gameInitialized) { - // Do not start updating before the wires network was - // computed to avoid dispatching all notifications - return; - } - - // Go over all notification blocks and check if the signal changed - for (let i = 0; i < this.allEntities.length; ++i) { - const entity = this.allEntities[i]; - - // Compute if the bottom pin currently has a truthy input - const pinsComp = entity.components.WiredPins; - const network = pinsComp.slots[0].linkedNetwork; - - let currentInput = false; - - if (network && network.hasValue()) { - const value = network.currentValue; - if (value && shapez.isTruthyItem(value)) { - currentInput = true; - } - } - - // If the value changed, show the notification if its truthy - const notificationComp = entity.components.NotificationBlock; - if (currentInput !== notificationComp.lastStoredInput) { - notificationComp.lastStoredInput = currentInput; - if (currentInput) { - this.root.hud.signals.notification.dispatch( - notificationComp.notificationText, - shapez.enumNotificationType.info - ); - } - } - } - } -} - -//////////////////////////////////////////////////////////////////////// -// The actual notification block building -class MetaNotificationBlockBuilding extends shapez.ModMetaBuilding { - constructor() { - super("notification_block"); - } - - static getAllVariantCombinations() { - return [ - { - variant: shapez.defaultBuildingVariant, - name: "Notification Block", - description: "Shows a predefined notification on screen when receiving a truthy signal", - - regularImageBase64: RESOURCES["notification_block.png"], - blueprintImageBase64: RESOURCES["notification_block.png"], - tutorialImageBase64: RESOURCES["notification_block.png"], - }, - ]; - } - - getSilhouetteColor() { - return "#daff89"; - } - - getIsUnlocked(root) { - return root.hubGoals.isRewardUnlocked(shapez.enumHubGoalRewards.reward_wires_painter_and_levers); - } - - getLayer() { - return "wires"; - } - - getDimensions() { - return new shapez.Vector(1, 1); - } - - getRenderPins() { - // Do not show pin overlays since it would hide our building icon - return false; - } - - setupEntityComponents(entity) { - // Accept logical input from the bottom - entity.addComponent( - new shapez.WiredPinsComponent({ - slots: [ - { - pos: new shapez.Vector(0, 0), - direction: shapez.enumDirection.bottom, - type: shapez.enumPinSlotType.logicalAcceptor, - }, - ], - }) - ); - - // Add your notification component to identify the building as a notification block - entity.addComponent(new NotificationBlockComponent()); - } -} - -//////////////////////////////////////////////////////////////////////// -// HUD Component to be able to edit notification blocks by clicking them -class HUDNotificationBlockEdit extends shapez.BaseHUDPart { - initialize() { - this.root.camera.downPreHandler.add(this.downPreHandler, this); - } - - /** - * @param {Vector} pos - * @param {enumMouseButton} button - */ - downPreHandler(pos, button) { - if (this.root.currentLayer !== "wires") { - return; - } - - const tile = this.root.camera.screenToWorld(pos).toTileSpace(); - const contents = this.root.map.getLayerContentXY(tile.x, tile.y, "wires"); - if (contents) { - const notificationComp = contents.components.NotificationBlock; - if (notificationComp) { - if (button === shapez.enumMouseButton.left) { - this.editNotificationText(contents, { - deleteOnCancel: false, - }); - return shapez.STOP_PROPAGATION; - } - } - } - } - - /** - * Asks the player to enter a notification text - * @param {Entity} entity - * @param {object} param0 - * @param {boolean=} param0.deleteOnCancel - */ - editNotificationText(entity, { deleteOnCancel = true }) { - const notificationComp = entity.components.NotificationBlock; - if (!notificationComp) { - return; - } - - // save the uid because it could get stale - const uid = entity.uid; - - // create an input field to query the text - const textInput = new shapez.FormElementInput({ - id: "notificationText", - placeholder: "", - defaultValue: notificationComp.notificationText, - validator: val => val.length > 0, - }); - - // create the dialog & show it - const dialog = new shapez.DialogWithForm({ - app: this.root.app, - title: shapez.T.mods.notificationBlocks.dialogTitle, - desc: shapez.T.mods.notificationBlocks.enterNotificationText, - formElements: [textInput], - buttons: ["cancel:bad:escape", "ok:good:enter"], - closeButton: false, - }); - this.root.hud.parts.dialogs.internalShowDialog(dialog); - - // When confirmed, set the text - dialog.buttonSignals.ok.add(() => { - if (!this.root || !this.root.entityMgr) { - // Game got stopped - return; - } - - const entityRef = this.root.entityMgr.findByUid(uid, false); - if (!entityRef) { - // outdated - return; - } - - const notificationComp = entityRef.components.NotificationBlock; - if (!notificationComp) { - // no longer interesting - return; - } - - // set the text - notificationComp.notificationText = textInput.getValue(); - }); - - // When cancelled, destroy the entity again - if (deleteOnCancel) { - dialog.buttonSignals.cancel.add(() => { - if (!this.root || !this.root.entityMgr) { - // Game got stopped - return; - } - - const entityRef = this.root.entityMgr.findByUid(uid, false); - if (!entityRef) { - // outdated - return; - } - - const notificationComp = entityRef.components.NotificationBlock; - if (!notificationComp) { - // no longer interesting - return; - } - - this.root.logic.tryDeleteBuilding(entityRef); - }); - } - } -} - -//////////////////////////////////////////////////////////////////////// -// The actual mod logic -class Mod extends shapez.Mod { - init() { - // Register the component - this.modInterface.registerComponent(NotificationBlockComponent); - - // Register the new building - this.modInterface.registerNewBuilding({ - metaClass: MetaNotificationBlockBuilding, - buildingIconBase64: RESOURCES["notification_block.png"], - }); - - // Add it to the regular toolbar - this.modInterface.addNewBuildingToToolbar({ - toolbar: "wires", - location: "secondary", - metaClass: MetaNotificationBlockBuilding, - }); - - // Register our game system so we can dispatch the notifications - this.modInterface.registerGameSystem({ - id: "notificationBlocks", - systemClass: NotificationBlocksSystem, - before: "constantSignal", - }); - - // Register our hud element to be able to edit the notification texts - this.modInterface.registerHudElement("notificationBlockEdit", HUDNotificationBlockEdit); - - // This mod also supports translations - this.modInterface.registerTranslations("en", { - mods: { - notificationBlocks: { - enterNotificationText: - "Enter the notification text to show once the signal switches from 0 to 1:", - }, - }, - }); - } -} - -const RESOURCES = { - "notification_block.png": - "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMAAAADACAYAAABS3GwHAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA4VpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDcuMS1jMDAwIDc5LmRhYmFjYmIsIDIwMjEvMDQvMTQtMDA6Mzk6NDQgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6NGQwZTY5MmYtOTRlNy00MDQyLWFjY2ItNmU3OGEzMGU1N2ZjIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjAyQTMyQTVGNzA1RTExRUNBQ0EzQTZCNEYxQjM5MkFBIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjAyQTMyQTVFNzA1RTExRUNBQ0EzQTZCNEYxQjM5MkFBIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE5IChXaW5kb3dzKSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjY3NDVhMzZlLTkyMWUtNDViYS04NDFjLWVjOWZjZTc0Y2MyNSIgc3RSZWY6ZG9jdW1lbnRJRD0iYWRvYmU6ZG9jaWQ6cGhvdG9zaG9wOjk3MDYxZWYyLTY2ZGMtZjI0Zi1iZTMyLTVhNTdhOWI3YTQzNCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pv8zaPsAABCFSURBVHja7J1pcFXlGcefuy/JTULInkAICFJRUbAqCrXW0c5Iq62lQ6c4/VS3sWpdpu30a2f6oYsdly6g1qWICGpdaatSdUQQlYiIllUIhKyQ3Cx3Pffe0/c53Lgk77nZ7nLuOf+f80zkJLnn5LzP/32f511tqqoSAFbFjlcAIAAAIAAAIAAAIAAAIAAAIAAAIAAAIAAAIAAAIAAAIAAAIAAAIAAAIAAAIAAAIAAAIAAAIAAAIAAAIAAAjIozVx98w0134O2CKfPQ2vvQAgAAAQBQjCFQjrChyAyPCgFkhyXClgtbIWypsCZhLviX4VGEtQvbJextYduEtUIAE+NCYdcL+4GwBvhSUcKVVEvaVqWvdQh7Vth6Ye8hBxgLv6w/CNsp7DY4v+loSJfrznQ5t0AAX7BG2EfC7oafWIK70+W9BgIgujXdLAbgF5YikC73W60sAG4SH4QvWJoH035guSSYm7/7M2ZSHhstON9HZ5zrpdpmFwUqHORwohfU6CQTKg0Fk9TdptChPVE68GGElFjGnlH2gz5hT1pFAJwA/VXvmzbRJi35ZiktuzpA/gDG6YoNrqQqqpyanbnUR5evKqcdW4ao9c1hUlO6v8b+sF3YESuEQLfqxfy+Ejut/nkVXbG6HM5vErgcuTxX31mllW+GnKAg+UC+vexC0unt4Rf1419U0+wzPfAaEzJ7gYfW/LI6U8XGfnGR2QWwRi/sufbGSppZ54SnmJjKWqdWzjb75PzDTAJYKbvIMf+sBaj5rQCXM5e3DteZWQBnC5s3+qLLbdMSXmAdLlkZ0Hr5JDQKO9+sAlguuzj3HC8SXovhK7VrXdyT8RMzCGCF7OL8xV54hAWZd443o5/YbLavmBkEsEx2sWGeG95gQerm6M5sv8CsLUCj7CKP8ALrkaHcG80oAG7DpFU9pjdYkwzl7qY8rvxD9gksDQQAIAAAjETrrp0IgYB1+WTv7gAEACxLSlV5UpgXAgBWhoeKbRAAsCq6XecQALAKLggAWBknBACsjB0CAAACAAACAAACAAACAAACAAACAAACAAACAGAKYC/CHKLEVeo+plDX0Th1ia/9PQmKhlLade3lu2zahrHlVQ6qaXJRfYub6ue4ye3FOmkIoEjp607Qh2+F6MShGHW1KeP+fLCXqPMo0b4PIp9fYzE0neGm8y4roaoGHIwJARQBR/8X0xz/4IeRaX9WT7uiWeubIWpZ5NWEgA3EIABDcmx/jN5/fZgO74nm5POPfBLVjFuEr18VgBAgAOPwxuYBzfnzQfuhuLBTtOgiP125poLcHuQJEECBCPYm6OW/91PHZ/G83/uTnWE6cThO37+lkqqbkB9AAPl2wHfD9OqG4HgHv2nY7Xby+nzk9/nJJ8ztcZPD4dSuM6qaokQiSUo8TpFohCLhEIXDYUqlUpkFeDJBj/6mh759fQUtXlGCQoEA8sOed8L07yf6x/05j8dDM2ZUUll5hfb/mXDzqle/n8qpQvs3i2FwcID6+/soEsmcUP9nfVDrUr3gilIUDgSQW/buGN/53W4PVVVXa84/UstPFpdQxMyqaqqcWUUDA0E62duTUQj/3TQg7mWjJZejJYAAchh3b3kss/NXCaetqa0TIU52drvmPfErKmZQuWhFeoUIurs6dX/29Y1BEpEVwqEpgKkQ4yW8It5+7clgRkdtaGikemHZcv7Rn19TU0uzZjeTPcPnvyqe8WSHggKDALLLOy8OUVwn4eUwZ3bzHC1kyTXcGrS0zCWnU97zo4pHfO2pARQYBJA9Pn0vrIU/ejUzO39ZWXnensfvL6E5LS1CBPLI9fiBGH2wdRgFBwFMn4FTSS2s0HX+2XMoECjL+3Nxd2rznLm64dCbzw7Sqc4EChACmB673xqmeFQe+nCyW1ZeXrBn8/v91NggP0kolVS1OUkAApg6wu8PfxzTDUOqq2sK/ogV2jiDXIR7d4S0FgxAAFOibX9Mt0elvr4hp8d2TgZ+Ftl4A7dc3IIBCGBK7NkmDyHKysrIX2KcvnYeeONBNxk8eQ5AAFPi8Mfyqc08Oms0KitnSq/zhLnIcAqFCQFMjs6jcWny63a7qaTEeHNutMl2IilGKwABZIV978vn3XCX51Tn9+QavbGIg7sjKFAIYHLo1ZqlpQHDPnOJzrO1H4qhQCGAiZNMqFoINBru9fH6jLsUkadcy0aHg71JGurHoBgEMEFCA/Kkkeff6M3BMQI8Cc/llh+n1dsRRcFCABMjPJzUEYDTsPH/CC4dgQ71J0lVVRQuBDA+sYiqU8Ma/zXpTcXmHq2Egu5QCGAC8DwaKbYi2IFB5xFTKRYApkVAABZGgQAgACujilaAWwIAAVg4vEMeAAFYuRVAAwABWFsAUAAEgBYAQAAAQAAAQAAAQAAAAgAAAgAAAgAAArAsZtxMauAkpkBAABOAz/fd+rR8d2W1CCaS6T3j7rei1HMcyyIhgHHY8cqQtiZYRiAQMPzzZ9qlevsr2B0CAsgAH3+ktw06b4dSXVNr+L+hvKKCZups3NXdlqDWN8IoaAhgLP09Cd1t0HkdMO+/WSzwrtV6i/fffn5IuuMFBGBx3v3XECUUeejDNb/HWzwns/Pi/foGfcHywd4AAvictn0x+ni7PDTg7QaNsA36ZBk5WE8Gb/q1dztCIQggjd5BEtrBd42zDLMN+mThQ/v0jlHC4RkQgAbPjz/QKu8d4VMZfT5f0f5tTpdLO7VSBucBp7rQLWp5Aejtm8nbDFbX1BT938ehUGmpfDfrE9gzFAJoPyjvEeH42WYzx2up0Dk84/hB9AZZXgBdx+RHIPEpjGaBWzMZfV04UNvyAtA7dN0Ki8dTWCAMAdQ1y3dTDkfM000Yjeoc9VTjoFgUibClBTBrvlwAA8EgpVLmmEHZ39cnF3+Lk6LhOLZKsbIA6lvcZHeM7edXlDh1dpwo+r+vt6ebwmF5n3/9nNNjBPEY9gy19EDYwqXyvv6+vlPUfvwYxePF11uSSCSoq7ODuro6dYTvpMCM08WeTGCtgNPKf/ziFX769D15zN/f30eDgwPayZB8QqTRR4VV8Z+iKBQOhbSveiy6+IueoST2C7W2AGYt8NCyqwO0Y8uQ9PvJZFITgVmYf56b5p3rRuaLEOgLVlxbRs0LPVn/XG4xpmPZhsOeFd/76hhHMZx8gxYgD6y+s4qeeeAUfbY3ewfK8VQEbSHNZHtabBx+9WtJbLaoanDQNTcGyOX5qrCcLggAAkiz6raZWij09guDWfm8UGiYGt1Tm1EaGh7K2t+1aJmHll8jH912e1D8eANfgvOBeed6afdbITp+MEanOqc+WMQ9SCdOHKemptmT+r3u7i4Kh6c3GFdWaae6OU466yIP1c6WF7GvBLkABCChpslFV605vaAkGkrR4b0h6utWKJUk3YPoTnUk6dBHY7tMeSDK4XBSXV3dhCbYneztoR4hABmN85zUtEDnrGKVl28SlZSfdvySssz3crocqP0hgPHxlthp0UUBioQVikf1uxZPHFKkAhhx6rAIh2pr68nnl4cisVhUOH43DQ3ph19nXeyhuWdPv9Z2uZ3kL0XtDwFMAp/fRS6RMLIQZGdtNZ7hoqVXeGnXVnkSzSHNkSOHp3z/hRdM3/ltdhv5RdjDtT+AACb/ooTjBMod2mmLPII6ehDp0u+6qLc9Rcf2Z3f0uKLaMab70uNzTfj37cLxHU47ujwhgOzADmV3O8hFY2vSH91VTU/8tpe62rIjAo7lr705QPZRt3IKh0ZNnqXyxCvILj/5dTUt/VbptD9n7jluWnVHGXn9NmlrBNACGJYrVpdT03w3bXthcNIL0LnW53ziaxfKR6fRfQkBFAVnLvHR/MVebS8etrb9UerRWYbJcT53X7LVNzvJ45f3t3IPDrovIYDiiS8dNpp9pkezS1YGKBKKUzw2tcE1DnvQfYkcoKjh8GUqIQz3+pQEPHiBaAGKHw5huDbnliChJHUXpdgd9vSIrQNdmBCAyZpdu4283Jc/if58gBAIAAgAAAgAAAgAAAgAAAgAAAgAAAgAAAgAAAgAACMJgHeHki6TSiawRbcVyVDucSUeV80mAKZDdnE4iA1archQUL41u6qqnWYNgbbLLp44jBMLrUjXUfnioFQqtdusAnhbdvHgR1F4gwU5/LG83BUlvsOsAtgmu/iZeBGRYYRBVoLL+8CH8kPKB4LBnWYVwF4W/hjFi3xn+ytD8AoLweWtxFRZ/N/10oub95hVAMwrsoutbw7T8QPIBawAbzrM5S0jkVBeyvfz5FsAG6SZv4iAXljXN+ktREBx0dedoBfW9mnlLQ1/BoKbzS4Aju/+KPtGeChFG37Xi5bAxDX/ht/3auWsk/z++fnnNu4yuwCYPwuTBv2RUIo2/ukkbd00oPuiQHHB5cjlufHek5nKdLi9/dgjhXi+QiyKPyLsFmHr9cKhXVuHac+2kLa51LxzvFTb7KJAhYMcThs8yuDwCC8PcnW3KVpX5/7WiDTh/TKh4aF7tr62pc0qAmCeFFYp7H69H+CXtndHWDNgXqLRyK+e2vDo5kLdv5CT4R4Q9jO4gLWdf/0TDz2U4UdSZhbASD5wvV5OAEzLsAh7bh7H+ZmE2QUwEg4tFnYv/ML8KEr8L0eOHPrGBMMexaw5gCwxvlvY0+kWYZWweriLOVBVtTuRUF4cCAY3Pf/Pja0T/TXSmUJvRgGM8F7abhe2RNhyYSuELRXWJAx7CRZBJS8cviOVSn3EE9sGBwfeffH5TVOZ3hBJi8BSAvgyrWn7vKfoH4+vLU+pqiGeedUPr1/ndDqvG329tDRAHo8v6/eLxSI0LDlAO5FIPPfMpvU3Sn+pAL3GWVrMwrF/XqYJF9XmuLFYjM8RLRNW8DOCHA7HJbLrLldu9vDX+1zxHMsVJW6mZXW8UiZvnSJFtSb4pzfezgU9mI/egUxcedXKhTabrU7ijGS350ab/Ln8+WMqeZut5sqrvrPQJM7P5TqYLue8UHTbo4+I4OF193vFV18hGvryihkXS1+mM7cpitPppmQyovc8+4o5T+aYX5Rt3ldH5UwAD629b1q/L2q28X6EXxbPnHOnk2Nnvlo0EY5cKr/uyvF9nSIMlN6Xn+exInP6VLrG565O7u1RRaVGphFAHmuOWNryRklJ6TLZ9UCgXBqmZAu3202KMrZr3OMhzkf6CJg7BzBICLaIJGMU7Pi5dP5x7lGXfi5glhZAVY3RsXHDTXeMvrRcJyzKy/PwfWR5QPq5PjHiO0QLYC6WFyL+n8B9lqNoIIB8sKLQLYBOi3kZytN6SXAh4P2Nmkdf7Os7WdCHSiaTvJ+OK98dAmgBrMc2Iz5UPB7biQoNAsiXAIy2YDkVDPa/S5gsCAHkAe5pucVIDxQOh+7Z8vJz+9LliTKFAHLOOmE3GaAlSAnnv2vD+kce/3KejOJBEpwvEbxDp7sfR9YtNOf6pqqqHueEl2N+DnvSNf/oMkUiDAHkLRxiW5v+97gTmJ547G8V6mRa3lFjWROY+owWAAIoGBmd8+F199vTIsnlEO1IHoCdxZADGA6Xye5T9NgwXyTHL/ir07pLhHnycFvOAUIoW7QAaAEAWgADtQD5TUhQtkiCC54VwwkRAgEAAQAAAQAAAQBgGP4vwABwNyC/lJLuDQAAAABJRU5ErkJggg==", -}; diff --git a/mod_examples/pasting.js b/mod_examples/pasting.js deleted file mode 100644 index 698edeff..00000000 --- a/mod_examples/pasting.js +++ /dev/null @@ -1,23 +0,0 @@ -// @ts-nocheck -const METADATA = { - website: "https://tobspr.io", - author: "tobspr", - name: "Mod Example: Pasting", - version: "1", - id: "pasting", - description: "Shows how to properly receive paste events ingame", - minimumGameVersion: ">=1.5.0", -}; - -class Mod extends shapez.Mod { - init() { - this.signals.gameInitialized.add(root => { - root.gameState.inputReciever.paste.add(event => { - event.preventDefault(); - - const data = event.clipboardData.getData("text"); - this.dialogs.showInfo("Pasted", `You pasted: '${data}'`); - }); - }); - } -} diff --git a/mod_examples/replace_builtin_sprites.js b/mod_examples/replace_builtin_sprites.js deleted file mode 100644 index 6e88b7fb..00000000 --- a/mod_examples/replace_builtin_sprites.js +++ /dev/null @@ -1,48 +0,0 @@ -// @ts-nocheck -const METADATA = { - website: "https://tobspr.io", - author: "tobspr", - name: "Mod Example: Replace builtin sprites", - version: "1", - id: "replace-builtin-sprites", - description: "Shows how to replace builtin sprites", - minimumGameVersion: ">=1.5.0", - - // You can specify this parameter if savegames will still work - // after your mod has been uninstalled - doesNotAffectSavegame: true, -}; - -class Mod extends shapez.Mod { - init() { - // Replace a builtin sprite - ["red", "green", "blue", "yellow", "purple", "cyan", "white"].forEach(color => { - this.modInterface.registerSprite(`sprites/colors/${color}.png`, RESOURCES[color + ".png"]); - }); - } -} - -//////////////////////////////////////////////////////////////////////// - -const RESOURCES = { - "red.png": - "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAADsQAAA7EB9YPtSQAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAABJ2SURBVHic7Z17dF1Vncc/+9z3zeOmeVOThj5oi20DpU3btAU7KCrDS1GcJSiKLhRRVxXmweDgAkUcl+LgyKAzLsdxGB4jICgFnAJSp3k0tKVNH5Q2PNLmNmmbtM19v8+eP1JGrOfcnPtK0rA/a+Wfu/f57Z17vnc/f/u3QaFQKBQKhUKhUCgUCoVCoVAoFAqFQqFQKBQKhUKhUCgU0wQxmYW3lZU12qW2SpOs1AXzBWI20ACUAT5gFEiDGAJ5EMEBqbPNJkVPRzzw5mTW/XTWVFQs0HWxUkiWCzhHh1kCGgE34JUQERARyCMS7U2kvl9Cj11ktmyORocmq94TLoC1ror5uk1cC1wBLM27DoI+Ac8gxUOd0cC2YtbRKqs9Fe1SiGuBywTMLsDUTgFPY5MPdYZC+4tVPytMlABEu6fiI0LT1iPlRcUvV/YKuL8yGnrwOUgU1/afsg7cKa/vsxL5FWBRkc1LhNgsdf2+7ljoKUAW2f6fUXIBrPVWXCHhboloLXVZwCDwHWc0+G+bIF1Mw8vA4fJU3iQEt8uxpr2kCOQuHb7RHQ1tKG05JWKVu+psTdN/DFxeqjKysE/TtJs7wqObimFsdbnvYjLyAQQLimEvFyQ8LXTtq13x0YOlsF8SAbSXVV4rJA8wNpCbLKSEf01Gy2/dzmA0HwOtNJSVe6M/BHEjkztgDiC5qSsWfLTYhov6T60De8Jb+WMBN+XyXIPXy4KqKub5qpjhclHhcFLucBBNJQmlUwQSKd4IjtI3GsAfCaPLXLpGsUfY9I/nOrhqr6hYKDLicXLo5zUhmFNZyTm+Kloqyql2e6h0OIilMwCEUimOx2O8HgzQNzrKsVgslyoh4f6maPBrj0EmpwezUDQBrKOuPOlNPA58yEr+lopKPtjczAeamqn3eCyXE0wl6Rga5CX/YXaMDJOxJoZRTcorOmKhDiuZ17grLpSa+C1QNV5eu6bRVl/P+9/TTFtDPZUOp5UiABiOxXjB72fjwCH6Q0FLz0h4LhL1XLOLoxHLBWWhKAJYxkyv0xN6RgixLls+TQgumjmTT86bz/yqcb/bcRmJx3i4r49nDvaTzIz7o4gKuKYzGnw2W6bV3srLgF8B3mz5vHY7V549h6vnzKXO486t4gYcCIzyaN8B/jA4aKWF63BGXZduYjhcaLkFC2AROH2eit8hxF9ky7ewagZfP/985vsKf/Gnczwe45f7X2NDf/9486aUlHymOxZ8xChxTVnldVLyC8BhZkATgstaWrhh4bnMcBX+4k+nLxDgn3p3sO/kyewZJS8mYsFLt0OqkPJshTwMcI7X91MEHzNLF8D1CxZy+7Ll1LqtN/W54LU7aG88i8U1tewYGSaaNp0B2oTgo012525/OvnaOxPWeMqvlohHALvZwzPLyrh7xSo+MnsOHrtptoKocbu5dFYLLpuN3pERc0EL5tgdzsaBVPLpQsorSABrvL6bQH7TLL3MbueO5W1cNXsOQpR+ED2zrIxLW1oYikboD4XMsgmEuLzJ4f6dP5UYAljr9S2TQjwNmHbgF86cyffb1/Ke8rIS1Py0CgrBkpoazq+tpfvIERKm3ZtYNsvhPjqQSuS9Epr3Wzk1St6OSV9Z6/bwgzVraCmvyLeIvNGl5IG9u3nijTeyZRvKSPsKp4hnMth7gGazjJ+ev4Abzn3vpMwD/eEwt3RtZjgWN8sSFTZ5Qb5LyPm2ANosu3sDcLZRYrXLzX1rL2TWJLx8GPsFrahvwG238crwsFm2Ck3o75No1wILDe0ANy9awqcWLJi0RYBKp5P2hrP4w9AgMeOuzSGlaPOnEv9OHkvHeQlgjbficyC+ZJTmtNn43qrVzPVN5hrQGIura3DZbWw3F8HMU3+GfHnxEq6ZN68kdcuFSqeT1ppaXvAPGE57BTQ1O539A6nkzlxt5yyAddSV647MU0C5UfpXl5zHhTNNv9MJZ3F1DcfjcQ4ERnN67uNz5/HZheeWqFa5U+fxUOVy0X3kiEkOsXJGquqnR4nkNCvQcq1I0hP/gtlmyIr6Bq6cXciuaGlY33oey+rqLedfUd/AlxYtLmGN8uPylrNZ1WC6D3VWuSf2+Vxt5iSAZeBAiPVGaQ7Nxtdaz5tcDxMT7JrGnW0rLC3Y1Hnc3L5sOdoEzFryYX3reThtJg234K8XZZnJGJGTAFwe31XALKO0q2fP4ayy0k+R8qXc4eDW85ZmzaMJwe0XtOFz5vQdTiiNXi8fmzPHLLnZ5624NBd7OQlACnm90ecOzcYnpsBgaTxWNjRy6awW0/TLW87m/NraCaxRfnxi7jm4TFoBgfaZXGxZFsBan2+GgA8bpV3S1Ey1u/jLoqXg5sVLDFcka9weblxUbAef0lDlcnFJk/GyhURetq7K+kaLZQHIJJdgskb+oVmmayhTjnKHg28ub6Pc/sd/pcxu545ly//ks6nOB5sNe2IAZyqRvtiqHcsL2lIYb/PWedwsqZn6zeY7WVJTw4MfuITuo0eQSNobGkuysVNKFtfU0OD1cjT6574uUogPAb+2Ysf6joaUa4yG+CvqG6fkyH88qlyurOOBqY4A2urq2XCw3yBVW23VjqUuYB115QjOMUpbWltntSxFkbnAdG1DnttKg6UpmSUBpDyJxWZ5FxTBsUORH1l8K2xlnpilEa0lAUiTTR+XzcbMKTz3n+7MLC/DbTdbzReW+jdLAhCaNBzm13u8U3bF7N2AABrcxp5rAt3S1MxaC6BrhsP8ujNk7j+dqTV3qLW0+WFNAEIayszrOHPmzdMVr8NkIqcJS/53FheCjI25bDlvJiqKjFszGQNIkdWr+W0svUENaeiPlMzoVh5XlJCEbuwvKE3e2elYEoAOx40+DySTVh5XlBCzdyBM3tnpWGvDJScMC0+U9CS2wgKjCZMfoSyiAIRmbGwkHsvxnJ6imOhSctzkfKHQjH+0p2NNAFK8ZvR5JJ22fKZNUXz6Q0HCaWMXQF1qr1qxYUkA8WigF4Sh1PacGOcIk6JkmH/3IpaMju6xYsOSAMbOn0lDl+O9xy11NYoS8OoJs+9ebrd6ZtDyRF4Ieow+7z46RMpkKqIoHSldZ8tRExdxKQzflRHWV3IkG40+DqVSdB85atmMojh0HTliOgWUmnzeqh3LAnBEg88Dhm/6fwYOWTWjKBJZvvNjrkjwRat2LAtgE6QFPG6U9vKxo4zEcwt3osifkXiMrcdMWl3Bo5tyiJCW02J+RkrDwAppXefhvr5cTCkK4KG+A6R142V4oWP4jszISQBbYqEuYJ9R2ob+/mxHmBVF4kQ8znMHjSPGSXi1Mxa0PACE3M8GSin4R6OElJ7hkdcP5GhOkSsP9R0wDRihCe4hxyPiOe/nNkWCDwGvG6Vt6O/HHy44bpHCBH84bOIFDMCbjkjwv3O1mbMAHoOMQNxrlJbSM9zbu6P0AW7fpdy3a2eWaGjie5vyCI+bl0dHZTTwC8BwHrJzZIQX/AP5mFVkYaP/ULZAF4d80cAv87GblwCegwRS3GqW/sCe3QRTylegWASSSX6y23xpX0r5tXyjpOft09UVCzwOGAZdHE0kuHfnjnxNK07jn3f3Mpo0fb8bu2OhJ/O1XZBTn8ywHjCc+/3v4CC/eeutQswrgKfeeovf+/1myQktI79aiP2C4gT6M4kTzQ6XG7jIKH378DCrGhupUe7jefFmMMBd2142jYcs4Z6ueOiJQsoo2K3XFw3eDRi29yk9w7e3bf3/aNkK68TSGe7cujVLkEheqYoG7ym0nIIF8BwkZIZPIDEMzTkQDnHXtpeV61gO6FJyzyvbGAgbRzuVEJE2eV0xrscpOFYwjHUFs5zOYyCuNEo/HAkTT2doq7ceqevdzE9e3cPvDplfECKEvKk7HHqhGGUVRQAAA6nkjll213wES4zS9548QZXLzcIZM4pV5LTk2YP9/OxVc3c+IXisKxL6RrHKK+rRnrjbdjMC0w2B+3f3sm34WDGLnFZsGz7GD3uzBPuU7I87bTcWs8yitQAAQ/F4osXmflYKrsMgiLQENg8O0noqvInij+w7eZLbt3STNNnmBU5ouvxATzgwWMxyiyoAgEPpxMlmm6MHIa4zsp+Wko7BIZbX16vp4SneCAb5m65OUxdvIIUmruqMBYt+QWbRBQAwkE4ebHY6D4O4yig9qetsHhpkVWMjVS5XKapwxuAPh7mls4OA+UofEvHl7kjA0BurUEoiAICBVHJns9NVCbQbpScyGf4wOMjqxkZ8znenCAYjEdZ3dnAiYe5II6T4QVcs8N1S1aFkAgAYSCWeb3K4mwRcYJQez2ToPDLIqoZ3nwj84TBf7+rI7kspebArFvwKJbxCtqQCAGhPJZ4JOV0LAMPw29F0mpcGD7O8rv6MiTZaKAdDQW7p6sz68gXiSWcs+Kn+It4RaETJBfAqyPpU4jd2h+sCYL5Rnngmw0uH/bTW1FDvmd6zg/2jo9za1cHJ7CerN/qiwY+/UOCNYFYouQAAhkBvSiWeFHbnaoQwvFAgqetsGvRzbnU1Z3mnZ+SxV0aGua27i3DK/L0K6AxHPVd0EpkQP/sJEQCAH9Jz074ndEfmfZiEnE/rkpcO+2ko8zK3cvKvnCkmmw4f5s5tLxPPcsGlgE5H1PXhHo4V5VZQK0yYAAD6iSbrUzWP2h3JNmCuUR5dSjqHhkCIMyJ0uxV+/eYb/KB3h6kvP4CUcpMoc12+OToyoV61EyoAgCFCqcpU4lcuh3ORQJheyrNzZITj8TgrGxrP2FiEGSm5b9dOHjywf7xh/AZXLHTV5qhB5OcSM+ECABiGTHsq+UTQ4WoRcL5ZvgOBUXqPj7CyoaFkN3WWikAyyR0v97Bp8HD2jIJHEtHgJ7thUpwoJ0UAMDY78KcSv21yuGoErDDLdzQa5feH/Syprs0WFHFK8UYwwC1dHfSNjnNTmeBHXZHgF4dKPNXLxqQJ4BTSn0o812R3jQrBBzG5yTSaTvO8f4Bat5tzSnD5dDH5vd/PP/RsMQ/eNIYEvtUVDd5GCRd5rDDZAgDAn070zLI79iC0KzG5lSQjJZ1HhjiRiNNW3zDlxgW6lPxs317+Zc9u0tm9nxJIPt0VC94/UXXLxpQQAMBAOrmv2ebYjBBXYnIfMcCB0VF6jx9n1RQaF4z191vYODDugZgRTcrLOmMhQ3f6yWDKCADGdhFn28se0YV+IVmudD0aGxsXLK6upW6SxwWvBwLc2t1B32hgnJxit10X798cD+6akIpZZEoJAOBgOhacm0o8mHG4msgyQ4im02wcGMCuaSyqrp6Q6+lPZ6P/EN98uWe8/h7g2YTLdvmW8KjZva+TxpQTAEA/pAdSid80O51hEBdj4rqmS8n24WPsPXGC5XX1E9YlBJJJvrVtK4/0HRivv5fAd7qiwS8MxeNTMnjC1BpJGbDaXX4RmvYY48S/r3K5uG3pBaw0v1u3KOwYGeae7dvHD4kjCYH43KkjdFOWKS8AgIs81c1pkX4CaMuWTwBXz53LF9+7CIdZGPU8yUjJfx3Yz3/uf238Mw6S12w2/WObw2FL0TonkynZBZzOwXQsWJ+qechhT842czt/m30nT9Jz7ChLa+uoLNIdwP5wmNu2dPGi3z/upF1IHg7HPFdsTZ4sqvNmqTgjWoB30l5WeT2SBwRk3TN22Wx8ZsG5/NW8eXmvGUjGop78ZO8uC8fbRExK+ffdseCP8ipskjjjBADQXlGxUGTEw0D268CBxdU1/O3SpTSXV+RUxpFIlO/t3M7OkREr2fdKkflkdySyO6dCpgBnRBdwOv5kcmRxKvEfCbvTgRCrySLkY7EYzx46iE1Ymy6+/au/Y2s3/vC42/ISwY990eA1L6VSZ0STfzpnZAvwTtaU+S6RUv4SOGu8vK01taxvbWWOibPJW6EgP9rVS6+1X/0wcENXNPhMThWeYpzxAgBYXV5ej679AvjL8fJqQrCsro7zaup4z6lLLw9HIvQeH2b78LC1U8ySF4VIX98ZjZ6Rv/p3Mi0EcAqx2lP5FSn47ngDxHyREAH+rjsafIBJ3sUrFtNJAACscledrYnMzxHi4mLaFdCZznBDTyI4rWLiTjsBnEKs9vpuBHkvUF6gqRiCu7oige8D0+6evOkqAADWun1zMkL/uRBiXT7PSyk32aT2+Y544M0iV23KMK0F8Dbt3oqPaHCXRLRae0L2SrizOxp6qrQ1m3zeFQJ4m7Ve3zId+VEkqxByAYjqsRR5QiBek7BFRzy5JRp4ZXJrqlAoFAqFQqFQKBQKhUKhUCgUCoVCoVAoFAqFQqFQKBQKRc78H7cNicQwxMlzAAAAAElFTkSuQmCC", - - "green.png": - "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAIwwAACMMBoDLh7AAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAABP2SURBVHic7Z15eFTV3cc/ZybJJJmQsEOQoqAVBaFVX7Uu5VXBpdW3uHZ56dviW1uL7VOXautSKy61Vay7Vn1tVV6tWkQUFI0RVFQsIPuO1hBMwhLJMskkmSQzv/5xb5K7zWSSzJKB+3me+yRz7nbO/f7uWX7n3HOUiOASP0opP3AhcC7wTaAeeEJEHk1rxHqJcg2ge5RSCk3smcClQIHlkDbgayKyNcVR6zNZ6Y5Af0cp9QPgTmBcjMOyAV9qYpRYXAOIglLqq8BjwLQ4Dl8EbExujJKDawAWlFI+4LfATcR+q3egCV8iIqWpiFsycA3AgFLqG8CzwJFRDgkA/wCeFpHlKYtYEnENQEcpNRVYCOQ77G4B/gTMEZGmlEYsybgGACilvg3MB3Iddi8ErhaRstTGKjUc9M1ApdQxwCrs4pcBvxSRxamPVeo4qHMAvcL3PHbx1wLniEh16mOVWjzpjkCauQuYbAn7J3DGwSA+HMRFgFJqNFCO+SXYD0wWkar0xCr19MscQCmVinhdhj39P02W+ClKU49JSw6glBoBnAAcAozS/xr/HwzUAVWWrVL/u1xE9vbh/gr4HDjMELxEROLx+kW75gjgFLrSMMqyDQRq6EpDpeX/VX1JU68RkZRswFjgWuADIAxIH7Yw8CHwa2BcL+IyzeGaP+jFdcbpafowQWn6QL/e2JTpkmTRJwO3Auv6+HC629bp95kUZ7xus5xfC+TGee6kFKdpcsYZAPA1oDTJDyja9lZ3Dw3N3Ws8Z0GcxvxWmtJUitbdnHCtEuoHUEoVo3WdzqSbCqZS4B/soXCEhwHDvRSO6Pjfg3+wh2BNhPo9EQJ7ItTvDhPYq/0NBaW7aJwDnKWUmgvcIiIVDsccavm9K0aaRgN3AD/qLk0APr+iqFhLT1Gxl8KRHopGdqWpYV+EwF5ta9inpStYE0FiJ2sasEYp9QzwOxHZ3V084iUhlUClVD5wvb75ox3n8cKhx+cw8RwfR0/zUTii5xXjYE2Ere+E2Lg4RNnKViQS8/Bm4AHgTyISMMR3J2Yj+I2IzLGkqRC4AbgayIt2A+WBsSfmMOnbWpr8g3uepsBeLU2bS0KUr24lEo55eBCYQ4L6JfpsAEqp6Wj95qOc9nuzYNzJOUw828dRU3v3gKLR+GWETW+F2PhGC1+sa4v1Fn2J5s9/Xo9zLVqtvIOZIvJsxw+l1Aw0wxnqdDGlYPTXspl8fi7HnOujYGji0hSsibBtSYjNb4f4/ONWwu1RD60CrhSR1/pyvz4ZgFLqRuAPgLLvg0nfzuWsX/sZOMrbhyjGR11VmI/nNvPP/2+K9QbdBfwO2ARMMITfICJ3683DO9HGAtjweOEb/5PPyT/KS1maSv8cZOPilmjGLcDNIvLH3t6jVwaglMoB/g+tXLRx6PHZnPvbAkZPzo7reu2tQmN1hIbqCIF9EYaO9TLiq72rnuzd0c7C2Q3sWtMW7ZB/AEOAqYawx4Fr0CqH33U6acxx2Xxn9gBGHJn67pOKDW28dXcj5aujpmkumhOrtafX7rEBKKWGAguA06z7Bo/xcvZ1BUw8O/pAmkgYdq1pY9vSEP/6uJX63RGa6+0F+Td+mMd5vxvQo7h1IAJrF7RQMqeRplrHSkI1MMzweyla3eUk64H5gzycc30Bx16Yi7Llc6ll89sh3r63kZpdjlnch8CFIvJlT67ZIwNQSk0AXkdz6pg49sJcpt8+AG+2/SmF22H7uyG2Lgmx471Wmupi19wAsnyKG5YPxefv/VNvro/w9p+DrJ7X3F0tOwyY8nSl4PhL8zj7137yivqPFzfcJrz2+wbWLmhx2l0GnC8iW+K9XtwGoJQ6EliBufKEUjDtmgKm/Mw+kEYENr3ZwjsPBKNZbVQGj/FydcmQhLx1m94M8fL19bEqVCa8WXDJnCKO+Vb/Hei77Mkm3rm/0cmw64CTRGRHPNeJywD0JtEK4ChjeHau4pI5hUw4y/6gyla0UjKnkcpNcT51HW8WHHZiDqfP8nPYCfHVIeJhx/utvPCretpDsdOb5VP84KEijvzPnITdO1lsKQ3x8vUB2lpsadqGZgQBh9NMdGsAes34NeC/jOEDhnv44V8GMmqiuVLUVBvhlRsb2P5eKOZ18woV48/0MfaEHApHeCgY5mHAMA/5gzxJK2vLVrby/Kz6qM4kn18x4y9FjD2x/4vfQdXmdp6bVUfDPluxugiYLt0IHI8B3A7cYgzzD/Hw83mDbE2hvZ+28/ysemornLN7/xAPx5zrY8I0H4edmIMn+S0pGxUb2ph7eR3NAXu6f/zXgRxxauaI30FdVZjHL60luN9mBHeIyO9jnRvTAJRSFwEvY2jne7Ng5tODbNnztndDvHxdwPHt8vkVp/0kn1MuyycnL81VabSm4jOX1dFoeWA3rRxGXmH649cbdq5q45nLaq31HAEuEZFXop0X1QD0Gv8KLN/BnX/LAE6aYfaMfvBUE6X3Ndrcst4s+I/v5XHGL/wJ9QAmgtZmYf5vA2wtDSECU6/yc/qsqF7sjGDF8828fkeDNbgRrT7g2DKIZQBLgDONYcddlMuFdxWajlv592YW3W67KQOGefjvR4vidgali6ZarTNm2OEHxvjYBTcFWPOKrYm4VESmOh3vaABKqWloXZCdHDIpm8ufH0hWTlcW+fnHrTx7eZ3N9XrIpGxmPFrEgOH9660/GGhvFZ6aUUflRpvX8CwReccaGE2hu4w/snIU33+w0CR+za4wL14dsIk/+bxcLn9uoCt+mnDSSucup+NtKimlLkYbr9fJSTPMnR8tDcJzP6+zuXCPnubjknsLyfJlZkXqQGHgKK+tngacoGtrwmQASikvWm9YJ74CxZQrzF6+0vsbqf7c/OqPHJ/FJfcUpt1f7qIx5Yp8fAU2Me7UNe7EmgP8GIu377Sf5JM/sOuw/eVhPnmp2XSSf4iHGX8pIiffVb+/kD/Qw2k/sbnnj0LTuBOrAdxo/FEwxMMpMy1v/32NtnL/e/cVpqR/3KVnnDIzn4IhtlLepHHnXv0jySOMO0+/0m9y3FRsaGNzidnFe9SZPsaelHnes4OBnDzF6VfafBtH6FoD5hxguvGoLJ/iuIvN30y+fW/Q9NvjhbOuzWznyYHOcRfnOlXKO7U2GsB3jEccfnI22bldJ9ZVhilbaR5w8vULchl+xIHhQDlQyc5VHH6yzRnXqbUHOodzm5p+488wd/FuXWLO+pUHzvyl+/ZnAlYt0ZqExdCVA5yPZWDn+NOtBmB++7/y9WyKit2KXyZg1RJN6/OhywBM5f+oCVmmMfvNAaH8E7MBOA0CcemfFI7wMGqCraieDuDRHQOmjoLxZ5rF3fF+yNb0mzDNNYBMwqopMFUp5fUAw7FMkTLO0qz7Yp25Y2Hk+CwGfcXN/jMJq6Zomg/3AMXWPUUjzc6Dxmqzz3/k0W7NP9OwaqpT7GgA1p68gGW8WaHb05dxROmdtRtAXqGyOQ6sAw4LhrnZf6aR5VNOw93sBjBguF3cxi/NBjBgmJsDZCIO2joZgFncUFBobzWPGioqdg0gE3EoBuwGYC3ffX7Focd3uRIHjvIyelL/Hufn4oxD3a04CzC1D7z2oURMv6OQZU8ECQWFKT/Np39OeObSHQ7a5mShTVHWSWCv/aOOYeO8XHx3oS3cJbNw0LbSg9UA9nT/5a5LZuKgbaUHME2iVO8awAGLg7YVthyguT7i9LWpS4bT1iJOE3HYcwDQZq1yObCIoqk9BwAI7OnZZA4u/Z8omlZ6RKQWMM0359YDDjwcNG0SkdqOFr2pGHCLgAMPB00roGtEUJlxz57tPZvWxaX/46BpGXQZwEfGPTtX9ni6OZd+joOmH0GXAbxv3NNQHWF/uVsRPFDYXx6modpWBLwPXQawAm1xxE7cXODAwUHLFjTNNQMQkVBHQAdlq6JOS+qSYThouULX3PRlkKkY2OkawAGDg5adWkc1gPrdYWq/cOsBmU7tF2Hqd9t0dDSAjwFTYeEWA5mPg4ataFoDBgMQkWa0NXQ7cSuCmY+Dhqt0rQH7BBGmYsDNATIfBw1NGsc0gLpKx/LDJUOo3x2mrjJ6+Q92A1gOmHyG25a6xUCm4qBdO5rGnZgMQEQa0VbP6GTDG44LE7hkAA7aLdU17sRpfO+Lxh9frG1zi4EMpH53mC/W2sr/F60BTgawAENzUAQ2Lo49979L/2Pj4pB1NZFWNG1N2AxAROqAEtPF3GIg43DQrETX1kS0TzxeMv6o2tLu9g5mEPvLw1RtsfX/v+R0bDQDWIi27Gonbi6QOTho1YymqQ1HAxCRBmCx6aJuPSBjcNBqsa6pjVhf+ZmyjH2ftbtDxTKAPdvb2fdZfNk/xDaA19GWG+nELQb6Pw4aNaJp6UhUA9A7DBaZLu4WA/0eB40WGTt/rHT3obfJcVBbEaZivdtB1F+pWN/mtGSfzfljpDsDKAHqjQGr/hHVmFzSjIM29Vh8OlZiGoA+bmyeMWzD66FoK3K7pJGm2ggbXrdl//M6xv5FI565Ph4x/mgPCatecnOB/saql5qd1kV+xOlYI90agIisx9KHvOLvzXGvxO2SfMLtmiYW3te1i0m8s/08aPzRsC/C5hK3Sdhf2FzS4rR49INOx1qJ1wAWAjuNAR/PdYuB/oKDFjuJ4vq1EpcBiEgYeNQYVrG+zW0S9gOi6PCorlm39GTCt6cA06JBbi6Qfhw0CKJpFRdxG4DelzzXGBal7HFJEVHqYnOd+v2j0dMpHx9CW5MeiFr7dEkRDq0xQdMobnpkACKyDcuq4lHany5JJoo/plTXKG56M+mrqXnRVBth/SK3SZhq1i9qcfLIxtX0M9IbA3gT+NQYsOzJJtcxlELC7dozt/ApmjY9oscGICK2cqZmV5hVL7p1gVSx6sVmanbZWnkP6dr0iN7O+/1XoMoY8N5jQUKNbl0g2YQahfceC1qDq9A06TG9MgB9gMFsY1iwJsIHT9myJZcE88FTTQRrbGX/7FiDPmLRl5n//wZsNwYsf6bJaTIilwTRUB1h+TO2l2w7mha9otcGoLsabzaGtbUISx+2ZU8uCWLpw0Gnibxvjtft60Sf1v4QkfnASmPYmvnNVP/LbRIkmup/tbNmvi2XX6lr0GsSsfjLDcYfkTCU3ufmAomm9L6gbfleLM++N/TZAETkXSzjzrYuCVG+2u0pTBTlq9vYusQ2sqtEf/Z9IlHLP92IoY8AoGROY5RDXXqKw7MUtGfeZxJiACKyFuu8Auva2FLqfkfQV7aUhmyLdwMv6s+8z6heOI+cL6TUOGAL0LlOeVGxl1++Nphc+5KlLnHQEhAemV5jnaAjBEwQkc8TcY+ErQCoR8jULKzfHea1WwOJusVBx2u3BpxmZ7k5UeJDAg1A535gmTFg05sh1i5wewt7ytoFLWx601aEvo/2jBNGwoqAzgsqNRbYABR0hOXkK37x6mAGj3FXHY+Hml1hHr2ghtYmkzYNwGQR2ZnIeyV8EVgRKQOuMYa1Ngnzrgs4tWNdLETCMO+6gFV8gKsSLT4kwQAAROQp4A1jWMWGNtdNHAdLHw5SscFW618oIk8n437JXAb6cmC/MWDZk0HKP3EdRNEo/6SNZU/aXpJq4KfJumfSDEBE9gCzTGERmHd9gJaAO27ASktAmHd9ALF3pv5MRPYl675JXQheROYBLxjD6neHWTjbcbqag5qFsxucmnzPisirybxvUg1A5xdYRg9tXNzCh39zB4908OHfmti42NZULgd+lex7J90A9JVJ/xdrX8E9jax8Ib3jCEONkvZhbCtfaKbkHpuvPwLMFJGke9ES7geIeiOlrgIeMIfBhX8s5NgLclMShw7qqsKU3hdkc4nmaJl4jo+zrvUzcFRq/RRrX21hwY0B65SuALNE5PFUxCFlBgCglLoTi7vY44WL7ylk8nnJN4JQUFj2RJDlz9o/ZsnyKU75cR5TrvDj8ye/72LDGy3M/42jb2S2iNyW9AjopNQAAJRST2Jp1igFU67wM/VXflQSCqVIGNa+0sw7DwZp/DL2mMWCoR6mXeXn2Ivy8CQhQ5AILHkoyLIngk5v/uMiMsvhtKSRDgPwoM07dJF135FTcrj03qKE9R5+WRZmzfxm1r7a0q3wVgqGejj2glyOuziPoWMTYwktAWHedfXsWOa4CMd84LsiDg3BJJJyAwBQSvmAZ4DvW/cNHuPlWzcWcNQZPtt58RAKCpvfCrH65WZ22efLt7JB/zs51kFjjs3m+EvymHiur9fFw7Z3Q7z5x0anDzpAG0sxs7sJnZJBWgyg8+ZK3QzcAdie6ujJ2Uy9ys8Rp+bEvEZrs7BrTRtlK1opW9lG1aa2eD5T+xS4la5BLN8HbgO+GuskbxaMOiabsSdmM/akHMYcl01OXmyD+OyjVpY86OjeBa1ldIuI/KHbGCeJtBoAgFLqAuA5wO+0f/AYL8VHZTFifBZDDvPSVCvUV4Wp2x2mtiLCnq1xCd5BBXA78LSImM5SSmUBlwG/B0bHczFvFow8OptBoz0MLPZSNMpL/iDF/rIwe7a3s3tbe6zFN4PAD5Pt6OkWEUn7hpYFf4b2RiR6i6CNUfgZ4IsjLj792GX6ucmI02doXbvpf/bpjoDhwWej9R1UJughrwN+A4zpQ5zG6NdYl6A4VeppzE738+7Y0l4EWFFK5QJXoo15H9aDU2uAj4APgTdEZHOC4zUROA84DTgVGNyD06uBPwGPiUi/Gh7V7wygA6VUPnAi8HXDdiTa/Le79a0SWA18AGyVFCVGKaWAo4FvAscDhwDF+laE9r3eOmC9/neliPTLzo9/Ayif0cMIPHkWAAAAAElFTkSuQmCC", - - "purple.png": - "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAADsQAAA7EB9YPtSQAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAABvqSURBVHic7Z15cFzHfec//ebGfYMgeIIEAZCiLItSvIoUWySlpLIlUZIt+ai141Qc25Lio5JUsrW7Xq2c2s1uktr1ZiviFTvrVNmKbW05iaLYskwAlChSEklINEkQMwSIiwCIY2YwF+ae1/vHYN7MEDO4ZnBR8/lrel7Pew283+vuX/f393tQoECBAgUKFChQoECBAgUKFChQoECBAgUKFChQ4I5FrHUDNF58UWk9c+tjUvKEQDwEmNe6SflCIm8qkl/GdMrr108d61/r9qSypgaw+7e/bjJEor8hpXwcydPA5rVszyrRj5CnpFROEQq9bjv7d961bMyqG8D+h56rDBt5BOTjAp4Ayla7DeuIIJK3hRCnpOSUtfPY+4BczQasigHse+Qr22KqckTCkwI+Dhgy1Ss2lNJc2UZzZRulxvLVaNqKo0qVcd8IfS4bQ54bRNTwfNWHkLwuFPG6Ggy1r0bvsGIGsPfgH+yTQn1MIh8Hfj3btSpMVTRXtdFcsZdtZU0oQlmpJq05qlQZ9Q3RN93DgLuPiZkxZPYHPgZcAl5Din9Zqd4hfwbwzDO6PdOVDwhVPCYQTwIt2arWWOppq95Pc+VeNhU35q0JG42ZiJd+13X6XD30u3sJRYPzVZ8U8CZSvqYYo691/+J7zny0IScD2PLAH1pKLP5HEDyGFEeATZnqKShsLt1Ga/V+2qr23zHdez5RUZmcucWAu4/e6WuMeocW7B0k8pSiKKfqo/WnT59+Mbqc6y7dAF58UWk7M/5pKfkc8ChgyVTNrDOzq7KV5sp97KpowaQzLad9H1oSvcMNl40Bdy+BqH++6nbgDZA/snaceI0lDBVLNoDWw8/9PVL+TqZjZcZydlftpaVyH9vKdqIT+qWevkAGVOITyRuu6/S7bdzyjqCiZq4sxf+ydh7748Wee0kG0Hr4qw8gxblMxypMVXx862/SWnUXeiXjJL9AHhifGeX9iXf51eSFbEOEqlOVlu7TR/sWc76lPaKq8gIic+/iCjl5te9H/FwxsLuylbtq7mVXResdPatfLez+CXqcl+m2X8IZtC9UXYkp8hvANxZz7kX3AK0HnzuAkBcAIRDcv+kJ/JFpxv0DOAI3M1pjqaGM1pr97K858KGe7S+Hxdx0o87C1vK72FZ+Fzqh51T/3yYO+RG6bdb2lxwLXWfxPYCifhspBMCm4l2UG2soN9bQUNxMMDbDpH+AMV8v3kjymt6Ihwu3znLh1lnN9bu79j7KTZWLvuyHiaXc9J0V99BY1po2z6oqasTpHwUoElL9feAvFrrmonqAtkNfuVeiXGT26X+g4VMUGyoyN9BYwrD3Cn3O8wQicxeyBILG0u3srznAvpqPYPyQewdLvelbylpRskyue53vcWbo5URxVFftaOp+5ZV5lx4XZQCth579Z+AIxJ/+/dUHM9YzGYqpK2sCQCK55e2l13meIddlompoTn29YqBZmy+0oAjdYpqz4Unc9Kv2D5gOZu6lF3vTU1FllJ90fxt/xAOAFHze1n78h/P9ZkED2Pfw8/fEFPV9Zp/+f7Ppk5QYM3fhdWVNmAzFc76PqhFuerrpc5xnxNuDlHNdGIu+iNbqu7ir5l62lu5cqFkbjkXddH0RW8v2Lemm386l8Td4/9a/JorvWzuOH5iv/oIG0Hro2Z8CTwFsKmpif82hjPVMhiLqynYt2EB/xMOA6wP6nBdw+G9mrFNjqaOt+m7uqj1ApalqwXOuV1brpqcSis3w46vfTva4Ujxs7Tz2Zrb68xpAy+Hn7hZSXorXEzzQ8BQlhsw3pLZsJ2ZDyZIa6wyM0ue8QP90l9ZtpTdOsKVsJyUZepX1jJQw5hvGE3ZnPG7SF7O9/G52VtxDQ2lz3oe+c8M/weo4C4AQ/HNP+/Ens9Wd1wBaDz37CvA0QF3RDj5S80jGekZ9EfXlCz/92ZBIJmf66XNepH/6fSKxeTdFNiQmXTFbyvfOPultKzrfcYcm+em1P0+45lJV1b3XT5+0Zqqbtb9pe/jZuyR8Ml4SNJV9NOsFy4vqc2qwQFBfvIv64l18rPFJBt1X6HW8xy3v9ZzOu9YYdGaaKu+NP+klzYhVWhQrN9WxpbyNm+5rAEIR4mvA1zLVzWoAqhDfEkgFoNayjVJjdcZ6Rr0F0xK7/vnQKyZ2V95HffFOXun+s/g1FAt7Sn8tb9dYSfxRN/0zlwCoMjfy4NbPrEk79tUeShgACOV39/3Wl17ItIWc0QCaH3m+TajqM4lyU9k9WS9UZqlfEVVJqqegEwbqTNtX4Cr5Z0afNIBgdO68ZrXYXNqcsjAki2MRw1eA/3F7vYx9kk5Vv5U4VmvZTpmpNuNFjHoLZmNp/lqdgppiAIpYP+LlhTCK5MJWIDazhi2Bu2ofTi1+fd8zzxhvrzPHANoe/Woz8OlEeWd59rG/zFK3gqLCVdVG5g2DYkLM/lci0QCqXJZOIy80VR6g2Kit2G6OOms+fXudOQagxsR/ZnZoqLFspdxYk/HkBr0Fs3HlBL2pPYDI3FGtUwQGJR7SIJEEomvXCyhCR0vNg1pZSPmHc+qkFnYf+souAZ9LlHfOM/aXr+jTDzJN8LBxhgAAo5KMaQlFfGvYEmireRC9og1L97Y+8uzDqcfTDECP8i1mn/5qyxYqTJndO4POjGUFn35InwRupDkAoPUAAIHo2hqASVdMc9X9yS9U0noBzQBaH352B/D5RLmp7N6sJy0rqstjEzOTvl+wkYaA9B4gEFvTwB8A9tZ+QpuXAI/te/j53YmC9p8ViniS2affYiijJMt2r0FnpmgVVL2pApON9fyDMdnlrvkQAGDRl1Bu0h5aRVXkY4lCch1AqF5k/F8diHh4c/RlNhXtpLGkNW0oKLOs/NMPt/cAG8sE0oeAtesBJmZuYLO/y6DrA6JqRPteIl2Jz5oBeP1FL5dYAn/CbECHKqOMzfQyNtNLib6CxpJWtpTtpci0Opp+NaUHUDbaECCSSvngKnsBodgMfY6L2BzncAXH5xwX0BNQgz9KlDUDGHnnO4HWw18+gtS9C6Rt+PuiLmyud+l1X2C7Zz8tNQ/SUNqcOq7knY3cA6TOAYKr1APY/TexOc7R57xILHv8oROdfGKw4/vablvaUrC1/W+vtz3y3KelKn9++zEAVcYYcF1iwHWJYmMFTZUH2FvzGxRnEYjkQsELWJhwLMDA9AdcmzrDdHBsoeoRKZSnrb882pv65Zw9SXv/xf7apvudwL9NfGfEjB4TMZLjSCQWZHJmgGv2t3D4RzDqzJSYqvPWK3hCk9yY7gKgSFfGJnNTXs67GkhijATiu686oWNf3SfyeG7JuO8GXWOv8fbwPzDkvjKnlykz1CCEQlSGU34nvmzrOPaPt58v42ZQT/uxl1oOP7tXSJ4HCBOiju0Y2YYPB14xTUzGjUFKlWH3FYbdVygylLGr6n5aax7Munu4WNJWAjdYbMFK9ADhqJ8B1yWu2d9iOnBrznG9MFBn3sEWSwveqJMeTzJ+R0r+0tZ57P9mOm/W7eCG2KZv3lLGWwQcBomdIRrYQxWNVMrNxAxRvDiYjoyTWLf3RzxcmWjn6kQHDaXNtNQ8yPby/csUP2zMvQAAvTCioKCiElVDxNQwOmXOPsyiSIztN5wX0mbyCUoN1TSa91BvjofiuSOT2Dzvascl/MxW4/iPWdua7cDp0y9G9z/03DMRo/ouiD0qkgn62UwLegzoIwZ2ld6DoczEaKCX8WA/YTUwe1HJmPc6Y97rmPWl7Kr6KC3VD1Jhzhg8nJH0HmBjzQEgruoNzu4GBqIzlBgXbwDBqJcb0+9js2eeyWtPu7mVEkNy/hVQvVxxd2pxg1LIS/qY7jO88kos27XmVSBeefvYdMvB544IId8BKqNEmKCfBvagIHB6J6gr38bukgPsKrkXV2ScUf91pkLD2kJOMOqle/ItuiffoqZoKy3Vv87uqvsWfCLStoM3mBsIYBBmgiQMwJtVSZ0gIaO32c8y5L6acRcx+bTvQCfS4y+jMsJlVwfhpPx+3KDTHbnafnTeMWhBCaqt85it7fBXPyOl+BmgD+HHzhB1xAUaU56bNFTuwqg3U2looLK8gZDqZyI4wEjAqj0FEO/O7P4fc2HsVXZWfpS2moeosmQLGdu4biDc5grOsxoYiHjpc57H5jiHJzQ3MEQvjNSZt9NoaaVUn9mIpFS54jrNTFQToQYUqT559Y3jmWXXKSxqcLYPdPXX7DzgAvHbAGGCCBTMlACSQNhHsblSCwTVCwPlhjq2FLVRaaxHlTH8qpfEuB6TURz+m1jtZ7np6QagwlyXJol2BMcYcl0GoFRfSe0GUQQlcIbH8EWnAWgsa6HaskU7lnjaL469ytmbP2bUayUUS4//LzVU01R8D3vLH6TWtB2TkjENAwC9vvNMhgaTp0d+safz5OuLaeeiZ2f2ga7ztTvvqwfuBwjgw4AJIxZUGSMc8VNsqUhzAwUCi66UOvMOGs3Ns+Oij0iKe+KPeLjp6aZn6gzeiJMiQzlFhnIcgZsMu68AUKKvpta0bbFNXRe4I5O4I1MA1JfspL64CX/Eg9X+Nm8N/YBrU2/iCo6n7XnoFQObzLtoK3uQncV3U2qoXlALcdN/jUH/Fa0sES/YOo4fXWw7lxSFUORWvzlTLlpBHATJFMMYMGPCQjAyg8MzSk3Zloy/NeosbCvax9aivUyHbzEW6GUqNIIkPj8Jq0Fs9nPY7OeoLtqaNmZutIUgiAtZE9zy9DI5M8CwuztDVJSgwlhHo3kPtebtS5rvOEKj9Pm6kl9IfmjrPPbfltLOJf9n9/3Wl6piEcN7wG4APUYa2YNuNvNbZUkD5UWZVUS3E1ZDjAf7GAv04o9lF1BalFJKswSkrFcCMR/eaPbobKNiYpN5N5stzRTplq6t8EWn6Zp+XVuPAc4F1eDhwdPfX1JQxbIerZaDz7Ug1HcFogLATDENJPYGBHUV2ylaoljUG3EwGrzORHAw9Y+640jO5JvQLTM4JKIG6Zr+Of6k1mBIL6Ifu9r+3YmlnmvZfWvroed/E9R/ZXYYKaFK8wyEUNhcuQuDfunpfqMyzGRwiJGADV80L5nQ1hy9YqTOtJ2tlr0U63PbTVVljA9cb2jzC8ArhXjI1n7s8nLOl9Pg2nL4q98UUvzvRLmKRiqI6wX0ipGGqt3olOWGQEnec76quTa7K+9ja8X+XJq7asyEpzk/+k+zJcHHqo7kfOPjSLo9bzMRHEh8ERNSHunpPPGz5Z4xp1BUW/uJv2479FyrRD4LMM0oBkwUU05UDTPpHqShsonl2Zlgs7mZXt9FIB7v9vGK7CLV9cSFsVe1z9XGhjzdfBj0X069+UjJH1lzuPmQB7FdkTv2DaAD4l7+JIOEiS8JhyJ+7J7RZZ+7wdyMMmujU/5h7FnCydcTUTXCdUdyLb6xqDUv550MDdHvS/byUorv2TqP/59cz5uzAXR1nYzoDJFngD6Iy7nH6de2jn3BaTz+BXMVZUSvGKgzJxeAbI6MGerWFf3TXYRmVUBmXTHVxtyTY3kjDq65z5KyQfamvsb+fM4nJk9y2+5ffM+pqurjCa1ZlDATDGiLHE7fGIHQ8pQxjZY92ucbzi7C6voOHbfaz2qft1hac9ZHhNQAl92dqGh7AzZDWDy1UO6fxZK3XZbrp09ahRSfhXhLg8wwxbB2fMpzk3B0bp6ghSg31Go7XlE1RL/jQn4avAJM+Pqx++N/s07oaDDvXuAX8xOTUS67ThFStWVip9DJx6+8fWw6t5Ymyes2m7Xz+C+E5E8TZR9OXEwCcfdl0jWIqi49Vq7RnOwFrOt4GOixn9E+15ubMCjLz4AmkVzznMEb1e51RMIzPb880Tvf75ZK3vdZezqPfwfJiUR5mlFmiLtyUTXMhHuIpYo9NpmbtO1PZ2CMyZmBBX6x+vgjHgZdyUlaoyW3yd8N3/tMhVImvVJ8zdZxvCOnk2ZgRTbaiz3q10F2QmbPwOFdmmegEwbqzTu0stW+/noBm/2stodfYajPunW7GG4F+xj2d2tlKflLa+exkzk3MgMrYgBdXScjCH0GzyD+D/IGpvEEluYZbDEnn6hB1weE1jj2PhVVxrjueEcrb8nB9VuqpCtXVkxqY21/yRFTlCMQ7//jnkF/0jPwjuFfgmdQYqik1BAXmkbVCH2Oi/lv9DIZdF1iJhIf5kyKhVrj1mWdJ6OkS1XmlXTlyopqrXpPHe1B8hnib7ggyAz2FM/AvkTPIHUyaHOcne+NGqvKtank5G+zpXVZKuZskq7u0/NLunJlxcV21s7jv0Dw7xNlL07cqZ6Be/GewSbLTu1dBK7gBBO+GyvQ4qXh9I9qk1KBjkbL0l0/ibxd0hWMS7qOrvjS56qoLa3tx/8nQmqTGCej+BOeQSzMpHuYxXgGCnrqTckAkfUwGey2J5Nw1pu3pQlBFkuv9zzTEU3rL0H+3rXOk+/lp4Xzs2py22KX/BqS05DwDIY0zyAYmcHuWTC0CUhfGRx0/WpNEzCEo376pz/QyluW4frd9PcwErBpZYl4wdpx4h/y0sBFsGoG0NV1MoKie1rADQCVWJpn4As68foX3v8v0VdSbohnLVNllD7nqjwoGbE53tECMUv11ZQZMmdTy4YjPEqfLzmZFfATW8fSJF25sqqCe2v7S46oojxOmmeQvmcwn4Q6QWovYJ1am8mgipq27r91ia6fP+ai230mte3nAmrwi6xySNSqR1z0njraI4X8LJpn4MNOfK4jkUy6h4nE5vcM6kw70M/m4/OGHYx5Vj+l7Ii7G284vpZhUMzUmXYs+rcRNchlV2dq8OaQXkQ/uVQ9Xz5Ykzc0OAa6+qp33B8QgkcBwgRQ0GGmGCklwbCPEnNFVndKCIWwDOKZlUVFZZimyuz5DFeCcyP/D9+sAWyztFFlWtyLz1UZ41fudi1mgLik69Ge9pNr8lr5NYu5snUe+ytAe8tRqmcQiYWY8mR+EVWC+DAQ32oddl/RFmJWA1dognFvfE9GINicMiTNj8TqPZeq51NB/rvl6vnywZoG3RW71T8A3oRUzyDeCwbCXqa9c8OgExTpyqg0xHMXSanSm6LCWWmuTSXH7hrzVsy6xb3PYNB/mfFUSZeQf2TtOPEvK9LIRbKmBhDfM9B9Kt0zuKF5Bp6AY949g81FKSuD9nPZ36aZRyKxYJomYbGu32RweK6kq/3EX+e9gUtkzcNure0vORQhb9szGNAyhU57bxEIZ/YM6kxbtYWXmYiLUU/Pirf3uuO8pkoq0lVovdB8eCMOrnneZiUkXbmy5gYA0N1+4pqK+BxZPIMp9zDRDJ6BQJeWOsY6dXZOnXwikVgdyXX/uOs3v+RrpSVdubIuDADgesexnwvEf0qUb98zmHANoapzu/hGS4umuxvxXMMXzptaag5jHhvuYLxN8UDO+d9uphLlirtjRSVdubJuDACgp+PYX0j4bqLsZOw2z2B4jmdg0ZVQZWwA4k9o6r58vrk29Zb2ucHcPCdJQyoSSbf7DJ7km1RXRNKVK+vKAAD01Y4Uz0DO9Qx8cz2DVDfM5jiHKvO/fe4LT6fMMQSNluZ566+WpCtX1p0BdL/ySlhV1aeBfoh7BhMpewYevwPvbZ5BjWkrJqUIiGfcuOm+mvd2XZt6U/Myqk2bKdJlj/ZZTUlXrqw7AwC4fvqkXSfk44AHIEIozTNw+m4RDCclYQJBgzn5RCbemZcvomqEXsd5rTyf67fakq5cWZcGAHHPQAjxRWaTBcU9gxEApJRMuofSPIPNlt3aZHDMcx1PaGruSZfJDecFTYNYpCulyph52XctJF25sq7f1mwfuGit2XEgghCHIbFnoI/vGSAJRHyUmCoRQqBXjHijTi3RhE4YaCxryUs73h5+Wcv6vaP4I9p2dCpRGeGS643UpFjjBr3uUHf70cyvBF8nrNseIIG188R/F8gfJMrxPYP4TY5EQ0x5hjTPIFUz2Ot8j1geXtg07uvDGYiLVRT0NJjnviF1LSVdubLuDQAgoIa+DLwDCc9ggIjmGfiY9sWTKVaZNmPWxV9iGYz6GHL9Kudr96QIPhssu9BnyG+4lpKuXNkQBjB4+vtBvYg+BXFJsXpbnIHHb8cbcM7uzCVFmblqBv0RD0PuZAauxgy7fmst6cqVDWEAAFfbvzuhU5UnQMxAJs9gjGBkhs3m5Dt6x319GVOtLhar/Yy2plBpqKfktmif9SDpypUNYwAA3aePXhLIL5DNM3ANIaQuLTBjuTkFVBnFas8e7TNH0iW46A1YfpcNluV6QxkAQE/H8X+U8EKi7MWBh/hEOxFnkLom0Os4P98bNLLS77qk5eE3KcXUpCSqzCjpIvrYyDvfCSzjT1pTNpwBANg6jv85kh8myg5GCKR4BtFAUMu9F44FGHBdWvI1Uid/Wyx7tDUGVca47O5MTdHmlUIcWU6KtvXAhjQAQAZl8PcRvAtxz2AizTOYoUJJpqZf6mTQ4b/J1MwgEM9U3qCt+68/SVeubFQDYPD094O6mPKUlHHhQNwzSKqJDBEzyuw61+TMAI7AyKLP3Z2y61dn3qll/l6Pkq5c2bAGANB9+ui4XipHkp5BUk2kQ0eRSKZgtS1SMxiM+hhwpUT7zE7+1qukK1c2tAFA3DNAqr9DimfgIJ6AokwmcxbfcF4gEltYdh+P9olnOCsz1FCmr17Xkq5cWdd7AYvFPtjVU7vjPongIEAIPzr0FFOJDxcqUVQZpdRYTU1R9th9FZUzQz8gPGsou0rvxaCY+cD1BlGpbTzZDGHx6NXX/379ZKjIgQ3fAyTo6Tz+X4GXE2U7IwTxUkby7WU9jrfnPcew64omKTMqZmoMW7jiWt+Srly5YwwAkEE1+CUQ2hr8OAOYKdFeuuD0jzLlH856gp6Uyd9m8x56vGfxRNe3pCtX7iQDiHsGqngy6RnEmGQQC8nU9bYsLqErOM74bMIJgSCgetMlXYKvr0dJV67cUQYAcc9AIJ4C/BDfMwiRjCvon36fcGzugl1qtI8QSlpSZiHkX1nbj5+Y86M7gDvOAACsnce6kPILzE7bYyQFOVE1xA1neoKpcCxAnzMZ7ZMqKpXws54q539Y4SavGXekAQBYO0/8FPh2pmOpy7wAvY73iKpzA082gqQrV+5YAwCwdhz/M2DO3rwrNMGELx6NLZFY7Rm9g1XJ0rXW3NEGAEhfwPIlAedvP5DIOTzq6cE9V0AaFKp4aiNIunLlTjcARt75ToCI+iSQthkwOP0BwagvzfWbRYL8vZ7Tx1Yv3nwNuSNWAhfCPtzlq95+4IwQ4gsQf7+dRCWqhtOyfAEIyX+xdp54aS3auRZsvDcy5kDrwWc/heAVsvzdAn7S03H8s2wwVU8ufCh6gAT2wYs9NTvuVxB8Ys5BwUVfwPKEZ+TdO/elhRn4UBkAgH3w4pu1O+/bC+xL+Xowoufg0Jt/s3qJhtYJHzoDANhede8/hc1KGDAgxSklqn7+eueJDSnpKlCgQIECBQoUKFCgQIECBQoUKFCgQIECBQoUKFAgK/8fE+blGn652UkAAAAASUVORK5CYII=", - - "blue.png": - "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAADdgAAA3YBfdWCzAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAABK7SURBVHja7V1neFRVGrasbYsr6logO3PvZFJIQghJSEgCSQgJCQkBQgopECAJCUhRqkqRJkUUpESKFJXepUsRCL0E1EVWBUGQYoC1ru6uq65nzxkmydxzzkxunzvj8XneHz4PhJv7vufc833n+97vDgDAHUaHr5krhLgM8QlEO094Zk+BJ5AfCPEzBLDjW4hHGHm/HQGsdSC/FtMYeb8BAUCiwyB+pQjg3xBPMgK9XwDbKOTXopIR6MUCgATHuCAf4b8QZkai9wpgryPhVosvTQRLGIleKABIbBJOdmLf4SC6axEugF8g/BmR3ieAY45E+/kFgN57PgJF6w4BX47HRbCaEelFAoCEdsRXf9LAUaDvqVs2xOaX4gJAUUIoI9MLBACJvBPifUeC/QOCQOm+83UC6LHpJLByFlwEmxmZ3iGAPHz1JwwcV0d+LVoXP0U7EEYzQj1YAJDAuyE+diQ1ICgUFO44Rwig5/b3YVRgxQWwhxHq2QLoSZz8h04BxQevEQJASCgbTNsFEhmpHigASNw9EJ85khnYrAXI33Ee9Dr0BVUAvXadBX5Wf1wARxipnimAfsTJ/7kZoPDdK6DkCF0ACG37P0/bBdIZsR4kAEjY/RDXHUls2iIK5O+8aBNA6bEapwIo2XsO+Ps3xQXwHoomGLmeI4Ch+CpOfmGujXyEPsdvOhUAQvLgcbRdIIeR6wECgET9EeIfjuQFt4wDBbsv1Qmg4qRrAZRWXQQBTUNwAXwEcRcj2PgCGI2v3vYvLq4jvwiib/UtlwJAaP/sFNouUMwINrAAIEGN7OVddaSFxLYFhXsu1wmg+76rDZKPUHboMowawnABXETRBSPZuAKYjK/aDtOW15GP0KPqmigBIHQYM4O2C1Qwkg0oAEjMYxA/OJIVmpAmIB+h54HrogVQfvQqCAqLxAVwDUUZjGjjCWAmvlozZq4nBNDrkHgBIGRMfI22CwxmRBtIAJAQH4gfHUkKS+lEkG9LAh2tkSSA8uPXQXBkDC6AWyjaYGQbRwAL8FWaOXcrVQBlx6UJAKHTtMW0XWAUI9sAAoBE+GJNHiA8PZdKvi0JdOKGZAFUnLwBmsXE4wL4BuIhRrj7BbAMX52dF+52KoCK6luSBYDQZeZy2i4wiRHuRgFAAoIg/udISmSXHk7JtyWBTskTQN/qm6B5fDIuABR1PMZId58ANggI4Sygy5tVTgXQff9V+QKAyJ67jrYLvMpId4MA4IuPwFu8ovLKnJKPUFx1XZEAEFq0S8cF8B8UhTDi9RfADkGTB+8Luq446lIAPQ8qF0Du4q20XWA+I15HAcAXHoeT0Kp7f5fkI/Q+/IViASBEdsjCBfAThIWRr58AqgSr39cP5KytblAApUdqVBFAt2W7abvAUka+DgKALzoFf/lxJUMbJN+WBDp2QxUBIER1zscFgKKRICYA7QVwQtjiFQhyN3wgSgB9GigEkYLCNQdou8B6JgANBQBfcGf8pbfuO1IU+YVKcgBOEJPbk9ZSFs4EoF2L1xlBi1dgMMjbfFYU+UV7r6ougO4bj9FaynYwAWgjgHyixWvQeNGrX2kSyBniulfQPgWRTADqt3idE7R4BYeCbls/Fi2AYgmVQFJQvPU0zWiiFxOAugIowVdZ26FTRZN/uxDkC00EgJDYbwRuMBHPBKAe+ffaDR3rW7xCw20tXlIEoFYSyNlFUerzL9k+B216D5xtJDIsPhY/K8clBAcH3+upAhiAr/52I2dKIt+WBDpao50AhPgE4m5DkM8J2uOONm7c+PceJQD40A9A1AhbvKJBgb3FSwrKjt/QSwAIbj8D+Pv4NLFfVNVnTM3mkZ4mgOH46k8ZN08y+QjlKiaBGiweeXXpLRge7keJIYiH3VIka+LmUaKTbzhO+yomtch/EOJLQYtXVGtQsPuyLAHoRX7PHR/YDKgcnvsDvVvKAsxm3n5BRQlRzRM9RQBj8YdPnbREFvnd9+knAOQ35O6WMl+Oe8u5Gab5e//G/o8aWgBo24T4zvHBm8UlgcI9n8sTwP5rugmg/Ng10LR5uNtayuAWH2gPRV04ovKvGF0ALxEtXi+vlEX+7W6ga3oeAEH6+Dm0F99Xp9UvdELneBAS3ZowxjabzU8aUgDw4Z6A+JegxSsxTTb5crqB1NgFgiOidW8p8+NIJ/S4wnKQu2gLIUiLma80qgBmEy1eszYoEkDvwzW6CgAhc+pC2i4wRNurcn4bXiZXvPWUvYqpC2GMzfO82VACgA9lsjt2O7R4dVZEfkOWMFqh4kQNaBbdRreWMhjjE07osT3611cxLSWrmKxmbrHRBLCIaPGat02xAPromwSqQ+cZS2m7wGiNbksxJ3QrKNz8XgNVTOafUarYEAKAD+SHt3hFZHRTTL7eSSDBLlB9AzRvnURrKWukLvlmwgk9tvcQog+ycPUB2i6wwigCWEm0eC3eo4oAKtxAfi26Vq7RvKUM5flxJ/ScDe9TvZDwKiaLmfvB7QJAE7uIFq+sYlXI16ISSHIzSVIaLoAbatnOWczmDKJMruI5p8mv7huPw8OhoIrpx4iIiHvcLYAc/JfIWnpAFQH0qHK/AHIWvE3bBRTb0cP/7rR7GAqc0PPe/tBlAUzWrBXAH+4SfgFNQfKQ8WuNsAPMFxg7xSSqQr5a3UBqRAQBgUG4AIYqPvmbeGLhxA8YKyr3UXGirj7ih35nvm3kbgF8KkhelA5TTQBaVgJJAWU0zU6Fq/8uu3ehgxN6s7oyOVd2uMRnYcPR2TzPB7hFAGhKF5H2nb5KNQFIeRFaosPYmbgAULbzPvnXvXwx4YQ+ZHJ9/YNII4yOL86tPxCauMnuEEAJnr2SUuypZzeQEhStP0I7B7SV84LRoc1+wVRfJhcSBvK3n3MwwhAX+vr7BwrDQo5L1VsAix0foHlShmrkuzMJRKsbpBhPjpF54VNBFMk+O12yGSZCUIuWeHKoWm8BbBYmMAarKgCxK0EPtMzMwwUwS8Z17/32iyWHMrmWgjI5KSXwyc+8QKsZyNJTAAcd//E2T41RjfyivVcMQ76T6WSSO4otHPcM4YQ+phK7/BIf+ZRWfQojlGD8uc6iQ6ZeAjijpN7fdSXQVUMJIL70afxFb5HyrkIff/wP8O/cdPwZQZGxoGDXJUUV0O1HTKZcF5uL9BLAFYGaR89RTQA9qq4ZSgBJg0bjL/qgxBu/kYQT+oRF5N2HRCu8PoeRMXZz/NkuJN6R+Ds9BPC9GnV/9Eqg64YSAGoewV7yhxK+/Q/BP/+1oEi2VQIo2IMVye6Vd/fRYcyr5FnAxPfRVACoWlbLHICm3UAykDGhEn/JVyTc+E3E31Xa1GWqNcH2QcbYZERw1Wq13qf1DvCNoO5/7Dz1soAHjbUDpAydQMwiErX1W61/QdW8giLZNinUItliBfWPGZPmkWcBeOjUWgCnBVFAv9GqCcDZfEB3Ia6IaCVfJ7LUaxJOTPqMNarveuh+ILhlLHFzCSOCu7UUwDqB119Ob/UOgfuNFQWEJ2fgL3eqyHf0N2GyrKNmPZCdXnmD2AX8OGkjdBWVgIfGp6qYBzCWAAJDiJN2g4csX1/fx/BK35Sxc51XP51QlvmsOFkDAoND8fTw81oKoK/gPrtpiHoCEDkgSg+UHfyMdheQJCLtSzijdFqwg/47Q8GrUf0Uk9cLPwzu0VIA7fFfsOvKY6qJoNwgqeC8JdtoAjDLif3zNv9dUxuc9HGz8ee8pKUAHsWLQRMHT1LvMuiEMS6D4ksG4S+1RkzTKFx9L+O3floferNmr8Kf9WutC0J2CvsA23lVLgCNnQkMboa/1Nniav74RWL7JIpVSnzlLNyEP+svWguAGPOeteyQ10QC2fPW07b/WDkCaNE+S3MjrKzK1YQApFwOyfUCEAx9ih84Xr0r4ZM33Rz/lxMZQLFVwegELjgk+wUSlz/1YldHAG37P0fcC+jRF7CRnPd7WZ2ysKPu+wyUVl2gTSN/WULhZy4RBbz+jvOwV4WoJywhBbsTkFa/KFcAuWqYQdG/je7LCGIWcpKNJK0mUzBR/TNsmmZ5ACRYrG8AwFB0lh4CICpdAoJCRVvBuoSbEkLILYRiInlKxru5IJyLVOSiCLZG9fOKxWRJ1qs5tIjoceulTomYjjZxrizlRR/+BAdBk3AmMpqPkO0kV6KkBgJlASPSOhMdzZreBVAMoY8Kq4RdD4ISnRXcd1XXrKCTbqDlct4Lz/Oh+M+K7FTgohBW3qE3ZfgkWvPoXL0NIlriue+wdh1leQO6KydQduAizRsAjZhrotqENHQnMH6BapY4+cv32Ery8d4Fi8VicodH0Jv4L2ubCCbTJMrxlFx+Unt7GMpMIQRFJo126zeh8SM8X3Scs0lxBhTdU4REtqKZSY3RpTWMIoAnIf6JP1CbfqOM3SpWfRPEFpTRyL+gpAvIIS08Af/ZqBk0bcpSakGsmJL4nu+cAeHkKDx08j+PStDd6ROYSbM7U1w0atsFtEkMJQ0YSSMfCTlMlZYreBjDfYBqEQMPy3lbPhL/KYDiQDn/wODmtO/+V0p6BdU0PiA6YNChMHXym8ryAhpUC1MKPoH9kitVrfdRXxpurqaJAO0GqK/CNj/R/rlEZXGO85JRnJ81ayWZ7HEwj/Lj+XjdGkNEiGAS7UHj+owABbs+c3u9YNmhSyCmW4mzl1miiR8QKhLhhA01hBgCg0GL1K42s4ikoVNATEEpCImKA67+Dlr58OemGXFgxFLaA6M+wpy1pxR0DivLDRStO0w77ddivJaWcKhm38pxU/GISTY47qRatnFaCAB1w75De3A0OiZ95jr53cMym0czp7wO/Kz+zl7oQt1mAphM7awm4Sg9ibhlMXHPqjlQQquhUWhu0GRnio/IzHd6SdJQaCglZMp5/W0QntzR2ctEDt2D3GEPb+V5VFm1D79VdQEYmfDD0JnC0wZHIlOkr5z9YuEdciR7C9pCpgYig+z5G0CLpA6uXujnENHyVrGl4+3Sb3Os0veDvAOQZazVbC61mPnXLGbuOArpINaglY7y+iaTqZGWHOkxO9iETxDFgQon0HCJnHWnRVvK414CxdtOg4yJlSAsMbWh1bRd7mAI1H4l3EHMSe7YQTxxfDwaJjVNzJaHrOZbVzwP0qevtt0r0BxI8jadBZ2X7AOZM1eCxIphNIdtGr6zTzWRZflmtVoftOK7GcedQe5fTADihdAYGS3gadKGgGLmkJi2IDg63jZ/WOLBCbWzjYNQNH4FnuLH036+heMKmADk2czPQF74qoRFdKDVOhqVsCl93sAmTR6hpbtr07BSr2B/8wJwEMLj9tVZrVKMjLJ5VRCD1XT6ht/6aa7+XYvZXMIEoFwMqK2qO8QKuz2rWNLRiX4hRFc1VjsONK1DxE51Wc9hj14pAIogkMUKmqmTjOb7IpcuCFR12wMiEcKq9VQPe6l3JU54dFYR7Tp2ABOAlwGlW/GhGOHtMmx1BEHhUUT3kI+PzwNMAF4EK+aLiJC7ZPtt187JC2i7wHAmAC+BxWLxx2scwlOzBKbSlPzDlwEBAX9iAvACQDJX4Ss8e8lO4ZiZ6W9RyrK5sUwAnr76fSzN8LA0omM+4WVccfIGCI1rS2Qc4VngYSYAT179Jm4TPtCx8+J3qXV7XeesJos1OHF2MkwAxjz5R+GEtuzay7mFDRRFWEJ7okSb47gnmAA88eTPcbuFq9kCspYeBMUuunmz52+kmTjPZgLwPPITiKRPQbmoEvXwFKLw5Ec5jRpMAG49+ZsP4c0c2auOi5pmkrdkO20XWMgE4Cnkc1waUb9fPMihHrHhotTI9Gxi2qevj6+VCcAz4v59AsNFqz/IWXta0jAL1LOnRsMmE4DOsHv7/yToZSgbIWuYRXSXQlwEF5kAPHD77/LGXlndSV1mLSd2ARgSckwARs78cUIHVJT46bbjvEOTqvjOpKL1h2mHwSQmAGNn/kYIRru0iJbdldTnyBWbgLCKoWwmAGMLYJBAABGtZFvWlB/+nDBvsJr4TCYAQwvAFEHc/K2pljXRNHfRFvwT8CsqKmUCMPJJ2NbLL5zykTJufr1djQSjinZPj8Grhs+wKMATDoImbpdgFkJCap3Lp9gooHT/pyAoLBL7/vOVTACekQYeRdjc9B1VN8ugolqOd7/t+5/DBOAZJWB/RuXd+E1graePSwvb6psgbeQ0Wo3gYaM3jTDyhZ2/rWleR8jtM2ddNXUXQHE/5SbQVhlk5AQQEwAQP/Ov1vk7qkshaD9ikq0WMPmZsSAqM5fm16d4nCsTgBuB7FwggfuVtagZ/xqYCaAB4wZU0SOD+J8hhrKqYO/5HCTB8PCACPJRBfEGnuebs8YQbzwc/tUSaUsXc9xbkOizt5tF+VPw/xf4mvhyJUaN7sb/AWO1JrNEJCohAAAAAElFTkSuQmCC", - - "yellow.png": - "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAADsQAAA7EB9YPtSQAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAABAfSURBVHic7Z15dBXVGcB/WdlBggoBURCIGAICdQeroKDVVqgbKoIW1MoRakUE1FbjQVEUlQqtG1oFROG4SyuyQ9UK4oIoQgDRyiIqGEgCMSS8/vElJ8nMnXnzlrn3bb9z3knOnXnvLvPNXb8Fkpc7gB+qP3cYLksKzZwLBCyfc42WyBDppgtgiFM9piU8ySoADTymJTzJKgApqkkJQJKTEoAkJyUASY5pAWgPzATeAa41XBadXIfUeSbSBklJFrCZ+mvxEZryLsS+D1CoKe+Rlnw3I21hBJM9wElAZ0vaQ0COgbLoIgeYYknrDPQwUBbArABsB6osaa2Aew2URRf3InWsSxXSFkYwKQDfA7MU6aOA7prLooN84I+K9BeA3ZrLEjO0Boqxj8dLfc63UJFnoc95LlTkuR/I9TlfV0yvAnYDDyjS+wODfMy31GNatBgMnK9Ivw/Y5WO+cUE2sAn727EV//bnuwEVdfL6Bemi/SAbKMJevy0k6fmDikHYGygAjPcxzwFIt7wQOM/HfCagrtvFPuYZl8TkGBkhpuY4cUk+9bvlms+zJgsVIc9hr08libnKiQozsDdYFdDLZKHCpBdSdmt9ZpgsVKzTCtiDvdGmmyxUmEzDXo892DeCjGJ6GWhlD+r1eKbmckSDw4q0QqSOKVzIBJZR+9b8BHQ1WqLw6AL8SP2JX8wJcprpAjjQCLgSaAq8Cuw0W5ywyUWWuD8BbwKHzBbHTqwKQDQ5CjgROAHIQx5KE0S4jqi+pxjZCSxDduY2VX82Im9xijiiDXA58BSwDfUmTCifncB84EbgOI31SBECRwDDgcXI5CvSh+72WQvcgvQsKQzTG5gHlOPvQ1d9yqvzjsc9irinD/A2/r/tXj/vISeYcUe8TQKPBf6GHK96IjsTTmwPeW2hS/WnaUNo2ghaNJF79pVB6UEoLYfNO6Fop/z96juoqAypfK8CtwLfhfQtg3gVgCxEb20HosmjmyxgLPBXZAbvSs+OMKAX9OsOZ3SFxmEeupaVw383wvL1sPgzWLfN29cQ1a9pmFn25QJtgc+95O9FANoDy4FOyN72LOBO9AlCJ2Ss/ZXbTbk5cNWvYVg/yPdJ0frL/8GcFfDSKti1N+jtHwFDkJWIDtoAk5HJcAaib9CfIL2RFwF4Fru6dglwPyLlv4Ra0hAYjJyotXS6oUNrGHMR3HA+NNCkXH2oCub9B6a8CkU7XG/djywf5/lYnAbIquQvQDPLtZnADW5f9iIA7wAXOFzbCtyG7HJFkzRERXyc0w1tc2DycLiiL2QYOtGoOiyCcOfsoD3CFMQJRSDKRRgETMWuXl/DO8CFbj+Q4SGTTJwnXTnIlm1f4FPE20akZABPA6OVhcmAURfCvPFwcmdINziNTU+D7h3ghoGQlQkfbhKhUNAXGcoWoD4kCpVuwFxkKHazo5iEzAUc8dp8I4CHg2RWiey+3UP4J16NkV23i1QXj28DL94GvTuF+es+8/FWGDoVtjkreS8ArgAOhplFDjLBvAn3g6W9wO3I8OmKlx4A5O1+pvr/Ux2+l159rWbM+Qi74YcbWcBrODz8QafBG3eJEMQqbXNgeH/4ZjdsUE+98oCeiJCH0hNkAtcj7dMP52P8SmTcvwR438sPexUAkJ2vJcDryFGn03vYCFGy7ItU1MtKOg34J3CZ7UIaTBoK026ARtkhlNYQDbLgkjMhKwNWfqG8JQ/Zz3jL608i7T4a9yXwEmonzQe8ljcUAajhR2A28BlwMs7DQkdkKfKZh998CLEIql+4dPjHKBj92zBKaZi++dC2FSz8GAL2qV9P5MF6UQ69Gvizy/UtyBB9F2GcXEYyf34TKEDUnvc73NPUw+9choxX9chIl/H+D34qbPvMiPNgzm2Oq5QJwO89/EwLh/SS6t8oIIJVWLTm0G2QfYHrqBWqPcjuoZsyRyfgYyyVTEuDJ0bF98Ovy5wVcP10ZU9QjBxouW0WtQPWU7sXchixJ4zKZlw4Q4CKUmRMexvZfvwEMYR0q1g2YgvQ0Xph0tD47Pad6NFBeoEV9jlBQ+AM5IE6TQpLkDf8MPKyjAKeJEqmbCYPgyaisAu89EyY67j9E98MeQje+FB56XZkQ0c7pgSgPbAByxzh+Dbw4VRo0dhMofymuAxOH6fcJziAGMZ8q7tMptTCH8fy8DMzZNKXqA8f4IgmMHusclLYGHhEf4nMCMAZKLaWb74odnf4oskpXeCm3ygvXYooumjFxBBgO1xq0xI+n57Yb39d9h+AHn9SHiAtAH6nsyy6e4DeKBwlPHht8jx8gOaNZaWj4CJkk0gbugVgApZep3OuHOkmG1f9WnmukYa0kTZ0CkAOCrcv4y81d55vkswMGKfeBxxMrcGK7+hs+iuxuETJzYGrz9ZYghhjWD+Z/1hoiBwZa0GnAAy3Jlx9tpyaJSvZmXDlWcpLw3SVQZcAtAdOsyYOTeK3v4ah5yiT+6DJNY4uAehnTejZEbodqyn3GKZHByiwWxymocnQRJcA2CozsLemnOOAgWrjMttL4we6BOAcW0KBppzjAIe20HIYrkMAjsZiVp2dCaefoCHnOKFvvrSJhePQ4E9IhwDY3Luc2B6aNNSQc5zQpCHktVNe8t01jg4BsL3reW015BpnOLSJ7/2kDgHIsyWopT2pOeEYdbLf+eoQANuOdyzr9pvi+NbKZN/3AmqmHucini5C2ZerABYBXwa5z2qwmFQnf15podb496JV3QtZZYViNVGF6G0uy0SMFieH8OW6HEKOMBe73GMTgGaNwswtgXFoE1vbWRgMvEL4yr0T0hGPFuGShVgHu2GT4tQKwE5TdZsEE4AxRKbZPTYJD2JT1CUdeCyC71cQXJ3Zpr9eVh5BjglKqbpNSoJ8bTqhGeBaeTQT0c1fA5yJB/87ddiL6LBtCHKfrRIl4RpHJzAObRJMAN4ATgEG4uJFRUEZYj28rGYVsBT/oljYKlFc5lNOcYxDm3ix/vm0+hMWOuYANvu1r034GYtxHNrE94hiOgSgyJqwOV59f/uIg7OpTX7nq0MANloTilICYMOhTWxtF210CIBNir/6LrUSqEtpOWxSRw9OiB7gByxGjxWV4oEzhfDeBvE9aOFbNISX0bURtNyaoLCVT1pWrFcmL9GRtzEBWBT2wiXxcGiLZTry1iUAtsqs2ya+d5Mdh3YIoHhp/ECXAGwHbL4xXlypKfcYxqEN3kNTVHGdh0GzrAlzV4bsjz+hqKiEl1cpL83RVQadAjAPi2fxXXtFCJKVWctgd7EtuRxxsKkFnQKwFzm8qMfDrzk6WE5oKqtg6uvKS68j7uO0oFsfYAoWl+lbdonL9WRj7kqls6gA4jVVG7oF4FPERUw9Jr4A+zx7t41/9h+Au+cqLy3Am2vdqGHCOPsbYGTdhLJyKK9wtJFLOCa+AMvVXvyvReIyacOEStgHSHStejzxDqzdYqA0mllTBE8tVF6aD6zWW5oYcxR57FGw+hHI8aIMHYcUl8Fp4ySegIUSJL6x1rcfzAwBIN7Fq4ABdRP3HYBt38Nl2r3l+U8gANc+BqvV53t3IjYW2om2APRCYvsNBr7G3X/9GsRfYD1Dsa+2i9uYvvlRLplh7p8PT7+rvLQacaztthguAO5DXOztIooh+/x0F7+X2mCTThyLrAzqBZ1IS4O/3wQjB6i/FG/MXAQ3P6m8FK67+OeRABHG3cU3QAxD5gOnU1+gGiEz/jUu39+HRLwYYr2w8BMxIz/RpyCQunj1A7hxhjJWQAC4CsUZiYUR1A8skYb0tDciwrCWCFTDI1kFDAK+QDZ3mjvc4xRJpC6vVf9GPaoOwzWPwLNuRmcxzjOLYNijjjudk/EW6cNJNbw50m5foPC/6JVweoBuwItIF+QWRm5Z9T1ejnuWAh2wuEkNBOBfayEzHc7qFkZJDREIyJg/8QXlmw8S2MmrSd4mxGbDFlijmprYjX0Qg8+Q4gaFIgAtgUJk/LHZ/NfhZyS23WhCCyv7NtAdWQ7VY8UXcm4+oGfsRw7bfwBGPA5Pqtf6ILt91+A9bNwh5IXbgQyzTsY7nZBh4RhkYulpb9WrAIxEHtAAl+9UAk8gMesWE/q4FEDCzpyEQsCKdsArH4hvoXa+e84JjzVFcEGhq77j28h8pyLEn64JF/McIgC9UQ/f6UiQ7ZHAT3gwGPEiANdVZ+xm1L0Emag8T/hRMUGEaD4S/tzmSK64DJ5fCrt+lmViQ03BooOx7wDcOQtGPwU/O9vyzELe/EiCbR8E/o2cGOYBxzvc1wiZF2wD1rn9oBcBeADn4MRbqY1ZF424wSA9wQJkhdEHy1I1AHyyFWYvhyObi7PJdEM2zpVVEhHs8gdh+XrH8T6ATPhuITJDzrr8QG3sxlNwnos1QGIMO+JFAPphfxtLkBjBwwnuISRcliJd2Pkoep/ScnhrjahUpQE9OooHbh1UVMJLq+CaR6VHcrFx2Ie00QyfirIJiddcirjibWC5vhIZdhzxshHUHpnRd0YkeBZRilnnkY7Ay0hcYkfatIQhZ4kH7u5216tR4fNv5I1/eZVSk8fKamS81xUIqg3S0wxHXuzNiOsfdRTjarzuBGYhM/QdgHNsbP/IQrrQe/DgN6fgODlaPqcA+uQ7et8ISmk5vL9BuvdFn3rWYi5BVkuP420JHG1aU7t7eCjYzSbjBobDMcA0JMCSJ7IyoOsx4pquS1uJUNKskYRtaV7trGr/AfmUHBQNpaIdYsC6cbvSYseN+cBYDJzqJRu9qA3BHoiBz2JkjZ5CMz2Bl5Dlke6HfhCZYZ/key1TBKUFMvlZjP+9wlpkPnKklpqlCJmjgcuR5dHXRP7AdyLDzY3IiiihiLdJYDgcifjc7YrsnuUiK4lm1Iat34fM3ksRhYsixDnDRjSYaJskVgUgG9EqOhLZ9tRiJ+cD7ZAVSymyl5FEyu/hk4lsPNV0wT8CXYyWKDwKEK2omnoso9Y3cwoXxmAfh4M5o4xFnsFejzFGS6Qg1lzFtkJ20azEWjm9oDryLURDGJh4Zgb2t6YK2fiJN3ohZbfWx6+DobgnH9m7tjbYTJOFipBnsdenEjlXSWHhXeyNtR9NETR94mhE9dtaL7/c8sYtg1FvwtzuY57nI0L3LuJs2S/Go65b2Jq8iUY2svFibaAt2BUcokUBMkmryesXRNvZD7IRxQ1r/bbiX/3iiomo35CLfcxznCK/cT7mN0iRXwCY4GOecUFrZBvW2jB+O0ksVORZ6HOeCxV5Gp/jmF5fP4jdqqiSyOIYxSq3YtfQaYbYVBrDpADkAsMU6U8i6kyJxlfICaWV4ShiK+rCpAC0w66VvAfR+0tU7sF+upiBxUReJyYFYB0y06/LeOQAJVHZi9SxLpsBtccgDZg8nToE9AfuRiyBXkaMHRKd55B5zhBEeXQSHrR3U0SXQvSvAmIS06uAFIZJCUCSkxKAJCclAElOsgqAykY/Erv9uCVZBUDlmSuYt64UCcZ4xMT9e+ybM0nD/wE68+WGI1XoMwAAAABJRU5ErkJggg==", - - "cyan.png": - "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAACXBIWXMAAAOxAAADsQH1g+1JAAAE8mlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNy4xLWMwMDAgNzkuZGFiYWNiYiwgMjAyMS8wNC8xNC0wMDozOTo0NCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIDIzLjAgKE1hY2ludG9zaCkiIHhtcDpDcmVhdGVEYXRlPSIyMDIyLTAxLTE0VDEwOjMwOjMyKzAxOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAyMi0wMS0xNFQxMDozMDo1MCswMTowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyMi0wMS0xNFQxMDozMDo1MCswMTowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6ZTFiZTU2MjEtMTU1ZC00YmFmLWFiNzgtYjM2Y2QzMmIzNTNkIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOmUxYmU1NjIxLTE1NWQtNGJhZi1hYjc4LWIzNmNkMzJiMzUzZCIgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ4bXAuZGlkOmUxYmU1NjIxLTE1NWQtNGJhZi1hYjc4LWIzNmNkMzJiMzUzZCI+IDx4bXBNTTpIaXN0b3J5PiA8cmRmOlNlcT4gPHJkZjpsaSBzdEV2dDphY3Rpb249ImNyZWF0ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6ZTFiZTU2MjEtMTU1ZC00YmFmLWFiNzgtYjM2Y2QzMmIzNTNkIiBzdEV2dDp3aGVuPSIyMDIyLTAxLTE0VDEwOjMwOjMyKzAxOjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgMjMuMCAoTWFjaW50b3NoKSIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz5OuZ8jAAAVWElEQVR4nO2daZBjV3WAv7erJfWi6e5ZPTM99ngZe7AdQjkEEi8xBNuA2ULAdghQhgQSUhUCJJUEqhKSFFVJVRJS5AcBV0xIsNnKYBYb22CYYcYLmBkvs9gztrtnaU93T29Sa3vbzY8rqbV1t7aW1C19VSpJT2+5eve8e88959xzFSEEXToXtdUF6NJaugLQ4XQFoMPpCkCH0xWADqcrAB1OVwA6nK4AdDhdAehwugLQ4XQFoMPpCkCH0xWADqcrAB1OVwA6nK4AdDhdAehwugLQ4Sg33nhjq8vQTF4FXAfsBUaA3sz2GDAKPAf8NPPeEeitLkAT2AT8EXAnsLPCY0aBu4AvAROrU6z2YD13AUHgH4AXgc9SeeWDbB3yjw02unDtwnptAV4D/B9wSf5Gq7+XDXsuIrRlmECkH80yAfDSNqnZeeLjk8wcf4n0fCx7SAj4DPBu4A+Ap5r2D5rEehSAdyArvye7Ibx1IyM3X0vk4pGKTjD7wiijD+5jYXwyu+ky4ADwh8A3GlraFrPeBOD3gXvIdG2qrnPpe9/M0N6LqzpJ5JIRIpeMcP7ZF3j+6z/Adz0AC/gaIIBvNrbYrUO78MILW12GRvE7wLcAA6BnKMIVH3gnkd3VdP2FBDcNsuHSC4mOnsWJJ0EK1q3AQeDl+ovcetaLErgX+DbyKSW4cZA9d9xK7wWb6z5xeNsmLrvtLQQ3DmY3WcB9wNV1n7wNWA8CsBP4ETAAYISDXPqeWwhtGV72oKTvcd61mXbTJH1v2X1DW4a59D23YIRzg4E+4PvAjjrL3nLWuiHocuBBYHt2w1UfuY2+kW1ld3aF4HgyyqidIOo5Bb/1aQYjVojLAr3oilL2+OjYOM988V6E72c3jQE3A8fq/ictYi3rAK8HHgZy7fzeO3+PgYvKP5STTpqfxCY4YydJC7/k97TwmXBSjNpxBjWLkFaqH1sDvfRu38LkoaPZTQPAe4H9wJk6/09LWIsCoAAfB/6HRVMuez/4LiKX7Cp7wIST5mexSdJ+acUX4wjBKTvBkG4RLiMEPYMD9F6wmanDuYc+CNyBNCc/WeV/aTlrTQC2Iodgf0pmCKsHLPbe+W4GltD2Z1ybR2NTuFVMg/eBM3aCrWaQHlUr+b1nKMLAhds5/9wJhOeRKcvNwDXAT4CFqv5VC1krAtADfAL4OnBFdmPvBZvZc8et9O3YWvaghO/xk+hk2SZ/JXzgrJ1khxXEVEp15UCkn8jFO1kYn8SO5ur7YuAjgAk8AbhVX7jJtLsA9CEdOfcA7yIzzAPY9ebrueTdN2H2hcseaAufH0enWPBrrwNXCCbcFCNWCK2MYmj2hdlyzZVopsHcibHcZuB64H1IATgG2DUXYpVpRwHQkAreXwFfAd4O9Gd/DG0ZZs/tb2Xj1XuWPEHK93k0OsmcV/99T/k+E06a7WZwydFB385t9O+6gMS5KexYPLu5H7gF+Biy64oC40hLYtvQDsPAXqSf/kpkxd8EDBXvZEX6uOgtNzB4xfJm3bjv8mh0qmSYVy/9ms4NfZsIltEJ8pk+coIXv/co6blouZ/PAw8g/QrPIOMOYuV2bBarLQB7gQ8jvXL5KnUY2JB5DSI1+7KEtgyz/YbXMnzlpStebM51+Flsingdzf5yhFSd6/uG6NfMFfedeuY4px99gvgrU8vtJoBpYCbzylceXeAF4L+AIzUXegVWUwDeD3yZGhxOweENDF5xMcNXXbaiRS/LiVSMQ4m5qrT9WtAVhVeHIuy2yusexcTHJ5l8+jgzR0+SmJqp5ZIOMpjlq7UcvBJLCUAvsim+HBlRs3y7V4qJ9J9XVPl9O7cS2jxM38g2+ka2EYj0r3xQBlsInlyY5pSdqLKI9bHDDHJNeEPZEcJSpGbmiY6eJTp2lvgrU0RPjVd6qAP8b+a9GjxkRNMRpMW0ZHhaLAAR4NPAnwCBKi9WlvC2TYy86bdQ1MUbpZkmejCA3hPACPYs0wEsjRDwsh3ncGKO1Aq2/NWiR9W4KjjALjPEEvrh8ghwEkncZAo3kcKzF5VW4fuMPrg/PyahXpLAfwL/BMxlN+YLwOXA/cBFjbpi744tXPz2NxLaurFRpwRgyk3z1MIsMw3Q8hvBoG7y66EIQ7q18s5VsDA+ycn7HiZ2+pVGnvYk8FbgOCw20duQdvXyFpUaEa7XsMoXAibcFC8kY5xxkg05Z6OYdm0emp9g2LC41Oplu9WDUkuzVkR468aspbGR7AYeQVotx7MCcDflK/9pZGBktaW4BQgtjE/i2Q6aadRYVoh5LmfsBCdSC3UZdZrBlJNmyknTm9TZHehlu9lDWK096MqznfwuIA78sMpTaMgW/aqi7duQUc8368hImjcU7XACuB34ZZUXzHIaGVCJm0xVJQCOEMy6NuecJKftFPNt0sxXQ8xzORSf5VB8lgHd4AIzyGY9QEQ3MapQFtxkKv/rLDLkrRauQcZJ7s7bdhNwnQ7cVrTzHFIgTtV4McjTNmcSCaxQad+Y9n3SwpPv+MRchxnPZsF3WU/pi+dchzl3nueYR1EgrBhEdIM+3cBCxdI0+a6WjibSiYKRTT0OpieRdfoM0rye5XYduLZo5y9SX+UDPIuMpOWJl0+SMLevsHtnIATEhEPMdiryDgRfPs3g4tdn67z8GLJuP5W37ToV2R/kc7DOC3VpXw4Ufd+mkumr82iuRaVLM4kXfQ+vh6DQLnXQFYAOpysAHU5XADqcrgB0OG0zOVQgSC2k8D0XRVXRdA1V09A0DVVbm3LqeR6+5+N7Hp7rITwfTdewwo3xFTSCthGAZDROKl7eyaOgoGoqqi6FQVVVFFVBVeV3RVVRNBW1Jp9s9fhCIDwf4fuygn0P4Qv87HfPw3d9xBLhf67nEe7vLftbs2kbAXDtpR09AoHneXgVeMYURUFRFRRFRVGQwpERDEVRCmIPVFV+8f28ihKQXUpPCIHwffkuyH2uF2+Z/9ps2kYAdMvAdeoP5BRCIDyBjOxvT3Srdu9oo9GRdyq/k21JhxvsDaFpGo5t43u+7DMrmMq1FljUaVR0w8AK9qx80OpQXLe+jnQz5vkcCj43FSsYwAouRqIJIfBd2fTL90y/6/sIL/PeYtehoihSJ8npJipaRl/RNA1V13JdUBtQHPNxTgcmKaz0+rMqNAhFUdAMHc1YuqcSQuBnBEMIEMJH+CLTby9+ljtTIDDZz/kVlK8nSP2BjD6Rp1uoiqx4TW2nyq2E4qnTZ3Rk8Eb+NJu9zStP/SiKgqZrVB+43JFcXvT9rEpp1M9rm1SYLs1FQWZJzedJldI57XuQiRK7rC/2Utq971OBn1EYn6Igs150WV+8v+h7HPiliowBfKjoxzupz0awaOnw11GAX7MpvHf1GEksSgXgW4CdHRfeU/TjbuprBXJRRYrbmlk76wHFKbAYFkfzVMNHKZ1x/WVYNAx8Exk0mM9nKQ0Xq5RFAXAqM3v6vs/CXJTo+Tni8wvYqXShiXaNInwfO5UmPh8jOj1LfC6G8CozcKlOwcNTa6jeAHK6Xz5HyMQHZpt5B/hn5NyxLLuQQvCJGi6a8+pU2gIkMpUO4DoO6YQ8hW4Y6JaBYZnopt42XrQlEeDYNq7t4KSdEvO2iwsIQgN95Y/Po0EtwL9Satz7NJlEFfn9/F3IbBb5NoE/B/YB363yorl50Gqqsokd3hKC4jryJqYW5AOgmwaarqEbBpqhoes6tc3MbAyu4+I5Dq7jZd5XbvE8t8IWoPDezdZQvPcAHyza9gR59ZkvAGngj5GjguwdVZHTkq8DflXFhUdzF5ivTHCtUIDE/MpzH1zbwbUd0shZM4oCqqajGRqapqKoGqquSjNsxjRbD1mXr+d7CDfzOePf973aJrFYocp8AUX3rtrcxK9HPtT52MgMa7lSF2v6+4HPI5/8LGHkZMJbgMcrvPhLuQvMVSYAgWAPuqFjJ9M4toNXoe4gBHiui+cusb+ioGZMt4qigpJxGee7hjMmYiGE/OxLX77vCxo1TUnTdUzLxOgx0Y3KvIFa4b17aan9yvAa4AeU6nB/Q9GaB+WGep9C5uzJTxwQQc4e/jBwbwUFWBSACp7q3L6Gkbs5wvdxbAcnbeOkpYewJoTA97KV2LwRiaKpGIbUXYyAiVpDS1R07yptAd6FnOxbnMLkh0h9oPAaZU7gIich7iMvJ1/mhPcgBeMvWD650TQwBQyrSRs1kcYPVjd3XlFVzICFGZDHea4r+1vXw7Md3DZyFyuqiq5rGceVgW7oGf9E7WjxFGo6p0CeIy+pwxIEgb8DPklpyo3HkPpASXO2lLFnBpnr7iHg14p++xDwRmRr8PAyBToIvA3AGj9Pcnf5BM6Vouk6ml5YXN/1cV3ZXWT7Zt+TruKlwrFqRUFB0WUYmoxT1KQSauioWuMdUebZ8/lfi6d0FXMr8O/IkVsxTwNvZonJpctZ+84jp47fC7yp6LedSOF4APh7pGZZzKIAnJ2uWwDKoeoqpm5BoLR18fPj9TyBQCB8Mn26n+nfMzsr2fAwFRQFqSpI96+qLcYhNhOrMgF4HfA5Sif4ZnkAOc1/bqnrrGTunUMqf3+NbF6K978583oMOVr4JrLpLyi0dapheW4qRlWzldY2UW9VYZ0uSC+XLwADyEWs3o/U9MshgH9BKn3LKj6V3B0fmVjox8B/k5n2XcRvZl5fQKZGPYjMLJICAua5GRTXQ9TZL3YKiuNhTuSG/QmkVn8H8om/muXr7QjS9Lu/kmtV83g8jtQHPoFM41ourllBBh0UBB7Ymzd0K78KhKFhb4pkhSBIoYV2KaaR1tx/owrHUbW5gl2kZN2V+fwqVkgnZ2/eQHr7MO5AGD+wcobNLqDPxNASKVTXQ0ukV9p9Gtnc34ZMVV/V0KjeTKEh5LjzfUhr4ZIWDmHonPvA7+IOVJZhs1PRZ2JsvvtHKMvbPdJIBe9e4HvUkdOhXg0pjly5I7t6xw3IhESvRman2kkmxbviuIQPv8jc9cUJq7rkEz58srjy55GBu8eR4Xu/QOpY8424XiNV5Bgy0eT9Rdt/g4wJufcXzzN/7asQZYZUru0Qn4/h+wKrxyIQCq7ZOYHF+J5PKp4gnUyjqiqh/l50s/TWK45H71Mn8je9hlVerrYZd/gJ8oYx4cMvlt0pPh/LTQZJxZPMT02zMBdtyGyhVuE6LguzUeYmp0nFkwjfx3Nd4uVTyRM+fDL/609pwlrFzRokf57MmLV/37PEL99ZohCKouAPIcBOprGTaXRDw+zpweqx6vburTbC90knUqST6SUdVKLMEjZaIk3/wYKs8J9fnRIW0qy7+W3gECzqAsUElnGRuo5HIrrA3OQ0sZl50olUxVE1zcD3fdKJJLGZOeYmp0nE4kt7J4FAuDTQKnT4JMripNGnKe1KV4VmtQA+8LdkUp3273+W5IVbcDYO5HYIhIPolkFqIYmTTpf1wgpBzjsYR3oPDUtHNy0Ms3mBIUIIGZeQdnAce9mZzVkUBYyARSDUU+IONidm6T9Q8PR/kibNbm2mnfQBZFzBGwA2PPwUE3cUDkF1wyAcMaTSlEjKJ30Zj182WgiSucAQ3dQzDhodTVfrdtT4ro/nuZmYAxn147lexWECiqquqNRGHi7o6h9E3qem0GxD+aeQwxjdHJ8mdGSU+BUjJTupmkqwN0QwHMJOp0knUjjp5UPL8gND8k0nOS+esjiBExZzA2TJBqDKiacCX3gIV9TsVTQsE6sngNFjLhvHGDw6hvlKLoLOBv6ypgvWSLMF4DDSe/UZgMiDv8QZ6sfeFCm/t0IuJsD3BU7axk6lcVN2xRUjEAhX4OPXF1lfAbqpy/L2BCryHprnZhj8YcHErH+k/pSwVdGKZeP2A28BtihCYJ6bJX7VymtUKIqCbuiZ5jSAbhmoiprJ4tGa8HFNz5QnHCTU3yv7d9OoeMbw0H0H0BZyAdSHkR6+pmq3rfCVusigkscB05yYJfLIr5h9w6srPoGiqpiWhWnJOADh+biui+s4eLaH67oIv/J+esXrKaBoWibqR0b86LqOUoehKvLQU5jnck1/Cln5TTd6tMpZfgi5IujdAOFDJ3GG+lm4urbVahRNxdBMDGvRtiAE+L5MLOHnEksIfN8ryRMAi3kBVFWTQaSqupiYStUaOsAIHzpJ+OmCofDHkKncm04royW+gpyK/hGQmrDQNeJ7RxpyckUBLZNmrp0IHh0j8khBhP0XKA3fbhqtXjr2YaQDaQdAz8mzuIO9OEOVLxu3lggeO8XgDwqi5/YhAz1aZtVqtV3VRiqEuYHw4PceJ/j8mdaVaJUIPn+awe8XTKs4DLyDFvT7+bRaAEC6NW9CrqMLwOD9BwkfOrn0EWuM8NMvMnj/Y/mbnkUaxGpaSrSRtIMAgIxAfiOZtewAIo/8iv59LdGLGkr/z58j8lCBpe8o8r9Ot6ZEhbRaB8hnAfgaUjEcARka3TN6DnvzBvxQQxYybRrm5CxD9x0keKxg+aUDyNZu2RWlm0k7CQDI8fDXkBMcrgTQYklCR0ZxhvpxB1eeUt0O9Lw4ztB9B9DnCuZifAN4J1A+GKBFtJsAgIxj/w5SQbwOUBVfEDx+msDYRFu3BsbUPEPfPUjfk8fzw7ocpCf04+SnzmkT2lEAsuxHesZuADYA6LEEoaNjuH1BnOGBVpathODRMYa/W/LUjyJnRxWn4Gkb2lkAAMaRBqNtyC5BUTyf4ImzhI6dQugazlKOpCYRfP4MQ985QPiZl/KfeoG0cr4NuVhz21JvWHgzuRb4D4rWwfVCAaKv38vClbualylECMLPvETfY8fQYiUR2YeBPwN+3pzC1MdaEgCQ+WA/inQnlyxLnt42RPR1l5MaWZ10x4GXz9H32NHiiZtZJpETZb9IMxMR1MlaE4AsFnK++2coXBAZAL/HIrVrM6kdG0nt3ITXF6zpIno0gTU2QWBsgsDoOdRk2aCUMeR0rC+xBhfdXKsCkMVA5jP8EPDblCZGAKRAOEN9OIN9uIN9eMEAvqUjMquaK7aDmnbREin06SjGdBTj/PxSFQ7Sdr8fmWvv67TYnFsPa10A8hlBTlG7nfIzmBvBMaSd4quU5lVck6wnAchnJzKVzY3IYeSWGs8zDjyKnBr/Y+pfVb3tWK8CUMwQMt/RnsxrE9CXeYG0zkWBCeRTfgzpnGoLe/1qorR6yZUuraVdvIFdWkRXADqcrgB0OF0B6HC6AtDhdAWgw+kKQIfTFYAOpysAHU5XADqcrgB0OF0B6HC6AtDhdAWgw+kKQIfTFYAOpysAHU5XADqc/wcMMJSWhXAMSQAAAABJRU5ErkJggg==", - - "white.png": - "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAADsQAAA7EB9YPtSQAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAABJrSURBVHic7Z13WBTX+se/u4uCNMWWoiBqELFx1eg1UcQoCiigIEq8GgsqSbgCxi4KlthQwBALoiiRX+wGCyKgXk2uDYk1YowaEwsWVGw0wV32/rG/XfZsLzM74+58nsfn8bw7c87Zeb+cec87Z+cAHBwcHObEEAB3AYgp/ncfwFATfg8OAxgOoAbUO1/6TwhgjMm+DYdehIJe53MiYDEjALwF/c43OxHwTNDGMABrALQ0QVtK1KtXH3Pjt8Haxlav86rfVGL54tF4+7aGpp5ppRhAFID9dDbCp7NyAGEA9sCEzhcIrODo2EhWfvu2BteKzupdz7WiM4TzHR2dIBBYUdJHHWkJybULo7MROgUwAsCPAEx21QQCAZYlrMfn/won7FcundC7rssXyXOGjwzHt8s2mVoEVgC2gcbbDV0CGAFgO0zu/FQEBI5AYBD5R3P71mW8flWqc11lr5/j79u/EbaBg0LQ3yeICREIAPwAmkRARwyg5HyBQIDk7zchOORzoyvPyc5CVOR4CIVCmU3e+VJCg71x/fffVFWhN+09PPHDj8dl5ePHDiIudjJEIrIPSSkbETJ8lNHtHT60D1O+Hkd8RwAiAOMhGVUpQ0BlZWCJ8wGgsrICZ07rP/SrYvTYKHTu0kNWbt3GHa1bu+PnEzkQi2sBAGKxGEfzc+DSqjU8OnQ2qj23dh5wa9ceebkHUVtbKzXzIUlE/QWAGmWDWgGwxvkA8GELF/yYuQFisdiodvl8PubFpcDOzoGwaxJBK9c28PDoZFS7phIBVQJglfMBwM7OHud/PYPi4rtGtd2jpzdCR05U+Zl6ERx6Z0RARTQTBoVo38rKCilrtyBwaKjRlR/JO6TC+VZISEyD/+AQjecGBo1EwdlfZOWmzT7AilXbwOOpDn3EYjFmzxiF0mclMpvvYNUCk9LfJwi1tSIsmP+VLCYQiUSYHhMBezsHDPQdovU7amJwQDBSRCLETAmXvwYCABmQJL92GVO/sbMAWp0PAAviZigGQ+j1iTe8+g7Ueq7PoEDY2NjIys+ePsLtP6+pPf7WzauE862tbfBZ/wCt7fT6dAA+7uFF2IRCIRbGz9R6ri4EDg1FytotsLIi/l6tILn2RuUJjBEA7c5Xx+lT/8Gg/p7YsD4R5eVlao+zt3dAv8/8CVvBmWNqjy84S37m5e0PW1t7tceXl7/GlvREBAd2xbkCagJOddAlAkMFYDLnJySuhZNTYyX7q1cvsCZlqVYhBASRQ/ivhSeURhRA8hd7vvBnwubnr/q7yDt+Y+pylL1+qXRMo0ZOWLFyjbqvZRB0iMCQIHAEJNkppYBvaPBIQ/qgkVaubTB2fASaNW2OoqIrqKysID6vrn6DwnMnsXN7OsrKXqNTp66wtq4b9p2dXbFrxxa8eVMFAKipqUabNu3x/gfORD1XLp/F6ZN5srKjoxNmxSZCIKi7RFWVFdi5LRXzZk/EmdNHUVP9Rqm/9g6OmBgxBRs2bUM7dw9KroE87u07qAsMg2FAYKivAGiN9tVRv359dO3eE2PGTlIrhJqaGly6WICdOzYTQuDzBSi+fwfXii7LjhWLxfi4hzdx/v6sDDx8cEdWDggaDS9vPwB1jp87awJOncxHtQbHb9y8Az4D/QkRUg2VswN9MoGM3fMVKS8vQ8bm9UhPW4MXL56rPKZhQyckJG6EV18fXLp4DmNG+ck+q1/fGsnfZ6FBA8kTwqqqSkyLDkFNTbXsmE1bctHZsydOnczHovhIlcM8IBnqJ30ZhfCJkbB3cKTwW2on+8BexdkBUPeoWqfZga4CYI3z5dEmhA8+bIljJ65CLBbDz6crkRMInzQbvb0kojh1MhcZ6Stln33YohV+OnABPB4Pgf6d8PTJI6W6mXS8PMaKQJcgkJXOByRRflTMbJwuvI5ZcxYqDbvSLCCPx8OQQLKv8hG/4szA1z9UlitQzBlYW9tg5pwFOFN4HdFT5zDqfMD4wFCbAFjrfHns7R3Qs1dvYggHgPHhU2T/DwgkA9Q/rl/Cy5elePmyFDf+uEx85udfN3MYMzaK+Kymphq9PvFi3PHyGCMCTQJQep4vfeLFJucDksxb/LxpRN7fzc0Do/5Vl8Jt07YdOnT0lJVra2tRWHAc584ekw+k0N7DE61c3WTl0JET0c697uGOWCxG7OxolVNJJgkcGoq1qVtViUDjegJ1swBGon1Dydi8Hnt3b5OVeTwekr7LgLOzK3FcVWUFTp+qe6xbXv4Sxff+wqtXdfGD4pM/Ho+Pth954NDB7TJb6bOnaNy4Cbp2qzuODRgyO1A1AoRBwflWVlb4fl0Gpc7PPXwAXTu3QrcurjiSd8jgep4+KUFy4hLCFhg0Ej169lY6dnBAKDGvv3vnFu7d+1NW5vMFGOir/Hyhi+c/4T+EvIUkrlqMp09KlI7VlSN5h9DdszW6dXFFXu5Bg+tRZHBAsKrbgfTZgdLtQHEEMMk9P/vAXkRFjkdFeTkqKytw7twpRHwVY1Bd8+ZMxZVL52VlBwdHrE3dAVtbO6Vj7ezscfFCAe7fv6Oyrh49vTF8RLjKzzp79sSBfZmyOKOmuhqlpc/g5x9kUL/DQv3w9EkJKisrkJuzH23ausG9fQeD6lLEvX0HtGnrhiN52YojwTAANwFckzdKGQYTOV9x2lLyWHmapQuF505j3087CNuU6Fg0bdpc7TnqHh8Dmp/8NW7cDBFfzyVsWXu349fCMzr2luRJyWPZ/4VCIWKmhCP7wF6D6lKFlsBwmNQgP8e5D7nVu6ZyvpR7jyr1qksoFGKI76e4/nuRzNbOvSP27vtZ45q98vIyePduhzdvyGyejU0DHD76h8aHPyKREOPHDMCtm3VtenTohJz8M4oXWisuHygvUzfhNb8PwAUgRwBi6bYpnW8ImRlphPN5PB7iFiRqXbBpb++Az/oPVrL36eun0fmAZB3CjNkJRG7g+u9FyMxI07P3qqFzJFBA9iBE7TSQzc4HgPXrkoiy/5AQdOveS6dzVd0G/LQs/JDi+Y9eSoHihvWrdTpXF+gSgTro/mGIplQlpVy+WIjnz5/pdGyfvgOUbL0+6a/TuS+eP8OVSwV69U1HZNeEDhGog1YBaMlTG8X8+OVE+eHD+5g+NZxYqq0OK6t6OtkUEQrfInZOOEpKHhD22LilWs/VgTFgQAS0CSAnO0uV86Vr241axwYAw0LCMHHyFMJWeO4kEhPija1aLSmr43DpwmnCNvnLaAwLoeTXW7sAjIKCCKL/PQFZCjMdKqFFAKpW8ULi/HGQpCYpYf6C5fDu50PYMremYt9PlDUhI+/wbuzZuYmwfdqnH+bOX6LmDIPYCwURSBeY0iUCygVgKucDkvT0mtStcGnVmrB/u2g6iq5epKydmzeuYsXSaYStRQtnrNuQqff0TwdMKgJKBWBK50tp1MgJGzfvlC3uAIDq6mpMjR6H56VPja7/1avnmDNznGxJGSB5JJy2eQeaNGlqdP1qMJkIKBNAfl62KucLAYwGTc6X0qFjZyxfRS7AfPSwGNO/mahTUKgOkUiIuTPH4+ED8sclCUnr0MWzm8H16sheKASGIpEIM6Z+ify8bMoaoUwA8bHTjFqaZCwhw0dRHhSmJMfhokLQNykiipIfgOrILqiYHSyYN52yBigTwKNHDxRNJnO+lHnxy/Bpb3KxZ+bWVBw8oH83cnN2Y/fOjYTt097eVE359EEqAhkPHxZTVjmdeQCTOh+Q5NI3pG+Hs4srYV8YF6NXUHjzxlUkLFMR9KX9Hx1Bny7Qdi1pzwSamkaNnLBxi+FBIUNBH2OYnQAAoGPHLliRuJawPXpYjJiosRpf+iQSCRE7a4JS0LcsIcUUQR8jmKUAACA45HOloPDihQIkr1qo9pyU5DhcOH+KsE2KiMKIsC/o6CIrYOSGZirmL1iOWzf/wH9/qVv2nbk1Fe3cOyodm5+7Rzno69OPiaDPpMgvCCFepaHvAg0VCxy0/ejEqPZ0pbT0GQL8+uBB8T2ZzcbGRmlBiLW1DfGTrxYtXZCTfxqNGzehpV+mvl7q2jPbW4CUJk2aIv2H3URQqOh8AITzra1tkJa+nTbnswmzFwCgOijUhDkHfYpYhAAA1UGhKsw96FPEYgQAqM4UymMJQZ8iFiUAKysrpG7appQpBGh9vMtqLEoAAODk1Bhp6duV7Jsydpllpk8bFicAAOjU+R862SwBixQARx2cACwcTgAWDicAC8fsBFBd/QYxUV+hfdvmcG/TDN9Ef42a6mrtJ1ooZjfpnTVjKvbtzZSVf9qzFTw+H8nfrWOwV+zF7EaAo3nKm2zl5WQx0JN3A7MTQL169ZVsfL4pdsd7NzE7AQzyG6Zk8/XXvK+AJWN2McDS5asAHnAkdz+EwrfwHRyCFSuTtJ9ooZj9iiB1KPaX6fbBrQjiYAJOABYOJwALx+wEIM0Eeri9D88OLpg5PZrLBGrA7GYB8pnACgC7tqcDAFYlfc9gr9iL2lkAxXWrgpZZgMdH76GigtxAysmpKa78fo+wveuzAAqwnFmAUPiW6S6wFnkBUPejc8mrSBnBT8Vuor5adhh9R6DFP/ICiKCokfv/XxcjrFi5GqFhE+Dg0BD2Dg0RGjYBCSuTmeoOldDiHyafknCZQBJGfGERMQCHejgBWDicACwcsxMAlwnUDy4TaOGY3QhwJHefCpvyOkEOCWYnAFVwmUD1mJ0AzDgTSAtmFwOsWLkaPD4f+YezIIZEEGaSCaQFLhPIkvbBZQI5mIATgIXDGgGYYos0tsCm78oaAVC1OxbbM4HS3dTYAmtmAdI9cQAYtSMHmzOBavZUYhTWzAKkCAQCJKVsNFgEbF0TqIPzuVkAQM/uWExnAtn4ly+FNQKQ3/VbujvWwf179K6HbZnAg/v3KDlf2w7npoQ1Avh22Sbiwkj3ztV3JGDTmsCc7CxMjZpIOJ/PFyB+ke4vrqYb1sQABRdKcfzYQcTFTib2+jM2JlAH3TGAqmGfzxdgweJ18PUfgV7dlV5Fz8UA/X2CsGjJBqXbwfSYCBzJO8Rgz/TjSN4hTPl6nNKwv3hpGnz9RzDYM2VYJQAA8BkUrFIEUZHjUXT1MoM9043frlxEVOR4iEQimU0gsMKiJRvgMyiYwZ6phnUCACQiUIwJqqoqERU5ATU17EnqKPL2bQ2+iZ6Mqqq624n0ns9G5wMsFQAguR3MmUcGb7f/vIEftmzQeB6TmcD0jWtx6+Z1WZnH42FefArrhn15WCsAAAgcOhpDg8cStvS0NRr3/pNmAivKX+PFi2fYtT0d82Jn0t1V1NRUY/MmMrofFjIOQwJNts+wQbBaAADw7+h42Nray8qPHz/EyV+Oqz2eqTWBv5w4hiclj2VlOzsHREYZvnG1qWC9ABwdneA/JIywHTt6WK86TJEJPP6fPKI8OPBzODg0pL1dY2G9AADAy9uPKF8ruqL2WKYygYozFK++fmqOZBfsyUlq4CM3cqfPO3/fVnssU2sCFfvk5taJ9jap4J0QQEPHRkRZ1caPUqxtbCQvhjbxy6GrqqqIsmPDRmqOZBfvxC3gTTXpcD6ffd0WCAREWX77eTbDviupguL7fxPlD1s4M9QT9Sj2SbHPbOWdEMCF8yeJspubO0M9UY9inwrP/cxMR/SE9QIQi8XIPrCNsHl5D2CoN+rp28+HKOdk74BYTPWLvaiH9QLIyd6Bu3duycr16tXHwEFDGOyRagb5BhB7Fdz5+yZysqlb1UQXrBbAvbu3kZI8n7ANDR6J5u+9z1CP1NP8vfcRGDScsKUkz8e9u+qnrGyAtQK4d/c2oiNDUFb2SmaztbXD9JnzNZzFLNNnxaFBg7qFJmVlrxAdGcJqEbBOALW1IuzP2ooJXwzA48fkW9Fmxy5Gi5YuDPVMO84urpg1dyFhe/y4GBO+GIAD+zJRWytSfSKDsGZJ2Ib0Qzj/60nk5uzGg2LlKVTYqHFYlZxKWeN0LgmbFhOBvbt/VLK3dG4N/yFh6P5xH3w1KUDxY0Z8wRoBaCJ05BisTFpP6dbudApAJBJh3uxobN+Woc9p3JpARRo0sMWiJUlI+i6NUufTjUAgwPJVa7Fw8SrY2DRgujsaYeUIYFWvHkKGj0LMN3Pg7OJKS+Om+mXQ3Tt/IWX1CuzP2qnthyGWfQto27Yd3D06wqtvf/j6BaJps+a0Nm7qn4Y9fVKC/LxsnDp5AjeuX8Pt2zcVD7FsATD9hg6m2wcXA3AwAScAC4cTgIXDmhiAg4sBOBiASQFQuQfOuw5jeywxKQCq9sB512F0jyUODg5L5n+QxxVcdcQzggAAAABJRU5ErkJggg==", -}; diff --git a/mod_examples/sandbox.js b/mod_examples/sandbox.js deleted file mode 100644 index f405ab59..00000000 --- a/mod_examples/sandbox.js +++ /dev/null @@ -1,21 +0,0 @@ -// @ts-nocheck -const METADATA = { - website: "https://tobspr.io", - author: "tobspr", - name: "Sandbox", - version: "1", - id: "sandbox", - description: "Blueprints are always unlocked and cost no money, also all buildings are unlocked", - minimumGameVersion: ">=1.5.0", -}; - -class Mod extends shapez.Mod { - init() { - this.modInterface.replaceMethod(shapez.Blueprint, "getCost", function () { - return 0; - }); - this.modInterface.replaceMethod(shapez.HubGoals, "isRewardUnlocked", function () { - return true; - }); - } -} diff --git a/mod_examples/smooth_zooming.js b/mod_examples/smooth_zooming.js deleted file mode 100644 index 94254f2d..00000000 --- a/mod_examples/smooth_zooming.js +++ /dev/null @@ -1,58 +0,0 @@ -// @ts-nocheck -const METADATA = { - website: "https://tobspr.io", - author: "tobspr", - name: "Smooth Zoom", - version: "1", - id: "smooth_zoom", - description: "Allows to zoom in and out smoothly, also disables map overview", - minimumGameVersion: ">=1.5.0", -}; - -class Mod extends shapez.Mod { - init() { - this.modInterface.registerIngameKeybinding({ - id: "smooth_zoom_zoom_in", - keyCode: shapez.keyToKeyCode("1"), - translation: "Zoom In (use with SHIFT)", - modifiers: { - shift: true, - }, - handler: root => { - root.camera.setDesiredZoom(5); - return shapez.STOP_PROPAGATION; - }, - }); - this.modInterface.registerIngameKeybinding({ - id: "smooth_zoom_zoom_out", - keyCode: shapez.keyToKeyCode("0"), - translation: "Zoom Out (use with SHIFT)", - modifiers: { - shift: true, - }, - handler: root => { - root.camera.setDesiredZoom(0.1); - return shapez.STOP_PROPAGATION; - }, - }); - - this.modInterface.extendClass(shapez.Camera, ({ $old }) => ({ - internalUpdateZooming(now, dt) { - if (!this.currentlyPinching && this.desiredZoom !== null) { - const diff = this.zoomLevel - this.desiredZoom; - if (Math.abs(diff) > 0.0001) { - const speed = 0.0005; - let step = Math.sign(diff) * Math.min(speed, Math.abs(diff)); - const pow = 1 / 2; - this.zoomLevel = Math.pow(Math.pow(this.zoomLevel, pow) - step, 1 / pow); - } else { - this.zoomLevel = this.desiredZoom; - this.desiredZoom = null; - } - } - }, - })); - - shapez.globalConfig.mapChunkOverviewMinZoom = -1; - } -} diff --git a/mod_examples/storing_data_in_savegame.js b/mod_examples/storing_data_in_savegame.js deleted file mode 100644 index 92f7733b..00000000 --- a/mod_examples/storing_data_in_savegame.js +++ /dev/null @@ -1,78 +0,0 @@ -// @ts-nocheck -const METADATA = { - website: "https://tobspr.io", - author: "tobspr", - name: "Mod Example: Storing Data in Savegame", - version: "1", - id: "storing-savegame-data", - description: "Shows how to add custom data to a savegame", - minimumGameVersion: ">=1.5.0", -}; - -class Mod extends shapez.Mod { - init() { - //////////////////////////////////////////////////////////////////// - // Option 1: For simple data - this.signals.gameSerialized.add((root, data) => { - data.modExtraData["storing-savegame-data"] = Math.random(); - }); - - this.signals.gameDeserialized.add((root, data) => { - alert("The value stored in the savegame was: " + data.modExtraData["storing-savegame-data"]); - }); - - //////////////////////////////////////////////////////////////////// - // Option 2: If you need a structured way of storing data - - class SomeSerializableObject extends shapez.BasicSerializableObject { - static getId() { - return "SomeSerializableObject"; - } - - static getSchema() { - return { - someInt: shapez.types.int, - someString: shapez.types.string, - someVector: shapez.types.vector, - - // this value is allowed to be null - nullableInt: shapez.types.nullable(shapez.types.int), - - // There is a lot more .. be sure to checkout src/js/savegame/serialization.js - // You can have maps, classes, arrays etc.. - // And if you need something specific you can always ask in the modding discord. - }; - } - - constructor() { - super(); - this.someInt = 42; - this.someString = "Hello World"; - this.someVector = new shapez.Vector(1, 2); - - this.nullableInt = null; - } - } - - // Store our object in the global game root - this.signals.gameInitialized.add(root => { - root.myObject = new SomeSerializableObject(); - }); - - // Save it within the savegame - this.signals.gameSerialized.add((root, data) => { - data.modExtraData["storing-savegame-data-2"] = root.myObject.serialize(); - }); - - // Restore it when the savegame is loaded - this.signals.gameDeserialized.add((root, data) => { - const errorText = root.myObject.deserialize(data.modExtraData["storing-savegame-data-2"]); - if (errorText) { - alert("Mod failed to deserialize from savegame: " + errorText); - } - alert("The other value stored in the savegame (option 2) was " + root.myObject.someInt); - }); - - //////////////////////////////////////////////////////////////////// - } -} diff --git a/mod_examples/translations.js b/mod_examples/translations.js deleted file mode 100644 index 6b9c708e..00000000 --- a/mod_examples/translations.js +++ /dev/null @@ -1,66 +0,0 @@ -// @ts-nocheck -const METADATA = { - website: "https://tobspr.io", - author: "tobspr", - name: "Mod Example: Translations", - version: "1", - id: "translations", - description: "Shows how to add and modify translations", - minimumGameVersion: ">=1.5.0", - - // You can specify this parameter if savegames will still work - // after your mod has been uninstalled - doesNotAffectSavegame: true, -}; - -class Mod extends shapez.Mod { - init() { - // Replace an existing translation in the english language - this.modInterface.registerTranslations("en", { - ingame: { - interactiveTutorial: { - title: "Hello", - hints: { - "1_1_extractor": "World!", - }, - }, - }, - }); - - // Replace an existing translation in german - this.modInterface.registerTranslations("de", { - ingame: { - interactiveTutorial: { - title: "Hallo", - hints: { - "1_1_extractor": "Welt!", - }, - }, - }, - }); - - // Add an entirely new translation which is localized in german and english - this.modInterface.registerTranslations("en", { - mods: { - mymod: { - test: "Test Translation", - }, - }, - }); - this.modInterface.registerTranslations("de", { - mods: { - mymod: { - test: "Test Übersetzung", - }, - }, - }); - - // Show a dialog in the main menu - this.signals.stateEntered.add(state => { - if (state instanceof shapez.MainMenuState) { - // Will show differently based on the selected language - this.dialogs.showInfo("My translation", shapez.T.mods.mymod.test); - } - }); - } -} diff --git a/mod_examples/usage_statistics.js b/mod_examples/usage_statistics.js deleted file mode 100644 index 80828d95..00000000 --- a/mod_examples/usage_statistics.js +++ /dev/null @@ -1,148 +0,0 @@ -// @ts-nocheck -const METADATA = { - website: "https://tobspr.io", - author: "tobspr", - name: "Mod Example: Usage Statistics", - version: "1", - id: "usage-statistics", - description: - "Shows how to add a new component to the game, how to save additional data and how to add custom logic and drawings", - - minimumGameVersion: ">=1.5.0", -}; - -/** - * Quick info on how this mod works: - * - * It tracks how many ticks a building was idle within X seconds to compute - * the usage percentage. - * - * Every tick the logic checks if the building is idle, if so, it increases aggregatedIdleTime. - * Once X seconds are over, the aggregatedIdleTime is copied to computedUsage which - * is displayed on screen via the UsageStatisticsSystem - */ - -const MEASURE_INTERVAL_SECONDS = 5; - -class UsageStatisticsComponent extends shapez.Component { - static getId() { - return "UsageStatistics"; - } - - static getSchema() { - // Here you define which properties should be saved to the savegame - // and get automatically restored - return { - lastTimestamp: shapez.types.float, - computedUsage: shapez.types.float, - aggregatedIdleTime: shapez.types.float, - }; - } - - constructor() { - super(); - this.lastTimestamp = 0; - this.computedUsage = 0; - this.aggregatedIdleTime = 0; - } -} - -class UsageStatisticsSystem extends shapez.GameSystemWithFilter { - constructor(root) { - // By specifying the list of components, `this.allEntities` will only - // contain entities which have *all* of the specified components - super(root, [UsageStatisticsComponent, shapez.ItemProcessorComponent]); - } - - update() { - const now = this.root.time.now(); - for (let i = 0; i < this.allEntities.length; ++i) { - const entity = this.allEntities[i]; - - const processorComp = entity.components.ItemProcessor; - const usageComp = entity.components.UsageStatistics; - - if (now - usageComp.lastTimestamp > MEASURE_INTERVAL_SECONDS) { - usageComp.computedUsage = shapez.clamp( - 1 - usageComp.aggregatedIdleTime / MEASURE_INTERVAL_SECONDS - ); - usageComp.aggregatedIdleTime = 0; - usageComp.lastTimestamp = now; - } - - if (processorComp.ongoingCharges.length === 0) { - usageComp.aggregatedIdleTime += this.root.dynamicTickrate.deltaSeconds; - } - } - } - - drawChunk(parameters, chunk) { - const contents = chunk.containedEntitiesByLayer.regular; - for (let i = 0; i < contents.length; ++i) { - const entity = contents[i]; - const usageComp = entity.components.UsageStatistics; - if (!usageComp) { - continue; - } - - const staticComp = entity.components.StaticMapEntity; - const context = parameters.context; - const center = staticComp.getTileSpaceBounds().getCenter().toWorldSpace(); - - // Culling for better performance - if (parameters.visibleRect.containsCircle(center.x, center.y, 40)) { - // Background badge - context.fillStyle = "rgba(250, 250, 250, 0.8)"; - context.beginRoundedRect(center.x - 10, center.y + 3, 20, 8, 2); - - context.fill(); - - // Text - const usage = usageComp.computedUsage * 100.0; - if (usage > 99.99) { - context.fillStyle = "green"; - } else if (usage > 70) { - context.fillStyle = "orange"; - } else { - context.fillStyle = "red"; - } - - context.textAlign = "center"; - context.font = "7px GameFont"; - context.fillText(Math.round(usage) + "%", center.x, center.y + 10); - } - } - } -} - -class Mod extends shapez.Mod { - init() { - // Register the component - this.modInterface.registerComponent(UsageStatisticsComponent); - - // Add our new component to all item processor buildings so we can see how many items it processed. - // You can also inspect the entity with F8 - const buildings = [ - shapez.MetaBalancerBuilding, - shapez.MetaCutterBuilding, - shapez.MetaRotaterBuilding, - shapez.MetaStackerBuilding, - shapez.MetaMixerBuilding, - shapez.MetaPainterBuilding, - ]; - - buildings.forEach(metaClass => { - this.modInterface.runAfterMethod(metaClass, "setupEntityComponents", function (entity) { - entity.addComponent(new UsageStatisticsComponent()); - }); - }); - - // Register our game system so we can update and draw stuff - this.modInterface.registerGameSystem({ - id: "demo_mod", - systemClass: UsageStatisticsSystem, - before: "belt", - drawHooks: ["staticAfter"], - }); - } -} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..c495152e --- /dev/null +++ b/package-lock.json @@ -0,0 +1,18492 @@ +{ + "name": "shapez", + "version": "1.6.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "shapez", + "version": "1.6.0", + "hasInstallScript": true, + "license": "GPL-3.0-or-later", + "dependencies": { + "@msgpack/msgpack": "^3.1.2", + "ajv": "^6.10.2", + "clipboard-copy": "^3.1.0", + "debounce-promise": "^3.1.2", + "howler": "^2.1.2" + }, + "devDependencies": { + "@electron/packager": "^18.3.6", + "@eslint/js": "^9.24.0", + "@tsconfig/node-lts": "^22.0.1", + "@tsconfig/strictest": "^2.0.5", + "@types/circular-dependency-plugin": "^5.0.5", + "@types/gulp": "^4.0.9", + "@types/gulp-htmlmin": "^1.3.32", + "@types/node": "^22.14.0", + "browser-sync": "^2.27.10", + "circular-dependency-plugin": "^5.2.2", + "css-mqpacker": "^7.0.0", + "cssnano": "^4.1.10", + "cssnano-preset-advanced": "^4.0.7", + "delete-empty": "^3.0.0", + "eslint": "^9.24.0", + "globals": "^15.0.0", + "gulp": "^4.0.2", + "gulp-audiosprite": "^1.1.0", + "gulp-cache": "^1.1.3", + "gulp-cached": "^1.1.1", + "gulp-clean": "^0.4.0", + "gulp-dart-sass": "^1.0.2", + "gulp-dom": "^1.0.0", + "gulp-fluent-ffmpeg": "^2.0.0", + "gulp-htmlmin": "^5.0.1", + "gulp-if": "^3.0.0", + "gulp-imagemin": "^7.1.0", + "gulp-plumber": "^1.2.1", + "gulp-postcss": "^8.0.0", + "gulp-rename": "^2.0.0", + "gulp-webserver": "^0.9.1", + "gulp-yaml": "^2.0.4", + "imagemin-gifsicle": "^7.0.0", + "imagemin-jpegtran": "^7.0.0", + "imagemin-mozjpeg": "^8.0.0", + "imagemin-pngquant": "^8.0.0", + "postcss-assets": "^5.0.0", + "postcss-critical-split": "^2.5.3", + "prettier": "^3.3.2", + "terser-webpack-plugin": "^5.3.14", + "ts-loader": "^9.5.2", + "typescript": "^5.8.2", + "typescript-eslint": "^8.29.1", + "webpack": "^5.99.9", + "webpack-deadcode-plugin": "^0.1.17", + "webpack-stream": "^7.0.0", + "webpack-strip-block": "^0.2.0", + "yaml": "^1.10.0" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.6.tgz", + "integrity": "sha512-Q+8MqP7TiHMWzSfwiJwXCjyf4GYA4Dgw3emg/7xmwsdLJOZUp+nMqcOwOzzYheuM1rhDu8FSj2l0aoMygEuXuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "regenerator-runtime": "^0.13.11" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@electron/asar": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/@electron/asar/-/asar-3.4.1.tgz", + "integrity": "sha512-i4/rNPRS84t0vSRa2HorerGRXWyF4vThfHesw0dmcWHp+cspK743UanA0suA5Q5y8kzY2y6YKrvbIUn69BCAiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^5.0.0", + "glob": "^7.1.6", + "minimatch": "^3.0.4" + }, + "bin": { + "asar": "bin/asar.js" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/@electron/asar/node_modules/commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/@electron/notarize": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-2.5.0.tgz", + "integrity": "sha512-jNT8nwH1f9X5GEITXaQ8IF/KdskvIkOFfB2CvwumsveVidzpSc+mvhhTMdAGSYF3O+Nq49lJ7y+ssODRXu06+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "fs-extra": "^9.0.1", + "promise-retry": "^2.0.1" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@electron/osx-sign": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@electron/osx-sign/-/osx-sign-1.3.3.tgz", + "integrity": "sha512-KZ8mhXvWv2rIEgMbWZ4y33bDHyUKMXnx4M0sTyPNK/vcB81ImdeY9Ggdqy0SWbMDgmbqyQ+phgejh6V3R2QuSg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "compare-version": "^0.1.2", + "debug": "^4.3.4", + "fs-extra": "^10.0.0", + "isbinaryfile": "^4.0.8", + "minimist": "^1.2.6", + "plist": "^3.0.5" + }, + "bin": { + "electron-osx-flat": "bin/electron-osx-flat.js", + "electron-osx-sign": "bin/electron-osx-sign.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@electron/osx-sign/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@electron/osx-sign/node_modules/isbinaryfile": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", + "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" + } + }, + "node_modules/@electron/osx-sign/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@electron/osx-sign/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@electron/packager": { + "version": "18.3.6", + "resolved": "https://registry.npmjs.org/@electron/packager/-/packager-18.3.6.tgz", + "integrity": "sha512-1eXHB5t+SQKvUiDpWGpvr90ZSSbXj+isrh3YbjCTjKT4bE4SQrKSBfukEAaBvp67+GXHFtCHjQgN9qSTFIge+Q==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@electron/asar": "^3.2.13", + "@electron/get": "^3.0.0", + "@electron/notarize": "^2.1.0", + "@electron/osx-sign": "^1.0.5", + "@electron/universal": "^2.0.1", + "@electron/windows-sign": "^1.0.0", + "debug": "^4.0.1", + "extract-zip": "^2.0.0", + "filenamify": "^4.1.0", + "fs-extra": "^11.1.0", + "galactus": "^1.0.0", + "get-package-info": "^1.0.0", + "junk": "^3.1.0", + "parse-author": "^2.0.0", + "plist": "^3.0.0", + "resedit": "^2.0.0", + "resolve": "^1.1.6", + "semver": "^7.1.3", + "yargs-parser": "^21.1.1" + }, + "bin": { + "electron-packager": "bin/electron-packager.js" + }, + "engines": { + "node": ">= 16.13.0" + }, + "funding": { + "url": "https://github.com/electron/packager?sponsor=1" + } + }, + "node_modules/@electron/packager/node_modules/@electron/get": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-3.1.0.tgz", + "integrity": "sha512-F+nKc0xW+kVbBRhFzaMgPy3KwmuNTYX1fx6+FxxoSnNgwYX6LD7AKBTWkU0MQ6IBoe7dz069CNkR673sPAgkCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "env-paths": "^2.2.0", + "fs-extra": "^8.1.0", + "got": "^11.8.5", + "progress": "^2.0.3", + "semver": "^6.2.0", + "sumchecker": "^3.0.1" + }, + "engines": { + "node": ">=14" + }, + "optionalDependencies": { + "global-agent": "^3.0.0" + } + }, + "node_modules/@electron/packager/node_modules/@electron/get/node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/@electron/packager/node_modules/@electron/get/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@electron/packager/node_modules/@electron/universal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-2.0.3.tgz", + "integrity": "sha512-Wn9sPYIVFRFl5HmwMJkARCCf7rqK/EurkfQ/rJZ14mHP3iYTjZSIOSVonEAnhWeAXwtw7zOekGRlc6yTtZ0t+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@electron/asar": "^3.3.1", + "@malept/cross-spawn-promise": "^2.0.0", + "debug": "^4.3.1", + "dir-compare": "^4.2.0", + "fs-extra": "^11.1.1", + "minimatch": "^9.0.3", + "plist": "^3.1.0" + }, + "engines": { + "node": ">=16.4" + } + }, + "node_modules/@electron/packager/node_modules/@electron/universal/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, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@electron/packager/node_modules/@malept/cross-spawn-promise": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-2.0.0.tgz", + "integrity": "sha512-1DpKU0Z5ThltBwjNySMC14g0CkbyhCaz9FkhxqNsZI6uAPJXFS8cMXlBKo26FJ8ZuW6S9GCMcR9IO5k2X5/9Fg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/malept" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund" + } + ], + "license": "Apache-2.0", + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/@electron/packager/node_modules/@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@electron/packager/node_modules/@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "dev": true, + "license": "MIT", + "dependencies": { + "defer-to-connect": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@electron/packager/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, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@electron/packager/node_modules/cacheable-request": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", + "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@electron/packager/node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@electron/packager/node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@electron/packager/node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/@electron/packager/node_modules/dir-compare": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/dir-compare/-/dir-compare-4.2.0.tgz", + "integrity": "sha512-2xMCmOoMrdQIPHdsTawECdNPwlVFB9zGcz3kuhmBO6U3oU+UQjsue0i8ayLKpgBcm+hcXPMVSGUN9d+pvJ6+VQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimatch": "^3.0.5", + "p-limit": "^3.1.0 " + } + }, + "node_modules/@electron/packager/node_modules/filenamify": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-4.3.0.tgz", + "integrity": "sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "filename-reserved-regex": "^2.0.0", + "strip-outer": "^1.0.1", + "trim-repeated": "^1.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@electron/packager/node_modules/flora-colossus": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/flora-colossus/-/flora-colossus-2.0.0.tgz", + "integrity": "sha512-dz4HxH6pOvbUzZpZ/yXhafjbR2I8cenK5xL0KtBFb7U2ADsR+OwXifnxZjij/pZWF775uSCMzWVd+jDik2H2IA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4", + "fs-extra": "^10.1.0" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@electron/packager/node_modules/flora-colossus/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@electron/packager/node_modules/flora-colossus/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@electron/packager/node_modules/flora-colossus/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@electron/packager/node_modules/fs-extra": { + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz", + "integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/@electron/packager/node_modules/fs-extra/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@electron/packager/node_modules/fs-extra/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@electron/packager/node_modules/galactus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/galactus/-/galactus-1.0.0.tgz", + "integrity": "sha512-R1fam6D4CyKQGNlvJne4dkNF+PvUUl7TAJInvTGa9fti9qAv95quQz29GXapA4d8Ec266mJJxFVh82M4GIIGDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4", + "flora-colossus": "^2.0.0", + "fs-extra": "^10.1.0" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@electron/packager/node_modules/galactus/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@electron/packager/node_modules/galactus/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@electron/packager/node_modules/galactus/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@electron/packager/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@electron/packager/node_modules/got": { + "version": "11.8.6", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", + "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=10.19.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/@electron/packager/node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@electron/packager/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@electron/packager/node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@electron/packager/node_modules/p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@electron/packager/node_modules/responselike": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lowercase-keys": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@electron/packager/node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@electron/packager/node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@electron/packager/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@electron/packager/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/@electron/windows-sign": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@electron/windows-sign/-/windows-sign-1.2.2.tgz", + "integrity": "sha512-dfZeox66AvdPtb2lD8OsIIQh12Tp0GNCRUDfBHIKGpbmopZto2/A8nSpYYLoedPIHpqkeblZ/k8OV0Gy7PYuyQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "cross-dirname": "^0.1.0", + "debug": "^4.3.4", + "fs-extra": "^11.1.1", + "minimist": "^1.2.8", + "postject": "^1.0.0-alpha.6" + }, + "bin": { + "electron-windows-sign": "bin/electron-windows-sign.js" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/@electron/windows-sign/node_modules/fs-extra": { + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz", + "integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/@electron/windows-sign/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@electron/windows-sign/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@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==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "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, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.0.tgz", + "integrity": "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==", + "dev": true, + "license": "Apache-2.0", + "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-helpers": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.1.tgz", + "integrity": "sha512-RI17tsD2frtDu/3dmI7QRrD4bedNKPM08ziRYaC5AhkGrzIAJelm9kJU1TznK+apx6V+cqRz8tfpEeG3oIyjxw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz", + "integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "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" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/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, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@eslint/eslintrc/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, + "license": "Python-2.0" + }, + "node_modules/@eslint/eslintrc/node_modules/eslint-visitor-keys": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", + "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/espree": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.0.1.tgz", + "integrity": "sha512-MWkrWZbJsL2UwnjxTX3gG8FneachS/Mwg7tdGXce011sJd5b0JG54vat5KHnfSBODZ3Wvzd2WnjxyzsRoVv+ww==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.11.3", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "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, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@eslint/eslintrc/node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@eslint/js": { + "version": "9.24.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.24.0.tgz", + "integrity": "sha512-uIY/y3z0uvOGX8cp1C2fiC4+ZmBhp6yZWkojtHL1YEMnRt1Y63HB9TM17proGEmeG7HeUY+UP36F0aknKYTpYA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "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, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz", + "integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.13.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz", + "integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "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, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "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, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", + "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.11.tgz", + "integrity": "sha512-C512c1ytBTio4MrpWKlJpyFHT6+qfFL8SZ58zBzJ1OOzUEjHeF1BtjY2fH7n4x/g2OV/KiiMLAivOp1DXmiMMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.9.tgz", + "integrity": "sha512-amBU75CKOOkcQLfyM6J+DnWwz41yTsWI7o8MQ003LwUIWb4NYX/evAblTx1oBBYJySqL/zHPxHXDw5ewpQaUFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.3.tgz", + "integrity": "sha512-AiR5uKpFxP3PjO4R19kQGIMwxyRyPuXmKEEy301V1C0+1rVjS94EZQXf1QKZYN8Q0YM+estSPhmx5JwNftv6nw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.28", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.28.tgz", + "integrity": "sha512-KNNHHwW3EIp4EDYOvYFGyIFfx36R2dNJYH4knnZlF8T5jdbD5Wx8xmSaQ2gP9URkJ04LGEtlcCtwArKcmFcwKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@msgpack/msgpack": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@msgpack/msgpack/-/msgpack-3.1.2.tgz", + "integrity": "sha512-JEW4DEtBzfe8HvUYecLU9e6+XJnKDlUAIve8FvPzF3Kzs6Xo/KuZkZJsDH0wJXl/qEZbeeE7edxDNY3kMs39hQ==", + "license": "ISC", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node-lts": { + "version": "22.0.1", + "resolved": "https://registry.npmjs.org/@tsconfig/node-lts/-/node-lts-22.0.1.tgz", + "integrity": "sha512-BwlbLiYurZKrj+Pa6etSE1jXmr3VEDdgJto1jEYKcpBVwZZSWVkCPyFEFYbHdIOaFMlSTtV206DYPlT109aqug==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/strictest": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@tsconfig/strictest/-/strictest-2.0.5.tgz", + "integrity": "sha512-ec4tjL2Rr0pkZ5hww65c+EEPYwxOi4Ryv+0MtjeaSQRJyq322Q27eOQiFbuNgw2hpL4hB1/W/HBGk3VKS43osg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/cacheable-request": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", + "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-cache-semantics": "*", + "@types/keyv": "^3.1.4", + "@types/node": "*", + "@types/responselike": "^1.0.0" + } + }, + "node_modules/@types/circular-dependency-plugin": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@types/circular-dependency-plugin/-/circular-dependency-plugin-5.0.5.tgz", + "integrity": "sha512-JU1sYQWNbUluWHseLUfokakx18+BXRA9Bxji56hdY5NW0nvrJSJd4SNAl0Btpm5ima9BnUkoGEcW/2PH1QuWQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "webpack": "^5.1.0" + } + }, + "node_modules/@types/circular-dependency-plugin/node_modules/@types/node": { + "version": "18.11.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.10.tgz", + "integrity": "sha512-juG3RWMBOqcOuXC643OAdSA525V44cVgGV6dUDuiFtss+8Fk5x1hI93Rsld43VeJVIeqlP9I7Fn9/qaVqoEAuQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/clean-css": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/@types/clean-css/-/clean-css-4.2.6.tgz", + "integrity": "sha512-Ze1tf+LnGPmG6hBFMi0B4TEB0mhF7EiMM5oyjLDNPE9hxrPU0W+5+bHvO+eFPA+bt0iC1zkQMoU/iGdRVjcRbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "source-map": "^0.6.0" + } + }, + "node_modules/@types/clean-css/node_modules/@types/node": { + "version": "18.11.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.10.tgz", + "integrity": "sha512-juG3RWMBOqcOuXC643OAdSA525V44cVgGV6dUDuiFtss+8Fk5x1hI93Rsld43VeJVIeqlP9I7Fn9/qaVqoEAuQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/cors": { + "version": "2.8.12", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz", + "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/eslint": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "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, + "license": "MIT" + }, + "node_modules/@types/expect": { + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/@types/expect/-/expect-1.20.4.tgz", + "integrity": "sha512-Q5Vn3yjTDyCMV50TB6VRIbQNxSE4OmZR86VSbGaNpfUolm0iePBB4KdEEHmxoY5sT2+2DIvXW0rvMDP2nHZ4Mg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "node_modules/@types/glob-stream": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/@types/glob-stream/-/glob-stream-6.1.1.tgz", + "integrity": "sha512-AGOUTsTdbPkRS0qDeyeS+6KypmfVpbT5j23SN8UPG63qjKXNKjXn6V9wZUr8Fin0m9l8oGYaPK8b2WUMF8xI1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/glob": "*", + "@types/node": "*" + } + }, + "node_modules/@types/glob-stream/node_modules/@types/glob": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-8.0.0.tgz", + "integrity": "sha512-l6NQsDDyQUVeoTynNpC9uRvCUint/gSUXQA2euwmTuWGvPY5LSDUu6tkCtJB2SvGQlJQzLaKqcGZP4//7EDveA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "node_modules/@types/glob-stream/node_modules/@types/node": { + "version": "18.11.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.10.tgz", + "integrity": "sha512-juG3RWMBOqcOuXC643OAdSA525V44cVgGV6dUDuiFtss+8Fk5x1hI93Rsld43VeJVIeqlP9I7Fn9/qaVqoEAuQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/glob/node_modules/@types/node": { + "version": "18.11.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.10.tgz", + "integrity": "sha512-juG3RWMBOqcOuXC643OAdSA525V44cVgGV6dUDuiFtss+8Fk5x1hI93Rsld43VeJVIeqlP9I7Fn9/qaVqoEAuQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/gulp": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/@types/gulp/-/gulp-4.0.10.tgz", + "integrity": "sha512-spgZHJFqiEJGwqGlf7T/k4nkBpBcLgP7T0EfN6G2vvnhUfvd4uO1h8RwpXOE8x/54DVYUs1XCAtBHkX/R3axAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/undertaker": ">=1.2.6", + "@types/vinyl-fs": "*", + "chokidar": "^3.3.1" + } + }, + "node_modules/@types/gulp-htmlmin": { + "version": "1.3.32", + "resolved": "https://registry.npmjs.org/@types/gulp-htmlmin/-/gulp-htmlmin-1.3.32.tgz", + "integrity": "sha512-G/xcBVxm0haTePU+Z8nGs+pL7tl489v2gKzm4ARrv7o9taoaXRgRbp58yDz/XkuqFUHeU5Z0/YiR9RsN+M6LSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/html-minifier": "*", + "@types/node": "*" + } + }, + "node_modules/@types/gulp-htmlmin/node_modules/@types/node": { + "version": "18.11.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.10.tgz", + "integrity": "sha512-juG3RWMBOqcOuXC643OAdSA525V44cVgGV6dUDuiFtss+8Fk5x1hI93Rsld43VeJVIeqlP9I7Fn9/qaVqoEAuQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/html-minifier": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/html-minifier/-/html-minifier-4.0.2.tgz", + "integrity": "sha512-4IkmkXJP/25R2fZsCHDX2abztXuQRzUAZq39PfCMz2loLFj8vS9y7aF6vDl58koXSTpsF+eL4Lc5Y4Aww/GCTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/clean-css": "*", + "@types/relateurl": "*", + "@types/uglify-js": "*" + } + }, + "node_modules/@types/http-cache-semantics": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", + "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "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" + }, + "node_modules/@types/keyv": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", + "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.14.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.0.tgz", + "integrity": "sha512-Kmpl+z84ILoG+3T/zQFyAJsU6EPTmOCj8/2+83fSN6djd6I4o7uOuGIH6vq3PrjY5BGitSbFuMN18j3iknubbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/q": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.5.tgz", + "integrity": "sha512-L28j2FcJfSZOnL1WBjDYp2vUHCeIFlyYI/53EwD/rKUBQ7MtUUfbQWiyKJGpcnv4/WgrhWsFKrcPstcAt/J0tQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/relateurl": { + "version": "0.2.29", + "resolved": "https://registry.npmjs.org/@types/relateurl/-/relateurl-0.2.29.tgz", + "integrity": "sha512-QSvevZ+IRww2ldtfv1QskYsqVVVwCKQf1XbwtcyyoRvLIQzfyPhj/C+3+PKzSDRdiyejaiLgnq//XTkleorpLg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/responselike": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", + "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/uglify-js": { + "version": "3.17.1", + "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.17.1.tgz", + "integrity": "sha512-GkewRA4i5oXacU/n4MA9+bLgt5/L3F1mKrYvFGm7r2ouLXhRKjuWwo9XHNnbx6WF3vlGW21S3fCvgqxvxXXc5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "source-map": "^0.6.1" + } + }, + "node_modules/@types/undertaker": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@types/undertaker/-/undertaker-1.2.8.tgz", + "integrity": "sha512-gW3PRqCHYpo45XFQHJBhch7L6hytPsIe0QeLujlnFsjHPnXLhJcPdN6a9368d7aIQgH2I/dUTPFBlGeSNA3qOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/undertaker-registry": "*", + "async-done": "~1.3.2" + } + }, + "node_modules/@types/undertaker-registry": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/undertaker-registry/-/undertaker-registry-1.0.1.tgz", + "integrity": "sha512-Z4TYuEKn9+RbNVk1Ll2SS4x1JeLHecolIbM/a8gveaHsW0Hr+RQMraZACwTO2VD7JvepgA6UO1A1VrbktQrIbQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/undertaker/node_modules/@types/node": { + "version": "18.11.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.10.tgz", + "integrity": "sha512-juG3RWMBOqcOuXC643OAdSA525V44cVgGV6dUDuiFtss+8Fk5x1hI93Rsld43VeJVIeqlP9I7Fn9/qaVqoEAuQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/vinyl": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/vinyl/-/vinyl-2.0.7.tgz", + "integrity": "sha512-4UqPv+2567NhMQuMLdKAyK4yzrfCqwaTt6bLhHEs8PFcxbHILsrxaY63n4wgE/BRLDWDQeI+WcTmkXKExh9hQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/expect": "^1.20.4", + "@types/node": "*" + } + }, + "node_modules/@types/vinyl-fs": { + "version": "2.4.12", + "resolved": "https://registry.npmjs.org/@types/vinyl-fs/-/vinyl-fs-2.4.12.tgz", + "integrity": "sha512-LgBpYIWuuGsihnlF+OOWWz4ovwCYlT03gd3DuLwex50cYZLmX3yrW+sFF9ndtmh7zcZpS6Ri47PrIu+fV+sbXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/glob-stream": "*", + "@types/node": "*", + "@types/vinyl": "*" + } + }, + "node_modules/@types/vinyl-fs/node_modules/@types/node": { + "version": "18.11.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.10.tgz", + "integrity": "sha512-juG3RWMBOqcOuXC643OAdSA525V44cVgGV6dUDuiFtss+8Fk5x1hI93Rsld43VeJVIeqlP9I7Fn9/qaVqoEAuQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/vinyl/node_modules/@types/node": { + "version": "18.11.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.10.tgz", + "integrity": "sha512-juG3RWMBOqcOuXC643OAdSA525V44cVgGV6dUDuiFtss+8Fk5x1hI93Rsld43VeJVIeqlP9I7Fn9/qaVqoEAuQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/yauzl/node_modules/@types/node": { + "version": "18.11.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.10.tgz", + "integrity": "sha512-juG3RWMBOqcOuXC643OAdSA525V44cVgGV6dUDuiFtss+8Fk5x1hI93Rsld43VeJVIeqlP9I7Fn9/qaVqoEAuQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.29.1.tgz", + "integrity": "sha512-ba0rr4Wfvg23vERs3eB+P3lfj2E+2g3lhWcCVukUuhtcdUx5lSIFZlGFEBHKr+3zizDa/TvZTptdNHVZWAkSBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.29.1", + "@typescript-eslint/type-utils": "8.29.1", + "@typescript-eslint/utils": "8.29.1", + "@typescript-eslint/visitor-keys": "8.29.1", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.0.1" + }, + "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.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.29.1.tgz", + "integrity": "sha512-zczrHVEqEaTwh12gWBIJWj8nx+ayDcCJs06yoNMY0kwjMWDM6+kppljY+BxWI06d2Ja+h4+WdufDcwMnnMEWmg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.29.1", + "@typescript-eslint/types": "8.29.1", + "@typescript-eslint/typescript-estree": "8.29.1", + "@typescript-eslint/visitor-keys": "8.29.1", + "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 <5.9.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.29.1.tgz", + "integrity": "sha512-2nggXGX5F3YrsGN08pw4XpMLO1Rgtnn4AzTegC2MDesv6q3QaTU5yU7IbS1tf1IwCR0Hv/1EFygLn9ms6LIpDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.29.1", + "@typescript-eslint/visitor-keys": "8.29.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/type-utils": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.29.1.tgz", + "integrity": "sha512-DkDUSDwZVCYN71xA4wzySqqcZsHKic53A4BLqmrWFFpOpNSoxX233lwGu/2135ymTCR04PoKiEEEvN1gFYg4Tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "8.29.1", + "@typescript-eslint/utils": "8.29.1", + "debug": "^4.3.4", + "ts-api-utils": "^2.0.1" + }, + "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 <5.9.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.29.1.tgz", + "integrity": "sha512-VT7T1PuJF1hpYC3AGm2rCgJBjHL3nc+A/bhOp9sGMKfi5v0WufsX/sHCFBfNTx2F+zA6qBc/PD0/kLRLjdt8mQ==", + "dev": true, + "license": "MIT", + "engines": { + "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": "8.29.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.29.1.tgz", + "integrity": "sha512-l1enRoSaUkQxOQnbi0KPUtqeZkSiFlqrx9/3ns2rEDhGKfTa+88RmXqedC1zmVTOWrLc2e6DEJrTA51C9iLH5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.29.1", + "@typescript-eslint/visitor-keys": "8.29.1", + "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.0.1" + }, + "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 <5.9.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/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, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "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.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.29.1.tgz", + "integrity": "sha512-QAkFEbytSaB8wnmB+DflhUPz6CLbFWE2SnSCrRMEa+KnXIzDYbpsn++1HGvnfAsUY44doDXmvRkO5shlM/3UfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.29.1", + "@typescript-eslint/types": "8.29.1", + "@typescript-eslint/typescript-estree": "8.29.1" + }, + "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 <5.9.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.29.1.tgz", + "integrity": "sha512-RGLh5CRaUEf02viP5c1Vh1cMGffQscyHe7HPAzGpfmfflFg1wUz2rYxd+OZqwpeypYvZ8UxSxuIpF++fmOzEcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.29.1", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xmldom/xmldom": { + "version": "0.8.10", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", + "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-globals": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.4.tgz", + "integrity": "sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^6.0.1", + "acorn-walk": "^6.0.1" + } + }, + "node_modules/acorn-globals/node_modules/acorn": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.2.0.tgz", + "integrity": "sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.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==", + "license": "MIT", + "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/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/alphanum-sort": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", + "integrity": "sha512-0FcBfdcmaumGPQ0qPn7Q5qTgz/ooXgIyp1rf8ik5bGX8mpE2YHjC0P/eyQvxu1GURYQgq9ozf2mteQ5ZD9YiyQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/ansi-colors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-wrap": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-cyan": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-cyan/-/ansi-cyan-0.1.1.tgz", + "integrity": "sha512-eCjan3AVo/SxZ0/MyIYRtkpxIu/H3xZN7URr1vXVrISxeyz8fUFz0FJziamK4sS8I+t35y4rHg1b2PklyBe/7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-gray": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", + "integrity": "sha512-HrgGIZUl8h2EHuZaU9hTR/cU5nhKxpVE1V6kdGsQ8e4zirElJ5fvtfc8N7Q1oq1aatO275i8pUFUCpNWCAnVWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-red": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-red/-/ansi-red-0.1.1.tgz", + "integrity": "sha512-ewaIr5y+9CUTGFwZfpECUbFlGcC0GCw1oqR9RI6h1gQCd9Aj2GxSckCnPsVJnmfMZbwFYE+leZGASgkWl06Jow==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-regex": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz", + "integrity": "sha512-sGwIGMjhYdW26/IhwK2gkWWI8DRCVO6uj3hYgHT+zD+QL1pa37tM3ujhyfcJIYSbsxp7Gxhy7zrRW/1AHm4BmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "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, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ansi-styles/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/ansi-wrap": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", + "integrity": "sha512-ZyznvL8k/FZeQHr2T6LzcJ/+vBApDnMNZvfVFy3At0knswWd6rJ3/0Hhmpu8oqa6C92npmozs890sX9Dl6q+Qw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "license": "ISC", + "dependencies": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "node_modules/anymatch/node_modules/normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "remove-trailing-separator": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/append-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/append-buffer/-/append-buffer-1.0.2.tgz", + "integrity": "sha512-WLbYiXzD3y/ATLZFufV/rZvWdZOs+Z/+5v1rBZ463Jn398pa6kcde27cvozYnBoxXblGZTFfoPpsaEw0orU5BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-equal": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arch": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", + "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/archive-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/archive-type/-/archive-type-4.0.0.tgz", + "integrity": "sha512-zV4Ky0v1F8dBrdYElwTvQhweQ0P7Kwc1aluqJsYtOBP01jXcWCyW2IEfI1YiqsG+Iy7ZR+o5LF1N+PGECBxHWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "file-type": "^4.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/archive-type/node_modules/file-type": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-4.4.0.tgz", + "integrity": "sha512-f2UbFQEk7LXgWpi5ntcO86OeA/cC80fuDDDaX/fZ2ZGel+AF7leRQqBBW1eJNiiQkrZlAoM6P+VYP5P6bOlDEQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==", + "dev": true, + "license": "MIT" + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-filter": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/arr-filter/-/arr-filter-1.1.2.tgz", + "integrity": "sha512-A2BETWCqhsecSvCkWAeVBFLH6sXEUGASuzkpjL3GR1SlL/PWL6M3J8EAAld2Uubmh39tvkJTqC9LeLHCUKmFXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "make-iterator": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/arr-map/-/arr-map-2.0.2.tgz", + "integrity": "sha512-tVqVTHt+Q5Xb09qRkbu+DidW1yYzz5izWS2Xm2yFm7qJnmUfz4HPzNxbHkdRJbz2lrqI7S+z17xNYdFcBBO8Hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "make-iterator": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-each": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", + "integrity": "sha512-zHjL5SZa68hkKHBFBK6DJCTtr9sfTCPCaph/L7tMSLcTFgy+zX7E+6q5UArbtOtMBCtxdICpfTCspRse+ywyXA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", + "integrity": "sha512-H3LU5RLiSsGXPhN+Nipar0iR0IofH+8r89G2y1tBKxQ/agagKyAjhkAFDRBfodP2caPrNKHpAWNIM/c9yeL7uA==", + "dev": true, + "license": "MIT" + }, + "node_modules/array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-initial": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/array-initial/-/array-initial-1.1.0.tgz", + "integrity": "sha512-BC4Yl89vneCYfpLrs5JU2aAu9/a+xWbeKhvISg9PT7eWFB9UlRvI+rKEtk6mgxWr3dSkk9gQ8hCrdqt06NXPdw==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-slice": "^1.0.0", + "is-number": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-initial/node_modules/is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-last": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/array-last/-/array-last-1.3.0.tgz", + "integrity": "sha512-eOCut5rXlI6aCOS7Z7kCplKRKyiFQ6dHFBem4PwlwKeNFk2/XxTrhRh5T9PyaEWGy/NHTZWbY+nsZlNFJu9rYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-last/node_modules/is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-slice": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", + "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-sort": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-sort/-/array-sort-1.0.0.tgz", + "integrity": "sha512-ihLeJkonmdiAsD7vpgN3CRcx2J2S0TiYW+IS/5zHBI7mKUq3ySvBdzzBfD236ubDBQFiiyG3SWCPc+msQ9KoYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "default-compare": "^1.0.0", + "get-value": "^2.0.6", + "kind-of": "^5.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-sort/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "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, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array.prototype.reduce": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.5.tgz", + "integrity": "sha512-kDdugMl7id9COE8R7MHF5jWk7Dqt/fs4Pv+JXoICnYwqpjjjbUurz6w5fT5IG6brLdJhv6/VoHB0H7oyIBXd+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-array-method-boxes-properly": "^1.0.0", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/assets": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/assets/-/assets-3.0.1.tgz", + "integrity": "sha512-fTyLNf/9V24y5zO83f4DAEuvaKj7MWBixbnqdZneAhsv1r21yQ/6ogZfvXHmphJAHsz4DhuOwHeJKVbGqqvk0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "async": "^2.5.0", + "bluebird": "^3.4.6", + "calipers": "^2.0.0", + "calipers-gif": "^2.0.0", + "calipers-jpeg": "^2.0.0", + "calipers-png": "^2.0.0", + "calipers-svg": "^2.0.0", + "calipers-webp": "^2.0.0", + "glob": "^7.0.6", + "lodash": "^4.15.0", + "mime": "^2.4.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/assets/node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash": "^4.17.14" + } + }, + "node_modules/async-done": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/async-done/-/async-done-1.3.2.tgz", + "integrity": "sha512-uYkTP8dw2og1tu1nmza1n1CMW0qb8gWWlwqMmLb7MhBVs4BXrFziT6HXUd+/RlRA/i4H9AkofYloUbs1fwMqlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.2", + "process-nextick-args": "^2.0.0", + "stream-exhaust": "^1.0.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/async-each": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", + "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/async-each-series": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/async-each-series/-/async-each-series-0.1.1.tgz", + "integrity": "sha512-p4jj6Fws4Iy2m0iCmI2am2ZNZCgbdgE+P8F/8csmn2vx7ixXrO2zGcuNsD46X5uZSVecmkEy/M06X2vG8KD6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/async-settle": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-settle/-/async-settle-1.0.0.tgz", + "integrity": "sha512-VPXfB4Vk49z1LHHodrEQ6Xf7W4gg1w0dAPROHngx7qgDjqmIQ+fXmwgGXTW/ITLai0YLSvWepJOP9EVpMnEAcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-done": "^1.2.2" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true, + "license": "(MIT OR Apache-2.0)", + "bin": { + "atob": "bin/atob.js" + }, + "engines": { + "node": ">= 4.5.0" + } + }, + "node_modules/audiosprite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/audiosprite/-/audiosprite-0.7.2.tgz", + "integrity": "sha512-9Z6UwUuv4To5nUQNRIw5/Q3qA7HYm0ANzoW5EDGPEsU2oIRVgmIlLlm9YZfpPKoeUxt54vMStl2/762189VmJw==", + "dev": true, + "dependencies": { + "async": "~0.9.0", + "glob": "^6.0.4", + "mkdirp": "^0.5.0", + "optimist": "~0.6.1", + "underscore": "~1.8.3", + "winston": "~1.0.0" + }, + "bin": { + "audiosprite": "cli.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/audiosprite/node_modules/async": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", + "integrity": "sha512-l6ToIJIotphWahxxHyzK9bnLR6kM4jJIIgLShZeqLY7iboHoGkdgFl7W2/Ivi4SkMJYGKqW8vSuk0uKUj6qsSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/audiosprite/node_modules/glob": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", + "integrity": "sha512-MKZeRNyYZAVVVG1oZeLaWie1uweH40m9AZwIwxyPbTSX4hHrVYSzLg0Ro5Z5R7XKkIX+Cc6oD1rqeDJnwsB8/A==", + "dev": true, + "license": "ISC", + "dependencies": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/author-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/author-regex/-/author-regex-1.0.0.tgz", + "integrity": "sha512-KbWgR8wOYRAPekEmMXrYYdc7BRyhn2Ftk7KWfMUnQ43hFdojWEFRxhhRUm3/OFEdPa1r0KAvTTg9YQK57xTe0g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/autoprefixer": { + "version": "9.8.8", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.8.tgz", + "integrity": "sha512-eM9d/swFopRt5gdJ7jrpCwgvEMIayITpojhkkSMRsFHYuH5bkSQ4p/9qTEHtmNudUZh22Tehu7I6CxAW0IXTKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.12.0", + "caniuse-lite": "^1.0.30001109", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "picocolors": "^0.2.1", + "postcss": "^7.0.32", + "postcss-value-parser": "^4.1.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + } + }, + "node_modules/autoprefixer/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, + "node_modules/aws4": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", + "dev": true, + "license": "MIT" + }, + "node_modules/axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.14.0" + } + }, + "node_modules/bach": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/bach/-/bach-1.2.0.tgz", + "integrity": "sha512-bZOOfCb3gXBXbTFXq3OZtGR88LwGeJvzu6szttaIzymOTS4ZttBNOWSv7aLZja2EMycKtRYV0Oa8SNKH/zkxvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "arr-filter": "^1.1.1", + "arr-flatten": "^1.0.1", + "arr-map": "^2.0.0", + "array-each": "^1.0.0", + "array-initial": "^1.0.0", + "array-last": "^1.1.1", + "async-done": "^1.2.2", + "async-settle": "^1.0.0", + "now-and-later": "^2.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "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, + "license": "MIT" + }, + "node_modules/base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, + "node_modules/batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", + "dev": true, + "license": "MIT" + }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/bin-build": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bin-build/-/bin-build-3.0.0.tgz", + "integrity": "sha512-jcUOof71/TNAI2uM5uoUaDq2ePcVBQ3R/qhxAz1rX7UfvduAL/RXD3jXzvn8cVcDJdGVkiR1shal3OH0ImpuhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "decompress": "^4.0.0", + "download": "^6.2.2", + "execa": "^0.7.0", + "p-map-series": "^1.0.0", + "tempfile": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/bin-build/node_modules/cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "node_modules/bin-build/node_modules/execa": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha512-RztN09XglpYI7aBBrJCPW95jEH7YF1UEPOoX9yDhUTPdp7mK+CQvnLTuD10BNXZ3byLTu2uehZ8EcKT/4CGiFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/bin-build/node_modules/lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "license": "ISC", + "dependencies": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "node_modules/bin-build/node_modules/yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", + "dev": true, + "license": "ISC" + }, + "node_modules/bin-check": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bin-check/-/bin-check-4.1.0.tgz", + "integrity": "sha512-b6weQyEUKsDGFlACWSIOfveEnImkJyK/FGW6FAG42loyoquvjdtOIqO6yBFzHyqyVVhNgNkQxxx09SFLK28YnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^0.7.0", + "executable": "^4.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/bin-check/node_modules/cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "node_modules/bin-check/node_modules/execa": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha512-RztN09XglpYI7aBBrJCPW95jEH7YF1UEPOoX9yDhUTPdp7mK+CQvnLTuD10BNXZ3byLTu2uehZ8EcKT/4CGiFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/bin-check/node_modules/lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "license": "ISC", + "dependencies": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "node_modules/bin-check/node_modules/yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", + "dev": true, + "license": "ISC" + }, + "node_modules/bin-version": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bin-version/-/bin-version-3.1.0.tgz", + "integrity": "sha512-Mkfm4iE1VFt4xd4vH+gx+0/71esbfus2LsnCGe8Pi4mndSPyT+NGES/Eg99jx8/lUGWfu3z2yuB/bt5UB+iVbQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^1.0.0", + "find-versions": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/bin-version-check": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/bin-version-check/-/bin-version-check-4.0.0.tgz", + "integrity": "sha512-sR631OrhC+1f8Cvs8WyVWOA33Y8tgwjETNPyyD/myRBXLkfS/vl74FmH/lFcRl9KY3zwGh7jFhvyk9vV3/3ilQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bin-version": "^3.0.0", + "semver": "^5.6.0", + "semver-truncate": "^1.1.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/bin-version-check/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/bin-wrapper": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bin-wrapper/-/bin-wrapper-4.1.0.tgz", + "integrity": "sha512-hfRmo7hWIXPkbpi0ZltboCMVrU+0ClXR/JgbCKKjlDjQf6igXa7OwdqNcFWQZPZTgiY7ZpzE3+LjjkLiTN2T7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "bin-check": "^4.1.0", + "bin-version-check": "^4.0.0", + "download": "^7.1.0", + "import-lazy": "^3.1.0", + "os-filter-obj": "^2.0.0", + "pify": "^4.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/bin-wrapper/node_modules/@sindresorhus/is": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.7.0.tgz", + "integrity": "sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/bin-wrapper/node_modules/cacheable-request": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-2.1.4.tgz", + "integrity": "sha512-vag0O2LKZ/najSoUwDbVlnlCFvhBE/7mGTY2B5FgCBDcRD+oVV1HYTOwM6JZfMg/hIcM6IwnTZ1uQQL5/X3xIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone-response": "1.0.2", + "get-stream": "3.0.0", + "http-cache-semantics": "3.8.1", + "keyv": "3.0.0", + "lowercase-keys": "1.0.0", + "normalize-url": "2.0.1", + "responselike": "1.0.2" + } + }, + "node_modules/bin-wrapper/node_modules/cacheable-request/node_modules/lowercase-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.0.tgz", + "integrity": "sha512-RPlX0+PHuvxVDZ7xX+EBVAp4RsVxP/TdDSN2mJYdiq1Lc4Hz7EUSjUI7RZrKKlmrIzVhf6Jo2stj7++gVarS0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/bin-wrapper/node_modules/clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha512-yjLXh88P599UOyPTFX0POsd7WxnbsVsGohcwzHOLspIhhpalPw1BcqED8NblyZLKcGrL8dTgMlcaZxV2jAD41Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^1.0.0" + } + }, + "node_modules/bin-wrapper/node_modules/download": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/download/-/download-7.1.0.tgz", + "integrity": "sha512-xqnBTVd/E+GxJVrX5/eUJiLYjCGPwMpdL+jGhGU57BvtcA7wwhtHVbXBeUk51kOpW3S7Jn3BQbN9Q1R1Km2qDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "archive-type": "^4.0.0", + "caw": "^2.0.1", + "content-disposition": "^0.5.2", + "decompress": "^4.2.0", + "ext-name": "^5.0.0", + "file-type": "^8.1.0", + "filenamify": "^2.0.0", + "get-stream": "^3.0.0", + "got": "^8.3.1", + "make-dir": "^1.2.0", + "p-event": "^2.1.0", + "pify": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/bin-wrapper/node_modules/download/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/bin-wrapper/node_modules/file-type": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-8.1.0.tgz", + "integrity": "sha512-qyQ0pzAy78gVoJsmYeNgl8uH8yKhr1lVhW7JbzJmnlRi0I4R2eEDEJZVKG8agpDnLpacwNbDhLNG/LMdxHD2YQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/bin-wrapper/node_modules/got": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/got/-/got-8.3.2.tgz", + "integrity": "sha512-qjUJ5U/hawxosMryILofZCkm3C84PLJS/0grRIpjAwu+Lkxxj5cxeCU25BG0/3mDSpXKTyZr8oh8wIgLaH0QCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^0.7.0", + "cacheable-request": "^2.1.1", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^3.0.0", + "into-stream": "^3.1.0", + "is-retry-allowed": "^1.1.0", + "isurl": "^1.0.0-alpha5", + "lowercase-keys": "^1.0.0", + "mimic-response": "^1.0.0", + "p-cancelable": "^0.4.0", + "p-timeout": "^2.0.1", + "pify": "^3.0.0", + "safe-buffer": "^5.1.1", + "timed-out": "^4.0.1", + "url-parse-lax": "^3.0.0", + "url-to-options": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/bin-wrapper/node_modules/got/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/bin-wrapper/node_modules/http-cache-semantics": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz", + "integrity": "sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/bin-wrapper/node_modules/keyv": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.0.0.tgz", + "integrity": "sha512-eguHnq22OE3uVoSYG0LVWNP+4ppamWr9+zWBe1bsNcovIMy6huUJFPgy4mGwCd/rnl3vOLGW1MTlu4c57CT1xA==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.0" + } + }, + "node_modules/bin-wrapper/node_modules/normalize-url": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-2.0.1.tgz", + "integrity": "sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "prepend-http": "^2.0.0", + "query-string": "^5.0.1", + "sort-keys": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/bin-wrapper/node_modules/p-cancelable": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.4.1.tgz", + "integrity": "sha512-HNa1A8LvB1kie7cERyy21VNeHb2CWJJYqyyC2o3klWFfMGlFmWv2Z7sFgZH8ZiaYL95ydToKTFVXgMV/Os0bBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/bin-wrapper/node_modules/p-event": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/p-event/-/p-event-2.3.1.tgz", + "integrity": "sha512-NQCqOFhbpVTMX4qMe8PF8lbGtzZ+LCiN7pcNrb/413Na7+TRoe1xkKUzuWa/YEJdGQ0FvKtj35EEbDoVPO2kbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-timeout": "^2.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/bin-wrapper/node_modules/p-timeout": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-2.0.1.tgz", + "integrity": "sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-finally": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/bin-wrapper/node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/bin-wrapper/node_modules/sort-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-2.0.0.tgz", + "integrity": "sha512-/dPCrG1s3ePpWm6yBbxZq5Be1dXGLyLn9Z791chDC3NFrpkVbWGzkBwPN1knaciexFXgRJ7hzdnwZ4stHSDmjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-plain-obj": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/bl": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.3.tgz", + "integrity": "sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/body-parser": { + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.8.4.tgz", + "integrity": "sha512-jTeWaZdC6r5o7FUSWNTPtxeudzg3cybUEgT56clWiW3FOpZ0fbQAUoD2k/BqmQlyEI2sK3TBqs9Zp6p6Fsv/sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "1.0.0", + "depd": "0.4.5", + "iconv-lite": "0.4.4", + "media-typer": "0.3.0", + "on-finished": "2.1.0", + "qs": "2.2.4", + "raw-body": "1.3.0", + "type-is": "~1.5.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/body-parser/node_modules/depd": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/depd/-/depd-0.4.5.tgz", + "integrity": "sha512-MyQx8POntp7sey9ghPezYB5gIKSbcce5pkoHdFmDYkiOcsE5f5yLLBzv8Qcs9Ll1hPgmEOfIae51n4Fa7l3zxw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/body-parser/node_modules/ee-first": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.0.5.tgz", + "integrity": "sha512-+FCut34oNiJD2jD+YL/onRxOHF5ut3xOGgTIyEIOdYfun8AexYhEyurzv9izwhTft1Z7pdy4VlTq51K/sIsQRA==", + "dev": true, + "license": "MIT" + }, + "node_modules/body-parser/node_modules/iconv-lite": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.4.tgz", + "integrity": "sha512-BnjNp13aZpK4WBGbmjaNHN2MCp3P850n8zd/JLinQJ8Lsnq2Br4o2467C2waMsY5kr7Z41SL1gEqh8Vbfzg15A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/body-parser/node_modules/on-finished": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.1.0.tgz", + "integrity": "sha512-33+g6TZkplndl+2k2VNO1YphX5hm79DGhBP6TJcDI9o1sCFbUvO2bgxPdGanIFqZK4su6OVLwPHY9GkLQrojgA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ee-first": "1.0.5" + } + }, + "node_modules/body-parser/node_modules/qs": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/qs/-/qs-2.2.4.tgz", + "integrity": "sha512-ptau9CngYR/IimcThDkAs7LzlZhxo92RiMHtLbOq3R6u9iDkixdSysaAVaZpYByrXWWantEJ4fVPl0xR2McSCQ==", + "dev": true + }, + "node_modules/body-parser/node_modules/raw-body": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-1.3.0.tgz", + "integrity": "sha512-iuI1bOSi9tEmVCrXq02ZysXatTrhAu+fSo7XOQHhMo4g87dSy9YB2W/9Udwhz0bPpFk4UcoLhjrHgpPbRD3ktA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "1", + "iconv-lite": "0.4.4" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true, + "license": "ISC" + }, + "node_modules/boolean": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", + "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", + "dev": true, + "license": "MIT", + "optional": 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==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/browser-sync": { + "version": "2.27.10", + "resolved": "https://registry.npmjs.org/browser-sync/-/browser-sync-2.27.10.tgz", + "integrity": "sha512-xKm+6KJmJu6RuMWWbFkKwOCSqQOxYe3nOrFkKI5Tr/ZzjPxyU3pFShKK3tWnazBo/3lYQzN7fzjixG8fwJh1Xw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "browser-sync-client": "^2.27.10", + "browser-sync-ui": "^2.27.10", + "bs-recipes": "1.3.4", + "bs-snippet-injector": "^2.0.1", + "chokidar": "^3.5.1", + "connect": "3.6.6", + "connect-history-api-fallback": "^1", + "dev-ip": "^1.0.1", + "easy-extender": "^2.3.4", + "eazy-logger": "3.1.0", + "etag": "^1.8.1", + "fresh": "^0.5.2", + "fs-extra": "3.0.1", + "http-proxy": "^1.18.1", + "immutable": "^3", + "localtunnel": "^2.0.1", + "micromatch": "^4.0.2", + "opn": "5.3.0", + "portscanner": "2.2.0", + "qs": "6.2.3", + "raw-body": "^2.3.2", + "resp-modifier": "6.0.2", + "rx": "4.1.0", + "send": "0.16.2", + "serve-index": "1.9.1", + "serve-static": "1.13.2", + "server-destroy": "1.0.1", + "socket.io": "^4.4.1", + "ua-parser-js": "1.0.2", + "yargs": "^17.3.1" + }, + "bin": { + "browser-sync": "dist/bin.js" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/browser-sync-client": { + "version": "2.27.10", + "resolved": "https://registry.npmjs.org/browser-sync-client/-/browser-sync-client-2.27.10.tgz", + "integrity": "sha512-KCFKA1YDj6cNul0VsA28apohtBsdk5Wv8T82ClOZPZMZWxPj4Ny5AUbrj9UlAb/k6pdxE5HABrWDhP9+cjt4HQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "etag": "1.8.1", + "fresh": "0.5.2", + "mitt": "^1.1.3", + "rxjs": "^5.5.6", + "typescript": "^4.6.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/browser-sync-client/node_modules/typescript": { + "version": "4.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz", + "integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/browser-sync-ui": { + "version": "2.27.10", + "resolved": "https://registry.npmjs.org/browser-sync-ui/-/browser-sync-ui-2.27.10.tgz", + "integrity": "sha512-elbJILq4Uo6OQv6gsvS3Y9vRAJlWu+h8j0JDkF0X/ua+3S6SVbbiWnZc8sNOFlG7yvVGIwBED3eaYQ0iBo1Dtw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "async-each-series": "0.1.1", + "connect-history-api-fallback": "^1", + "immutable": "^3", + "server-destroy": "1.0.1", + "socket.io-client": "^4.4.1", + "stream-throttle": "^0.1.3" + } + }, + "node_modules/browser-sync/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, + "license": "MIT", + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-sync/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, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-sync/node_modules/fs-extra": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-3.0.1.tgz", + "integrity": "sha512-V3Z3WZWVUYd8hoCL5xfXJCaHWYzmtwW5XWYSlLgERi8PWd8bx1kUHUk8L1BT57e49oKnDDD180mjfrHc1yA9rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^3.0.0", + "universalify": "^0.1.0" + } + }, + "node_modules/browser-sync/node_modules/jsonfile": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-3.0.1.tgz", + "integrity": "sha512-oBko6ZHlubVB5mRFkur5vgYR1UyqX+S6Y/oCfLhqNdcc2fYFlDpIoNc7AfKS1KOGcnNAkvsr0grLck9ANM815w==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/browser-sync/node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/browserslist": { + "version": "4.25.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz", + "integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001726", + "electron-to-chromium": "^1.5.173", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bs-recipes": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/bs-recipes/-/bs-recipes-1.3.4.tgz", + "integrity": "sha512-BXvDkqhDNxXEjeGM8LFkSbR+jzmP/CYpCiVKYn+soB1dDldeU15EBNDkwVXndKuX35wnNUaPd0qSoQEAkmQtMw==", + "dev": true, + "license": "ISC" + }, + "node_modules/bs-snippet-injector": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/bs-snippet-injector/-/bs-snippet-injector-2.0.1.tgz", + "integrity": "sha512-4u8IgB+L9L+S5hknOj3ddNSb42436gsnGm1AuM15B7CdbkpQTyVWgIM5/JUBiKiRwGOR86uo0Lu/OsX+SAlJmw==", + "dev": true, + "license": "MIT" + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-alloc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", + "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-alloc-unsafe": "^1.1.0", + "buffer-fill": "^1.0.0" + } + }, + "node_modules/buffer-alloc-unsafe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", + "dev": true, + "license": "MIT" + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/buffer-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.1.tgz", + "integrity": "sha512-QoV3ptgEaQpvVwbXdSO39iqPQTCxSF7A5U99AxbHYqUdCizL/lH2Z0A2y6nbZucxMEOtNyZfG2s6gsVugGpKkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/buffer-fill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", + "integrity": "sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/bufferstreams": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/bufferstreams/-/bufferstreams-2.0.1.tgz", + "integrity": "sha512-ZswyIoBfFb3cVDsnZLLj2IDJ/0ppYdil/v2EGlZXvoefO689FokEmFEldhN5dV7R2QBxFneqTJOMIpfqhj+n0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "^2.3.6" + }, + "engines": { + "node": ">=6.9.5" + } + }, + "node_modules/bytes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-1.0.0.tgz", + "integrity": "sha512-/x68VkHLeTl3/Ll8IvxdwzhrT+IyKc52e/oyHhA2RwqPqswSnjVbSddfPRwAsJtbilMAPSRWwAlpxdYsSWOTKQ==", + "dev": true + }, + "node_modules/cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cache-swap": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/cache-swap/-/cache-swap-0.3.0.tgz", + "integrity": "sha512-rwePCa4iVqXHrEEmQEoLR3Kea4aCTCf7JfX+mJA4Fd61Vb738TItRRv1v++emp9wfnRUKbXpIYfRJY4ThWK09g==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "mkdirp": "^0.5.1", + "object-assign": "^4.0.1", + "rimraf": "^2.4.0" + } + }, + "node_modules/cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.6.0" + } + }, + "node_modules/calipers": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/calipers/-/calipers-2.1.0.tgz", + "integrity": "sha512-D54tptnPCX7SJ5JJIpY6896GNxka+oEO3pefTIUh4tMVeeFuVPiao8Ty3ud+jBLvlzXiBmjPAdjPkMWxFrCpaQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/calipers-gif": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/calipers-gif/-/calipers-gif-2.0.0.tgz", + "integrity": "sha512-ZePtjAmTmugWWHDjZhrh7SZ8/8hG2sS5Dz6xyZ3bWkofLqq31c01GR0AvBg5Cn5/x4BjT9PhIi0VMjHBEO+kog==", + "dev": true, + "license": "MIT", + "dependencies": { + "bluebird": "3.x.x" + }, + "peerDependencies": { + "calipers": "2.x.x" + } + }, + "node_modules/calipers-jpeg": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/calipers-jpeg/-/calipers-jpeg-2.1.0.tgz", + "integrity": "sha512-zNTtd+dWEAGcWw7qFtShcQRXotk+iI4n9chPmYAHNLzlkIA2A0/zUA5IPMBRxH6uArOv/E3D4m54Z/mkK7ulXA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "calipers": "2.x.x" + } + }, + "node_modules/calipers-png": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/calipers-png/-/calipers-png-2.1.0.tgz", + "integrity": "sha512-Iu4kwHtEXoNowsoR9CUUDc+KyFJSFhgVI0AsfYXYmo9kfEoTaQM/tLgWGzO3oisxkdeb5II3TMPDvpI+HDykjw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "calipers": "2.x.x" + } + }, + "node_modules/calipers-svg": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/calipers-svg/-/calipers-svg-2.0.1.tgz", + "integrity": "sha512-3PROqHARmj8wWudUC7DzXm1+mSocqgY7jNuehFNHgrUVrKf8o7MqDjS92vJz5LvZsAofJsoAFMajkqwbxBROSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bluebird": "3.x.x" + } + }, + "node_modules/calipers-webp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/calipers-webp/-/calipers-webp-2.0.0.tgz", + "integrity": "sha512-np7ZtCwUjpxz+DF/RYNYFxxaltJxlF7rIVKWnU/cKcgQ7r06/Fquw1Q/oMZiJe6hfjI911lhDY6ZC+X0MtA5rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "bluebird": "3.x.x" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/caller-callsite": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", + "integrity": "sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/caller-callsite/node_modules/callsites": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", + "integrity": "sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/caller-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", + "integrity": "sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "caller-callsite": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "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, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camel-case": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", + "integrity": "sha512-+MbKztAYHXPr1jNTSKQF52VpcFjwY5RkR7fxksV8Doo4KAYc5Fl4UJRgthBbTmEx8C54DqahhbLJkDwjI3PI/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "no-case": "^2.2.0", + "upper-case": "^1.1.1" + } + }, + "node_modules/camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha512-4nhGqUkc4BqbBBB4Q6zLuD7lzzrHYrjKGeYaEji/3tFR5VdJu9v+LilhGIVe8wxEJPPOeWo7eg8dwY13TZ1BNg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha512-bA/Z/DERHKqoEOrp+qeGKw1QlvEQkGZSc0XaY6VnTxZr+Kv1G5zFwttpjv8qxZ/sBPT4nthwZaAcsAZTJlSKXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/camelcase-keys/node_modules/camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha512-DLIsRzJVBQu72meAKPkWQOLcujdXT32hwdfnkI1frSiSRMK1MofjKHf+MEx0SB6fjEFXL8fBDv1dKymBlOp4Qw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001727", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz", + "integrity": "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/caw": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/caw/-/caw-2.0.1.tgz", + "integrity": "sha512-Cg8/ZSBEa8ZVY9HspcGUYaK63d/bN7rqS3CYCzEGUxuYv6UlmcjzDUz2fCFFHyTvUW5Pk0I+3hkA3iXlIj6guA==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-proxy": "^2.0.0", + "isurl": "^1.0.0-alpha5", + "tunnel-agent": "^0.6.0", + "url-to-options": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/chalk/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/chokidar/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, + "license": "MIT", + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chokidar/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, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0" + } + }, + "node_modules/circular-dependency-plugin": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-5.2.2.tgz", + "integrity": "sha512-g38K9Cm5WRwlaH6g03B9OEz/0qRizI+2I7n+Gz+L5DxXJAPAiWQvwlYNm1V1jkdpUv95bOe/ASm2vfi/G560jQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=6.0.0" + }, + "peerDependencies": { + "webpack": ">=4.0.1" + } + }, + "node_modules/class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/clean-css": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.4.tgz", + "integrity": "sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A==", + "dev": true, + "license": "MIT", + "dependencies": { + "source-map": "~0.6.0" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/clipboard-copy": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/clipboard-copy/-/clipboard-copy-3.2.0.tgz", + "integrity": "sha512-vooFaGFL6ulEP1liiaWFBmmfuPm3cY3y7T9eB83ZTnYc/oFeAKsq3NcDrOkBC8XaauEE8zHQwI7k0+JSYiVQSQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/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, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/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, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clone-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", + "integrity": "sha512-KLLTJWrvwIP+OPfMn0x2PheDEP20RPUcGXj/ERegTgdmPEZylALQldygiqrPPu8P45uNuPs7ckmReLY6v/iA5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/clone-response": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clone-stats": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", + "integrity": "sha512-au6ydSpg6nsrigcZ4m8Bc9hxjeW+GJ8xh5G3BJCMt4WXe1H10UNaVOamqQTmrx1kjVuxAHIQSNU6hY4Nsn9/ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/cloneable-readable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz", + "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1", + "process-nextick-args": "^2.0.0", + "readable-stream": "^2.3.5" + } + }, + "node_modules/coa": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", + "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/q": "^1.5.1", + "chalk": "^2.4.1", + "q": "^1.1.2" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/coa/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/coa/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/collection-map": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-map/-/collection-map-1.0.0.tgz", + "integrity": "sha512-5D2XXSpkOnleOI21TG7p3T0bGAsZ/XknZpKBmGYyluO8pw4zA3K8ZlrBIbC4FXg3m6z/RNFiUFfT2sQK01+UHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "arr-map": "^2.0.2", + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + } + }, + "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, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-convert/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true, + "license": "ISC", + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/colors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "integrity": "sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/compare-version": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/compare-version/-/compare-version-0.1.2.tgz", + "integrity": "sha512-pJDh5/4wrEnXX/VWRZvruAGHkzKdr46z11OlTPN+VrATlWWhSKewNCJ1futCO5C7eJB3nPMFZA1LeYtcFboZ2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "dev": true, + "engines": [ + "node >= 6.0" + ], + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/concat-stream/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/concat-stream/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "node_modules/connect": { + "version": "3.6.6", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.6.6.tgz", + "integrity": "sha512-OO7axMmPpu/2XuX1+2Yrg0ddju31B6xLZMWkJ5rYBu4YRmRVlOjvlY6kw2FJKiAzyxGwnrDUAG4s1Pf0sbBMCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "finalhandler": "1.1.0", + "parseurl": "~1.3.2", + "utils-merge": "1.0.1" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/connect-history-api-fallback": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", + "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/connect-livereload": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/connect-livereload/-/connect-livereload-0.4.1.tgz", + "integrity": "sha512-y/4fDdcveHQsVXTJeAr8HHLwul5M3VI071pEaov/jiCz2HIrmZynFHuOn1CfQ8euUsdjR1Ppwsb9mgBOSij3vw==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/connect/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/console-stream": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/console-stream/-/console-stream-0.1.1.tgz", + "integrity": "sha512-QC/8l9e6ofi6nqZ5PawlDgzmMw3OxIXtvolBzap/F4UDBJlDaZRSNbL/lb41C29FcbSJncBFlJFj2WJoNyZRfQ==", + "dev": true + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/copy-props": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/copy-props/-/copy-props-2.0.5.tgz", + "integrity": "sha512-XBlx8HSqrT0ObQwmSzM7WE5k8FxTV75h1DX1Z3n6NhQ/UYYAvInWYmG06vFt7hQZArE2fuO62aihiWIVQwh1sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "each-props": "^1.3.2", + "is-plain-object": "^5.0.0" + } + }, + "node_modules/copy-props/node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/core-js": { + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.26.1.tgz", + "integrity": "sha512-21491RRQVzUn0GGM9Z1Jrpr6PNPxPi+Za8OM9q4tksTSnlbXXGKK1nXNg/QvwFYettXvSX6zWKCtHHfjN4puyA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/cosmiconfig": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", + "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", + "dev": true, + "license": "MIT", + "dependencies": { + "import-fresh": "^2.0.0", + "is-directory": "^0.3.1", + "js-yaml": "^3.13.1", + "parse-json": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cosmiconfig/node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dev": true, + "license": "MIT", + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cross-dirname": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/cross-dirname/-/cross-dirname-0.1.0.tgz", + "integrity": "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/cross-spawn/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/cross-spawn/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/css-color-names": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", + "integrity": "sha512-zj5D7X1U2h2zsXOAM8EyUREBnnts6H+Jm+d1M2DbiQQcUtnqgQsMrdo8JW9R80YFUmIdBZeMu5wvYM7hcgWP/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/css-declaration-sorter": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz", + "integrity": "sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss": "^7.0.1", + "timsort": "^0.3.0" + }, + "engines": { + "node": ">4" + } + }, + "node_modules/css-mqpacker": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/css-mqpacker/-/css-mqpacker-7.0.0.tgz", + "integrity": "sha512-temVrWS+sB4uocE2quhW8ru/KguDmGhCU7zN213KxtDvWOH3WS/ZUStfpF4fdCT7W8fPpFrQdWRFqtFtPPfBLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0", + "postcss": "^7.0.0" + }, + "bin": { + "mqpacker": "bin/mqpacker.js" + } + }, + "node_modules/css-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz", + "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^3.2.1", + "domutils": "^1.7.0", + "nth-check": "^1.0.2" + } + }, + "node_modules/css-select-base-adapter": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", + "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==", + "dev": true, + "license": "MIT" + }, + "node_modules/css-tree": { + "version": "1.0.0-alpha.37", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", + "integrity": "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.4", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/css-what": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz", + "integrity": "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cssnano": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-4.1.11.tgz", + "integrity": "sha512-6gZm2htn7xIPJOHY824ERgj8cNPgPxyCSnkXc4v7YvNW+TdVfzgngHcEhy/8D11kUWRUMbke+tC+AUcUsnMz2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "cosmiconfig": "^5.0.0", + "cssnano-preset-default": "^4.0.8", + "is-resolvable": "^1.0.0", + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-preset-advanced": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/cssnano-preset-advanced/-/cssnano-preset-advanced-4.0.8.tgz", + "integrity": "sha512-DlZ5+XNKwB3ZnrtJ7jdj8WxT5Zgt1WIr4gdP9v1Sdn3SObqcLwbBobQaM7BqLIVHS74TE5iWn2TSYmOVSsmozQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "autoprefixer": "^9.4.7", + "cssnano-preset-default": "^4.0.8", + "postcss-discard-unused": "^4.0.1", + "postcss-merge-idents": "^4.0.1", + "postcss-reduce-idents": "^4.0.2", + "postcss-zindex": "^4.0.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-preset-default": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-4.0.8.tgz", + "integrity": "sha512-LdAyHuq+VRyeVREFmuxUZR1TXjQm8QQU/ktoo/x7bz+SdOge1YKc5eMN6pRW7YWBmyq59CqYba1dJ5cUukEjLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "css-declaration-sorter": "^4.0.1", + "cssnano-util-raw-cache": "^4.0.1", + "postcss": "^7.0.0", + "postcss-calc": "^7.0.1", + "postcss-colormin": "^4.0.3", + "postcss-convert-values": "^4.0.1", + "postcss-discard-comments": "^4.0.2", + "postcss-discard-duplicates": "^4.0.2", + "postcss-discard-empty": "^4.0.1", + "postcss-discard-overridden": "^4.0.1", + "postcss-merge-longhand": "^4.0.11", + "postcss-merge-rules": "^4.0.3", + "postcss-minify-font-values": "^4.0.2", + "postcss-minify-gradients": "^4.0.2", + "postcss-minify-params": "^4.0.2", + "postcss-minify-selectors": "^4.0.2", + "postcss-normalize-charset": "^4.0.1", + "postcss-normalize-display-values": "^4.0.2", + "postcss-normalize-positions": "^4.0.2", + "postcss-normalize-repeat-style": "^4.0.2", + "postcss-normalize-string": "^4.0.2", + "postcss-normalize-timing-functions": "^4.0.2", + "postcss-normalize-unicode": "^4.0.1", + "postcss-normalize-url": "^4.0.1", + "postcss-normalize-whitespace": "^4.0.2", + "postcss-ordered-values": "^4.1.2", + "postcss-reduce-initial": "^4.0.3", + "postcss-reduce-transforms": "^4.0.2", + "postcss-svgo": "^4.0.3", + "postcss-unique-selectors": "^4.0.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-util-get-arguments": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz", + "integrity": "sha512-6RIcwmV3/cBMG8Aj5gucQRsJb4vv4I4rn6YjPbVWd5+Pn/fuG+YseGvXGk00XLkoZkaj31QOD7vMUpNPC4FIuw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-util-get-match": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz", + "integrity": "sha512-JPMZ1TSMRUPVIqEalIBNoBtAYbi8okvcFns4O0YIhcdGebeYZK7dMyHJiQ6GqNBA9kE0Hym4Aqym5rPdsV/4Cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-util-raw-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz", + "integrity": "sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-util-same-parent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz", + "integrity": "sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/csso": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", + "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "dev": true, + "license": "MIT", + "dependencies": { + "css-tree": "^1.1.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/csso/node_modules/css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/csso/node_modules/mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cssstyle": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.4.0.tgz", + "integrity": "sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssom": "0.3.x" + } + }, + "node_modules/currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha512-/fITjgjGU50vjQ4FH6eUoYu+iUoUKIXws2hL15JJpIR+BbTxaXQsMuuyjtNh2WqsSBS5nsaZHFsFecyw5CCAng==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-find-index": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cycle": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", + "integrity": "sha512-TVF6svNzeQCOpjCqsy0/CSy8VgObG3wXusJ73xW2GbG5rGx7lC8zxDSURicsXI2UsGdi2L0QNRCi745/wUDvsA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "dev": true, + "license": "ISC", + "dependencies": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", + "dev": true, + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/data-urls": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz", + "integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "abab": "^2.0.0", + "whatwg-mimetype": "^2.2.0", + "whatwg-url": "^7.0.0" + } + }, + "node_modules/dateformat": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz", + "integrity": "sha512-5sFRfAAmbHdIts+eKjR9kYJoF0ViCMVX9yqLu5A7S/v+nd077KgCITOMiirmyCBiZpKLDXbBOkYm6tu7rX/TKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-stdin": "^4.0.1", + "meow": "^3.3.0" + }, + "bin": { + "dateformat": "bin/cli.js" + }, + "engines": { + "node": "*" + } + }, + "node_modules/debounce-promise": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/debounce-promise/-/debounce-promise-3.1.2.tgz", + "integrity": "sha512-rZHcgBkbYavBeD9ej6sP56XfG53d51CD4dnaw989YX/nZ/ZJfgRx/9ePKmTNiUiyQvh4mtrMoS3OAWW+yoYtpg==", + "license": "MIT" + }, + "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, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/debug/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, + "license": "MIT" + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decode-uri-component": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/decompress": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/decompress/-/decompress-4.2.1.tgz", + "integrity": "sha512-e48kc2IjU+2Zw8cTb6VZcJQ3lgVbS4uuB1TfCHbiZIP/haNXm+SVyhu+87jts5/3ROpd82GSVCoNs/z8l4ZOaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "decompress-tar": "^4.0.0", + "decompress-tarbz2": "^4.0.0", + "decompress-targz": "^4.0.0", + "decompress-unzip": "^4.0.1", + "graceful-fs": "^4.1.10", + "make-dir": "^1.0.0", + "pify": "^2.3.0", + "strip-dirs": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/decompress-tar": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-tar/-/decompress-tar-4.1.1.tgz", + "integrity": "sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "file-type": "^5.2.0", + "is-stream": "^1.1.0", + "tar-stream": "^1.5.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/decompress-tarbz2": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz", + "integrity": "sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A==", + "dev": true, + "license": "MIT", + "dependencies": { + "decompress-tar": "^4.1.0", + "file-type": "^6.1.0", + "is-stream": "^1.1.0", + "seek-bzip": "^1.0.5", + "unbzip2-stream": "^1.0.9" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/decompress-tarbz2/node_modules/file-type": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-6.2.0.tgz", + "integrity": "sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/decompress-targz": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-targz/-/decompress-targz-4.1.1.tgz", + "integrity": "sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "decompress-tar": "^4.1.1", + "file-type": "^5.2.0", + "is-stream": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/decompress-unzip": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/decompress-unzip/-/decompress-unzip-4.0.1.tgz", + "integrity": "sha512-1fqeluvxgnn86MOh66u8FjbtJpAFv5wgCT9Iw8rcBqQcCo5tO8eiJw7NNTrvt9n4CRBVq7CstiS922oPgyGLrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "file-type": "^3.8.0", + "get-stream": "^2.2.0", + "pify": "^2.3.0", + "yauzl": "^2.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/decompress-unzip/node_modules/file-type": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", + "integrity": "sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decompress-unzip/node_modules/get-stream": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz", + "integrity": "sha512-AUGhbbemXxrZJRD5cDvKtQxLuYaIbNtDTK8YqupCI393Q2KSTreEsLUN3ZxAWFGiKTzL6nKuzfcIvieflUX9qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "object-assign": "^4.0.1", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/deep-is": { + "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, + "license": "MIT" + }, + "node_modules/default-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/default-compare/-/default-compare-1.0.0.tgz", + "integrity": "sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "kind-of": "^5.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-compare/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-resolution": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/default-resolution/-/default-resolution-2.0.0.tgz", + "integrity": "sha512-2xaP6GiwVwOEbXCGoJ4ufgC76m8cj805jrghScewJC2ZDsb9U0b4BIrba+xt/Uytyd0HvQ6+WymSRTfnYj59GQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/define-properties": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", + "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-property/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-property/node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-property/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-property/node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-property/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-property/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/delete-empty": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/delete-empty/-/delete-empty-3.0.0.tgz", + "integrity": "sha512-ZUyiwo76W+DYnKsL3Kim6M/UOavPdBJgDYWOmuQhYaZvJH0AXAHbUNyEDtRbBra8wqqr686+63/0azfEk1ebUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^4.1.0", + "minimist": "^1.2.0", + "path-starts-with": "^2.0.0", + "rimraf": "^2.6.2" + }, + "bin": { + "delete-empty": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/delete-empty/node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha512-3NdhDuEXnfun/z7x9GOElY49LoqVHoGScmOKwmxhsS8N5Y+Z8KyPPDnaSzqWgYt/ji4mqwfTS34Htrk0zPIXVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/detect-file": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", + "integrity": "sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/dev-ip": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dev-ip/-/dev-ip-1.0.1.tgz", + "integrity": "sha512-LmVkry/oDShEgSZPNgqCIp2/TlqtExeGmymru3uCELnfyjY11IzpAproLYs+1X88fXO6DBoYP3ul2Xo2yz2j6A==", + "dev": true, + "bin": { + "dev-ip": "lib/dev-ip.js" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "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, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dir-glob/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, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true, + "license": "MIT" + }, + "node_modules/dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + } + }, + "node_modules/dom-serializer/node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/domexception": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", + "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", + "dev": true, + "license": "MIT", + "dependencies": { + "webidl-conversions": "^4.0.2" + } + }, + "node_modules/domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "node_modules/dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/download": { + "version": "6.2.5", + "resolved": "https://registry.npmjs.org/download/-/download-6.2.5.tgz", + "integrity": "sha512-DpO9K1sXAST8Cpzb7kmEhogJxymyVUd5qz/vCOSyvwtp2Klj2XcDt5YUuasgxka44SxF0q5RriKIwJmQHG2AuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "caw": "^2.0.0", + "content-disposition": "^0.5.2", + "decompress": "^4.0.0", + "ext-name": "^5.0.0", + "file-type": "5.2.0", + "filenamify": "^2.0.0", + "get-stream": "^3.0.0", + "got": "^7.0.0", + "make-dir": "^1.0.0", + "p-event": "^1.0.0", + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/download/node_modules/got": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/got/-/got-7.1.0.tgz", + "integrity": "sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "decompress-response": "^3.2.0", + "duplexer3": "^0.1.4", + "get-stream": "^3.0.0", + "is-plain-obj": "^1.1.0", + "is-retry-allowed": "^1.0.0", + "is-stream": "^1.0.0", + "isurl": "^1.0.0-alpha5", + "lowercase-keys": "^1.0.0", + "p-cancelable": "^0.3.0", + "p-timeout": "^1.1.1", + "safe-buffer": "^5.0.1", + "timed-out": "^4.0.0", + "url-parse-lax": "^1.0.0", + "url-to-options": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/download/node_modules/p-cancelable": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.3.0.tgz", + "integrity": "sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/download/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/download/node_modules/prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha512-PhmXi5XmoyKw1Un4E+opM2KcsJInDvKyuOumcjjw3waw86ZNjHwVUOOWLc4bCzLdcKNaWBH9e99sbWzDQsVaYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/download/node_modules/url-parse-lax": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", + "integrity": "sha512-BVA4lR5PIviy2PMseNd2jbFQ+jwSwQGdJejf5ctd1rEXt0Ypd7yanUK9+lYechVlN5VaTJGsu2U/3MDDu6KgBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "prepend-http": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/duplexer2": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.0.2.tgz", + "integrity": "sha512-+AWBwjGadtksxjOQSFDhPNQbed7icNXApT4+2BNpsXzcCBiInq2H9XW0O8sfHFaPmnQRs7cg/P0fAr2IWQSW0g==", + "dev": true, + "license": "BSD", + "dependencies": { + "readable-stream": "~1.1.9" + } + }, + "node_modules/duplexer2/node_modules/readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/duplexer3": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.5.tgz", + "integrity": "sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/duplexify": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.2.tgz", + "integrity": "sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.0" + } + }, + "node_modules/duplexify/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/duplexify/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/each-props": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/each-props/-/each-props-1.3.2.tgz", + "integrity": "sha512-vV0Hem3zAGkJAyU7JSjixeU66rwdynTAa1vofCrSA5fEln+m67Az9CcnkVD776/fsN/UjIWmBDoNRS6t6G9RfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-plain-object": "^2.0.1", + "object.defaults": "^1.1.0" + } + }, + "node_modules/easy-extender": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/easy-extender/-/easy-extender-2.3.4.tgz", + "integrity": "sha512-8cAwm6md1YTiPpOvDULYJL4ZS6WfM5/cTeVVh4JsvyYZAoqlRVUpHL9Gr5Fy7HA6xcSZicUia3DeAgO3Us8E+Q==", + "dev": true, + "dependencies": { + "lodash": "^4.17.10" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/eazy-logger": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eazy-logger/-/eazy-logger-3.1.0.tgz", + "integrity": "sha512-/snsn2JqBtUSSstEl4R0RKjkisGHAhvYj89i7r3ytNUKW12y178KDZwXLXIgwDqLW6E/VRMT9qfld7wvFae8bQ==", + "dev": true, + "dependencies": { + "tfunk": "^4.0.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true, + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.178", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.178.tgz", + "integrity": "sha512-wObbz/ar3Bc6e4X5vf0iO8xTN8YAjN/tgiAOJLr7yjYFtP9wAjq8Mb5h0yn6kResir+VYx2DXBj9NNobs0ETSA==", + "dev": true, + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/engine.io": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.2.1.tgz", + "integrity": "sha512-ECceEFcAaNRybd3lsGQKas3ZlMVjN3cyWwMP25D2i0zWfyiytVbTpRPa34qrr+FHddtpBVOmq4H/DCv1O0lZRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.0.3", + "ws": "~8.2.3" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io-client": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.2.3.tgz", + "integrity": "sha512-aXPtgF1JS3RuuKcpSrBtimSjYvrbhKW9froICH4s0F3XQWLxsKNxqzG39nnvQZQnva4CMvUK63T7shevxRyYHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.0.3", + "ws": "~8.2.3", + "xmlhttprequest-ssl": "~2.0.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.4.tgz", + "integrity": "sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io/node_modules/@types/node": { + "version": "18.11.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.10.tgz", + "integrity": "sha512-juG3RWMBOqcOuXC643OAdSA525V44cVgGV6dUDuiFtss+8Fk5x1hI93Rsld43VeJVIeqlP9I7Fn9/qaVqoEAuQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/enhanced-resolve": { + "version": "5.18.2", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.2.tgz", + "integrity": "sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "license": "BSD-2-Clause", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "dev": true, + "license": "MIT" + }, + "node_modules/errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "prr": "~1.0.1" + }, + "bin": { + "errno": "cli.js" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-abstract": { + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.4.tgz", + "integrity": "sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.1.3", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.2", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.4.3", + "safe-regex-test": "^1.0.0", + "string.prototype.trimend": "^1.0.5", + "string.prototype.trimstart": "^1.0.5", + "unbox-primitive": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-array-method-boxes-properly": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", + "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es5-ext": { + "version": "0.10.62", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz", + "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==", + "dev": true, + "hasInstallScript": true, + "license": "ISC", + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "dev": true, + "license": "MIT", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "dev": true, + "license": "ISC", + "dependencies": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + }, + "node_modules/es6-weak-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", + "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", + "dev": true, + "license": "ISC", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.46", + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true, + "license": "MIT" + }, + "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": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/escodegen": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", + "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=4.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/escodegen/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/escodegen/node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/escodegen/node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/eslint": { + "version": "9.24.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.24.0.tgz", + "integrity": "sha512-eh/jxIEJyZrvbWRe4XuVclLPDYSYYYgLy5zXGGxD6j8zjSAxFEzI2fL/8xNq6O2yKqVt+eF2YhV+hxjV6UKXwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.20.0", + "@eslint/config-helpers": "^0.2.0", + "@eslint/core": "^0.12.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.24.0", + "@eslint/plugin-kit": "^0.2.7", + "@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.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.3.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "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", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "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, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-scope/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "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/eslint/node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/eslint/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, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/eslint-scope": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz", + "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/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, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/eslint/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, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/espree": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.14.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree/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, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "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, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "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", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "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, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true, + "license": "MIT" + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/exec-buffer": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/exec-buffer/-/exec-buffer-3.2.0.tgz", + "integrity": "sha512-wsiD+2Tp6BWHoVv3B+5Dcx6E7u5zky+hUwOHjuH2hKSLR3dvRmX8fk8UD8uqQixHs4Wk6eDmiegVrMPjKj7wpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^0.7.0", + "p-finally": "^1.0.0", + "pify": "^3.0.0", + "rimraf": "^2.5.4", + "tempfile": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/exec-buffer/node_modules/cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "node_modules/exec-buffer/node_modules/execa": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha512-RztN09XglpYI7aBBrJCPW95jEH7YF1UEPOoX9yDhUTPdp7mK+CQvnLTuD10BNXZ3byLTu2uehZ8EcKT/4CGiFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/exec-buffer/node_modules/lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "license": "ISC", + "dependencies": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "node_modules/exec-buffer/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/exec-buffer/node_modules/yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", + "dev": true, + "license": "ISC" + }, + "node_modules/execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/execa/node_modules/get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/executable": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz", + "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^2.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "homedir-polyfill": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ext": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", + "dev": true, + "license": "ISC", + "dependencies": { + "type": "^2.7.2" + } + }, + "node_modules/ext-list": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/ext-list/-/ext-list-2.2.2.tgz", + "integrity": "sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "^1.28.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ext-name": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ext-name/-/ext-name-5.0.0.tgz", + "integrity": "sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ext-list": "^2.0.0", + "sort-keys-length": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ext/node_modules/type": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", + "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==", + "dev": true, + "license": "ISC" + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true, + "license": "MIT" + }, + "node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "node_modules/extract-zip/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "license": "MIT" + }, + "node_modules/eyes": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", + "integrity": "sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ==", + "dev": true, + "engines": { + "node": "> 0.1.90" + } + }, + "node_modules/fancy-log": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz", + "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-gray": "^0.1.1", + "color-support": "^1.1.3", + "parse-node-version": "^1.0.0", + "time-stamp": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-fifo": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.1.0.tgz", + "integrity": "sha512-Kl29QoNbNvn4nhDsLYjyIAaIqaJB6rBx5p3sL9VjaefJ+eMFBWVZiaoguaoZfzEKr5RhAti0UgM8703akGPJ6g==", + "dev": true, + "license": "MIT" + }, + "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==", + "dev": true, + "license": "MIT", + "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" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/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, + "license": "MIT", + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fast-glob/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, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fast-glob/node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "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", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", + "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fast-xml-parser": { + "version": "3.21.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-3.21.1.tgz", + "integrity": "sha512-FTFVjYoBOZTJekiUsawGsSYV9QL0A+zDYCRj7y34IO6Jg+2IMYEtQa+bbictpdpV8dHxXywqU7C0gRDEOFtBFg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "strnum": "^1.0.4" + }, + "bin": { + "xml2js": "cli.js" + }, + "funding": { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + }, + "node_modules/fastq": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.14.0.tgz", + "integrity": "sha512-eR2D+V9/ExcbF9ls441yIuN6TI2ED1Y2ZcA5BmMtJsOkWOFRJQ0Jt0g1UwqXJJVAb+V+umH5Dfr8oh4EVP7VVg==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/faye-websocket": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.7.3.tgz", + "integrity": "sha512-Y00rKGptNaZuPbJuWmFApQf1GIxfrUCi39oQiNyGAQpRDx1L7BQcgq5XPf8JCmXgct3Z1b/6epxqA9z9VMI42Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "websocket-driver": ">=0.3.6" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/figures": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", + "integrity": "sha512-UxKlfCRuCBxSXU4C6t9scbDyWZ4VlaFFdojKtzJuSkuOBQ5CNFum+zZXFwHjo+CxBC1t6zlYPgHIgFjL8ggoEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^1.0.5", + "object-assign": "^4.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/file-entry-cache": { + "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, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/file-type": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz", + "integrity": "sha512-Iq1nJ6D2+yIO4c8HHg4fyVb8mAJieo1Oloy1mLLaB2PvezNedhBVm+QU7g0qM42aiMbRXTxKKwGD17rjKNJYVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/filename-reserved-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", + "integrity": "sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/filenamify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-2.1.0.tgz", + "integrity": "sha512-ICw7NTT6RsDp2rnYKVd8Fu4cr6ITzGy3+u4vUujPkabyaz+03F24NWEX7fs5fp+kBonlaqPH8fAO2NM+SXt/JA==", + "dev": true, + "license": "MIT", + "dependencies": { + "filename-reserved-regex": "^2.0.0", + "strip-outer": "^1.0.0", + "trim-repeated": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fill-range/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/finalhandler": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz", + "integrity": "sha512-ejnvM9ZXYzp6PUPUyQBMBf0Co5VX2gr5H2VQe2Ui2jWXNlxv+PYZo8wpAymJNJdLsG1R4p+M4aynF8KuoUEwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.1", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "statuses": "~1.3.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/statuses": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", + "integrity": "sha512-wuTCPGlJONk/a1kqZ4fQM2+908lC7fa7nPYpTC1EhnvqLX/IICbeP1OZGDtA374trpSq68YubKUMo8oRhN46yg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha512-jvElSjyuo4EMQGoTwo1uJU5pQMwTW5lS1x05zzfJuTIyLR3zwO27LYrxNg+dlvKpGOuGy/MzBdXh80g0ve5+HA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/find-versions": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-3.2.0.tgz", + "integrity": "sha512-P8WRou2S+oe222TOCHitLy8zj+SIsVJh52VP4lvXkaFVnOFFdoWv1H1Jjvel1aI6NCFOAaeAVm8qrI0odiLcww==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver-regex": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/findup-sync": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz", + "integrity": "sha512-vs+3unmJT45eczmcAZ6zMJtxN3l/QXeccaXQx5cu/MeJMhewVfoWZqibRkOxPnmoR59+Zy5hjabfQc6JLSah4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-file": "^1.0.0", + "is-glob": "^3.1.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/findup-sync/node_modules/is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fined": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fined/-/fined-1.2.0.tgz", + "integrity": "sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng==", + "dev": true, + "license": "MIT", + "dependencies": { + "expand-tilde": "^2.0.2", + "is-plain-object": "^2.0.3", + "object.defaults": "^1.1.0", + "object.pick": "^1.2.0", + "parse-filepath": "^1.0.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/flagged-respawn": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz", + "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "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, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fluent-ffmpeg": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fluent-ffmpeg/-/fluent-ffmpeg-2.1.3.tgz", + "integrity": "sha512-Be3narBNt2s6bsaqP6Jzq91heDgOEaDCJAXcE3qcma/EJBSy5FB4cvO31XBInuAuKBx8Kptf8dkhjK0IOru39Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "async": "^0.2.9", + "which": "^1.1.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/fluent-ffmpeg/node_modules/async": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha512-eAkdoKxU6/LkKDBzLpT+t6Ff5EtfSF4wx1WfJiPEEV7WNLnDaRXk0oVysiEPm262roaachGexwUv94WhSgN5TQ==", + "dev": true + }, + "node_modules/flush-write-stream": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", + "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "readable-stream": "^2.3.6" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/for-own": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", + "integrity": "sha512-0OABksIGrxKK8K4kynWkQ7y1zounQxP+CWnyclVwj81KW3vlLlGUx57DKGcP/LH216GzqnstnPocF16Nxs0Ycg==", + "dev": true, + "license": "MIT", + "dependencies": { + "for-in": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, + "node_modules/fork-stream": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/fork-stream/-/fork-stream-0.0.4.tgz", + "integrity": "sha512-Pqq5NnT78ehvUnAk/We/Jr22vSvanRlFTpAmQ88xBY/M1TlHe+P0ILuEyXS595ysdGfaj22634LBkGMA2GTcpA==", + "dev": true, + "license": "BSD" + }, + "node_modules/form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==", + "dev": true, + "license": "MIT", + "dependencies": { + "map-cache": "^0.2.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true, + "license": "MIT" + }, + "node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fs-extra/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/fs-extra/node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/fs-mkdirp-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz", + "integrity": "sha512-+vSd9frUnapVC2RZYfL3FCB2p3g4TBhaUmrsWlSudsGdnxIuUvBB2QM1VZeBtc49QFwrp+wQLrDs3+xxDgI5gQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.11", + "through2": "^2.0.3" + }, + "engines": { + "node": ">= 0.10" + } + }, + "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, + "license": "ISC" + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true, + "license": "MIT" + }, + "node_modules/function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", + "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-info": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-package-info/-/get-package-info-1.0.0.tgz", + "integrity": "sha512-SCbprXGAPdIhKAXiG+Mk6yeoFH61JlYunqdFQFHDtLjJlDjFf6x07dsS8acO+xWt52jpdVo49AlVDnUVK1sDNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "bluebird": "^3.1.1", + "debug": "^2.2.0", + "lodash.get": "^4.0.0", + "read-pkg-up": "^2.0.0" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/get-package-info/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/get-package-info/node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/get-package-info/node_modules/load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha512-3p6ZOGNbiX4CdvEd1VcE6yi78UrGNpjHO33noGwHCnT/o2fyllJDepsm8+mFFv/DvtwFHht5HIHSyOy5a+ChVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/get-package-info/node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/get-package-info/node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/get-package-info/node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/get-package-info/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/get-package-info/node_modules/path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha512-dUnb5dXUf+kzhC/W/F4e5/SkluXIFf5VUHolW1Eg1irn1hGWjPGdsRcvYJ1nD6lhk8Ir7VM0bHJKsYTx8Jx9OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/get-package-info/node_modules/read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha512-eFIBOPW7FGjzBuk3hdXEuNSiTZS/xEMlH49HxMyzb0hyPfu4EhVjT2DH32K1hSSmVq4sebAWnZuuY5auISUTGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/get-package-info/node_modules/read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha512-1orxQfbWGUiTn9XsPlChs6rLie/AV9jwZTGmu2NZw/CUDJQchXJFYE0Fq5j7+n558T1JhDWLdhyd1Zj+wLY//w==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/get-package-info/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/get-proxy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/get-proxy/-/get-proxy-2.1.0.tgz", + "integrity": "sha512-zmZIaQTWnNQb4R4fJUEp/FC51eZsc6EkErspy3xtIYStaq8EB/hDIWipxsal+E8rz0qD7f2sL/NA9Xee4RInJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "npm-conf": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha512-F5aQMywwJ2n85s4hJPTT9RPxGmubonuB10MNYo17/xph174n2MIR33HRguhzVag10O/npM7SPk73LMZNP+FaWw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", + "dev": true, + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0" + } + }, + "node_modules/gifsicle": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/gifsicle/-/gifsicle-5.3.0.tgz", + "integrity": "sha512-FJTpgdj1Ow/FITB7SVza5HlzXa+/lqEY0tHQazAJbuAdvyJtkH4wIdsR2K414oaTwRXHFLLF+tYbipj+OpYg+Q==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "bin-build": "^3.0.0", + "bin-wrapper": "^4.0.0", + "execa": "^5.0.0" + }, + "bin": { + "gifsicle": "cli.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/imagemin/gisicle-bin?sponsor=1" + } + }, + "node_modules/gifsicle/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/gifsicle/node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/gifsicle/node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gifsicle/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gifsicle/node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/gifsicle/node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/gifsicle/node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/gifsicle/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "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, + "license": "ISC", + "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": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob-stream": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-6.1.0.tgz", + "integrity": "sha512-uMbLGAP3S2aDOHUDfdoYcdIePUCfysbAd0IAoWVZbeGU/oNQ8asHVSshLDJUPWxfzj8zsCG7/XeHPHTtow0nsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "extend": "^3.0.0", + "glob": "^7.1.1", + "glob-parent": "^3.1.0", + "is-negated-glob": "^1.0.0", + "ordered-read-streams": "^1.0.0", + "pumpify": "^1.3.5", + "readable-stream": "^2.1.5", + "remove-trailing-separator": "^1.0.1", + "to-absolute-glob": "^2.0.0", + "unique-stream": "^2.0.2" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/glob-stream/node_modules/glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + } + }, + "node_modules/glob-stream/node_modules/is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/glob-watcher": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-5.0.5.tgz", + "integrity": "sha512-zOZgGGEHPklZNjZQaZ9f41i7F2YwE+tS5ZHrDhbBCk3stwahn5vQxnFmBJZHoYdusR6R1bLSXeGUy/BhctwKzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "^2.0.0", + "async-done": "^1.2.0", + "chokidar": "^2.0.0", + "is-negated-glob": "^1.0.0", + "just-debounce": "^1.0.0", + "normalize-path": "^3.0.0", + "object.defaults": "^1.1.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/glob-watcher/node_modules/binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob-watcher/node_modules/chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + }, + "optionalDependencies": { + "fsevents": "^1.2.7" + } + }, + "node_modules/glob-watcher/node_modules/glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + } + }, + "node_modules/glob-watcher/node_modules/glob-parent/node_modules/is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob-watcher/node_modules/is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob-watcher/node_modules/readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/global-agent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", + "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "boolean": "^3.0.1", + "es6-error": "^4.1.1", + "matcher": "^3.0.0", + "roarr": "^2.15.3", + "semver": "^7.3.2", + "serialize-error": "^7.0.1" + }, + "engines": { + "node": ">=10.0" + } + }, + "node_modules/global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "dev": true, + "license": "MIT", + "dependencies": { + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/globals": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.0.0.tgz", + "integrity": "sha512-m/C/yR4mjO6pXDTm9/R/SpYTAIyaUB4EOzcaaMEl7mds7Mshct9GfejiJNQGjHHbdMPey13Kpu4TMbYi9ex1pw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globby": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.2.tgz", + "integrity": "sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/glob": "^7.1.1", + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.0.3", + "glob": "^7.1.3", + "ignore": "^5.1.1", + "merge2": "^1.2.3", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/glogg": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.2.tgz", + "integrity": "sha512-5mwUoSuBk44Y4EshyiqcH95ZntbDdTQqA3QYSrxmzj28Ai0vXBGMH1ApSANH14j2sIRtqCEyg6PfsuP7ElOEDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "sparkles": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/gulp": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/gulp/-/gulp-4.0.2.tgz", + "integrity": "sha512-dvEs27SCZt2ibF29xYgmnwwCYZxdxhQ/+LFWlbAW8y7jt68L/65402Lz3+CKy0Ov4rOs+NERmDq7YlZaDqUIfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "glob-watcher": "^5.0.3", + "gulp-cli": "^2.2.0", + "undertaker": "^1.2.1", + "vinyl-fs": "^3.0.0" + }, + "bin": { + "gulp": "bin/gulp.js" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/gulp-audiosprite": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/gulp-audiosprite/-/gulp-audiosprite-1.1.0.tgz", + "integrity": "sha512-CwSfZjmNPlTyzcAFaE8RiKzh1dQFDLPPZMHshKwvGqNeTB86s30K8hMXGrrjFqHNF9xb0SUnXfbYT32MO4aNog==", + "dev": true, + "license": "MIT", + "dependencies": { + "audiosprite": "*", + "through2": "*", + "vinyl": "*" + } + }, + "node_modules/gulp-audiosprite/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/gulp-audiosprite/node_modules/replace-ext": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-2.0.0.tgz", + "integrity": "sha512-UszKE5KVK6JvyD92nzMn9cDapSk6w/CaFZ96CnmDMUqH9oowfxF/ZjRITD25H4DnOQClLA4/j7jLGXXLVKxAug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/gulp-audiosprite/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/gulp-audiosprite/node_modules/through2": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "3" + } + }, + "node_modules/gulp-audiosprite/node_modules/vinyl": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-3.0.0.tgz", + "integrity": "sha512-rC2VRfAVVCGEgjnxHUnpIVh3AGuk62rP3tqVrn+yab0YH7UULisC085+NYH+mnqf3Wx4SpSi1RQMwudL89N03g==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone": "^2.1.2", + "clone-stats": "^1.0.0", + "remove-trailing-separator": "^1.1.0", + "replace-ext": "^2.0.0", + "teex": "^1.0.1" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/gulp-cache": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/gulp-cache/-/gulp-cache-1.1.3.tgz", + "integrity": "sha512-NE814LdX1NWQn2sMzn+Rf673o4mqlgg7OyLf92oQ4KEl6DdPfduEGLNH+HexLVcFZXH93DBuxFOvpv4/Js5VaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.5.5", + "cache-swap": "^0.3.0", + "core-js": "3", + "object.pick": "^1.3.0", + "plugin-error": "^1.0.1", + "through2": "3.0.1", + "vinyl": "^2.2.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/gulp-cache/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/gulp-cache/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/gulp-cache/node_modules/through2": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", + "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "2 || 3" + } + }, + "node_modules/gulp-cached": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/gulp-cached/-/gulp-cached-1.1.1.tgz", + "integrity": "sha512-OEGsICR6Vmx0VK3nhpy5MGPzAjeDYC3+NKxNtJAu4DW8L15oy8tCe2WuD6HDEj9BsbSopnOBiXPK95YHvO0DpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash.defaults": "^4.2.0", + "through2": "^2.0.1" + }, + "engines": { + "node": ">= 0.9.0" + } + }, + "node_modules/gulp-clean": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/gulp-clean/-/gulp-clean-0.4.0.tgz", + "integrity": "sha512-DARK8rNMo4lHOFLGTiHEJdf19GuoBDHqGUaypz+fOhrvOs3iFO7ntdYtdpNxv+AzSJBx/JfypF0yEj9ks1IStQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fancy-log": "^1.3.2", + "plugin-error": "^0.1.2", + "rimraf": "^2.6.2", + "through2": "^2.0.3", + "vinyl": "^2.1.0" + }, + "engines": { + "node": ">=0.9" + } + }, + "node_modules/gulp-clean/node_modules/arr-diff": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz", + "integrity": "sha512-OQwDZUqYaQwyyhDJHThmzId8daf4/RFNLaeh3AevmSeZ5Y7ug4Ga/yKc6l6kTZOBW781rCj103ZuTh8GAsB3+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "arr-flatten": "^1.0.1", + "array-slice": "^0.2.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-clean/node_modules/arr-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz", + "integrity": "sha512-t5db90jq+qdgk8aFnxEkjqta0B/GHrM1pxzuuZz2zWsOXc5nKu3t+76s/PQBA8FTcM/ipspIH9jWG4OxCBc2eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-clean/node_modules/array-slice": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", + "integrity": "sha512-rlVfZW/1Ph2SNySXwR9QYkChp8EkOEiTMO5Vwx60usw04i4nWemkm9RXmQqgkQFaLHsqLuADvjp6IfgL9l2M8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-clean/node_modules/extend-shallow": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz", + "integrity": "sha512-L7AGmkO6jhDkEBBGWlLtftA80Xq8DipnrRPr0pyi7GQLXkaq9JYA4xF4z6qnadIC6euiTDKco0cGSU9muw+WTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "kind-of": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-clean/node_modules/kind-of": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", + "integrity": "sha512-aUH6ElPnMGon2/YkxRIigV32MOpTVcoXQ1Oo8aYn40s+sJ3j+0gFZsT8HKDcxNy7Fi9zuquWtGaGAahOdv5p/g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-clean/node_modules/plugin-error": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz", + "integrity": "sha512-WzZHcm4+GO34sjFMxQMqZbsz3xiNEgonCskQ9v+IroMmYgk/tas8dG+Hr2D6IbRPybZ12oWpzE/w3cGJ6FJzOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-cyan": "^0.1.1", + "ansi-red": "^0.1.1", + "arr-diff": "^1.0.1", + "arr-union": "^2.0.1", + "extend-shallow": "^1.1.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-cli": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-2.3.0.tgz", + "integrity": "sha512-zzGBl5fHo0EKSXsHzjspp3y5CONegCm8ErO5Qh0UzFzk2y4tMvzLWhoDokADbarfZRL2pGpRp7yt6gfJX4ph7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^1.0.1", + "archy": "^1.0.0", + "array-sort": "^1.0.0", + "color-support": "^1.1.3", + "concat-stream": "^1.6.0", + "copy-props": "^2.0.1", + "fancy-log": "^1.3.2", + "gulplog": "^1.0.0", + "interpret": "^1.4.0", + "isobject": "^3.0.1", + "liftoff": "^3.1.0", + "matchdep": "^2.0.0", + "mute-stdout": "^1.0.0", + "pretty-hrtime": "^1.0.0", + "replace-homedir": "^1.0.0", + "semver-greatest-satisfied-range": "^1.1.0", + "v8flags": "^3.2.0", + "yargs": "^7.1.0" + }, + "bin": { + "gulp": "bin/gulp.js" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/gulp-cli/node_modules/cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha512-0yayqDxWQbqk3ojkYqUKqaAQ6AfNKeKWRNA8kR0WXzAsdHpP4BIaOmMAG87JGuO6qcobyW4GjxHd9PmhEd+T9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, + "node_modules/gulp-cli/node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "engines": [ + "node >= 0.8" + ], + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/gulp-cli/node_modules/get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true, + "license": "ISC" + }, + "node_modules/gulp-cli/node_modules/is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "number-is-nan": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-cli/node_modules/string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-cli/node_modules/wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha512-vAaEaDM946gbNpH5pLVNR+vX2ht6n0Bt3GXwVB1AuAqZosOvHNF3P7wDnh8KLkSqgUh0uh77le7Owgoz+Z9XBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-cli/node_modules/y18n": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz", + "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/gulp-cli/node_modules/yargs": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.2.tgz", + "integrity": "sha512-ZEjj/dQYQy0Zx0lgLMLR8QuaqTihnxirir7EwUHp1Axq4e3+k8jXU5K0VLbNvedv1f4EWtBonDIZm0NUr+jCcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "camelcase": "^3.0.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.2", + "which-module": "^1.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^5.0.1" + } + }, + "node_modules/gulp-cli/node_modules/yargs-parser": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.1.tgz", + "integrity": "sha512-wpav5XYiddjXxirPoCTUPbqM0PXvJ9hiBMvuJgInvo4/lAOTZzUprArw17q2O1P2+GHhbBr18/iQwjL5Z9BqfA==", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^3.0.0", + "object.assign": "^4.1.0" + } + }, + "node_modules/gulp-dart-sass": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/gulp-dart-sass/-/gulp-dart-sass-1.0.2.tgz", + "integrity": "sha512-8fLttA824mbuc0jRVlGs00zWYZXBckat6INawx5kp66Eqsz5srNWTA51t0mbfB4C8a/a/GZ9muYLwXGklgAHlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^2.3.0", + "lodash.clonedeep": "^4.3.2", + "plugin-error": "^1.0.1", + "replace-ext": "^1.0.0", + "sass": "^1.26.3", + "strip-ansi": "^4.0.0", + "through2": "^2.0.0", + "vinyl-sourcemaps-apply": "^0.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/gulp-dart-sass/node_modules/ansi-regex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/gulp-dart-sass/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/gulp-dart-sass/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/gulp-dart-sass/node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/gulp-dom": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gulp-dom/-/gulp-dom-1.0.0.tgz", + "integrity": "sha512-hD2w2t3fsjPicX2mT6MFFb+eP3FyCVtEHdejGMMH4+w9EBFxA2xIZadqlzYdAEdE+39dP1aGatuhdHJteUvn1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "jsdom": "12.2.0", + "plugin-error": "1.0.1", + "through2": "2.0.3" + } + }, + "node_modules/gulp-dom/node_modules/through2": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", + "integrity": "sha512-tmNYYHFqXmaKSSlOU4ZbQ82cxmFQa5LRWKFtWCNkGIiZ3/VHmOffCeWfBRZZRyXAhNP9itVMR+cuvomBOPlm8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "^2.1.5", + "xtend": "~4.0.1" + } + }, + "node_modules/gulp-fluent-ffmpeg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/gulp-fluent-ffmpeg/-/gulp-fluent-ffmpeg-2.0.0.tgz", + "integrity": "sha512-pwG6N+NKwLzO/0ybzgcwiADKZ4OzpFjNR4drqCvbvluYcSh/yvsAW7wm63jFzpJIjfFnanYGPNWiUn8+TuTR/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "concat-stream": "^2.0.0", + "fluent-ffmpeg": "^2.1.2", + "plugin-error": "^1.0.1", + "through2": "^3.0.1" + } + }, + "node_modules/gulp-fluent-ffmpeg/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/gulp-fluent-ffmpeg/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/gulp-fluent-ffmpeg/node_modules/through2": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", + "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "2 || 3" + } + }, + "node_modules/gulp-htmlmin": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/gulp-htmlmin/-/gulp-htmlmin-5.0.1.tgz", + "integrity": "sha512-ASlyDPZOSKjHYUifYV0rf9JPDflN9IRIb8lw2vRqtYMC4ljU3zAmnnaVXwFQ3H+CfXxZSUesZ2x7jrnPJu93jA==", + "dev": true, + "license": "MIT", + "dependencies": { + "html-minifier": "^3.5.20", + "plugin-error": "^1.0.1", + "through2": "^2.0.3" + }, + "engines": { + "node": ">= 6.0" + } + }, + "node_modules/gulp-if": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/gulp-if/-/gulp-if-3.0.0.tgz", + "integrity": "sha512-fCUEngzNiEZEK2YuPm+sdMpO6ukb8+/qzbGfJBXyNOXz85bCG7yBI+pPSl+N90d7gnLvMsarthsAImx0qy7BAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "gulp-match": "^1.1.0", + "ternary-stream": "^3.0.0", + "through2": "^3.0.1" + } + }, + "node_modules/gulp-if/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/gulp-if/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/gulp-if/node_modules/through2": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", + "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "2 || 3" + } + }, + "node_modules/gulp-imagemin": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/gulp-imagemin/-/gulp-imagemin-7.1.0.tgz", + "integrity": "sha512-6xBTNybmPY2YrvrhhlS8Mxi0zn0ypusLon63p9XXxDtIf7U7c6KcViz94K7Skosucr3378A6IY2kJSjJyuwylQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^3.0.0", + "fancy-log": "^1.3.2", + "imagemin": "^7.0.0", + "plugin-error": "^1.0.1", + "plur": "^3.0.1", + "pretty-bytes": "^5.3.0", + "through2-concurrent": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + }, + "optionalDependencies": { + "imagemin-gifsicle": "^7.0.0", + "imagemin-mozjpeg": "^8.0.0", + "imagemin-optipng": "^7.0.0", + "imagemin-svgo": "^7.0.0" + }, + "peerDependencies": { + "gulp": ">=4" + }, + "peerDependenciesMeta": { + "gulp": { + "optional": true + } + } + }, + "node_modules/gulp-imagemin/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/gulp-imagemin/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/gulp-match": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/gulp-match/-/gulp-match-1.1.0.tgz", + "integrity": "sha512-DlyVxa1Gj24DitY2OjEsS+X6tDpretuxD6wTfhXE/Rw2hweqc1f6D/XtsJmoiCwLWfXgR87W9ozEityPCVzGtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimatch": "^3.0.3" + } + }, + "node_modules/gulp-plumber": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/gulp-plumber/-/gulp-plumber-1.2.1.tgz", + "integrity": "sha512-mctAi9msEAG7XzW5ytDVZ9PxWMzzi1pS2rBH7lA095DhMa6KEXjm+St0GOCc567pJKJ/oCvosVAZEpAey0q2eQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^1.1.3", + "fancy-log": "^1.3.2", + "plugin-error": "^0.1.2", + "through2": "^2.0.3" + }, + "engines": { + "node": ">=0.10", + "npm": ">=1.2.10" + } + }, + "node_modules/gulp-plumber/node_modules/arr-diff": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz", + "integrity": "sha512-OQwDZUqYaQwyyhDJHThmzId8daf4/RFNLaeh3AevmSeZ5Y7ug4Ga/yKc6l6kTZOBW781rCj103ZuTh8GAsB3+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "arr-flatten": "^1.0.1", + "array-slice": "^0.2.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-plumber/node_modules/arr-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz", + "integrity": "sha512-t5db90jq+qdgk8aFnxEkjqta0B/GHrM1pxzuuZz2zWsOXc5nKu3t+76s/PQBA8FTcM/ipspIH9jWG4OxCBc2eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-plumber/node_modules/array-slice": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", + "integrity": "sha512-rlVfZW/1Ph2SNySXwR9QYkChp8EkOEiTMO5Vwx60usw04i4nWemkm9RXmQqgkQFaLHsqLuADvjp6IfgL9l2M8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-plumber/node_modules/extend-shallow": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz", + "integrity": "sha512-L7AGmkO6jhDkEBBGWlLtftA80Xq8DipnrRPr0pyi7GQLXkaq9JYA4xF4z6qnadIC6euiTDKco0cGSU9muw+WTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "kind-of": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-plumber/node_modules/kind-of": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", + "integrity": "sha512-aUH6ElPnMGon2/YkxRIigV32MOpTVcoXQ1Oo8aYn40s+sJ3j+0gFZsT8HKDcxNy7Fi9zuquWtGaGAahOdv5p/g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-plumber/node_modules/plugin-error": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz", + "integrity": "sha512-WzZHcm4+GO34sjFMxQMqZbsz3xiNEgonCskQ9v+IroMmYgk/tas8dG+Hr2D6IbRPybZ12oWpzE/w3cGJ6FJzOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-cyan": "^0.1.1", + "ansi-red": "^0.1.1", + "arr-diff": "^1.0.1", + "arr-union": "^2.0.1", + "extend-shallow": "^1.1.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-postcss": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/gulp-postcss/-/gulp-postcss-8.0.0.tgz", + "integrity": "sha512-Wtl6vH7a+8IS/fU5W9IbOpcaLqKxd5L1DUOzaPmlnCbX1CrG0aWdwVnC3Spn8th0m8D59YbysV5zPUe1n/GJYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fancy-log": "^1.3.2", + "plugin-error": "^1.0.1", + "postcss": "^7.0.2", + "postcss-load-config": "^2.0.0", + "vinyl-sourcemaps-apply": "^0.2.1" + } + }, + "node_modules/gulp-rename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/gulp-rename/-/gulp-rename-2.0.0.tgz", + "integrity": "sha512-97Vba4KBzbYmR5VBs9mWmK+HwIf5mj+/zioxfZhOKeXtx5ZjBk57KFlePf5nxq9QsTtFl0ejnHE3zTC9MHXqyQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/gulp-util": { + "version": "2.2.20", + "resolved": "https://registry.npmjs.org/gulp-util/-/gulp-util-2.2.20.tgz", + "integrity": "sha512-9rtv4sj9EtCWYGD15HQQvWtRBtU9g1t0+w29tphetHxjxEAuBKQJkhGqvlLkHEtUjEgoqIpsVwPKU1yMZAa+wA==", + "dev": true, + "dependencies": { + "chalk": "^0.5.0", + "dateformat": "^1.0.7-1.2.3", + "lodash._reinterpolate": "^2.4.1", + "lodash.template": "^2.4.1", + "minimist": "^0.2.0", + "multipipe": "^0.1.0", + "through2": "^0.5.0", + "vinyl": "^0.2.1" + }, + "engines": { + "node": ">= 0.9" + } + }, + "node_modules/gulp-util/node_modules/ansi-styles": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz", + "integrity": "sha512-f2PKUkN5QngiSemowa6Mrk9MPCdtFiOSmibjZ+j1qhLGHHYsqZwmBMRF3IRMVXo8sybDqx2fJl2d/8OphBoWkA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-util/node_modules/chalk": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz", + "integrity": "sha512-bIKA54hP8iZhyDT81TOsJiQvR1gW+ZYSXFaZUAvoD4wCHdbHY2actmpTE4x344ZlFqHbvoxKOaESULTZN2gstg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^1.1.0", + "escape-string-regexp": "^1.0.0", + "has-ansi": "^0.1.0", + "strip-ansi": "^0.3.0", + "supports-color": "^0.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-util/node_modules/clone-stats": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", + "integrity": "sha512-dhUqc57gSMCo6TX85FLfe51eC/s+Im2MLkAgJwfaRRexR2tA4dd3eLEW4L6efzHc2iNorrRRXITifnDLlRrhaA==", + "dev": true, + "license": "MIT" + }, + "node_modules/gulp-util/node_modules/has-ansi": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz", + "integrity": "sha512-1YsTg1fk2/6JToQhtZkArMkurq8UoWU1Qe0aR3VUHjgij4nOylSWLWAtBXoZ4/dXOmugfLGm1c+QhuD0JyedFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^0.2.0" + }, + "bin": { + "has-ansi": "cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-util/node_modules/minimist": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.2.2.tgz", + "integrity": "sha512-g92kDfAOAszDRtHNagjZPPI/9lfOFaRBL/Ud6Z0RKZua/x+49awTydZLh5Gkhb80Xy5hmcvZNLGzscW5n5yd0g==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gulp-util/node_modules/readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/gulp-util/node_modules/strip-ansi": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz", + "integrity": "sha512-DerhZL7j6i6/nEnVG0qViKXI0OKouvvpsAiaj7c+LfqZZZxdwZtv8+UiA/w4VUJpT8UzX0pR1dcHOii1GbmruQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^0.2.1" + }, + "bin": { + "strip-ansi": "cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-util/node_modules/supports-color": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz", + "integrity": "sha512-tdCZ28MnM7k7cJDJc7Eq80A9CsRFAAOZUy41npOZCs++qSjfIy7o5Rh46CBk+Dk5FbKJ33X3Tqg4YrV07N5RaA==", + "dev": true, + "license": "MIT", + "bin": { + "supports-color": "cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-util/node_modules/through2": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.5.1.tgz", + "integrity": "sha512-zexCrAOTbjkBCXGyozn7hhS3aEaqdrc59mAD2E3dKYzV1vFuEGQ1hEDJN2oQMQFwy4he2zyLqPZV+AlfS8ZWJA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "~1.0.17", + "xtend": "~3.0.0" + } + }, + "node_modules/gulp-util/node_modules/vinyl": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.2.3.tgz", + "integrity": "sha512-4gFk9xrecazOTuFKcUYrE1TjHSYL63dio72D+q0d1mHF51FEcxTT2RHFpHbN5TNJgmPYHuVsBdhvXEOCDcytSA==", + "dev": true, + "dependencies": { + "clone-stats": "~0.0.1" + }, + "engines": { + "node": ">= 0.9" + } + }, + "node_modules/gulp-util/node_modules/xtend": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-3.0.0.tgz", + "integrity": "sha512-sp/sT9OALMjRW1fKDlPeuSZlDQpkqReA0pyJukniWbTGoEKefHxhGJynE3PNhUMlcM8qWIjPwecwCw4LArS5Eg==", + "dev": true, + "engines": { + "node": ">=0.4" + } + }, + "node_modules/gulp-webserver": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/gulp-webserver/-/gulp-webserver-0.9.1.tgz", + "integrity": "sha512-Z9UVbOglUaLy4xsUN+3TvVZ3a1O/8r9xd7F552Zx2ARieR+Ko41jbOJjEmdOj1KteCuuPs0jRHlNvZgA+yW1zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "connect": "^3.0.1", + "connect-livereload": "^0.4.0", + "gulp-util": "^2.2.19", + "isarray": "0.0.1", + "node.extend": "^1.0.10", + "open": "^0.0.5", + "proxy-middleware": "^0.5.0", + "serve-index": "^1.1.4", + "serve-static": "^1.3.0", + "through2": "^0.5.1", + "tiny-lr": "0.1.4", + "watch": "^0.11.0" + } + }, + "node_modules/gulp-webserver/node_modules/connect": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", + "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "finalhandler": "1.1.2", + "parseurl": "~1.3.3", + "utils-merge": "1.0.1" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/gulp-webserver/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/gulp-webserver/node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/gulp-webserver/node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/gulp-webserver/node_modules/finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/gulp-webserver/node_modules/finalhandler/node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "dev": true, + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/gulp-webserver/node_modules/finalhandler/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/gulp-webserver/node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/gulp-webserver/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/gulp-webserver/node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/gulp-webserver/node_modules/readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/gulp-webserver/node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/gulp-webserver/node_modules/send/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, + "license": "MIT" + }, + "node_modules/gulp-webserver/node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/gulp-webserver/node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true, + "license": "ISC" + }, + "node_modules/gulp-webserver/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/gulp-webserver/node_modules/through2": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.5.1.tgz", + "integrity": "sha512-zexCrAOTbjkBCXGyozn7hhS3aEaqdrc59mAD2E3dKYzV1vFuEGQ1hEDJN2oQMQFwy4he2zyLqPZV+AlfS8ZWJA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "~1.0.17", + "xtend": "~3.0.0" + } + }, + "node_modules/gulp-webserver/node_modules/xtend": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-3.0.0.tgz", + "integrity": "sha512-sp/sT9OALMjRW1fKDlPeuSZlDQpkqReA0pyJukniWbTGoEKefHxhGJynE3PNhUMlcM8qWIjPwecwCw4LArS5Eg==", + "dev": true, + "engines": { + "node": ">=0.4" + } + }, + "node_modules/gulp-yaml": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/gulp-yaml/-/gulp-yaml-2.0.4.tgz", + "integrity": "sha512-S/9Ib8PO+jGkCvWDwBUkmFkeW7QM0pp4PO8NNrMEfWo5Sk30P+KqpyXc4055L/vOX326T/b9MhM4nw5EenyX9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "bufferstreams": "^2.0.1", + "js-yaml": "^3.13.1", + "object-assign": "^4.1.1", + "plugin-error": "^1.0.1", + "replace-ext": "^1.0.0", + "through2": "^3.0.0" + }, + "engines": { + "node": ">=6.9.5" + } + }, + "node_modules/gulp-yaml/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/gulp-yaml/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/gulp-yaml/node_modules/through2": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", + "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "2 || 3" + } + }, + "node_modules/gulplog": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gulplog/-/gulplog-1.0.0.tgz", + "integrity": "sha512-hm6N8nrm3Y08jXie48jsC55eCZz9mnb4OirAStEk2deqeyhXU3C1otDVh+ccttMuc1sBi6RX6ZJ720hs9RCvgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "glogg": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=4" + } + }, + "node_modules/har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-ansi/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbol-support-x": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz", + "integrity": "sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-to-string-tag-x": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz", + "integrity": "sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbol-support-x": "^1.4.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/hex-color-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", + "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse-passwd": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true, + "license": "ISC" + }, + "node_modules/howler": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/howler/-/howler-2.2.3.tgz", + "integrity": "sha512-QM0FFkw0LRX1PR8pNzJVAY25JhIWvbKMBFM4gqk+QdV+kPXOhleWGCB6AiAF/goGjIHK2e/nIElplvjQwhr0jg==", + "license": "MIT" + }, + "node_modules/hsl-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz", + "integrity": "sha512-M5ezZw4LzXbBKMruP+BNANf0k+19hDQMgpzBIYnya//Al+fjNct9Wf3b1WedLqdEs2hKBvxq/jh+DsHJLj0F9A==", + "dev": true, + "license": "MIT" + }, + "node_modules/hsla-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hsla-regex/-/hsla-regex-1.0.0.tgz", + "integrity": "sha512-7Wn5GMLuHBjZCb2bTmnDOycho0p/7UVaAeqXZGbHrBCl6Yd/xDhQJAXe6Ga9AXJH2I5zY1dEdYw2u1UptnSBJA==", + "dev": true, + "license": "MIT" + }, + "node_modules/html-encoding-sniffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz", + "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-encoding": "^1.0.1" + } + }, + "node_modules/html-minifier": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.21.tgz", + "integrity": "sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "camel-case": "3.0.x", + "clean-css": "4.2.x", + "commander": "2.17.x", + "he": "1.2.x", + "param-case": "2.1.x", + "relateurl": "0.2.x", + "uglify-js": "3.4.x" + }, + "bin": { + "html-minifier": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/html-minifier/node_modules/commander": { + "version": "2.17.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", + "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==", + "dev": true, + "license": "MIT" + }, + "node_modules/http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/http-errors/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "dev": true, + "license": "ISC" + }, + "node_modules/http-parser-js": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" + } + }, + "node_modules/http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.1.tgz", + "integrity": "sha512-d2qQLzTJ9WxQftPAuEQpSPmKqzxePjzVbpAVv62AQ64NTL+wR4JkrVqR/LqFsFEUsHDAiId52mJteHDFuDkElA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/imagemin": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/imagemin/-/imagemin-7.0.1.tgz", + "integrity": "sha512-33AmZ+xjZhg2JMCe+vDf6a9mzWukE7l+wAtesjE7KyteqqKjzxv7aVQeWnul1Ve26mWvEQqyPwl0OctNBfSR9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "file-type": "^12.0.0", + "globby": "^10.0.0", + "graceful-fs": "^4.2.2", + "junk": "^3.1.0", + "make-dir": "^3.0.0", + "p-pipe": "^3.0.0", + "replace-ext": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/imagemin-gifsicle": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/imagemin-gifsicle/-/imagemin-gifsicle-7.0.0.tgz", + "integrity": "sha512-LaP38xhxAwS3W8PFh4y5iQ6feoTSF+dTAXFRUEYQWYst6Xd+9L/iPk34QGgK/VO/objmIlmq9TStGfVY2IcHIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^1.0.0", + "gifsicle": "^5.0.0", + "is-gif": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/imagemin/imagemin-gifsicle?sponsor=1" + } + }, + "node_modules/imagemin-jpegtran": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/imagemin-jpegtran/-/imagemin-jpegtran-7.0.0.tgz", + "integrity": "sha512-MJoyTCW8YjMJf56NorFE41SR/WkaGA3IYk4JgvMlRwguJEEd3PnP9UxA8Y2UWjquz8d+On3Ds/03ZfiiLS8xTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "exec-buffer": "^3.0.0", + "is-jpg": "^2.0.0", + "jpegtran-bin": "^5.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/imagemin-mozjpeg": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/imagemin-mozjpeg/-/imagemin-mozjpeg-8.0.0.tgz", + "integrity": "sha512-+EciPiIjCb8JWjQNr1q8sYWYf7GDCNDxPYnkD11TNIjjWNzaV+oTg4DpOPQjl5ZX/KRCPMEgS79zLYAQzLitIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^1.0.0", + "is-jpg": "^2.0.0", + "mozjpeg": "^6.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/imagemin-optipng": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/imagemin-optipng/-/imagemin-optipng-7.1.0.tgz", + "integrity": "sha512-JNORTZ6j6untH7e5gF4aWdhDCxe3ODsSLKs/f7Grewy3ebZpl1ZsU+VUTPY4rzeHgaFA8GSWOoA8V2M3OixWZQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "exec-buffer": "^3.0.0", + "is-png": "^2.0.0", + "optipng-bin": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/imagemin-pngquant": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/imagemin-pngquant/-/imagemin-pngquant-8.0.0.tgz", + "integrity": "sha512-PVq0diOxO+Zyq/zlMCz2Pfu6mVLHgiT1GpW702OwVlnej+NhS6ZQegYi3OFEDW8d7GxouyR5e8R+t53SMciOeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^1.0.0", + "is-png": "^2.0.0", + "is-stream": "^2.0.0", + "ow": "^0.13.2", + "pngquant-bin": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/imagemin-pngquant/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imagemin-svgo": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/imagemin-svgo/-/imagemin-svgo-7.1.0.tgz", + "integrity": "sha512-0JlIZNWP0Luasn1HT82uB9nU9aa+vUj6kpT+MjPW11LbprXC+iC4HDwn1r4Q2/91qj4iy9tRZNsFySMlEpLdpg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "is-svg": "^4.2.1", + "svgo": "^1.3.2" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sindresorhus/imagemin-svgo?sponsor=1" + } + }, + "node_modules/imagemin/node_modules/file-type": { + "version": "12.4.2", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-12.4.2.tgz", + "integrity": "sha512-UssQP5ZgIOKelfsaB5CuGAL+Y+q7EmONuiwF3N5HAH0t27rvrttgi6Ra9k/+DVaY9UF6+ybxu5pOXLUdA8N7Vg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/imagemin/node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imagemin/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/immutable": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.8.2.tgz", + "integrity": "sha512-15gZoQ38eYjEjxkorfbcgBKBL6R7T459OuK+CpcWt7O3KF4uPCx2tD0uFETlUDIyo+1789crbMhTvQBSR5yBMg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/import-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz", + "integrity": "sha512-Ew5AZzJQFqrOV5BTW3EIoHAnoie1LojZLXKcCQ/yTRyVZosBhK1x1ViYjHGf5pAFOq8ZyChZp6m/fSN7pJyZtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "import-from": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/import-fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", + "integrity": "sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==", + "dev": true, + "license": "MIT", + "dependencies": { + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/import-from": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-from/-/import-from-2.1.0.tgz", + "integrity": "sha512-0vdnLL2wSGnhlRmzHJAg5JHjt1l2vYhzJ7tNLGbeVg0fse56tpGaH0uzH+r9Slej+BSXXEHvBKDEnVSLLE9/+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/import-lazy": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-3.1.0.tgz", + "integrity": "sha512-8/gvXvX2JMn0F+CDlSC4l6kOmVaLOO3XLkksI7CI3Ud95KDYJuYur2b9P/PUt/i/pDAMd/DulQsNbbbmRRsDIQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha512-aqwDFWSgSgfRaEwao5lg5KEcVd/2a+D1rvoG7NdilmYz0NwRk6StWpWdz/Hpk34MKPpx7s8XxUqimfcQK6gGlg==", + "dev": true, + "license": "MIT", + "dependencies": { + "repeating": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/indexes-of": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", + "integrity": "sha512-bup+4tap3Hympa+JBJUG7XuOsdNQ6fxt0MHyXMKuLBKn0OqsTfvUxkUrroEX1+B2VsSHvCjiIcZVxRtYa4nllA==", + "dev": true, + "license": "MIT" + }, + "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, + "license": "ISC", + "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, + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC" + }, + "node_modules/internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/into-stream": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-3.1.0.tgz", + "integrity": "sha512-TcdjPibTksa1NQximqep2r17ISRiNE9fwlfbg3F8ANdvP5/yrFTew86VcO//jk4QTaMlbjypPBq76HN2zaKfZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "from2": "^2.1.1", + "p-is-promise": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha512-xgs2NH9AE66ucSq4cNG1nhSFghr5l6tdL15Pk+jl46bmmBapgoaY/AacXyaDznAqmGL99TiLSQgO/XazFSKYeQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/irregular-plurals": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-2.0.0.tgz", + "integrity": "sha512-Y75zBYLkh0lJ9qxeHlMjQ7bSbyiSqNW/UOPWDmzC7cXskL1hekSITh1Oc6JV0XCWWZ9DE8VYSB71xocLk3gmGw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/is": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/is/-/is-3.3.0.tgz", + "integrity": "sha512-nW24QBoPcFGGHJGUwnfpI7Yc5CdqWNdsyHQszVE/z2pKHXzh7FZ5GWhJqSyaQ9wMkQnsTx+kAI8bHlCX4tKdbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/is-absolute": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", + "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-relative": "^1.0.0", + "is-windows": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-absolute-url": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz", + "integrity": "sha512-vOx7VprsKyllwjSkLV79NIhpyLfr3jAp7VaTCMXOJHu4m0Ew1CZ2fcjASwmV1jI3BWuWHB013M48eyeldk9gYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-color-stop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz", + "integrity": "sha512-H1U8Vz0cfXNujrJzEcvvwMDW9Ra+biSYA3ThdQvAnMLJkEHQXn6bWzLkxHtVYJ+Sdbx0b6finn3jZiaVe7MAHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "css-color-names": "^0.0.4", + "hex-color-regex": "^1.1.0", + "hsl-regex": "^1.0.0", + "hsla-regex": "^1.0.0", + "rgb-regex": "^1.0.1", + "rgba-regex": "^1.0.0" + } + }, + "node_modules/is-core-module": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-descriptor/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-directory": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", + "integrity": "sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finite": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", + "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-gif": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-gif/-/is-gif-3.0.0.tgz", + "integrity": "sha512-IqJ/jlbw5WJSNfwQ/lHEDXF8rxhRgF6ythk2oiEvhpG29F704eX9NO6TvPfMiq9DrbwgcEDnETYNcZDPewQoVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "file-type": "^10.4.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-gif/node_modules/file-type": { + "version": "10.11.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-10.11.0.tgz", + "integrity": "sha512-uzk64HRpUZyTGZtVuvrjP0FYxzQrBf4rojot6J65YMEbwBLB0CWm0CLojVpwpmFmxcE/lkvYICgfcGozbBq6rw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-jpg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-jpg/-/is-jpg-2.0.0.tgz", + "integrity": "sha512-ODlO0ruzhkzD3sdynIainVP5eoOFNN85rxA1+cwwnPe4dKyX0r5+hxNO5XpCrxlHcmb9vkOit9mhRD2JVuimHg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-natural-number": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-4.0.1.tgz", + "integrity": "sha512-Y4LTamMe0DDQIIAlaer9eKebAlDSV6huy+TWhJVPlzZh2o4tRP5SQWFlLn5N0To4mDD22/qdOq+veo1cSISLgQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-negated-glob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz", + "integrity": "sha512-czXVVn/QEmgvej1f50BZ648vUI+em0xqMq2Sn+QncCLN4zj1UAxlT+kw/6ggQTOaZPd1HqKQGEqbpQVtJucWug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number-like": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/is-number-like/-/is-number-like-1.0.8.tgz", + "integrity": "sha512-6rZi3ezCyFcn5L71ywzz2bS5b2Igl1En3eTlZlvKjpz1n3IZLAYMbKYAIQgFmEu0GENg92ziU/faEOA/aixjbA==", + "dev": true, + "license": "ISC", + "dependencies": { + "lodash.isfinite": "^3.3.2" + } + }, + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-object": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.2.tgz", + "integrity": "sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "license": "MIT", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-png": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-png/-/is-png-2.0.0.tgz", + "integrity": "sha512-4KPGizaVGj2LK7xwJIz8o5B2ubu1D/vcQsgOGFEDlpcvgZHto4gBnyd0ig7Ws+67ixmwKoNmu0hYnpo6AaKb5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-relative": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", + "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-unc-path": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-resolvable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", + "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", + "dev": true, + "license": "ISC" + }, + "node_modules/is-retry-allowed": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", + "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-svg": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-4.3.2.tgz", + "integrity": "sha512-mM90duy00JGMyjqIVHu9gNTjywdZV+8qNasX8cm/EEYZ53PHDgajvbBwNVvty5dwSAxLUD3p3bdo+7sR/UMrpw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "fast-xml-parser": "^3.19.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-unc-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", + "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "unc-path-regex": "^0.1.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-valid-glob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-1.0.0.tgz", + "integrity": "sha512-AhiROmoEFDSsjx8hW+5sGwgKVIORcXnrlAx/R0ZSeaPw70Vw0CqkGBBhHGL58Uox2eXnU1AnvXJl1XlyedO5bA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", + "dev": true, + "license": "MIT" + }, + "node_modules/isurl": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz", + "integrity": "sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-to-string-tag-x": "^1.2.0", + "is-object": "^1.0.1" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/@types/node": { + "version": "18.11.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.10.tgz", + "integrity": "sha512-juG3RWMBOqcOuXC643OAdSA525V44cVgGV6dUDuiFtss+8Fk5x1hI93Rsld43VeJVIeqlP9I7Fn9/qaVqoEAuQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jpegtran-bin": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/jpegtran-bin/-/jpegtran-bin-5.0.2.tgz", + "integrity": "sha512-4FSmgIcr8d5+V6T1+dHbPZjaFH0ogVyP4UVsE+zri7S9YLO4qAT2our4IN3sW3STVgNTbqPermdIgt2XuAJ4EA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "bin-build": "^3.0.0", + "bin-wrapper": "^4.0.0", + "logalot": "^2.0.0" + }, + "bin": { + "jpegtran": "cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsdom": { + "version": "12.2.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-12.2.0.tgz", + "integrity": "sha512-QPOggIJ8fquWPLaYYMoh+zqUmdphDtu1ju0QGTitZT1Yd8I5qenPpXM1etzUegu3MjVp8XPzgZxdn8Yj7e40ig==", + "dev": true, + "license": "MIT", + "dependencies": { + "abab": "^2.0.0", + "acorn": "^6.0.2", + "acorn-globals": "^4.3.0", + "array-equal": "^1.0.0", + "cssom": "^0.3.4", + "cssstyle": "^1.1.1", + "data-urls": "^1.0.1", + "domexception": "^1.0.1", + "escodegen": "^1.11.0", + "html-encoding-sniffer": "^1.0.2", + "nwsapi": "^2.0.9", + "parse5": "5.1.0", + "pn": "^1.1.0", + "request": "^2.88.0", + "request-promise-native": "^1.0.5", + "saxes": "^3.1.3", + "symbol-tree": "^3.2.2", + "tough-cookie": "^2.4.3", + "w3c-hr-time": "^1.0.1", + "webidl-conversions": "^4.0.2", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.2.0", + "whatwg-url": "^7.0.0", + "ws": "^6.1.0", + "xml-name-validator": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jsdom/node_modules/acorn": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/jsdom/node_modules/ws": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz", + "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-limiter": "~1.0.0" + } + }, + "node_modules/json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha512-CuUqjv0FUZIdXkHPI8MezCnFCdaTAacej1TZYulLoAg1h/PhwkdXFN4V/gzY4g+fMBCOV2xF+rp7t2XD2ns/NQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "dev": true, + "license": "(AFL-2.1 OR BSD-3-Clause)" + }, + "node_modules/json-schema-traverse": { + "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==", + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "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" + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true, + "license": "ISC" + }, + "node_modules/json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsprim": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/junk": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/junk/-/junk-3.1.0.tgz", + "integrity": "sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/just-debounce": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/just-debounce/-/just-debounce-1.1.0.tgz", + "integrity": "sha512-qpcRocdkUmf+UTNBYx5w6dexX5J31AKK1OmPwH630a83DdVVUIngk55RSAiIGpQyoH0dlr872VHfPjnQnK1qDQ==", + "dev": true, + "license": "MIT" + }, + "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, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/keyv/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, + "license": "MIT" + }, + "node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/last-run": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/last-run/-/last-run-1.1.1.tgz", + "integrity": "sha512-U/VxvpX4N/rFvPzr3qG5EtLKEnNI0emvIQB3/ecEwv+8GHaUKbIB8vxv1Oai5FAF0d0r7LXHhLLe5K/yChm5GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "default-resolution": "^2.0.0", + "es6-weak-map": "^2.0.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" + } + }, + "node_modules/lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha512-YiGkH6EnGrDGqLMITnGjXtGmNtjoXw9SVUzcaos8RBi7Ps0VBylkq+vOcY9QE5poLasPCR849ucFUkl0UzUyOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "invert-kv": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lead": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lead/-/lead-1.0.0.tgz", + "integrity": "sha512-IpSVCk9AYvLHo5ctcIXxOBpMWUe+4TKN3VPWAKUbJikkmsGp0VrSM8IttVc32D6J4WUsiPE6aEFRNmIoF/gdow==", + "dev": true, + "license": "MIT", + "dependencies": { + "flush-write-stream": "^1.0.2" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/levn/node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/levn/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, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/liftoff": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-3.1.0.tgz", + "integrity": "sha512-DlIPlJUkCV0Ips2zf2pJP0unEoT1kwYhiiPUGF3s/jtxTCjziNLoiVVh+jqWOWeFi6mmwQ5fNxvAUyPad4Dfog==", + "dev": true, + "license": "MIT", + "dependencies": { + "extend": "^3.0.0", + "findup-sync": "^3.0.0", + "fined": "^1.0.1", + "flagged-respawn": "^1.0.0", + "is-plain-object": "^2.0.4", + "object.map": "^1.0.0", + "rechoir": "^0.6.2", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/liftoff/node_modules/findup-sync": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz", + "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-file": "^1.0.0", + "is-glob": "^4.0.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/limiter": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", + "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==", + "dev": true + }, + "node_modules/load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha512-cy7ZdNRXdablkXYNI049pthVeXFurRyb9+hA/dZzerZ0pGTx42z+y+ssxBaVV2l70t1muq5IdKhn4UtcoGUY9A==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/localtunnel": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/localtunnel/-/localtunnel-2.0.2.tgz", + "integrity": "sha512-n418Cn5ynvJd7m/N1d9WVJISLJF/ellZnfsLnx8WBWGzxv/ntNcFkJ1o6se5quUhCplfLGBNL5tYHiq5WF3Nug==", + "dev": true, + "license": "MIT", + "dependencies": { + "axios": "0.21.4", + "debug": "4.3.2", + "openurl": "1.1.1", + "yargs": "17.1.1" + }, + "bin": { + "lt": "bin/lt.js" + }, + "engines": { + "node": ">=8.3.0" + } + }, + "node_modules/localtunnel/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, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/localtunnel/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/localtunnel/node_modules/debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/localtunnel/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, + "license": "MIT" + }, + "node_modules/localtunnel/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, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/localtunnel/node_modules/yargs": { + "version": "17.1.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.1.1.tgz", + "integrity": "sha512-c2k48R0PwKIqKhPMWjeiF6y2xY/gPMUlro0sgxqXpbOIohWiLNXWslsootttv7E1e73QPAMQSg5FeySbVcpsPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=12" + } + }, + "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, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash._escapehtmlchar": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._escapehtmlchar/-/lodash._escapehtmlchar-2.4.1.tgz", + "integrity": "sha512-eHm2t2Lg476lq5v4FVmm3B5mCaRlDyTE8fnMfPCEq2o46G4au0qNXIKh7YWhjprm1zgSMLcMSs1XHMgkw02PbQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash._htmlescapes": "~2.4.1" + } + }, + "node_modules/lodash._escapestringchar": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._escapestringchar/-/lodash._escapestringchar-2.4.1.tgz", + "integrity": "sha512-iZ6Os4iipaE43pr9SBks+UpZgAjJgRC+lGf7onEoByMr1+Nagr1fmR7zCM6Q4RGMB/V3a57raEN0XZl7Uub3/g==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash._htmlescapes": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._htmlescapes/-/lodash._htmlescapes-2.4.1.tgz", + "integrity": "sha512-g79hNmMOBVyV+4oKIHM7MWy9Awtk3yqf0Twlawr6f+CmG44nTwBh9I5XiLUnk39KTfYoDBpS66glQGgQCnFIuA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash._isnative": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._isnative/-/lodash._isnative-2.4.1.tgz", + "integrity": "sha512-BOlKGKNHhCHswGOWtmVb5zBygyxN7EmTuzVOSQI6QSoGhG+kvv71gICFS1TBpnqvT1n53txK8CDK3u5D2/GZxQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash._objecttypes": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._objecttypes/-/lodash._objecttypes-2.4.1.tgz", + "integrity": "sha512-XpqGh1e7hhkOzftBfWE7zt+Yn9mVHFkDhicVttvKLsoCMLVVL+xTQjfjB4X4vtznauxv0QZ5ZAeqjvat0dh62Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash._reinterpolate": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-2.4.1.tgz", + "integrity": "sha512-QGEOOjJi7W9LIgDAMVgtGBb8Qgo8ieDlSOCoZjtG45ZNRvDJZjwVMTYlfTIWdNRUiR1I9BjIqQ3Zaf1+DYM94g==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash._reunescapedhtml": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._reunescapedhtml/-/lodash._reunescapedhtml-2.4.1.tgz", + "integrity": "sha512-CfmZRU1Mk4E/5jh+Wu8lc7tuc3VkuwWZYVIgdPDH9NRSHgiL4Or3AA4JCIpgrkVzHOM+jKu2OMkAVquruhRHDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash._htmlescapes": "~2.4.1", + "lodash.keys": "~2.4.1" + } + }, + "node_modules/lodash._shimkeys": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._shimkeys/-/lodash._shimkeys-2.4.1.tgz", + "integrity": "sha512-lBrglYxLD/6KAJ8IEa5Lg+YHgNAL7FyKqXg4XOUI+Du/vtniLs1ZqS+yHNKPkK54waAgkdUnDOYaWf+rv4B+AA==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash._objecttypes": "~2.4.1" + } + }, + "node_modules/lodash.clone": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-4.5.0.tgz", + "integrity": "sha512-GhrVeweiTD6uTmmn5hV/lzgCQhccwReIVRLHp7LT4SopOjqEZ5BbX8b5WWEtAKasjmy8hR7ZPwsYlxRCku5odg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.escape": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-2.4.1.tgz", + "integrity": "sha512-PiEStyvZ8gz37qBE+HqME1Yc/ewb/59AMOu8pG7Ztani86foPTxgzckQvMdphmXPY6V5f20Ex/CaNBqHG4/ycQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash._escapehtmlchar": "~2.4.1", + "lodash._reunescapedhtml": "~2.4.1", + "lodash.keys": "~2.4.1" + } + }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isfinite": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/lodash.isfinite/-/lodash.isfinite-3.3.2.tgz", + "integrity": "sha512-7FGG40uhC8Mm633uKW1r58aElFlBlxCrg9JfSi3P6aYiWmfiWF0PgMd86ZUsxE5GwWPdHoS2+48bwTh2VPkIQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isobject": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-2.4.1.tgz", + "integrity": "sha512-sTebg2a1PoicYEZXD5PBdQcTlIJ6hUslrlWr7iV0O7n+i4596s2NQ9I5CaZ5FbXSfya/9WQsrYLANUJv9paYVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash._objecttypes": "~2.4.1" + } + }, + "node_modules/lodash.keys": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-2.4.1.tgz", + "integrity": "sha512-ZpJhwvUXHSNL5wYd1RM6CUa2ZuqorG9ngoJ9Ix5Cce+uX7I5O/E06FCJdhSZ33b5dVyeQDnIlWH7B2s5uByZ7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash._isnative": "~2.4.1", + "lodash._shimkeys": "~2.4.1", + "lodash.isobject": "~2.4.1" + } + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true, + "license": "MIT" + }, + "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, + "license": "MIT" + }, + "node_modules/lodash.some": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz", + "integrity": "sha512-j7MJE+TuT51q9ggt4fSgVqro163BEFjAt3u97IqU+JA2DkWl80nFTrowzLpZ/BnpN7rrl0JA/593NAdd8p/scQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.template": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-2.4.1.tgz", + "integrity": "sha512-5yLOQwlS69xbaez3g9dA1i0GMAj8pLDHp8lhA4V7M1vRam1lqD76f0jg5EV+65frbqrXo1WH9ZfKalfYBzJ5yQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash._escapestringchar": "~2.4.1", + "lodash._reinterpolate": "~2.4.1", + "lodash.defaults": "~2.4.1", + "lodash.escape": "~2.4.1", + "lodash.keys": "~2.4.1", + "lodash.templatesettings": "~2.4.1", + "lodash.values": "~2.4.1" + } + }, + "node_modules/lodash.template/node_modules/lodash.defaults": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-2.4.1.tgz", + "integrity": "sha512-5wTIPWwGGr07JFysAZB8+7JB2NjJKXDIwogSaRX5zED85zyUAQwtOqUk8AsJkkigUcL3akbHYXd5+BPtTGQPZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash._objecttypes": "~2.4.1", + "lodash.keys": "~2.4.1" + } + }, + "node_modules/lodash.templatesettings": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-2.4.1.tgz", + "integrity": "sha512-vY3QQ7GxbeLe8XfTvoYDbaMHO5iyTDJS1KIZrxp00PRMmyBKr8yEcObHSl2ppYTwd8MgqPXAarTvLA14hx8ffw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash._reinterpolate": "~2.4.1", + "lodash.escape": "~2.4.1" + } + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.values": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.values/-/lodash.values-2.4.1.tgz", + "integrity": "sha512-fQwubKvj2Nox2gy6YnjFm8C1I6MIlzKUtBB+Pj7JGtloGqDDL5CPRr4DUUFWPwXWwAl2k3f4C3Aw8H1qAPB9ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash.keys": "~2.4.1" + } + }, + "node_modules/logalot": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/logalot/-/logalot-2.1.0.tgz", + "integrity": "sha512-Ah4CgdSRfeCJagxQhcVNMi9BfGYyEKLa6d7OA6xSbld/Hg3Cf2QiOa1mDpmG7Ve8LOH6DN3mdttzjQAvWTyVkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "figures": "^1.3.5", + "squeak": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/longest": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", + "integrity": "sha512-k+yt5n3l48JU4k8ftnKG6V7u32wyH2NfKzeMto9F/QRE0amxy/LayxwlvjjkZEIzqR+19IrtFO8p5kB9QaYUFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha512-RPNliZOFkqFumDhvYqOaNY4Uz9oJM2K9tC6JWsJJsNdhuONW4LQHRBpb0qf4pJApVffI5N39SwzWZJuEhfd7eQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lower-case": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", + "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lpad-align": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/lpad-align/-/lpad-align-1.1.2.tgz", + "integrity": "sha512-MMIcFmmR9zlGZtBcFOows6c2COMekHCIFJz3ew/rRpKZ1wR4mXDPzvcVqLarux8M33X4TPSq2Jdw8WJj0q0KbQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-stdin": "^4.0.1", + "indent-string": "^2.1.0", + "longest": "^1.0.0", + "meow": "^3.3.0" + }, + "bin": { + "lpad-align": "cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/make-dir/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/make-iterator": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", + "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==", + "dev": true, + "license": "MIT", + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/make-iterator/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/matchdep": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/matchdep/-/matchdep-2.0.0.tgz", + "integrity": "sha512-LFgVbaHIHMqCRuCZyfCtUOq9/Lnzhi7Z0KFUE2fhD54+JN2jLh3hC02RLkqauJ3U4soU6H1J3tfj/Byk7GoEjA==", + "dev": true, + "license": "MIT", + "dependencies": { + "findup-sync": "^2.0.0", + "micromatch": "^3.0.4", + "resolve": "^1.4.0", + "stack-trace": "0.0.10" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/matcher": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", + "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "escape-string-regexp": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/matcher/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, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mdn-data": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", + "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memory-fs": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", + "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", + "dev": true, + "license": "MIT", + "dependencies": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + }, + "engines": { + "node": ">=4.3.0 <5.0.0 || >=5.10" + } + }, + "node_modules/meow": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha512-TNdwZs0skRlpPpCUK25StC4VH+tP5GgeY1HQOOGP+lQ2xtdkN2VtT/5tiX9k3IWpkBPV9b3LsAWXn4GGi/PrSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "camelcase-keys": "^2.0.0", + "decamelize": "^1.1.2", + "loud-rejection": "^1.0.0", + "map-obj": "^1.0.1", + "minimist": "^1.1.3", + "normalize-package-data": "^2.3.4", + "object-assign": "^4.0.1", + "read-pkg-up": "^1.0.1", + "redent": "^1.0.0", + "trim-newlines": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/merge": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/merge/-/merge-1.2.1.tgz", + "integrity": "sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "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, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "license": "MIT", + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/micromatch/node_modules/define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/micromatch/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/micromatch/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/micromatch/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mime": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "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", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mitt": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-1.2.0.tgz", + "integrity": "sha512-r6lj77KlwqLhIUku9UWYes7KJtsczvolZkzp8hbaDPPaE24OmWl5s539Mytlj22siEQKosZ26qCBgda2PKwoJw==", + "dev": true, + "license": "MIT" + }, + "node_modules/mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mixin-deep/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mozjpeg": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/mozjpeg/-/mozjpeg-6.0.1.tgz", + "integrity": "sha512-9Z59pJMi8ni+IUvSH5xQwK5tNLw7p3dwDNCZ3o1xE+of3G5Hc/yOz6Ue/YuLiBXU3ZB5oaHPURyPdqfBX/QYJA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "bin-build": "^3.0.0", + "bin-wrapper": "^4.0.0", + "logalot": "^2.1.0" + }, + "bin": { + "mozjpeg": "cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/multipipe": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/multipipe/-/multipipe-0.1.2.tgz", + "integrity": "sha512-7ZxrUybYv9NonoXgwoOqtStIu18D1c3eFZj27hqgf5kBrBF8Q+tE8V0MW8dKM5QLkQPh1JhhbKgHLY9kifov4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "duplexer2": "0.0.2" + } + }, + "node_modules/mute-stdout": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mute-stdout/-/mute-stdout-1.0.1.tgz", + "integrity": "sha512-kDcwXR4PS7caBpuRYYBUz9iVixUk3anO3f5OYFiIPwK/20vCzKCHyKoulbiDY1S53zD2bxUpxN/IJ+TnXjfvxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nanomatch/node_modules/define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nanomatch/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nanomatch/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nanomatch/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "license": "MIT" + }, + "node_modules/next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/no-case": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", + "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "lower-case": "^1.1.1" + } + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node.extend": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/node.extend/-/node.extend-1.1.8.tgz", + "integrity": "sha512-L/dvEBwyg3UowwqOUTyDsGBU6kjBQOpOhshio9V3i3BMPv5YUb9+mWNN8MK0IbWqT0AqaTSONZf0aTuMMahWgA==", + "dev": true, + "license": "(MIT OR GPL-2.0)", + "dependencies": { + "has": "^1.0.3", + "is": "^3.2.1" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/normalize-package-data/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz", + "integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/now-and-later": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/now-and-later/-/now-and-later-2.0.1.tgz", + "integrity": "sha512-KGvQ0cB70AQfg107Xvs/Fbu+dGmZoTRJp2TaPwcwQm3/7PteUyN2BCgk8KBMPGBUXZdVwyWS8fDCGFygBm19UQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "once": "^1.3.2" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/npm-conf": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.3.tgz", + "integrity": "sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw==", + "dev": true, + "license": "MIT", + "dependencies": { + "config-chain": "^1.1.11", + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-conf/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/nth-check": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "~1.0.0" + } + }, + "node_modules/num2fraction": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", + "integrity": "sha512-Y1wZESM7VUThYY+4W+X4ySH2maqcA+p7UR+w8VWNWVAd6lwuXXWz/w/Cz43J/dI2I+PS6wD5N+bJUF+gjWvIqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nwsapi": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.2.tgz", + "integrity": "sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw==", + "dev": true, + "license": "MIT" + }, + "node_modules/oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.assign": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.defaults": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", + "integrity": "sha512-c/K0mw/F11k4dEUBMW8naXUuBuhxRCfG7W+yFy8EcijU/rSmazOUd1XAEEe6bC0OuXY4HUKjTJv7xbxIMqdxrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-each": "^1.0.1", + "array-slice": "^1.0.0", + "for-own": "^1.0.0", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.getownpropertydescriptors": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.5.tgz", + "integrity": "sha512-yDNzckpM6ntyQiGTik1fKV1DcVDRS+w8bvpWNCBanvH5LfRX9O8WTHqQzG4RZwRAM4I0oU7TV11Lj5v0g20ibw==", + "dev": true, + "license": "MIT", + "dependencies": { + "array.prototype.reduce": "^1.0.5", + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz", + "integrity": "sha512-3+mAJu2PLfnSVGHwIWubpOFLscJANBKuB/6A4CxBstc4aqwQY0FWcsppuy4jU5GSB95yES5JHSI+33AWuS4k6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.reduce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.reduce/-/object.reduce-1.0.1.tgz", + "integrity": "sha512-naLhxxpUESbNkRqc35oQ2scZSJueHGQNUfMW/0U37IgN6tE2dgDWg3whf+NEliy3F/QysrO48XKUz/nGPe+AQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.values": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", + "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "dev": true, + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "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, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/open/-/open-0.0.5.tgz", + "integrity": "sha512-+X/dJYLapVO1VbC620DhtNZK9U4/kQVaTQp/Gh7cb6UTLYfGZzzU2ZXkWrOA/wBrf4UqAFwtLqXYTxe4tSnWQQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/openurl": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/openurl/-/openurl-1.1.1.tgz", + "integrity": "sha512-d/gTkTb1i1GKz5k3XE3XFV/PxQ1k45zDqGP2OA7YhgsaLoqm6qRvARAZOFer1fcXritWlGBRCu/UgeS4HAnXAA==", + "dev": true, + "license": "MIT" + }, + "node_modules/opn": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.3.0.tgz", + "integrity": "sha512-bYJHo/LOmoTd+pfiYhfZDnf9zekVJrY+cnS2a5F2x+w5ppvTqObojTP7WiFG+kVZs9Inw+qQ/lw7TroWwhdd2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-wsl": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/optimist": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha512-snN4O4TkigujZphWLN0E//nQmm7790RYaE53DdL7ZYwee2D8DDo9/EyYiKUfN3rneWUjhJnueija3G9I2i0h3g==", + "dev": true, + "license": "MIT/X11", + "dependencies": { + "minimist": "~0.0.1", + "wordwrap": "~0.0.2" + } + }, + "node_modules/optimist/node_modules/minimist": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", + "integrity": "sha512-iotkTvxc+TwOm5Ieim8VnSNvCDjCK9S8G3scJ50ZthspSxa7jx50jkhYduuAtAjvfDUwSgOwf8+If99AlOEhyw==", + "dev": true, + "license": "MIT" + }, + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/optionator/node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/optionator/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, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/optipng-bin": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/optipng-bin/-/optipng-bin-6.0.0.tgz", + "integrity": "sha512-95bB4y8IaTsa/8x6QH4bLUuyvyOoGBCLDA7wOgDL8UFqJpSUh1Hob8JRJhit+wC1ZLN3tQ7mFt7KuBj0x8F2Wg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "bin-build": "^3.0.0", + "bin-wrapper": "^4.0.0", + "logalot": "^2.0.0" + }, + "bin": { + "optipng": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ordered-read-streams": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz", + "integrity": "sha512-Z87aSjx3r5c0ZB7bcJqIgIRX5bxR7A4aSzvIbaxd0oTkWBCOoKfuGHiKj60CHVUgg1Phm5yMZzBdt8XqRs73Mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "^2.0.1" + } + }, + "node_modules/os-filter-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/os-filter-obj/-/os-filter-obj-2.0.0.tgz", + "integrity": "sha512-uksVLsqG3pVdzzPvmAHpBK0wKxYItuzZr7SziusRPoz67tGV8rL1szZ6IdeUrbqLjGDwApBtN29eEE3IqGHOjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "arch": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha512-PRT7ZORmwu2MEFt4/fv3Q+mEfN4zetKxufQrkShY2oGvUms9r8otu5HfdyIFHkYXjO7laNsoVGmM2MANfuTA8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "lcid": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ow": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/ow/-/ow-0.13.2.tgz", + "integrity": "sha512-9wvr+q+ZTDRvXDjL6eDOdFe5WUl/wa5sntf9kAolxqSpkBqaIObwLgFCGXSJASFw+YciXnOVtDWpxXa9cqV94A==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.5.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/p-event": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-event/-/p-event-1.3.0.tgz", + "integrity": "sha512-hV1zbA7gwqPVFcapfeATaNjQ3J0NuzorHPyG8GPL9g/Y/TplWVBVoCKCXL6Ej2zscrCEv195QNWJXuBH6XZuzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-timeout": "^1.1.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/p-is-promise": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz", + "integrity": "sha512-zL7VE4JVS2IFSkR2GQKDSPEVxkoH43/p7oEnwpdCndKYJO0HVeRB7fA8TJwuLOTBREtK0ea8eHaxdwcpob5dmg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "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, + "license": "MIT", + "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, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map-series": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-map-series/-/p-map-series-1.0.0.tgz", + "integrity": "sha512-4k9LlvY6Bo/1FcIdV33wqZQES0Py+iKISU9Uc8p8AjWoZPnFKMpVIVD3s0EYn4jzLh1I+WeUZkJ0Yoa4Qfw3Kg==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-reduce": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-pipe": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-pipe/-/p-pipe-3.1.0.tgz", + "integrity": "sha512-08pj8ATpzMR0Y80x50yJHn37NF6vjrqHutASaX5LiH5npS9XPvrUmscd9MF5R4fuYRHOxQR1FfMIlF7AzwoPqw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-reduce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-1.0.0.tgz", + "integrity": "sha512-3Tx1T3oM1xO/Y8Gj0sWyE78EIJZ+t+aEmXUdvQgvGmSMri7aPTHoovbXEreWKkL5j21Er60XAWLTzKbAKYOujQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/p-timeout": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-1.2.1.tgz", + "integrity": "sha512-gb0ryzr+K2qFqFv6qi3khoeqMZF/+ajxQipEF6NteZVnvz9tzdsfAVj3lYtn1gAXvH5lfLwfxEII799gt/mRIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-finally": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/param-case": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", + "integrity": "sha512-eQE845L6ot89sk2N8liD8HAuH4ca6Vvr7VWAWwt7+kvvG5aBcPmmphQ68JsEG2qa9n1TykS2DLeMt363AAH8/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "no-case": "^2.2.0" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "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", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-author": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/parse-author/-/parse-author-2.0.0.tgz", + "integrity": "sha512-yx5DfvkN8JsHL2xk2Os9oTia467qnvRgey4ahSm2X8epehBLx/gWLcy5KI+Y36ful5DzGbCS6RazqZGgy1gHNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "author-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parse-filepath": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", + "integrity": "sha512-FwdRXKCohSVeXqwtYonZTXtbGJKrn+HNyWDYVcp5yuJlesTwNH4rsmRZ+GrKAPJ5bLpRxESMeS+Rl0VCHRvB2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-absolute": "^1.0.0", + "map-cache": "^0.2.0", + "path-root": "^0.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "error-ex": "^1.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parse-node-version": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", + "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parse5": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz", + "integrity": "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha512-yTltuKuhtNeFJKa1PiRzfLAU5182q1y4Eb4XCJ3PBqyzEDkAZRzBrKKBct682ls9reBVHf9udYLN5Nd+K1B9BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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, + "license": "MIT", + "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", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-root": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", + "integrity": "sha512-QLcPegTHF11axjfojBIoDygmS2E3Lf+8+jI6wOVmNVenrKSo3mFdSGiIgdSHenczw3wPtlVMQaFVwGmM7BJdtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-root-regex": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-root-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", + "integrity": "sha512-4GlJ6rZDhQZFE0DPVKh0e9jmZ5egZfxTkp7bcRDuPlJXbAwhxcl2dINPUAsjLdejqaLsCeg8axcLjIbvBjN4pQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-starts-with": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-starts-with/-/path-starts-with-2.0.0.tgz", + "integrity": "sha512-3UHTHbJz5+NLkPafFR+2ycJOjoc4WV2e9qCZCnm71zHiWaFrm1XniLVTkZXvaRgxr1xFh9JsTdicpH2yM03nLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha512-S4eENJz1pkiQn9Znv33Q+deTOKmbl+jj1Fl+qiP/vYezj+S8x+J3Uo0ISrx/QoEvIlOaDWJhPaRd1flJ9HXZqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pe-library": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pe-library/-/pe-library-1.0.1.tgz", + "integrity": "sha512-nh39Mo1eGWmZS7y+mK/dQIqg7S1lp38DpRxkyoHf0ZcUs/HDc+yyTjuOtTvSMZHmfSLuSQaX945u05Y2Q6UWZg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14", + "npm": ">=7" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jet2jet" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true, + "license": "MIT" + }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "pinkie": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pkginfo": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.1.tgz", + "integrity": "sha512-yO5feByMzAp96LtP58wvPKSbaKAi/1C4kV9XpTctr6EepnP6F33RBNOiVrdz9BrPA98U2BMFsTNHo44TWcbQ2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/plist": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", + "integrity": "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@xmldom/xmldom": "^0.8.8", + "base64-js": "^1.5.1", + "xmlbuilder": "^15.1.1" + }, + "engines": { + "node": ">=10.4.0" + } + }, + "node_modules/plugin-error": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", + "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^1.0.1", + "arr-diff": "^4.0.0", + "arr-union": "^3.1.0", + "extend-shallow": "^3.0.2" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/plugin-error/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/plugin-error/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/plur": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/plur/-/plur-3.1.1.tgz", + "integrity": "sha512-t1Ax8KUvV3FFII8ltczPn2tJdjqbd1sIzu6t4JL7nQ3EyeL/lTrj5PWKb06ic5/6XYDr65rQ4uzQEGN70/6X5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "irregular-plurals": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", + "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==", + "dev": true, + "license": "MIT" + }, + "node_modules/pngquant-bin": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/pngquant-bin/-/pngquant-bin-5.0.2.tgz", + "integrity": "sha512-OLdT+4JZx5BqE1CFJkrvomYV0aSsv6x2Bba+aWaVc0PMfWlE+ZByNKYAdKeIqsM4uvW1HOSEHnf8KcOnykPNxA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "bin-build": "^3.0.0", + "bin-wrapper": "^4.0.1", + "execa": "^0.10.0", + "logalot": "^2.0.0" + }, + "bin": { + "pngquant": "cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pngquant-bin/node_modules/execa": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.10.0.tgz", + "integrity": "sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^6.0.0", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/portscanner": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/portscanner/-/portscanner-2.2.0.tgz", + "integrity": "sha512-IFroCz/59Lqa2uBvzK3bKDbDDIEaAY8XJ1jFxcLWTqosrsc32//P4VuSB2vZXoHiHqOmx8B5L5hnKOxL/7FlPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "async": "^2.6.0", + "is-number-like": "^1.0.3" + }, + "engines": { + "node": ">=0.4", + "npm": ">=1.0.0" + } + }, + "node_modules/posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-assets": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-assets/-/postcss-assets-5.0.0.tgz", + "integrity": "sha512-tq6EhGqdXUCQxiDFA2p22NYD2ru+nvNL2aT4n76jaXTd8kAN4rslPBT6O7R9lyUJem8ncMy3Hrr1V1sHVoOsuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "assets": "^3.0.0", + "bluebird": "^3.5.0", + "postcss": "^6.0.10", + "postcss-functions": "^3.0.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/postcss-assets/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-assets/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-assets/node_modules/postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/postcss-calc": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.5.tgz", + "integrity": "sha512-1tKHutbGtLtEZF6PT4JSihCHfIVldU72mZ8SdZHIYriIZ9fh9k9aWSppaT8rHsyI3dX+KSR+W+Ix9BMY3AODrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss": "^7.0.27", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.0.2" + } + }, + "node_modules/postcss-calc/node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-calc/node_modules/postcss-selector-parser": { + "version": "6.0.11", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz", + "integrity": "sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-calc/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/postcss-colormin": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-4.0.3.tgz", + "integrity": "sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.0.0", + "color": "^3.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-convert-values": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz", + "integrity": "sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-critical-split": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/postcss-critical-split/-/postcss-critical-split-2.5.3.tgz", + "integrity": "sha512-FDG+evU4RBGM9/LQ5nCktzFKjYH2O/SLollJwtrdGagXXbMvk620Bc9o8WpqHJnu573uxVkx9lhob1HZvSWhZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "merge": "^1.2.0", + "postcss": "^6.0.1" + } + }, + "node_modules/postcss-critical-split/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-critical-split/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-critical-split/node_modules/postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/postcss-discard-comments": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz", + "integrity": "sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-discard-duplicates": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz", + "integrity": "sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-discard-empty": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz", + "integrity": "sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-discard-overridden": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz", + "integrity": "sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-discard-unused": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-4.0.1.tgz", + "integrity": "sha512-/3vq4LU0bLH2Lj4NYN7BTf2caly0flUB7Xtrk9a5K3yLuXMkHMqMO/x3sDq8W2b1eQFSCyY0IVz2L+0HP8kUUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0", + "uniqs": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-functions": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-functions/-/postcss-functions-3.0.0.tgz", + "integrity": "sha512-N5yWXWKA+uhpLQ9ZhBRl2bIAdM6oVJYpDojuI1nF2SzXBimJcdjFwiAouBVbO5VuOF3qA6BSFWFc3wXbbj72XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "glob": "^7.1.2", + "object-assign": "^4.1.1", + "postcss": "^6.0.9", + "postcss-value-parser": "^3.3.0" + } + }, + "node_modules/postcss-functions/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-functions/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-functions/node_modules/postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/postcss-load-config": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.1.2.tgz", + "integrity": "sha512-/rDeGV6vMUo3mwJZmeHfEDvwnTKKqQ0S7OHUi/kJvvtx3aWtyWG2/0ZWnzCt2keEclwN6Tf0DST2v9kITdOKYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cosmiconfig": "^5.0.0", + "import-cwd": "^2.0.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-merge-idents": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-4.0.1.tgz", + "integrity": "sha512-43S/VNdF6II0NZ31YxcvNYq4gfURlPAAsJW/z84avBXQCaP4I4qRHUH18slW/SOlJbcxxCobflPNUApYDddS7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssnano-util-same-parent": "^4.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-merge-longhand": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz", + "integrity": "sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "css-color-names": "0.0.4", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "stylehacks": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-merge-rules": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz", + "integrity": "sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.0.0", + "caniuse-api": "^3.0.0", + "cssnano-util-same-parent": "^4.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0", + "vendors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-minify-font-values": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz", + "integrity": "sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-minify-gradients": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz", + "integrity": "sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssnano-util-get-arguments": "^4.0.0", + "is-color-stop": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-minify-params": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz", + "integrity": "sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "alphanum-sort": "^1.0.0", + "browserslist": "^4.0.0", + "cssnano-util-get-arguments": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "uniqs": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-minify-selectors": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz", + "integrity": "sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "alphanum-sort": "^1.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-charset": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz", + "integrity": "sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-display-values": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz", + "integrity": "sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-positions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz", + "integrity": "sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssnano-util-get-arguments": "^4.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-repeat-style": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz", + "integrity": "sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssnano-util-get-arguments": "^4.0.0", + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-string": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz", + "integrity": "sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA==", + "dev": true, + "license": "MIT", + "dependencies": { + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-timing-functions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz", + "integrity": "sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-unicode": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz", + "integrity": "sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-url": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz", + "integrity": "sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-absolute-url": "^2.0.0", + "normalize-url": "^3.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-whitespace": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz", + "integrity": "sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-ordered-values": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz", + "integrity": "sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssnano-util-get-arguments": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-reduce-idents": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-4.0.2.tgz", + "integrity": "sha512-Tz70Ri10TclPoCtFfftjFVddx3fZGUkr0dEDbIEfbYhFUOFQZZ77TEqRrU0e6TvAvF+Wa5VVzYTpFpq0uwFFzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-reduce-initial": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz", + "integrity": "sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.0.0", + "caniuse-api": "^3.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-reduce-transforms": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz", + "integrity": "sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssnano-util-get-match": "^4.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-selector-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "dot-prop": "^5.2.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/postcss-svgo": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-4.0.3.tgz", + "integrity": "sha512-NoRbrcMWTtUghzuKSoIm6XV+sJdvZ7GZSc3wdBN0W19FTtp2ko8NqLsgoh/m9CzNhU3KLPvQmjIwtaNFkaFTvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "svgo": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-unique-selectors": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz", + "integrity": "sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg==", + "dev": true, + "license": "MIT", + "dependencies": { + "alphanum-sort": "^1.0.0", + "postcss": "^7.0.0", + "uniqs": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/postcss-zindex": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-4.0.1.tgz", + "integrity": "sha512-d/8BlQcUdEugZNRM9AdCA2V4fqREUtn/wcixLN3L6ITgc2P/FMcVVYz8QZkhItWT9NB5qr8wuN2dJCE4/+dlrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "has": "^1.0.0", + "postcss": "^7.0.0", + "uniqs": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postject": { + "version": "1.0.0-alpha.6", + "resolved": "https://registry.npmjs.org/postject/-/postject-1.0.0-alpha.6.tgz", + "integrity": "sha512-b9Eb8h2eVqNE8edvKdwqkrY6O7kAwmI8kcnBv1NScolYJbo59XUF0noFq+lxbC1yN20bmC0WBEbDC5H/7ASb0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^9.4.0" + }, + "bin": { + "postject": "dist/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/postject/node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/prettier": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.2.tgz", + "integrity": "sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/pretty-bytes": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", + "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pretty-hrtime": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", + "integrity": "sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", + "dev": true, + "license": "ISC" + }, + "node_modules/proxy-middleware": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/proxy-middleware/-/proxy-middleware-0.5.1.tgz", + "integrity": "sha512-bMULIe/lp43YW3ZcPX4s8ND3GJPCjMyMGL049A+ikSZa2F+iPV0KiQZJsd+rwQd/7td0o3cpL/TPTg5apjUAyQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", + "dev": true, + "license": "MIT" + }, + "node_modules/pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", + "dev": true, + "license": "MIT" + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + } + }, + "node_modules/pumpify/node_modules/duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "node_modules/pumpify/node_modules/pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.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==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.0", + "teleport": ">=0.2.0" + } + }, + "node_modules/qs": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.2.3.tgz", + "integrity": "sha512-AY4g8t3LMboim0t6XWFdz6J5OuJ1ZNYu54SXihS/OMpgyCqYmcAJnWqkNSOjSjWmq3xxy+GF9uWQI2lI/7tKIA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/query-string": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz", + "integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==", + "dev": true, + "license": "MIT", + "dependencies": { + "decode-uri-component": "^0.2.0", + "object-assign": "^4.1.0", + "strict-uri-encode": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/queue-tick": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", + "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==", + "dev": true, + "license": "MIT" + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true, + "license": "ISC" + }, + "node_modules/raw-body/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha512-7BGwRHqt4s/uVbuyoeejRn4YmFnYZiFl4AuaeXHlgZf3sONF0SOGlxs2Pw8g6hCKupo08RafIO5YXFNOKTfwsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha512-WD9MTlNtI55IwYUS27iHh9tK3YoIVhxis8yKhLpTqWtml739uXc9NWTpxoHkfZf3+DkCCsXox94/VWZniuZm6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/readable-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", + "dev": true, + "dependencies": { + "resolve": "^1.1.6" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha512-qtW5hKzGQZqKoh6JNSD+4lfitfPKGz42e6QwiRmPM5mmKtR0N41AbJRYu0xJi7nhOJ4WDgRkKvAk6tw4WIwR4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "indent-string": "^2.1.0", + "strip-indent": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "dev": true, + "license": "MIT" + }, + "node_modules/regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "license": "MIT", + "dependencies": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/regex-not/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/regex-not/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", + "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/remove-bom-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz", + "integrity": "sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-buffer": "^1.1.5", + "is-utf8": "^0.2.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/remove-bom-stream": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/remove-bom-stream/-/remove-bom-stream-1.2.0.tgz", + "integrity": "sha512-wigO8/O08XHb8YPzpDDT+QmRANfW6vLqxfaXm1YXhnFf3AkSLyjfG3GEFg4McZkmgL7KvCj5u2KczkvSP6NfHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "remove-bom-buffer": "^3.0.0", + "safe-buffer": "^5.1.0", + "through2": "^2.0.3" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==", + "dev": true, + "license": "ISC" + }, + "node_modules/repeat-element": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", + "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha512-ZqtSMuVybkISo2OWvqvm7iHSWngvdaW3IpsT9/uP8v4gMi591LY6h35wdOfvQdWCKFWZWm2Y1Opp4kV7vQKT6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-finite": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/replace-ext": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz", + "integrity": "sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/replace-homedir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-homedir/-/replace-homedir-1.0.0.tgz", + "integrity": "sha512-CHPV/GAglbIB1tnQgaiysb8H2yCy8WQ7lcEwQ/eT+kLj0QHV8LnJW0zpqpE7RSkrMSRoa+EBoag86clf7WAgSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "homedir-polyfill": "^1.0.1", + "is-absolute": "^1.0.0", + "remove-trailing-separator": "^1.1.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/request-promise-core": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz", + "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==", + "dev": true, + "license": "ISC", + "dependencies": { + "lodash": "^4.17.19" + }, + "engines": { + "node": ">=0.10.0" + }, + "peerDependencies": { + "request": "^2.34" + } + }, + "node_modules/request-promise-native": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz", + "integrity": "sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==", + "dev": true, + "license": "ISC", + "dependencies": { + "request-promise-core": "1.1.4", + "stealthy-require": "^1.1.1", + "tough-cookie": "^2.3.3" + }, + "engines": { + "node": ">=0.12.0" + }, + "peerDependencies": { + "request": "^2.34" + } + }, + "node_modules/request/node_modules/qs": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha512-IqSUtOVP4ksd1C/ej5zeEh/BIP2ajqpn8c5x+q99gvcIG/Qf0cud5raVnE/Dwd0ua9TXYDoDc0RE5hBSdz22Ug==", + "dev": true, + "license": "ISC" + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/resedit": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/resedit/-/resedit-2.0.3.tgz", + "integrity": "sha512-oTeemxwoMuxxTYxXUwjkrOPfngTQehlv0/HoYFNkB4uzsP1Un1A9nI8JQKGOFkxpqkC7qkMs0lUsGrvUlbLNUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pe-library": "^1.0.1" + }, + "engines": { + "node": ">=14", + "npm": ">=7" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jet2jet" + } + }, + "node_modules/resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-options": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/resolve-options/-/resolve-options-1.1.0.tgz", + "integrity": "sha512-NYDgziiroVeDC29xq7bp/CacZERYsA9bXYd1ZmcJlF3BcrZv5pTb4NG7SjdyKDnXZ84aC4vo2u6sNKIA1LCu/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "value-or-function": "^3.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==", + "dev": true, + "license": "MIT" + }, + "node_modules/resp-modifier": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/resp-modifier/-/resp-modifier-6.0.2.tgz", + "integrity": "sha512-U1+0kWC/+4ncRFYqQWTx/3qkfE6a4B/h3XXgmXypfa0SPZ3t7cbbaFk297PjQS/yov24R18h6OZe6iZwj3NSLw==", + "dev": true, + "dependencies": { + "debug": "^2.2.0", + "minimatch": "^3.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/resp-modifier/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha512-/Fpe5guzJk1gPqdJLJR5u7eG/gNY4nImjbRDaVWVMRhne55TCmj2i9Q+54PBRfatRC8v/rIiv9BN0pMd9OV5EQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "lowercase-keys": "^1.0.0" + } + }, + "node_modules/ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12" + } + }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "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, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rgb-regex": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz", + "integrity": "sha512-gDK5mkALDFER2YLqH6imYvK6g02gpNGM4ILDZ472EwWfXZnC2ZEpoB2ECXTyOVUKuk/bPJZMzwQPBYICzP+D3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/rgba-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz", + "integrity": "sha512-zgn5OjNQXLUTdq8m17KdaicF6w89TZs8ZU8y0AYENIU6wG8GG6LLm0yLSiPY8DmaYmHdgRW8rnApjoT0fQRfMg==", + "dev": true, + "license": "MIT" + }, + "node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/roarr": { + "version": "2.15.4", + "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", + "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "boolean": "^3.0.1", + "detect-node": "^2.0.4", + "globalthis": "^1.0.1", + "json-stringify-safe": "^5.0.1", + "semver-compare": "^1.0.0", + "sprintf-js": "^1.1.2" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/roarr/node_modules/sprintf-js": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rx": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/rx/-/rx-4.1.0.tgz", + "integrity": "sha512-CiaiuN6gapkdl+cZUr67W6I8jquN4lkak3vtIsIWCl4XIPP8ffsoyN6/+PuGXnQy8Cu8W2y9Xxh31Rq4M6wUug==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/rxjs": { + "version": "5.5.12", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.12.tgz", + "integrity": "sha512-xx2itnL5sBbqeeiVgNPVuQQ1nC8Jp2WfNJhXWHmElW9YmrpS9UVnNzhP3EH3HFqexO5Tlp8GhYY+WEcqcVMvGw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "symbol-observable": "1.0.1" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ret": "~0.1.10" + } + }, + "node_modules/safe-regex-test": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", + "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/sass": { + "version": "1.56.1", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.56.1.tgz", + "integrity": "sha512-VpEyKpyBPCxE7qGDtOcdJ6fFbcpOM+Emu7uZLxVrkX8KVU/Dp5UF7WLvzqRuUhB6mqqQt1xffLoG+AndxTZrCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/sass/node_modules/immutable": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.1.0.tgz", + "integrity": "sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true, + "license": "ISC" + }, + "node_modules/saxes": { + "version": "3.1.11", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-3.1.11.tgz", + "integrity": "sha512-Ydydq3zC+WYDJK1+gRxRapLIED9PWeSuuS41wqyoRmzvhhh9nc+QQrVMKJYzJFULazeGhzSV0QleN2wD3boh2g==", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/seek-bzip": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.6.tgz", + "integrity": "sha512-e1QtP3YL5tWww8uKaOCQ18UxIT2laNBXHjV/S2WYCiK4udiv8lkG89KRIoCjUagnAmCBurjF4zEVX2ByBbnCjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^2.8.1" + }, + "bin": { + "seek-bunzip": "bin/seek-bunzip", + "seek-table": "bin/seek-bzip-table" + } + }, + "node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/semver-greatest-satisfied-range": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-1.1.0.tgz", + "integrity": "sha512-Ny/iyOzSSa8M5ML46IAx3iXc6tfOsYU2R4AXi2UpHk60Zrgyq6eqPj/xiOfS0rRl/iiQ/rdJkVjw/5cdUyCntQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "sver-compat": "^1.5.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/semver-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-2.0.0.tgz", + "integrity": "sha512-mUdIBBvdn0PLOeP3TEkMH7HHeUP3GjsXCwKarjv/kGmUFOYg1VqEemKhoQpWMu6X2I8kHeuVdGibLGkVK+/5Qw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/semver-truncate": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/semver-truncate/-/semver-truncate-1.1.2.tgz", + "integrity": "sha512-V1fGg9i4CL3qesB6U0L6XAm4xOJiHmt4QAacazumuasc03BvtFGIMCduv01JWQ69Nv+JST9TqhSCiJoxoY031w==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^5.3.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/semver-truncate/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/send": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", + "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.6.2", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "~2.3.0", + "range-parser": "~1.2.0", + "statuses": "~1.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serialize-error": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", + "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "type-fest": "^0.13.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/serialize-error/node_modules/type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-index/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/serve-static": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", + "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.2", + "send": "0.16.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/server-destroy": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/server-destroy/-/server-destroy-1.0.1.tgz", + "integrity": "sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true, + "license": "ISC" + }, + "node_modules/set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/simple-swizzle/node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "license": "MIT", + "dependencies": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "kind-of": "^3.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/snapdragon/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/socket.io": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.5.4.tgz", + "integrity": "sha512-m3GC94iK9MfIEeIBfbhJs5BqFibMtkRk8ZpKwG2QwxV0m/eEhPIV4ara6XCF1LWNAus7z58RodiZlAH71U3EhQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "debug": "~4.3.2", + "engine.io": "~6.2.1", + "socket.io-adapter": "~2.4.0", + "socket.io-parser": "~4.2.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.4.0.tgz", + "integrity": "sha512-W4N+o69rkMEGVuk2D/cvca3uYsvGlMwsySWV447y99gUPghxq42BxqLNMndb+a1mm/5/7NeXVQS7RLa2XyXvYg==", + "dev": true, + "license": "MIT" + }, + "node_modules/socket.io-client": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.5.4.tgz", + "integrity": "sha512-ZpKteoA06RzkD32IbqILZ+Cnst4xewU7ZYK12aS1mzHftFFjpoMz69IuhP/nL25pJfao/amoPI527KnuhFm01g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.2.3", + "socket.io-parser": "~4.2.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.1.tgz", + "integrity": "sha512-V4GrkLy+HeF1F/en3SpUaM+7XxYXpuMUWLGde1kSSh5nQMN4hLrbPIkD+otwh6q9R6NOQBN4AMaOZ2zVjui82g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/sort-keys": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", + "integrity": "sha512-vzn8aSqKgytVik0iwdBEi+zevbTYZogewTUM6dtpmGwEcdzbub/TX4bCzRhebDCRC3QzXgJsLRKB2V/Oof7HXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-plain-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sort-keys-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sort-keys-length/-/sort-keys-length-1.0.1.tgz", + "integrity": "sha512-GRbEOUqCxemTAk/b32F2xa8wDTs+Z1QHOkbhJDQTvv/6G3ZkbJ+frYWsTcc7cBB3Fu4wy4XlLCuNtJuMn7Gsvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "sort-keys": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-url": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", + "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", + "dev": true, + "license": "MIT" + }, + "node_modules/sparkles": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.1.tgz", + "integrity": "sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true, + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz", + "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "extend-shallow": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split-string/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split-string/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/squeak": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/squeak/-/squeak-1.3.0.tgz", + "integrity": "sha512-YQL1ulInM+ev8nXX7vfXsCsDh6IqXlrremc1hzi77776BtpWgYJUMto3UM05GSAaGzJgWekszjoKDrVNB5XG+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^1.0.0", + "console-stream": "^0.1.1", + "lpad-align": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sshpk": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", + "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/stealthy-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", + "integrity": "sha512-ZnWpYnYugiOVEY5GkcuJK1io5V8QmNYChG62gSit9pQVGErXtrKuPC55ITaVSukmMta5qpMU7vqLt2Lnni4f/g==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stream-exhaust": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stream-exhaust/-/stream-exhaust-1.0.2.tgz", + "integrity": "sha512-b/qaq/GlBK5xaq1yrK9/zFcyRSTNxmcZwFLGSTG0mXgZl/4Z6GgiyYOXOvY7N3eEvFRAG1bkDRz5EPGSvPYQlw==", + "dev": true, + "license": "MIT" + }, + "node_modules/stream-shift": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/stream-throttle": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/stream-throttle/-/stream-throttle-0.1.3.tgz", + "integrity": "sha512-889+B9vN9dq7/vLbGyuHeZ6/ctf5sNuGWsDy89uNxkFTAgzy0eK7+w5fL3KLNRTkLle7EgZGvHUphZW0Q26MnQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "commander": "^2.2.0", + "limiter": "^1.0.5" + }, + "bin": { + "throttleproxy": "bin/throttleproxy.js" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/streamx": { + "version": "2.12.5", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.12.5.tgz", + "integrity": "sha512-Y+nkFw57Z5JHT3zLlqFm3GccOy2FeYdUrrqita6Dd8kr/8enPn9GKa8IYf3/DmEKfZl/E2sWoSKUnd4qhonrgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-fifo": "^1.0.0", + "queue-tick": "^1.0.0" + } + }, + "node_modules/strict-uri-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", + "integrity": "sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/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, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/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, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", + "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", + "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-ansi/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-utf8": "^0.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-dirs": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-2.1.0.tgz", + "integrity": "sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-natural-number": "^4.0.1" + } + }, + "node_modules/strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha512-I5iQq6aFMM62fBEAIB/hXzwJD6EEZ0xEGCX2t7oXqaKPIRgt4WruAQ285BISgdkP+HLGWyeGmNJcpIwFeRYRUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-stdin": "^4.0.1" + }, + "bin": { + "strip-indent": "cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-outer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz", + "integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^1.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/stylehacks": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-4.0.3.tgz", + "integrity": "sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/sumchecker": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", + "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "debug": "^4.1.0" + }, + "engines": { + "node": ">= 8.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==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-color/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/sver-compat": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/sver-compat/-/sver-compat-1.5.0.tgz", + "integrity": "sha512-aFTHfmjwizMNlNE6dsGmoAM4lHjL0CyiobWaFiXWSlD7cIxshW422Nb8KbXCmR6z+0ZEPY+daXJrDyh/vuwTyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es6-iterator": "^2.0.1", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/svgo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz", + "integrity": "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^2.4.1", + "coa": "^2.0.2", + "css-select": "^2.0.0", + "css-select-base-adapter": "^0.1.1", + "css-tree": "1.0.0-alpha.37", + "csso": "^4.0.2", + "js-yaml": "^3.13.1", + "mkdirp": "~0.5.1", + "object.values": "^1.1.0", + "sax": "~1.2.4", + "stable": "^0.1.8", + "unquote": "~1.1.1", + "util.promisify": "~1.0.0" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/svgo/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/svgo/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/symbol-observable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz", + "integrity": "sha512-Kb3PrPYz4HanVF1LVGuAdW6LoVgIwjUYJGzFe7NDrBLCN4lsV/5J0MFurV+ygS4bRVwrCEt2c7MQ1R2a72oJDw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/tar-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz", + "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "bl": "^1.0.0", + "buffer-alloc": "^1.2.0", + "end-of-stream": "^1.0.0", + "fs-constants": "^1.0.0", + "readable-stream": "^2.3.0", + "to-buffer": "^1.1.1", + "xtend": "^4.0.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/teex": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz", + "integrity": "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "streamx": "^2.12.5" + } + }, + "node_modules/temp-dir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz", + "integrity": "sha512-xZFXEGbG7SNC3itwBzI3RYjq/cEhBkx2hJuKGIUOcEULmkQExXiHat2z/qkISYsuR+IKumhEfKKbV5qXmhICFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/tempfile": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tempfile/-/tempfile-2.0.0.tgz", + "integrity": "sha512-ZOn6nJUgvgC09+doCEF3oB+r3ag7kUvlsXEGX069QRD60p+P3uP7XG9N2/at+EyIRGSN//ZY3LyEotA1YpmjuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "temp-dir": "^1.0.0", + "uuid": "^3.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ternary-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ternary-stream/-/ternary-stream-3.0.0.tgz", + "integrity": "sha512-oIzdi+UL/JdktkT+7KU5tSIQjj8pbShj3OASuvDEhm0NT5lppsm7aXWAmAq4/QMaBIyfuEcNLbAQA+HpaISobQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "duplexify": "^4.1.1", + "fork-stream": "^0.0.4", + "merge-stream": "^2.0.0", + "through2": "^3.0.1" + } + }, + "node_modules/ternary-stream/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/ternary-stream/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/ternary-stream/node_modules/through2": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", + "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "2 || 3" + } + }, + "node_modules/terser": { + "version": "5.43.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.43.1.tgz", + "integrity": "sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.14.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.14", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.14.tgz", + "integrity": "sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "jest-worker": "^27.4.5", + "schema-utils": "^4.3.0", + "serialize-javascript": "^6.0.2", + "terser": "^5.31.1" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz", + "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/tfunk": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/tfunk/-/tfunk-4.0.0.tgz", + "integrity": "sha512-eJQ0dGfDIzWNiFNYFVjJ+Ezl/GmwHaFTBTjrtqNPW0S7cuVDBrZrmzUz6VkMeCR4DZFqhd4YtLwsw3i2wYHswQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^1.1.3", + "dlv": "^1.1.3" + } + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/through2-concurrent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/through2-concurrent/-/through2-concurrent-2.0.0.tgz", + "integrity": "sha512-R5/jLkfMvdmDD+seLwN7vB+mhbqzWop5fAjx5IX8/yQq7VhBhzDmhXgaHAOnhnWkCpRMM7gToYHycB0CS/pd+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "through2": "^2.0.0" + } + }, + "node_modules/through2-filter": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-3.0.0.tgz", + "integrity": "sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==", + "dev": true, + "license": "MIT", + "dependencies": { + "through2": "~2.0.0", + "xtend": "~4.0.0" + } + }, + "node_modules/time-stamp": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", + "integrity": "sha512-gLCeArryy2yNTRzTGKbZbloctj64jkZ57hj5zdraXue6aFgd6PmvVtEyiUU+hvU0v7q08oVv8r8ev0tRo6bvgw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/timed-out": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", + "integrity": "sha512-G7r3AhovYtr5YKOWQkta8RKAPb+J9IsO4uVmzjl8AZwfhs8UcUwTiD6gcJYSgOtzyjvQKrKYn41syHbUWMkafA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/timsort": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", + "integrity": "sha512-qsdtZH+vMoCARQtyod4imc2nIJwg9Cc7lPRrw9CzF8ZKR0khdr8+2nX80PBhET3tcyTtJDxAffGh2rXH4tyU8A==", + "dev": true, + "license": "MIT" + }, + "node_modules/tiny-lr": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/tiny-lr/-/tiny-lr-0.1.4.tgz", + "integrity": "sha512-whc75HNnTqUGFBbpsbrpFgNaz5yI/D8QkhFdJlc/sAZsVsYX6o9Dj+z2NPoRqCNILX9yDJZJZyYoNAsyRobLbQ==", + "dev": true, + "dependencies": { + "body-parser": "~1.8.0", + "debug": "~0.8.1", + "faye-websocket": "~0.7.2", + "parseurl": "~1.3.0", + "qs": "~2.2.3" + } + }, + "node_modules/tiny-lr/node_modules/debug": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-0.8.1.tgz", + "integrity": "sha512-HlXEJm99YsRjLJ8xmuz0Lq8YUwrv7hAJkTEr6/Em3sUlSUNl0UdFA+1SrY4fnykeq1FVkUEUtwRGHs9VvlYbGA==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/tiny-lr/node_modules/qs": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/qs/-/qs-2.2.5.tgz", + "integrity": "sha512-z/IJSH2F7g7FbYokpSQqLcMAk0VXtR8boQS1EqLxeB7T5xcFK/RuGY0PZbPtF9r151X+a/ZaRauCOok4Xiyjmg==", + "dev": true + }, + "node_modules/to-absolute-glob": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz", + "integrity": "sha512-rtwLUQEwT8ZeKQbyFJyomBRYXyE16U5VKuy0ftxLMK/PZb2fkOsg5r9kHdauuVDbsNdIBoC/HCthpidamQFXYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-absolute": "^1.0.0", + "is-negated-glob": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-buffer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", + "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/to-regex-range/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, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/to-regex/node_modules/define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-through": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-through/-/to-through-2.0.0.tgz", + "integrity": "sha512-+QIz37Ly7acM4EMdw2PRN389OneM5+d844tirkGp4dPKzI5OE72V9OsbFp+CIYJDahZ41ZV05hNtcPAQUAm9/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "through2": "^2.0.3" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tr46": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", + "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha512-Nm4cF79FhSTzrLKGDMi3I4utBtFv8qKy4sq1enftf2gMdpqI8oVQTAfySkTz5r49giVzDj88SVZXP4CeYQwjaw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/trim-repeated": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", + "integrity": "sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^1.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/ts-loader": { + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.2.tgz", + "integrity": "sha512-Qo4piXvOTWcMGIgRiuFa6nHNm+54HbYaZCKqc9eeZCLRy3XqafQgwX2F7mofrbJG3g7EEb+lkiR+z2Lic2s3Zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "enhanced-resolve": "^5.0.0", + "micromatch": "^4.0.0", + "semver": "^7.3.4", + "source-map": "^0.7.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "typescript": "*", + "webpack": "^5.0.0" + } + }, + "node_modules/ts-loader/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, + "license": "MIT", + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ts-loader/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "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/ts-loader/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, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ts-loader/node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/ts-loader/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 8" + } + }, + "node_modules/ts-loader/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", + "dev": true, + "license": "Unlicense" + }, + "node_modules/type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", + "dev": true, + "license": "ISC" + }, + "node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.5.2.tgz", + "integrity": "sha512-DWkS49EQKVX//Tbupb9TFa19c7+MK1XmzkrZUR8TAktmE/DizXoaoJV6TZ/tSIPXipqNiRI6CyAe7x69Jb6RSw==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=6" + } + }, + "node_modules/type-is": { + "version": "1.5.7", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.5.7.tgz", + "integrity": "sha512-of68V0oUmVH4thGc1cLR3sKdICPsaL7kzpYc7FX1pcagY4eIllhyMqQcoOq289f+xj2orm8oPWwsCwxiCgVJbQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.0.9" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/type-is/node_modules/mime-db": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.12.0.tgz", + "integrity": "sha512-5aMAW7I4jZoZB27fXRuekqc4DVvJ7+hM8UcWrNj2mqibE54gXgPSonBYBdQW5hyaVNGmiYjY0ZMqn9fBefWYvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/type-is/node_modules/mime-types": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.0.14.tgz", + "integrity": "sha512-2ZHUEstNkIf2oTWgtODr6X0Cc4Ns/RN/hktdozndiEhhAC2wxXejF1FH0XLHTEImE9h6gr/tcnr3YOnSGsxc7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "~1.12.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/typescript": { + "version": "5.8.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", + "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.29.1.tgz", + "integrity": "sha512-f8cDkvndhbQMPcysk6CUSGBWV+g1utqdn71P5YKwMumVMOG/5k7cHq0KyG4O52nB0oKS4aN2Tp5+wB4APJGC+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.29.1", + "@typescript-eslint/parser": "8.29.1", + "@typescript-eslint/utils": "8.29.1" + }, + "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 <5.9.0" + } + }, + "node_modules/ua-parser-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.2.tgz", + "integrity": "sha512-00y/AXhx0/SsnI51fTc0rLRmafiGOM4/O+ny10Ps7f+j/b8p/ZY11ytMgznXkOVo4GQ+KwQG5UQLkLGirsACRg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + } + ], + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/uglify-js": { + "version": "3.4.10", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.10.tgz", + "integrity": "sha512-Y2VsbPVs0FIshJztycsO2SfPk7/KAF/T72qzv9u5EpQ4kB2hQoHlhNQTsNyy6ul7lQtqJN/AoWeS23OzEiEFxw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "commander": "~2.19.0", + "source-map": "~0.6.1" + }, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/uglify-js/node_modules/commander": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", + "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==", + "dev": true, + "license": "MIT" + }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/unbzip2-stream": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", + "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer": "^5.2.1", + "through": "^2.3.8" + } + }, + "node_modules/unc-path-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", + "integrity": "sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/underscore": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "integrity": "sha512-5WsVTFcH1ut/kkhAaHf4PVgI8c7++GiVcpCGxPouI6ZVjsqPnSDf8h/8HtVqc0t4fzRXwnMK70EcZeAs3PIddg==", + "dev": true, + "license": "MIT" + }, + "node_modules/undertaker": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/undertaker/-/undertaker-1.3.0.tgz", + "integrity": "sha512-/RXwi5m/Mu3H6IHQGww3GNt1PNXlbeCuclF2QYR14L/2CHPz3DFZkvB5hZ0N/QUkiXWCACML2jXViIQEQc2MLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "arr-flatten": "^1.0.1", + "arr-map": "^2.0.0", + "bach": "^1.0.0", + "collection-map": "^1.0.0", + "es6-weak-map": "^2.0.1", + "fast-levenshtein": "^1.0.0", + "last-run": "^1.1.0", + "object.defaults": "^1.0.0", + "object.reduce": "^1.0.0", + "undertaker-registry": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/undertaker-registry": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/undertaker-registry/-/undertaker-registry-1.0.1.tgz", + "integrity": "sha512-UR1khWeAjugW3548EfQmL9Z7pGMlBgXteQpr1IZeZBtnkCJQJIJ1Scj0mb9wQaPvUZ9Q17XqW6TIaPchJkyfqw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/undertaker/node_modules/fast-levenshtein": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-1.1.4.tgz", + "integrity": "sha512-Ia0sQNrMPXXkqVFt6w6M1n1oKo3NfKs+mvaV811Jwir7vAk9a6PVV9VPYf6X3BU97QiLEmuW3uXH9u87zDFfdw==", + "dev": true, + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA==", + "dev": true, + "license": "MIT" + }, + "node_modules/uniqs": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", + "integrity": "sha512-mZdDpf3vBV5Efh29kMw5tXoup/buMgxLzOt/XKFKcVmi+15ManNQWr6HfZ2aiZTYlYixbdNJ0KFmIZIv52tHSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/unique-stream": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.3.1.tgz", + "integrity": "sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-stable-stringify-without-jsonify": "^1.0.1", + "through2-filter": "^3.0.0" + } + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/unquote": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", + "integrity": "sha512-vRCqFv6UhXpWxZPyGDh/F3ZpNv8/qo7w6iufLpQg9aKnQ71qM4B5KiI7Mia9COcjEhrO9LueHpMYjYzsWH3OIg==", + "dev": true, + "license": "MIT" + }, + "node_modules/unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-value/node_modules/isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==", + "dev": true, + "license": "MIT", + "dependencies": { + "isarray": "1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4", + "yarn": "*" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/update-browserslist-db/node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/upper-case": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", + "integrity": "sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA==", + "dev": true, + "license": "MIT" + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha512-NjFKA0DidqPa5ciFcSrXnAltTtzz84ogy+NebPvfEgAck0+TNg4UJ4IN+fB7zRZfbgUf0syOo9MDxFkDSMuFaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prepend-http": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/url-to-options": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz", + "integrity": "sha512-0kQLIzG4fdk/G5NONku64rSH/x32NOA39LVQqlK8Le6lvTF6GGRJpqaQFGgU+CLwySIqBSMdwYM0sYcW9f6P4A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/util.promisify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz", + "integrity": "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.2", + "has-symbols": "^1.0.1", + "object.getownpropertydescriptors": "^2.1.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/v8flags": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.2.0.tgz", + "integrity": "sha512-mH8etigqMfiGWdeXpaaqGfs6BndypxusHHcv2qSHyZkGEznCd/qAXCWWRzeowtL54147cktFOC4P5y+kl8d8Jg==", + "dev": true, + "license": "MIT", + "dependencies": { + "homedir-polyfill": "^1.0.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/value-or-function": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/value-or-function/-/value-or-function-3.0.0.tgz", + "integrity": "sha512-jdBB2FrWvQC/pnPtIqcLsMaQgjhdb6B7tk1MMyTKapox+tQZbdRP4uLxu/JY0t7fbfDCUMnuelzEYv5GsxHhdg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vendors": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.4.tgz", + "integrity": "sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "node_modules/verror/node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/verror/node_modules/extsprintf": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz", + "integrity": "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "license": "MIT" + }, + "node_modules/vinyl": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.1.tgz", + "integrity": "sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone": "^2.1.1", + "clone-buffer": "^1.0.0", + "clone-stats": "^1.0.0", + "cloneable-readable": "^1.0.0", + "remove-trailing-separator": "^1.0.1", + "replace-ext": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vinyl-fs": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-3.0.3.tgz", + "integrity": "sha512-vIu34EkyNyJxmP0jscNzWBSygh7VWhqun6RmqVfXePrOwi9lhvRs//dOaGOTRUQr4tx7/zd26Tk5WeSVZitgng==", + "dev": true, + "license": "MIT", + "dependencies": { + "fs-mkdirp-stream": "^1.0.0", + "glob-stream": "^6.1.0", + "graceful-fs": "^4.0.0", + "is-valid-glob": "^1.0.0", + "lazystream": "^1.0.0", + "lead": "^1.0.0", + "object.assign": "^4.0.4", + "pumpify": "^1.3.5", + "readable-stream": "^2.3.3", + "remove-bom-buffer": "^3.0.0", + "remove-bom-stream": "^1.2.0", + "resolve-options": "^1.1.0", + "through2": "^2.0.0", + "to-through": "^2.0.0", + "value-or-function": "^3.0.0", + "vinyl": "^2.0.0", + "vinyl-sourcemap": "^1.1.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vinyl-sourcemap": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/vinyl-sourcemap/-/vinyl-sourcemap-1.1.0.tgz", + "integrity": "sha512-NiibMgt6VJGJmyw7vtzhctDcfKch4e4n9TBeoWlirb7FMg9/1Ov9k+A5ZRAtywBpRPiyECvQRQllYM8dECegVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "append-buffer": "^1.0.2", + "convert-source-map": "^1.5.0", + "graceful-fs": "^4.1.6", + "normalize-path": "^2.1.1", + "now-and-later": "^2.0.0", + "remove-bom-buffer": "^3.0.0", + "vinyl": "^2.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vinyl-sourcemap/node_modules/normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "remove-trailing-separator": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/vinyl-sourcemaps-apply": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz", + "integrity": "sha512-+oDh3KYZBoZC8hfocrbrxbLUeaYtQK7J5WU5Br9VqWqmCll3tFJqKp97GC9GmMsVIL0qnx2DgEDVxdo5EZ5sSw==", + "dev": true, + "license": "ISC", + "dependencies": { + "source-map": "^0.5.1" + } + }, + "node_modules/vinyl-sourcemaps-apply/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/w3c-hr-time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "browser-process-hrtime": "^1.0.0" + } + }, + "node_modules/watch": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/watch/-/watch-0.11.0.tgz", + "integrity": "sha512-kfWV9VP0kzdOQzouKDEXdWFssokWv7Mat7cnB4ixI3SjJN2UW3RK4x6yrRF+Zq9kc3mfHvKjAvLnyENGTxE9BA==", + "dev": true, + "engines": [ + "node >=0.1.95" + ] + }, + "node_modules/watchpack": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz", + "integrity": "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/webpack": { + "version": "5.99.9", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.99.9.tgz", + "integrity": "sha512-brOPwM3JnmOa+7kd3NsmOUOwbDAj8FT9xDsG3IW0MgbN9yZV7Oi/s/+MNQ/EcSMqw7qfoRyXPoeEWT8zLVdVGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.14.0", + "browserslist": "^4.24.0", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.17.1", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^4.3.2", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.11", + "watchpack": "^2.4.1", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-deadcode-plugin": { + "version": "0.1.17", + "resolved": "https://registry.npmjs.org/webpack-deadcode-plugin/-/webpack-deadcode-plugin-0.1.17.tgz", + "integrity": "sha512-m57+nCO3wYa/QEJiJNUTE2OpRZj6DY6dbf4G4h9rFyNiq0g1XBH05O09UAJoXyqedv7Dfvqp+YeratTDRuqQuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^3.0.0", + "fast-glob": "^3.1.1" + }, + "engines": { + "node": ">=6.11.5" + }, + "peerDependencies": { + "webpack": "> 3" + } + }, + "node_modules/webpack-deadcode-plugin/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/webpack-deadcode-plugin/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack-stream": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webpack-stream/-/webpack-stream-7.0.0.tgz", + "integrity": "sha512-XoAQTHyCaYMo6TS7Atv1HYhtmBgKiVLONJbzLBl2V3eibXQ2IT/MCRM841RW/r3vToKD5ivrTJFWgd/ghoxoRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fancy-log": "^1.3.3", + "lodash.clone": "^4.3.2", + "lodash.some": "^4.2.2", + "memory-fs": "^0.5.0", + "plugin-error": "^1.0.1", + "supports-color": "^8.1.1", + "through": "^2.3.8", + "vinyl": "^2.2.1" + }, + "engines": { + "node": ">= 10.0.0" + }, + "peerDependencies": { + "webpack": "^5.21.2" + } + }, + "node_modules/webpack-stream/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/webpack-strip-block": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/webpack-strip-block/-/webpack-strip-block-0.2.0.tgz", + "integrity": "sha512-SX0Qir0bJ94gyqyqAjoH1h72sHngq2onR8pA1ldWNNisaU+QCMJBvK2K//zfStUHcqqkWYYoyMAqiPAj0CXm1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "loader-utils": "^1.1.0" + }, + "peerDependencies": { + "webpack": ">=2.2.0" + } + }, + "node_modules/webpack/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/webpack/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/webpack/node_modules/schema-utils": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz", + "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/whatwg-encoding": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "0.4.24" + } + }, + "node_modules/whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", + "dev": true, + "license": "MIT" + }, + "node_modules/whatwg-url": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + }, + "node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha512-F6+WgncZi/mJDrammbTuHe1q0R5hOXv/mBaiNA2TCNT/LTHusX0V+CJnj9XT8ki5ln2UZyyddDgHfCzyrOH7MQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/winston": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/winston/-/winston-1.0.2.tgz", + "integrity": "sha512-BLxJH3KCgJ2paj2xKYTQLpxdKr9URPDDDLJnRVcbud7izT+m8Xzt5Rod6mnNgEcfT0fRvhEy2Cj3cEnnQpa6qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "async": "~1.0.0", + "colors": "1.0.x", + "cycle": "1.0.x", + "eyes": "0.1.x", + "isstream": "0.1.x", + "pkginfo": "0.3.x", + "stack-trace": "0.0.x" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/winston/node_modules/async": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz", + "integrity": "sha512-5mO7DX4CbJzp9zjaFXusQQ4tzKJARjNB1Ih1pVBi8wkbmXy/xzIDgEMXxWePLzt2OdFwaxfneIlT1nCiXubrPQ==", + "dev": true, + "license": "MIT" + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha512-1tMA907+V4QmxV7dbRvb4/8MaRALK6q9Abid3ndMYnbyo8piisCmeONVqVSXqQA3KaP4SLt5b7ud6E2sqP8TFw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/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, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/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, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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, + "license": "ISC" + }, + "node_modules/ws": { + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", + "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml-name-validator": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/xmlbuilder": { + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", + "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, + "license": "MIT" + }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", + "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "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, + "license": "ISC" + }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, + "node_modules/yargs": { + "version": "17.6.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz", + "integrity": "sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "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, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/package.json b/package.json index e7e4c2e9..a54d3278 100644 --- a/package.json +++ b/package.json @@ -1,106 +1,80 @@ -{ - "name": "shapez.io", - "version": "1.0.0", - "main": "index.js", - "repository": "https://github.com/tobspr-games/shapez.io", - "author": "tobspr Games ", - "license": "MIT", - "private": true, - "scripts": { - "dev": "cd gulp && yarn gulp", - "devStandalone": "cd gulp && yarn gulp serve.standalone-steam", - "tslint": "cd src/js && tsc", - "lint": "eslint src/js", - "prettier-all": "prettier --write src/**/*.* && prettier --write gulp/**/*.*", - "publishOnItchWindows": "butler push tmp_standalone_files/shapez.io-standalone-win32-x64 tobspr/shapezio:windows --userversion-file version", - "publishOnItchLinux": "butler push tmp_standalone_files/shapez.io-standalone-linux-x64 tobspr/shapezio:linux --userversion-file version", - "publishOnItch": "yarn publishOnItchWindows && yarn publishOnItchLinux", - "publishOnSteam": "cd gulp/steampipe && ./upload.bat", - "publishStandalone": "yarn publishOnItch && yarn publishOnSteam", - "publishWeb": "cd gulp && yarn main.deploy.prod", - "publish": "yarn publishStandalone && yarn publishWeb", - "syncTranslations": "node sync-translations.js", - "buildTypes": "tsc src/js/application.js --declaration --allowJs --emitDeclarationOnly --skipLibCheck --out types.js" - }, - "dependencies": { - "@babel/core": "^7.5.4", - "@babel/plugin-transform-block-scoping": "^7.4.4", - "@babel/plugin-transform-classes": "^7.5.5", - "@babel/preset-env": "^7.5.4", - "@nastyox/rando.js": "^2.0.5", - "@types/cordova": "^0.0.34", - "@types/filesystem": "^0.0.29", - "ajv": "^6.10.2", - "babel-core": "^6.26.3", - "babel-loader": "^8.0.4", - "circular-dependency-plugin": "^5.0.2", - "circular-json": "^0.5.9", - "clipboard-copy": "^3.1.0", - "colors": "^1.3.3", - "core-js": "3", - "crc": "^3.8.0", - "cssnano-preset-advanced": "^4.0.7", - "debounce-promise": "^3.1.2", - "email-validator": "^2.0.4", - "eslint": "7.1.0", - "fastdom": "^1.0.8", - "flatted": "^2.0.1", - "howler": "^2.1.2", - "html-loader": "^0.5.5", - "ignore-loader": "^0.1.2", - "logrocket": "^1.0.7", - "lz-string": "^1.4.4", - "markdown-loader": "^4.0.0", - "match-all": "^1.2.5", - "phonegap-plugin-mobile-accessibility": "^1.0.5", - "postcss": ">=5.0.0", - "promise-polyfill": "^8.1.0", - "query-string": "^6.8.1", - "rusha": "^0.8.13", - "semver": "^7.3.5", - "strictdom": "^1.0.1", - "string-replace-webpack-plugin": "^0.1.3", - "terser-webpack-plugin": "^1.1.0", - "typescript": "3.9.3", - "uglify-template-string-loader": "^1.1.0", - "unused-files-webpack-plugin": "^3.4.0", - "webpack": "^4.43.0", - "webpack-bundle-analyzer": "^3.0.3", - "webpack-cli": "^3.1.0", - "webpack-deep-scope-plugin": "^1.6.0", - "webpack-plugin-replace": "^1.1.1", - "webpack-strip-block": "^0.2.0", - "whatwg-fetch": "^3.0.0", - "worker-loader": "^2.0.0", - "yaml": "^1.10.0", - "yawn-yaml": "^1.5.0" - }, - "devDependencies": { - "@octokit/rest": "^18.0.6", - "@typescript-eslint/eslint-plugin": "3.0.1", - "@typescript-eslint/parser": "3.0.1", - "autoprefixer": "^9.4.3", - "babel-plugin-closure-elimination": "^1.3.0", - "babel-plugin-console-source": "^2.0.2", - "babel-plugin-danger-remove-unused-import": "^1.1.2", - "css-mqpacker": "^7.0.0", - "cssnano": "^4.1.10", - "eslint-config-prettier": "6.11.0", - "eslint-plugin-prettier": "3.1.3", - "faster.js": "^1.1.0", - "glob": "^7.1.3", - "imagemin-mozjpeg": "^8.0.0", - "imagemin-pngquant": "^8.0.0", - "jimp": "^0.6.1", - "js-yaml": "^3.13.1", - "postcss-assets": "^5.0.0", - "postcss-preset-env": "^6.5.0", - "postcss-round-subpixels": "^1.2.0", - "postcss-unprefix": "^2.1.3", - "prettier": "^2.0.4", - "sass-unused": "^0.3.0", - "strip-json-comments": "^3.0.1", - "trim": "^0.0.1", - "yarn": "^1.22.4" - } -} +{ + "name": "shapez", + "version": "1.6.0", + "main": "index.js", + "repository": "https://github.com/tobspr-games/shapez.io", + "author": "tobspr Games ", + "license": "GPL-3.0-or-later", + "private": true, + "type": "module", + "scripts": { + "postinstall": "gulp -f gulp/environment.js prepare", + "gulp": "gulp --cwd gulp", + "lint": "eslint .", + "prettier-all": "prettier --write .", + "package-win32-x64": "gulp --cwd gulp package.standalone.win32-x64", + "package-win32-arm64": "gulp --cwd gulp package.standalone.win32-arm64", + "package-linux-x64": "gulp --cwd gulp package.standalone.linux-x64", + "package-linux-arm64": "gulp --cwd gulp package.standalone.linux-arm64", + "package-darwin-x64": "gulp --cwd gulp package.standalone.darwin-x64", + "package-darwin-arm64": "gulp --cwd gulp package.standalone.darwin-arm64", + "package-all": "gulp --cwd gulp package.standalone.all" + }, + "dependencies": { + "@msgpack/msgpack": "^3.1.2", + "ajv": "^6.10.2", + "clipboard-copy": "^3.1.0", + "debounce-promise": "^3.1.2", + "howler": "^2.1.2" + }, + "devDependencies": { + "@electron/packager": "^18.3.6", + "@eslint/js": "^9.24.0", + "@tsconfig/node-lts": "^22.0.1", + "@tsconfig/strictest": "^2.0.5", + "@types/circular-dependency-plugin": "^5.0.5", + "@types/gulp": "^4.0.9", + "@types/gulp-htmlmin": "^1.3.32", + "@types/node": "^22.14.0", + "browser-sync": "^2.27.10", + "circular-dependency-plugin": "^5.2.2", + "css-mqpacker": "^7.0.0", + "cssnano": "^4.1.10", + "cssnano-preset-advanced": "^4.0.7", + "delete-empty": "^3.0.0", + "eslint": "^9.24.0", + "globals": "^15.0.0", + "gulp": "^4.0.2", + "gulp-audiosprite": "^1.1.0", + "gulp-cache": "^1.1.3", + "gulp-cached": "^1.1.1", + "gulp-clean": "^0.4.0", + "gulp-dart-sass": "^1.0.2", + "gulp-dom": "^1.0.0", + "gulp-fluent-ffmpeg": "^2.0.0", + "gulp-htmlmin": "^5.0.1", + "gulp-if": "^3.0.0", + "gulp-imagemin": "^7.1.0", + "gulp-plumber": "^1.2.1", + "gulp-postcss": "^8.0.0", + "gulp-rename": "^2.0.0", + "gulp-webserver": "^0.9.1", + "gulp-yaml": "^2.0.4", + "imagemin-gifsicle": "^7.0.0", + "imagemin-jpegtran": "^7.0.0", + "imagemin-mozjpeg": "^8.0.0", + "imagemin-pngquant": "^8.0.0", + "postcss-assets": "^5.0.0", + "postcss-critical-split": "^2.5.3", + "prettier": "^3.3.2", + "terser-webpack-plugin": "^5.3.14", + "ts-loader": "^9.5.2", + "typescript": "^5.8.2", + "typescript-eslint": "^8.29.1", + "webpack": "^5.99.9", + "webpack-deadcode-plugin": "^0.1.17", + "webpack-stream": "^7.0.0", + "webpack-strip-block": "^0.2.0", + "yaml": "^1.10.0" + } +} diff --git a/res/logo_cn.png b/res/logo_cn.png deleted file mode 100644 index bec48a66..00000000 Binary files a/res/logo_cn.png and /dev/null differ diff --git a/res/logo_demo.png b/res/logo_demo.png deleted file mode 100644 index 9ea6ee62..00000000 Binary files a/res/logo_demo.png and /dev/null differ diff --git a/res/logo_wegame.png b/res/logo_wegame.png deleted file mode 100644 index 06237666..00000000 Binary files a/res/logo_wegame.png and /dev/null differ diff --git a/res/puzzle_dlc_logo_china.png b/res/puzzle_dlc_logo_china.png deleted file mode 100644 index c45b6cdc..00000000 Binary files a/res/puzzle_dlc_logo_china.png and /dev/null differ diff --git a/res/ui/building_icons/rotater.png b/res/ui/building_icons/rotator.png similarity index 100% rename from res/ui/building_icons/rotater.png rename to res/ui/building_icons/rotator.png diff --git a/res/ui/building_tutorials/rotater-ccw.png b/res/ui/building_tutorials/rotator-ccw.png similarity index 100% rename from res/ui/building_tutorials/rotater-ccw.png rename to res/ui/building_tutorials/rotator-ccw.png diff --git a/res/ui/building_tutorials/rotater-rotate180.png b/res/ui/building_tutorials/rotator-rotate180.png similarity index 100% rename from res/ui/building_tutorials/rotater-rotate180.png rename to res/ui/building_tutorials/rotator-rotate180.png diff --git a/res/ui/building_tutorials/rotater.png b/res/ui/building_tutorials/rotator.png similarity index 100% rename from res/ui/building_tutorials/rotater.png rename to res/ui/building_tutorials/rotator.png diff --git a/res/ui/building_tutorials/virtual_processor-rotater.png b/res/ui/building_tutorials/virtual_processor-rotator.png similarity index 100% rename from res/ui/building_tutorials/virtual_processor-rotater.png rename to res/ui/building_tutorials/virtual_processor-rotator.png diff --git a/res/ui/cursor.png b/res/ui/cursor.png deleted file mode 100644 index 6d2514e4..00000000 Binary files a/res/ui/cursor.png and /dev/null differ diff --git a/res/ui/demo_badge.png b/res/ui/demo_badge.png deleted file mode 100644 index 6c80db7d..00000000 Binary files a/res/ui/demo_badge.png and /dev/null differ diff --git a/res/ui/get_on_itch_io.svg b/res/ui/get_on_itch_io.svg deleted file mode 100644 index f6dde21e..00000000 --- a/res/ui/get_on_itch_io.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/res/ui/icons/advantage_achievements.png b/res/ui/icons/advantage_achievements.png deleted file mode 100644 index 55860f99..00000000 Binary files a/res/ui/icons/advantage_achievements.png and /dev/null differ diff --git a/res/ui/icons/advantage_buildings.png b/res/ui/icons/advantage_buildings.png deleted file mode 100644 index 91e7a565..00000000 Binary files a/res/ui/icons/advantage_buildings.png and /dev/null differ diff --git a/res/ui/icons/advantage_dark_mode.png b/res/ui/icons/advantage_dark_mode.png deleted file mode 100644 index c176b8ad..00000000 Binary files a/res/ui/icons/advantage_dark_mode.png and /dev/null differ diff --git a/res/ui/icons/advantage_markers.png b/res/ui/icons/advantage_markers.png deleted file mode 100644 index af3835c7..00000000 Binary files a/res/ui/icons/advantage_markers.png and /dev/null differ diff --git a/res/ui/icons/advantage_mods.png b/res/ui/icons/advantage_mods.png deleted file mode 100644 index bacdda89..00000000 Binary files a/res/ui/icons/advantage_mods.png and /dev/null differ diff --git a/res/ui/icons/advantage_new_levels.png b/res/ui/icons/advantage_new_levels.png deleted file mode 100644 index 730732fe..00000000 Binary files a/res/ui/icons/advantage_new_levels.png and /dev/null differ diff --git a/res/ui/icons/advantage_savegames.png b/res/ui/icons/advantage_savegames.png deleted file mode 100644 index 79e7c327..00000000 Binary files a/res/ui/icons/advantage_savegames.png and /dev/null differ diff --git a/res/ui/icons/advantage_support.png b/res/ui/icons/advantage_support.png deleted file mode 100644 index b86703a5..00000000 Binary files a/res/ui/icons/advantage_support.png and /dev/null differ diff --git a/res/ui/icons/advantage_upgrades.png b/res/ui/icons/advantage_upgrades.png deleted file mode 100644 index e06b6d82..00000000 Binary files a/res/ui/icons/advantage_upgrades.png and /dev/null differ diff --git a/res/ui/icons/advantage_wires.png b/res/ui/icons/advantage_wires.png deleted file mode 100644 index bb26fa87..00000000 Binary files a/res/ui/icons/advantage_wires.png and /dev/null differ diff --git a/res/ui/icons/demo_steam_link_indicator.png b/res/ui/icons/demo_steam_link_indicator.png deleted file mode 100644 index 98add9df..00000000 Binary files a/res/ui/icons/demo_steam_link_indicator.png and /dev/null differ diff --git a/res/ui/icons/savegame_correct.png b/res/ui/icons/savegame_correct.png deleted file mode 100644 index bcbce3dc..00000000 Binary files a/res/ui/icons/savegame_correct.png and /dev/null differ diff --git a/res/ui/interactive_tutorial.cn.noinline/1_1_extractor.gif b/res/ui/interactive_tutorial.cn.noinline/1_1_extractor.gif deleted file mode 100644 index c7208ac2..00000000 Binary files a/res/ui/interactive_tutorial.cn.noinline/1_1_extractor.gif and /dev/null differ diff --git a/res/ui/interactive_tutorial.cn.noinline/1_2_conveyor.gif b/res/ui/interactive_tutorial.cn.noinline/1_2_conveyor.gif deleted file mode 100644 index 8432b676..00000000 Binary files a/res/ui/interactive_tutorial.cn.noinline/1_2_conveyor.gif and /dev/null differ diff --git a/res/ui/interactive_tutorial.cn.noinline/1_3_expand.gif b/res/ui/interactive_tutorial.cn.noinline/1_3_expand.gif deleted file mode 100644 index 9c655ab1..00000000 Binary files a/res/ui/interactive_tutorial.cn.noinline/1_3_expand.gif and /dev/null differ diff --git a/res/ui/interactive_tutorial.cn.noinline/21_1_place_quad_painter.gif b/res/ui/interactive_tutorial.cn.noinline/21_1_place_quad_painter.gif deleted file mode 100644 index ea854cf2..00000000 Binary files a/res/ui/interactive_tutorial.cn.noinline/21_1_place_quad_painter.gif and /dev/null differ diff --git a/res/ui/interactive_tutorial.cn.noinline/21_2_switch_to_wires.gif b/res/ui/interactive_tutorial.cn.noinline/21_2_switch_to_wires.gif deleted file mode 100644 index 78ab6fd2..00000000 Binary files a/res/ui/interactive_tutorial.cn.noinline/21_2_switch_to_wires.gif and /dev/null differ diff --git a/res/ui/interactive_tutorial.cn.noinline/21_3_place_button.gif b/res/ui/interactive_tutorial.cn.noinline/21_3_place_button.gif deleted file mode 100644 index 52ffb076..00000000 Binary files a/res/ui/interactive_tutorial.cn.noinline/21_3_place_button.gif and /dev/null differ diff --git a/res/ui/interactive_tutorial.cn.noinline/21_4_press_button.gif b/res/ui/interactive_tutorial.cn.noinline/21_4_press_button.gif deleted file mode 100644 index 5d79f1e3..00000000 Binary files a/res/ui/interactive_tutorial.cn.noinline/21_4_press_button.gif and /dev/null differ diff --git a/res/ui/interactive_tutorial.cn.noinline/2_1_place_cutter.gif b/res/ui/interactive_tutorial.cn.noinline/2_1_place_cutter.gif deleted file mode 100644 index 1678c0b2..00000000 Binary files a/res/ui/interactive_tutorial.cn.noinline/2_1_place_cutter.gif and /dev/null differ diff --git a/res/ui/interactive_tutorial.cn.noinline/2_2_place_trash.gif b/res/ui/interactive_tutorial.cn.noinline/2_2_place_trash.gif deleted file mode 100644 index 0d60fa9f..00000000 Binary files a/res/ui/interactive_tutorial.cn.noinline/2_2_place_trash.gif and /dev/null differ diff --git a/res/ui/interactive_tutorial.cn.noinline/2_3_more_cutters.gif b/res/ui/interactive_tutorial.cn.noinline/2_3_more_cutters.gif deleted file mode 100644 index 50ce88f9..00000000 Binary files a/res/ui/interactive_tutorial.cn.noinline/2_3_more_cutters.gif and /dev/null differ diff --git a/res/ui/interactive_tutorial.cn.noinline/3_1_rectangles.gif b/res/ui/interactive_tutorial.cn.noinline/3_1_rectangles.gif deleted file mode 100644 index 81668af8..00000000 Binary files a/res/ui/interactive_tutorial.cn.noinline/3_1_rectangles.gif and /dev/null differ diff --git a/res/ui/kiwi_clicker.png b/res/ui/kiwi_clicker.png deleted file mode 100644 index bc411e93..00000000 Binary files a/res/ui/kiwi_clicker.png and /dev/null differ diff --git a/res/ui/main_menu/bg_pattern.png b/res/ui/main_menu/bg_pattern.png deleted file mode 100644 index 00291768..00000000 Binary files a/res/ui/main_menu/bg_pattern.png and /dev/null differ diff --git a/res/ui/main_menu/changelog.svg b/res/ui/main_menu/changelog.svg deleted file mode 100644 index f12bff7b..00000000 --- a/res/ui/main_menu/changelog.svg +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/res/ui/main_menu/github.svg b/res/ui/main_menu/github.svg deleted file mode 100644 index 79a2756d..00000000 --- a/res/ui/main_menu/github.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/res/ui/main_menu/opensea.png b/res/ui/main_menu/opensea.png deleted file mode 100644 index 33e2db44..00000000 Binary files a/res/ui/main_menu/opensea.png and /dev/null differ diff --git a/res/ui/main_menu/steam.svg b/res/ui/main_menu/steam.svg deleted file mode 100644 index 9f6e00f2..00000000 --- a/res/ui/main_menu/steam.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/res/ui/main_menu/translate.svg b/res/ui/main_menu/translate.svg deleted file mode 100644 index e38b3392..00000000 --- a/res/ui/main_menu/translate.svg +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/res/ui/main_menu/twitter.svg b/res/ui/main_menu/twitter.svg deleted file mode 100644 index 047a527c..00000000 --- a/res/ui/main_menu/twitter.svg +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/res/ui/memes/cat1.png b/res/ui/memes/cat1.png deleted file mode 100644 index 7ec97051..00000000 Binary files a/res/ui/memes/cat1.png and /dev/null differ diff --git a/res/ui/puzzle_dlc_logo_china.png b/res/ui/puzzle_dlc_logo_china.png deleted file mode 100644 index c45b6cdc..00000000 Binary files a/res/ui/puzzle_dlc_logo_china.png and /dev/null differ diff --git a/res/ui/sad_ghost.png b/res/ui/sad_ghost.png deleted file mode 100644 index 19cd0cac..00000000 Binary files a/res/ui/sad_ghost.png and /dev/null differ diff --git a/res/ui/shapez2.png b/res/ui/shapez2.png deleted file mode 100644 index 1db84543..00000000 Binary files a/res/ui/shapez2.png and /dev/null differ diff --git a/res/ui/steam_link_btn/0.png b/res/ui/steam_link_btn/0.png deleted file mode 100644 index ceb8631b..00000000 Binary files a/res/ui/steam_link_btn/0.png and /dev/null differ diff --git a/res/ui/steam_signin.png b/res/ui/steam_signin.png deleted file mode 100644 index cd3120f8..00000000 Binary files a/res/ui/steam_signin.png and /dev/null differ diff --git a/res/ui/toolbar_bg.lossless.png b/res/ui/toolbar_bg.lossless.png deleted file mode 100644 index 93967a91..00000000 Binary files a/res/ui/toolbar_bg.lossless.png and /dev/null differ diff --git a/res/ui/wegame_isbn_rating.jpg b/res/ui/wegame_isbn_rating.jpg deleted file mode 100644 index 581dd744..00000000 Binary files a/res/ui/wegame_isbn_rating.jpg and /dev/null differ diff --git a/res_raw/sounds/music/theme-short.mp3 b/res_raw/sounds/music/theme-short.mp3 deleted file mode 100644 index 1cc3b7da..00000000 Binary files a/res_raw/sounds/music/theme-short.mp3 and /dev/null differ diff --git a/res_raw/sprites/belt/generate_wire_sprites.js b/res_raw/sprites/belt/generate_wire_sprites.js index 7db9782c..1a88c508 100644 --- a/res_raw/sprites/belt/generate_wire_sprites.js +++ b/res_raw/sprites/belt/generate_wire_sprites.js @@ -1,212 +1,212 @@ -/** - * - * Run `yarn global add canvas` first - */ - -const { createCanvas } = require("canvas"); -const fs = require("fs"); -const path = require("path"); - -const outputFolder = path.join(__dirname, "..", "wires", "sets"); - -const dimensions = 192; -const lineSize = 14; -const lowerLineSize = 32; - -const variants = { - first: "#61ef6f", - second: "#5fb2f1", - conflict: "#f74c4c", -}; - -function hexToRGB(h) { - let r = 0, - g = 0, - b = 0; - - // 3 digits - if (h.length == 4) { - r = "0x" + h[1] + h[1]; - g = "0x" + h[2] + h[2]; - b = "0x" + h[3] + h[3]; - - // 6 digits - } else if (h.length == 7) { - r = "0x" + h[1] + h[2]; - g = "0x" + h[3] + h[4]; - b = "0x" + h[5] + h[6]; - } - - return [+r, +g, +b]; -} - -function RGBToHSL(r, g, b) { - // Make r, g, and b fractions of 1 - r /= 255; - g /= 255; - b /= 255; - - // Find greatest and smallest channel values - let cmin = Math.min(r, g, b), - cmax = Math.max(r, g, b), - delta = cmax - cmin, - h = 0, - s = 0, - l = 0; - // Calculate hue - // No difference - if (delta == 0) h = 0; - // Red is max - else if (cmax == r) h = ((g - b) / delta) % 6; - // Green is max - else if (cmax == g) h = (b - r) / delta + 2; - // Blue is max - else h = (r - g) / delta + 4; - - h = Math.round(h * 60); - - // Make negative hues positive behind 360° - if (h < 0) h += 360; - - // Calculate lightness - l = (cmax + cmin) / 2; - - // Calculate saturation - s = delta == 0 ? 0 : delta / (1 - Math.abs(2 * l - 1)); - - // Multiply l and s by 100 - s = +(s * 100).toFixed(1); - l = +(l * 100).toFixed(1); - - return [h, s, l]; -} - -function HSLToRGB(h, s, l) { - // Must be fractions of 1 - s /= 100; - l /= 100; - - let c = (1 - Math.abs(2 * l - 1)) * s, - x = c * (1 - Math.abs(((h / 60) % 2) - 1)), - m = l - c / 2, - r = 0, - g = 0, - b = 0; - - if (0 <= h && h < 60) { - r = c; - g = x; - b = 0; - } else if (60 <= h && h < 120) { - r = x; - g = c; - b = 0; - } else if (120 <= h && h < 180) { - r = 0; - g = c; - b = x; - } else if (180 <= h && h < 240) { - r = 0; - g = x; - b = c; - } else if (240 <= h && h < 300) { - r = x; - g = 0; - b = c; - } else if (300 <= h && h < 360) { - r = c; - g = 0; - b = x; - } - r = Math.round((r + m) * 255); - g = Math.round((g + m) * 255); - b = Math.round((b + m) * 255); - - return [r, g, b]; -} - -async function run() { - console.log("Running"); - - const promises = []; - - for (const variantId in variants) { - const variantColor = variants[variantId]; - const variantHSL = RGBToHSL(...hexToRGB(variantColor)); - const darkenedColor = HSLToRGB(variantHSL[0], variantHSL[1] - 15, variantHSL[2] - 20); - const hexDarkenedColor = "rgb(" + darkenedColor.join(",") + ")"; - - console.log(variantColor, "->", hexToRGB(variantColor), variantHSL, "->", darkenedColor); - - const parts = { - forward: [[0.5, 0, 0.5, 1]], - turn: [ - [0.5, 0.5, 0.5, 1], - [0.5, 0.5, 1, 0.5], - ], - split: [ - [0.5, 0.5, 0.5, 1], - [0, 0.5, 1, 0.5], - ], - cross: [ - [0, 0.5, 1, 0.5], - [0.5, 0, 0.5, 1], - ], - }; - - for (const partId in parts) { - const partLines = parts[partId]; - - const canvas = createCanvas(dimensions, dimensions); - const context = canvas.getContext("2d"); - context.quality = "best"; - context.clearRect(0, 0, dimensions, dimensions); - - const lineCanvas = createCanvas(dimensions, dimensions); - const lineContext = lineCanvas.getContext("2d"); - lineContext.quality = "best"; - lineContext.clearRect(0, 0, dimensions, dimensions); - lineContext.strokeStyle = hexDarkenedColor; - lineContext.lineWidth = lowerLineSize; - lineContext.lineCap = "square"; - lineContext.imageSmoothingEnabled = false; - - // Draw lower lines - partLines.forEach(([x1, y1, x2, y2]) => { - lineContext.beginPath(); - lineContext.moveTo(x1 * dimensions, y1 * dimensions); - lineContext.lineTo(x2 * dimensions, y2 * dimensions); - lineContext.stroke(); - }); - - context.globalAlpha = 0.4; - context.drawImage(lineCanvas, 0, 0, dimensions, dimensions); - - context.globalAlpha = 1; - context.imageSmoothingEnabled = false; - context.lineCap = "square"; - context.strokeStyle = variantColor; - context.lineWidth = lineSize; - - // Draw upper lines - partLines.forEach(([x1, y1, x2, y2]) => { - context.beginPath(); - context.moveTo(x1 * dimensions, y1 * dimensions); - context.lineTo(x2 * dimensions, y2 * dimensions); - context.stroke(); - }); - - const out = fs.createWriteStream(path.join(outputFolder, variantId + "_" + partId + ".png")); - const stream = canvas.createPNGStream(); - stream.pipe(out); - promises.push(new Promise(resolve => stream.on("end", resolve))); - } - } - - console.log("Waiting for completion"); - await Promise.all(promises); - - console.log("Done!"); -} - -run(); +/** + * + * Run `yarn global add canvas` first + */ + +const { createCanvas } = require("canvas"); +const fs = require("fs"); +const path = require("path"); + +const outputFolder = path.join(__dirname, "..", "wires", "sets"); + +const dimensions = 192; +const lineSize = 14; +const lowerLineSize = 32; + +const variants = { + first: "#61ef6f", + second: "#5fb2f1", + conflict: "#f74c4c", +}; + +function hexToRGB(h) { + let r = 0, + g = 0, + b = 0; + + // 3 digits + if (h.length == 4) { + r = "0x" + h[1] + h[1]; + g = "0x" + h[2] + h[2]; + b = "0x" + h[3] + h[3]; + + // 6 digits + } else if (h.length == 7) { + r = "0x" + h[1] + h[2]; + g = "0x" + h[3] + h[4]; + b = "0x" + h[5] + h[6]; + } + + return [+r, +g, +b]; +} + +function RGBToHSL(r, g, b) { + // Make r, g, and b fractions of 1 + r /= 255; + g /= 255; + b /= 255; + + // Find greatest and smallest channel values + let cmin = Math.min(r, g, b), + cmax = Math.max(r, g, b), + delta = cmax - cmin, + h = 0, + s = 0, + l = 0; + // Calculate hue + // No difference + if (delta == 0) h = 0; + // Red is max + else if (cmax == r) h = ((g - b) / delta) % 6; + // Green is max + else if (cmax == g) h = (b - r) / delta + 2; + // Blue is max + else h = (r - g) / delta + 4; + + h = Math.round(h * 60); + + // Make negative hues positive behind 360° + if (h < 0) h += 360; + + // Calculate lightness + l = (cmax + cmin) / 2; + + // Calculate saturation + s = delta == 0 ? 0 : delta / (1 - Math.abs(2 * l - 1)); + + // Multiply l and s by 100 + s = +(s * 100).toFixed(1); + l = +(l * 100).toFixed(1); + + return [h, s, l]; +} + +function HSLToRGB(h, s, l) { + // Must be fractions of 1 + s /= 100; + l /= 100; + + let c = (1 - Math.abs(2 * l - 1)) * s, + x = c * (1 - Math.abs(((h / 60) % 2) - 1)), + m = l - c / 2, + r = 0, + g = 0, + b = 0; + + if (0 <= h && h < 60) { + r = c; + g = x; + b = 0; + } else if (60 <= h && h < 120) { + r = x; + g = c; + b = 0; + } else if (120 <= h && h < 180) { + r = 0; + g = c; + b = x; + } else if (180 <= h && h < 240) { + r = 0; + g = x; + b = c; + } else if (240 <= h && h < 300) { + r = x; + g = 0; + b = c; + } else if (300 <= h && h < 360) { + r = c; + g = 0; + b = x; + } + r = Math.round((r + m) * 255); + g = Math.round((g + m) * 255); + b = Math.round((b + m) * 255); + + return [r, g, b]; +} + +async function run() { + console.log("Running"); + + const promises = []; + + for (const variantId in variants) { + const variantColor = variants[variantId]; + const variantHSL = RGBToHSL(...hexToRGB(variantColor)); + const darkenedColor = HSLToRGB(variantHSL[0], variantHSL[1] - 15, variantHSL[2] - 20); + const hexDarkenedColor = "rgb(" + darkenedColor.join(",") + ")"; + + console.log(variantColor, "->", hexToRGB(variantColor), variantHSL, "->", darkenedColor); + + const parts = { + forward: [[0.5, 0, 0.5, 1]], + turn: [ + [0.5, 0.5, 0.5, 1], + [0.5, 0.5, 1, 0.5], + ], + split: [ + [0.5, 0.5, 0.5, 1], + [0, 0.5, 1, 0.5], + ], + cross: [ + [0, 0.5, 1, 0.5], + [0.5, 0, 0.5, 1], + ], + }; + + for (const partId in parts) { + const partLines = parts[partId]; + + const canvas = createCanvas(dimensions, dimensions); + const context = canvas.getContext("2d"); + context.quality = "best"; + context.clearRect(0, 0, dimensions, dimensions); + + const lineCanvas = createCanvas(dimensions, dimensions); + const lineContext = lineCanvas.getContext("2d"); + lineContext.quality = "best"; + lineContext.clearRect(0, 0, dimensions, dimensions); + lineContext.strokeStyle = hexDarkenedColor; + lineContext.lineWidth = lowerLineSize; + lineContext.lineCap = "square"; + lineContext.imageSmoothingEnabled = false; + + // Draw lower lines + partLines.forEach(([x1, y1, x2, y2]) => { + lineContext.beginPath(); + lineContext.moveTo(x1 * dimensions, y1 * dimensions); + lineContext.lineTo(x2 * dimensions, y2 * dimensions); + lineContext.stroke(); + }); + + context.globalAlpha = 0.4; + context.drawImage(lineCanvas, 0, 0, dimensions, dimensions); + + context.globalAlpha = 1; + context.imageSmoothingEnabled = false; + context.lineCap = "square"; + context.strokeStyle = variantColor; + context.lineWidth = lineSize; + + // Draw upper lines + partLines.forEach(([x1, y1, x2, y2]) => { + context.beginPath(); + context.moveTo(x1 * dimensions, y1 * dimensions); + context.lineTo(x2 * dimensions, y2 * dimensions); + context.stroke(); + }); + + const out = fs.createWriteStream(path.join(outputFolder, variantId + "_" + partId + ".png")); + const stream = canvas.createPNGStream(); + stream.pipe(out); + promises.push(new Promise(resolve => stream.on("end", resolve))); + } + } + + console.log("Waiting for completion"); + await Promise.all(promises); + + console.log("Done!"); +} + +run(); diff --git a/res_raw/sprites/blueprints/rotater-ccw.png b/res_raw/sprites/blueprints/rotator-ccw.png similarity index 100% rename from res_raw/sprites/blueprints/rotater-ccw.png rename to res_raw/sprites/blueprints/rotator-ccw.png diff --git a/res_raw/sprites/blueprints/rotater-rotate180.png b/res_raw/sprites/blueprints/rotator-rotate180.png similarity index 100% rename from res_raw/sprites/blueprints/rotater-rotate180.png rename to res_raw/sprites/blueprints/rotator-rotate180.png diff --git a/res_raw/sprites/blueprints/rotater.png b/res_raw/sprites/blueprints/rotator.png similarity index 100% rename from res_raw/sprites/blueprints/rotater.png rename to res_raw/sprites/blueprints/rotator.png diff --git a/res_raw/sprites/blueprints/virtual_processor-rotater.png b/res_raw/sprites/blueprints/virtual_processor-rotator.png similarity index 100% rename from res_raw/sprites/blueprints/virtual_processor-rotater.png rename to res_raw/sprites/blueprints/virtual_processor-rotator.png diff --git a/res_raw/sprites/buildings/rotater-ccw.png b/res_raw/sprites/buildings/rotator-ccw.png similarity index 100% rename from res_raw/sprites/buildings/rotater-ccw.png rename to res_raw/sprites/buildings/rotator-ccw.png diff --git a/res_raw/sprites/buildings/rotater-rotate180.png b/res_raw/sprites/buildings/rotator-rotate180.png similarity index 100% rename from res_raw/sprites/buildings/rotater-rotate180.png rename to res_raw/sprites/buildings/rotator-rotate180.png diff --git a/res_raw/sprites/buildings/rotater.png b/res_raw/sprites/buildings/rotator.png similarity index 100% rename from res_raw/sprites/buildings/rotater.png rename to res_raw/sprites/buildings/rotator.png diff --git a/res_raw/sprites/buildings/virtual_processor-rotater.png b/res_raw/sprites/buildings/virtual_processor-rotator.png similarity index 100% rename from res_raw/sprites/buildings/virtual_processor-rotater.png rename to res_raw/sprites/buildings/virtual_processor-rotator.png diff --git a/shapez.code-workspace b/shapez.code-workspace deleted file mode 100644 index 8cd13e00..00000000 --- a/shapez.code-workspace +++ /dev/null @@ -1,25 +0,0 @@ -{ - "folders": [ - { - "path": "." - } - ], - "settings": { - "files.exclude": { - "**/build": true, - "**/node_modules": true, - "**/tmp_standalone_files": true, - "**/typedefs_gen": true - }, - "vetur.format.defaultFormatter.js": "vscode-typescript", - "vetur.format.defaultFormatter.ts": "vscode-typescript", - "editor.defaultFormatter": "esbenp.prettier-vscode", - "editor.formatOnSave": true, - "files.trimTrailingWhitespace": true, - "workbench.colorCustomizations": { - "activityBar.background": "#163328", - "titleBar.activeBackground": "#1F4738", - "titleBar.activeForeground": "#F7FBFA" - } - } -} \ No newline at end of file diff --git a/src/css/adinplay.scss b/src/css/adinplay.scss deleted file mode 100644 index 9707a502..00000000 --- a/src/css/adinplay.scss +++ /dev/null @@ -1,110 +0,0 @@ -#aip_gdpr { - &, - * { - text-shadow: none !important; - pointer-events: all; - color: #111 !important; - } - - #aip_gdpr_banner { - padding: 5px 0; - } - - #aip_gdpr_message { - padding: 0px 15px; - } -} - -#adinplayVideoContainer { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - z-index: 20000; - background: rgba($mainBgColor, 0.9); - pointer-events: all; - cursor: default; - display: flex; - justify-content: center; - align-items: center; - - *, - & { - pointer-events: all; - } - - &:not(.visible) { - display: none; - } - - &.waitingForFinish { - .videoInner { - @include S(border-radius, $globalBorderRadius); - overflow: hidden; - - &::after { - content: " "; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - @include InlineAnimation(0.2s ease-in-out) { - 0% { - opacity: 0; - } - 100% { - opacity: 1; - } - } - - & { - background: rgba($mainBgColor, 0.9) uiResource("loading.svg") center center / #{D(60px)} no-repeat; - } - } - } - } - - @include InlineAnimation(1s ease-in-out) { - 0% { - background: rgba($mainBgColor, 0.1); - } - 100% { - background: rgba($mainBgColor, 0.9); - } - } - - .adInner { - @include BoxShadow3D(lighten($mainBgColor, 15)); - @include S(border-radius, $globalBorderRadius); - @include S(padding, 15px); - // max-width: 960px; - display: block !important; - - .topbar { - display: grid; - grid-template-columns: 1fr auto; - @include S(margin-bottom, 15px); - @include S(grid-column-gap, 10px); - - .desc { - @include TextShadow3D(#fff); - @include PlainText; - } - - button.getOnSteam { - @include Text; - } - } - - .videoInner { - // width: 960px; - // height: 570px; - // min-width: 960px; - // min-height: 570px; - background: darken($mainBgColor, 1); - display: block !important; - } - } -} diff --git a/src/css/animations.scss b/src/css/animations.scss deleted file mode 100644 index 16ba7fd1..00000000 --- a/src/css/animations.scss +++ /dev/null @@ -1,19 +0,0 @@ -@each $animName in ("changeAnimEven", "changeAnimOdd") { - @keyframes #{$animName} { - 0% { - transform: scale(1, 1); - } - - 50% { - transform: scale(1.03, 1.03); - } - - 100% { - transform: scale(1, 1); - } - } - - .#{$animName} { - animation: $animName 0.2s ease-in-out; - } -} diff --git a/src/css/changelog_skins.scss b/src/css/changelog_skins.scss deleted file mode 100644 index f63849ad..00000000 --- a/src/css/changelog_skins.scss +++ /dev/null @@ -1,18 +0,0 @@ -// [data-changelog-skin="achievements"] { -// background: #f8f8f8; - -// @include DarkThemeOverride { -// background: rgba(0, 10, 20, 0.2); -// } - -// @include S(border-radius, 5px); -// &::before { -// content: " "; -// width: 100%; -// display: block; -// background: uiResource("changelog_skins/achievements.noinline.png") center center / cover no-repeat !important; -// @include S(height, 80px); -// @include S(border-radius, 5px); -// @include S(margin-bottom, 5px); -// } -// } diff --git a/src/css/common.scss b/src/css/common.scss index 448ebe4f..619706ff 100644 --- a/src/css/common.scss +++ b/src/css/common.scss @@ -2,7 +2,7 @@ * { margin: 0; padding: 0; - touch-action: pan-x pan-y !important; + touch-action: pan-x pan-y; pointer-events: none; -webkit-tap-highlight-color: rgba(255, 255, 255, 0); } @@ -21,18 +21,7 @@ body { } html { - position: fixed; - // scroll-behavior: smooth; background: $mainBgColor; - // Disable zooming and thus - -ms-touch-action: pan-x, pan-y; - touch-action: pan-x, pan-y; - -ms-content-zooming: none; - top: 0; - left: 0; - bottom: 0; - right: 0; - background: #dee1ea; @include DarkThemeOverride { background: $darkModeGameBackground; } @@ -41,77 +30,11 @@ html { body { color: #555; user-select: none; - -moz-user-select: none; - -ms-user-select: none; - background: inherit !important; - text-transform: none; - white-space: normal; - word-break: normal; - word-spacing: normal; - word-wrap: break-word; - font-style: normal; - line-break: auto; - font-stretch: 100%; + background: inherit; + overflow-wrap: break-word; text-rendering: optimizeLegibility; - text-decoration: none; - text-size-adjust: 100%; - letter-spacing: normal; - scrollbar-width: 6px; -webkit-font-smoothing: antialiased; - // -webkit-overflow-scrolling: touch; /* stop scrolling immediately */ - -webkit-touch-callout: none; - /* prevent callout to copy image, etc when tap to hold */ - -webkit-text-size-adjust: none; - /* prevent webkit from resizing text to fit */ - // Internet explorer - scrollbar-face-color: #888; - scrollbar-track-color: rgba(255, 255, 255, 0.1); - // Firefox - scrollbar-color: #cdd0d4 rgba(#000, 0.05); - overflow: hidden; @include Text; - &.externalAdOpen { - &::before { - text-transform: uppercase; - @include SuperSmallText; - content: "Loading Advertisement..."; - color: #333; - position: fixed; - top: 0; - pointer-events: all; - left: 0; - right: 0; - bottom: 0; - background: rgba(50, 60, 70, 0.8); - z-index: 999999; - display: flex; - justify-content: center; - align-items: center; - color: #fff; - @include InlineAnimation(1s ease-in-out infinite) { - 50% { - transform: scale(1.05); - } - } - } - } - // For recording the bg video - // filter: blur(5px); - // &::after { - // position: fixed; - // top: 0; - // left: 0; - // right: 0; - // bottom: 0; - // z-index: 9999; - // content: " "; - // background: rgba($ingameHudBg, 0.5); - // } -} - -img { - -webkit-touch-callout: none; - /* prevent callout to copy image, etc when tap to hold */ } i { @@ -132,8 +55,6 @@ input, textarea, select { font-size: inherit; - font-weight: inherit; - font-family: inherit; line-height: inherit; } @@ -143,70 +64,25 @@ button { pointer-events: all; cursor: pointer; position: relative; - @include TextShadow3D; - &.prefab_BuyButtonWithResources { - display: flex; - box-sizing: border-box; - @include S(padding, 6px, 4px); - // letter-spacing: 0; - background-color: color($cyan, 400); - flex-direction: row; - justify-content: center; - align-items: center; - @include S(width, 85px); - &.tooExpensive { - color: $colorRedBright; - background-color: #555; - cursor: default; - } - .cost_entry { - display: flex; - flex-grow: 1; - justify-content: center; - align-items: center; - } - b { - display: flex; - flex-grow: 1; - justify-content: center; - align-items: center; - } - &.tooExpensive { - cursor: default !important; - background-color: #565859 !important; - b { - color: $colorRedBright !important; - } - .cost_entry { - opacity: 0.6; - } - } - } + color: $legacyTextShadow3DColor; } .styledButton { - background: $themeColor; text-transform: uppercase; box-sizing: content-box; - @include S(padding, 3px, 10px); - @include IncreasedClickArea(10px); - @include TextShadow3D(#fff, $borderColor: #28292a); + padding: 0.3rem 1rem; @include ButtonText; border: 0; background: $colorBlueBright; color: #fff; - - @include S(border-radius, 0.8 * $globalBorderRadius); - // border: #{D(1px)} solid rgba(0, 10, 20, 0.2); - @include S(border-bottom-width, 2px); - // color: $accentColorDark; - letter-spacing: 0em !important; - // box-shadow: 0 #{D(1px)} #{D(2px)} 0 rgba(0, 10, 20, 0.2); - .keybinding { - @include S(bottom, -2.5px); - @include S(right, -2px); - } + border-radius: 0.8 * $globalBorderRadius; transition: opacity 0.12s ease-in-out; + + kbd { + bottom: -0.25rem; + right: -0.2rem; + } + &:hover { opacity: 0.9; } @@ -217,17 +93,11 @@ button { /* WebKit/Blink Browsers */ } -::-moz-selection { - background: $colorGreenBright; - /* Gecko Browsers */ -} - input[type="text"], input[type="email"] { - @include S(padding, 11px, 12px); - @include S(margin, 10px, 0); + padding: 1.1rem 1.2rem; + margin: 1rem 0; border: 0; - cursor: text; display: block; text-align: left; box-sizing: border-box; @@ -237,14 +107,13 @@ input[type="email"] { user-select: text !important; pointer-events: all !important; @include Text; - @include IncreasedClickArea(15px); - @include S(border-radius, $globalBorderRadius); + border-radius: $globalBorderRadius; &::placeholder { color: #fff; opacity: 0.4; } transition: background-color 0.1s ease-in-out !important; - @include TextShadow3D(#fff); + color: #fff; @include BoxShadow3D(lighten($mainBgColor, 30)); &:focus { @include BoxShadow3D(lighten($mainBgColor, 35)); @@ -258,8 +127,8 @@ input[type="email"] { &.input-token { @include SuperHeading; text-align: center; - @include S(letter-spacing, 30px); - @include S(padding-left, 30px); + letter-spacing: 3rem; + padding-left: 3rem; } } @@ -282,8 +151,6 @@ a { a { text-decoration: none; - cursor: pointer; - pointer-events: all; } i { @@ -292,35 +159,20 @@ i { input { user-select: text; - -moz-user-select: text; pointer-events: all; - cursor: text; border-radius: 0; } canvas { pointer-events: all; - // image-rendering: pixelated; - // &.smoothed { - // }1 - // &.unsmoothed { - // } - letter-spacing: 0 !important; transform: translateZ(0); backface-visibility: hidden; - -webkit-backface-visibility: hidden; -} - -.fontPreload { - position: absolute; - top: -100px; - left: -100px; } // Scrollbar ::-webkit-scrollbar { - @include S(width, 6px); - @include S(height, 6px); + width: 0.6rem; + height: 0.6rem; } ::-webkit-scrollbar-track { @@ -329,7 +181,7 @@ canvas { ::-webkit-scrollbar-thumb { // border-radius: 4px; - @include S(border-radius, $globalBorderRadius); + border-radius: $globalBorderRadius; background: #cdd0d4; } @@ -337,21 +189,6 @@ canvas { background: #d8dce0; } -#uiTestPlaybackCursor { - position: fixed; - top: 100px; - left: 100px; - z-index: 9999; - border-radius: 50%; - background: rgba(255, 255, 0, 0.4); - width: 24px; - height: 24px; - border: 3px solid rgba(0, 0, 0, 0.5); - margin-top: -12px; - margin-left: -12px; - box-sizing: border-box; -} - .pressed:not(.noPressEffect) { transform: scale(0.98) !important; animation: none !important; @@ -375,25 +212,20 @@ canvas { display: inline-block; } -.badged { - color: color($purple, 300); -} - -.prefab_LoadingTextWithAnim, -.prefab_LoadingTextWithAnimDelayed { +.prefab_LoadingTextWithAnim { display: inline-flex; align-items: center; justify-content: center; text-transform: uppercase; @include Text; - @include TextShadow3D; + color: $legacyTextShadow3DColor; opacity: 1; z-index: 20; color: #393747; &::after { content: " "; - @include S(width, 35px); - @include S(height, 35px); + width: 3.5rem; + height: 3.5rem; display: inline-block; vertical-align: middle; @@ -413,67 +245,16 @@ canvas { } } -.prefab_LoadingTextWithAnimDelayed { - @include InlineAnimation(0.6s ease-in-out) { - 0% { - opacity: 0; - } - 50% { - opacity: 0; - } - 100% { - opacity: 1; - } - } -} - .prefab_LoadingProgressIndicator { @include PlainText; - @include S(margin-top, 20px); + margin-top: 2rem; width: 100%; color: #336c9f; - @include S(height, 20px); + height: 2rem; text-transform: uppercase; text-align: center; } -.prefab_FeatureComingSoon { - position: relative; - &::after { - @include S(top, -5px); - @include S(left, -5px); - @include S(right, -5px); - @include S(bottom, -5px); - content: "Coming soon!"; - z-index: 10000; - background: rgba(lighten($mainBgColor, 0), 0.4); - @include S(border-radius, $globalBorderRadius); - position: absolute; - display: flex; - justify-content: center; - align-items: center; - pointer-events: all; - @include PlainText; - text-transform: uppercase; - } - opacity: 0.6; - > * { - opacity: 0.5 !important; - } -} - -.prefab_InfoIcon { - @include S(width, 25px); - @include S(height, 25px); - z-index: 100; - opacity: 0.8; - cursor: pointer; - pointer-events: all; - display: inline-block; - position: relative; - @include IncreasedClickArea(10px); -} - .gameState.prefab_LoadingState { text-align: center; display: flex; @@ -492,15 +273,15 @@ canvas { } & { - background: uiResource("loading.svg") center center / #{D(40px)} no-repeat; + background: uiResource("loading.svg") center center / 4rem no-repeat; } } .prefab_GameHint { position: absolute; - @include S(left, 20px); - @include S(right, 20px); - @include S(bottom, 60px); + left: 2rem; + right: 2rem; + bottom: 6rem; @include Text; color: #666; @@ -511,9 +292,9 @@ canvas { .loadingStatus { position: absolute; - @include S(left, 20px); - @include S(right, 20px); - @include S(bottom, 30px); + left: 2rem; + right: 2rem; + bottom: 3rem; @include Text; @include PlainText; color: #aaa; @@ -528,11 +309,11 @@ canvas { align-items: center; > .bar { display: none; - @include S(margin-top, 15px); + margin-top: 1.5rem; width: 80vw; @include BoxShadow3D(lighten($mainBgColor, 10), $size: 1px); position: relative; - @include TextShadow3D(#fff); + color: #fff; height: 2px; .inner { position: absolute !important; @@ -541,7 +322,7 @@ canvas { bottom: 0; z-index: 1; @include BoxShadow3D($themeColor, $size: 1px); - @include S(border-radius, $globalBorderRadius); + border-radius: $globalBorderRadius; transform-origin: 0% 50%; @include InlineAnimation(1.3s ease-in-out infinite) { 0% { @@ -563,7 +344,7 @@ canvas { position: relative; z-index: 2; display: inline-flex; - @include S(padding, 5px); + padding: 0.5rem; @include PlainText; } } @@ -577,17 +358,19 @@ canvas { .checkbox { $bgColor: darken($mainBgColor, 3); background-color: $bgColor; - @include S(width, 35px); - @include S(height, 17px); + width: 3.5rem; + height: 1.7rem; display: flex; - @include S(padding, 3px); + padding: 0.3rem; box-sizing: content-box; cursor: pointer; pointer-events: all; - transition: opacity 0.2s ease-in-out, background-color 0.3s ease-in-out, box-shadow 0.4s ease-in-out !important; + transition: + opacity 0.2s ease-in-out, + background-color 0.3s ease-in-out, + box-shadow 0.4s ease-in-out !important; position: relative; - @include BorderRadius(20px); - @include IncreasedClickArea(10px); + border-radius: 2rem; @include BoxShadow3D($bgColor, $size: 2px); &.loading { opacity: 0.2; @@ -596,20 +379,20 @@ canvas { background-color: darken($bgColor, 5); } .knob { - @include S(width, 20px); - @include S(height, 17px); + width: 2rem; + height: 1.7rem; display: inline-block; transition: margin-left 0.4s ease-in-out !important; background: #fff; position: relative; - @include BorderRadius(20px); + border-radius: 2rem; @include BoxShadow3D(#fff, $size: 1px); } &.checked { background-color: $themeColor; @include BoxShadow3D($themeColor, $size: 2px); .knob { - @include S(margin-left, 15px); + margin-left: 1.5rem; } &:hover { background-color: lighten($themeColor, 15); @@ -630,7 +413,7 @@ canvas { justify-content: center; label { - @include S(margin-right, 5px); + margin-right: 0.5rem; &, & * { @include PlainText; @@ -641,14 +424,14 @@ canvas { input.rangeInput { cursor: pointer; background-color: transparent; - @include S(width, 100px); - @include S(height, 16px); + width: 10rem; + height: 1.6rem; &::-webkit-slider-runnable-track { background-color: darken($mainBgColor, 3); color: darken($mainBgColor, 3); - // @include S(height, 16px); - @include S(border-radius, 8px); + // height: 1.6rem; + border-radius: 0.8rem; } @include DarkThemeOverride { @@ -657,14 +440,13 @@ input.rangeInput { } &::-webkit-slider-thumb { - box-shadow: inset 0 0 0 D(10px) #eee; + box-shadow: inset 0 0 0 1rem #eee; } } &::-webkit-slider-thumb { appearance: none; - -webkit-appearance: none; - box-shadow: inset 0 0 0 D(10px) $themeColor; + box-shadow: inset 0 0 0 1rem $themeColor; border-radius: 50%; transition: box-shadow 0.3s; @@ -672,104 +454,43 @@ input.rangeInput { &:hover { &::-webkit-slider-thumb { - box-shadow: inset 0 0 0 D(10px) lighten($themeColor, 15); + box-shadow: inset 0 0 0 1rem lighten($themeColor, 15); } } } +// FIXME: Translations are still using code.keybinding instead of kbd +kbd, .keybinding { background: #fff; text-transform: uppercase; - @include S(padding, 1.5px, 3px, 2px); + font-family: inherit; + padding: 0.15rem 0.3rem 0.2rem; @include PlainText; - @include S(border-radius, 0.5 * $globalBorderRadius); + border-radius: 0.5 * $globalBorderRadius; &, > span { - @include S(font-size, 9px); - @include S(line-height, 11px); - font-weight: bold !important; - text-shadow: none !important; - // font-family: Arial, sans-serif !important; + font-size: 0.9rem; + line-height: 1.1rem; } - font-weight: bold; color: $accentColorDark; text-align: center; justify-content: center; align-items: center; - @include S(min-width, 12px); + min-width: 1.2rem; display: inline-flex; position: absolute; - @include S(bottom, 0px); - @include S(right, 0px); + bottom: 0rem; + right: 0rem; z-index: 999; box-sizing: border-box; - @include S(height, 12px); + height: 1.2rem; overflow: hidden; - border: #{D(0px)} solid $accentColorDark; - .keybinding_space { - @include S(font-size, 17px); - @include S(line-height, 11px); - @include S(margin-top, -12px); - } -} - -.xpaystation-widget-lightbox { - z-index: 19999; - .xpaystation-widget-lightbox-overlay { - background: rgba($mainBgColor, 0.94); - } - &, - iframe { - pointer-events: all; - user-select: all; - } + border: 0rem solid $accentColorDark; } iframe { pointer-events: all; user-select: all; } - -// Steam overlay fix -#steamOverlayCanvasFix { - position: fixed; - top: 0px; - left: 0; - right: 0; - bottom: 0; - width: 100%; - height: 100%; - opacity: 0.01; - pointer-events: none; - z-index: -1; -} - -.sentry-error-embed-wrapper { - z-index: 10000; - background: rgba(0, 0, 0, 0.9); - * { - text-shadow: none !important; - pointer-events: all; - } -} - -.cpmsrendertarget { - &, - * { - pointer-events: all; - } - background: rgba($mainBgColor, 0.94) !important; - .cpmsvideoclosebanner { - font-family: GameFont !important; - font-size: 16px !important; - border-radius: 2px !important; - background: $themeColor !important; - @include BoxShadow3D(darken($mainBgColor, 12)); - color: #eee !important; - &:active { - @include BoxShadow3D(darken($mainBgColor, 12), $size: 1px); - transform: translateY(2px); - } - } -} diff --git a/src/css/dynamic_ui.scss b/src/css/dynamic_ui.scss deleted file mode 100644 index 8b005eff..00000000 --- a/src/css/dynamic_ui.scss +++ /dev/null @@ -1,43 +0,0 @@ -// Removes the unit (px, %, etc) from a value -@function strip-unit($number) { - @if type-of($number) == "number" and not unitless($number) { - @return $number / ($number * 0 + 1); - } - - @return $number; -} - -// Helper method to scale a value, for use in calc() etc -@function D($v) { - $baseValue: strip-unit($v) * 1px; - @return calc(#{$baseValue} * var(--ui-scale)); -} - -// Helper method to scale the font size -@mixin ScaleFont($fontSize, $lineHeight) { - font-size: D($fontSize * $mainFontScale); - line-height: D($lineHeight * $mainFontScale); -} - -// Helper method to scale a property value -@mixin S($propName, $v1, $v2: "", $v3: "", $v4: "", $important: false) { - $impSuffix: ""; - @if $important == true { - $impSuffix: "!important"; - } - - $v1: D($v1); - - @if $v2 != "" { - $v2: D($v2); - } - - @if $v3 != "" { - $v3: D($v3); - } - @if $v4 != "" { - $v4: D($v4); - } - - #{$propName}: #{$v1} #{$v2} #{$v3} #{$v4} #{$impSuffix}; -} diff --git a/src/css/error_handler.scss b/src/css/error_handler.scss new file mode 100644 index 00000000..f94ed651 --- /dev/null +++ b/src/css/error_handler.scss @@ -0,0 +1,89 @@ +:root { + // Provide a fallback font-size for UI scale calculations before the app loads + // TODO: perhaps use this as the primary source of UI scale? + font-size: #{"round(clamp(1px, calc(min(100vw, 100vh) * 0.019), 100px), 0.01px)"}; +} + +#errorHandler { + --background-primary: hsl(0deg 40% 18%); + --background-container: hsl(0deg 40% 10%); + --foreground: hsl(0deg 100% 95%); + + --background-button: rgba(255 255 255 / 0.05); + --background-button-hover: rgba(255 255 255 / 0.03); + --background-button-active: hsl(from var(--background-container) h s l / 0.4); + --background-button-success: hsl(130deg 40% 25%); + + background: var(--background-primary); + color: var(--foreground); + + font-size: 1.2rem; + line-height: 1.3; + + display: grid; + padding: 3rem; + gap: 0.8rem; + + grid-template-rows: auto 1fr 1fr auto; + grid-template-columns: auto minmax(20rem, 20%); + + pre { + background: var(--background-container); + border-radius: 0.3rem; + + font-size: 85%; + padding: 0.2rem 0.4rem; + overflow-y: auto; + white-space: pre-wrap; + overflow-wrap: break-word; + pointer-events: all; + } + + button { + background: var(--background-button); + border-radius: 0.3rem; + color: inherit; + transition: 0.15s ease-out background-color; + + padding: 0.6rem 0.6rem 0.8rem; + min-width: 10rem; + width: 15%; + font-size: inherit; + + &:hover { + background: var(--background-button-hover); + } + + &:active { + background: var(--background-button-active); + } + + &.success { + background: var(--background-button-success); + } + } + + .header, + .actions { + grid-column: 1 / 3; + } + + .stackTrace { + grid-row: 2 / 4; + } + + .loadedMods, + .buildInformation { + display: grid; + grid-template-rows: auto 1fr; + gap: 0.2rem; + overflow: auto; + } + + .actions { + display: flex; + padding-top: 1rem; + gap: 1rem; + justify-content: flex-end; + } +} diff --git a/src/css/game_state.scss b/src/css/game_state.scss index 60fab61d..31d2944c 100644 --- a/src/css/game_state.scss +++ b/src/css/game_state.scss @@ -6,22 +6,10 @@ $gameStateTransition: 0.2s ease-out; } .gameState { - display: block; - // background: $mainBgColor; - height: 100%; - width: 100%; - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - z-index: 0; - overflow: hidden !important; @include Text; @include StateAnim(opacity, transform, filter); opacity: 0; - // transform: scaleX(0.99) skewX(1deg) translate(1%, 0.5%); &.arrived { opacity: 1; diff --git a/src/css/ingame_hud/beta_overlay.scss b/src/css/ingame_hud/beta_overlay.scss index 08eba960..18535ae0 100644 --- a/src/css/ingame_hud/beta_overlay.scss +++ b/src/css/ingame_hud/beta_overlay.scss @@ -1,6 +1,6 @@ #ingame_HUD_BetaOverlay { position: fixed; - @include S(top, 70px); + top: 7rem; left: 50%; transform: translateX(-50%); color: $colorRedBright; diff --git a/src/css/ingame_hud/blueprint_placer.scss b/src/css/ingame_hud/blueprint_placer.scss index 36d1cdad..184fea76 100644 --- a/src/css/ingame_hud/blueprint_placer.scss +++ b/src/css/ingame_hud/blueprint_placer.scss @@ -1,40 +1,40 @@ -#ingame_HUD_BlueprintPlacer { - position: absolute; - @include S(top, 70px); - left: 50%; - transform: translateX(-50%); - color: #333; - z-index: 9999; - background: $ingameHudBg; - @include S(padding, 5px); - display: flex; - flex-direction: column; - color: #fff; - @include S(width, 120px); - align-items: center; - justify-content: center; - @include S(border-radius, $globalBorderRadius); - - .label { - @include PlainText; - text-transform: uppercase; - } - .costContainer { - display: flex; - align-items: center; - @include Heading; - - > canvas { - @include S(margin-left, 5px); - @include S(width, 30px); - @include S(height, 30px); - } - } - - &:not(.canAfford) { - background: rgba(98, 27, 41, 0.8); - // .costContainer { - color: rgb(255, 97, 128); - // } - } -} +#ingame_HUD_BlueprintPlacer { + position: absolute; + top: 7rem; + left: 50%; + transform: translateX(-50%); + color: #333; + z-index: 9999; + background: $ingameHudBg; + padding: 0.5rem; + display: flex; + flex-direction: column; + color: #fff; + width: 12rem; + align-items: center; + justify-content: center; + border-radius: $globalBorderRadius; + + .label { + @include PlainText; + text-transform: uppercase; + } + .costContainer { + display: flex; + align-items: center; + @include Heading; + + > canvas { + margin-left: 0.5rem; + width: 3rem; + height: 3rem; + } + } + + &:not(.canAfford) { + background: rgba(98, 27, 41, 0.8); + // .costContainer { + color: rgb(255, 97, 128); + // } + } +} diff --git a/src/css/ingame_hud/building_placer.scss b/src/css/ingame_hud/building_placer.scss index 12965f0f..6b350f6c 100644 --- a/src/css/ingame_hud/building_placer.scss +++ b/src/css/ingame_hud/building_placer.scss @@ -1,14 +1,14 @@ #ingame_HUD_PlacementHints { position: fixed; - @include S(top, 60px); - @include S(right, 10px); + top: 6rem; + right: 1rem; display: grid; - @include S(padding, 6px); - @include S(border-radius, $globalBorderRadius); - @include S(width, 240px); - @include S(grid-column-gap, 5px); + padding: 0.6rem; + border-radius: $globalBorderRadius; + width: 24rem; + grid-column-gap: 0.5rem; background: $ingameHudBg; grid-template-columns: 1fr auto; @@ -28,7 +28,7 @@ .buildingLabel { @include PlainText; - @include S(margin-bottom, 2px); + margin-bottom: 0.2rem; color: #fff; text-transform: uppercase; grid-column: 1 / 3; @@ -55,7 +55,7 @@ label { color: lighten($colorGreenBright, 10); font-weight: bold; - @include S(margin-right, 5px); + margin-right: 0.5rem; } } @@ -65,19 +65,20 @@ display: flex; flex-direction: row; align-items: center; - .keybinding { + + kbd { position: relative; - @include S(margin-left, 5px); + margin-left: 0.5rem; } } .buildingImage { grid-column: 2 / 3; grid-row: 1 / 3; - @include S(width, 100px); - @include S(height, 100px); + width: 10rem; + height: 10rem; background: top left / 100% 100% no-repeat; - @include S(border-radius, $globalBorderRadius); + border-radius: $globalBorderRadius; transition: opacity 0.1s ease-in-out; } @@ -97,15 +98,15 @@ #ingame_HUD_PlacerVariants { position: absolute; - @include S(right, 10px); - @include S(top, 200px); + right: 1rem; + top: 20rem; display: flex; - @include S(grid-gap, 5px); + grid-gap: 0.5rem; flex-direction: column; align-items: flex-end; &.compact { - @include S(top, 150px); + top: 15rem; } .explanation { @@ -113,10 +114,11 @@ grid-row: 1 / 2; @include SuperSmallText; text-align: right; - .keybinding { + font-weight: bold !important; + + .kbd { position: relative; } - font-weight: bold !important; @include DarkThemeOverride { color: rgba(#fff, 0.5); @@ -125,22 +127,22 @@ .variants { display: grid; - @include S(grid-gap, 5px); + grid-gap: 0.5rem; .variant { pointer-events: all; cursor: pointer; grid-row: 2 / 3; - @include S(border-radius, $globalBorderRadius); + border-radius: $globalBorderRadius; background: rgba($ingameHudBg, 0.5); opacity: 0.5; display: inline-flex; vertical-align: top; position: relative; align-items: center; - @include S(padding, 3px); - @include S(grid-gap, 10px); + padding: 0.3rem; + grid-gap: 1rem; transition: background-color 0.12s ease-in-out; @@ -154,30 +156,30 @@ background-color: rgba($colorBlueBright, 0.8); } - $iconSize: 25px; + $iconSize: 2.5rem; .iconWrap { grid-column: 1 / 2; grid-row: 1 / 2; position: relative; - @include S(width, $iconSize); - @include S(height, $iconSize); + width: $iconSize; + height: $iconSize; background: center center / contain no-repeat; &[data-tile-w="2"] { - @include S(width, 2 * $iconSize); + width: 2 * $iconSize; } &[data-tile-h="2"] { - @include S(height, 2 * $iconSize); + height: 2 * $iconSize; } &[data-tile-h="3"] { - @include S(height, 3 * $iconSize); + height: 3 * $iconSize; } &[data-tile-w="3"] { - @include S(width, 3 * $iconSize); + width: 3 * $iconSize; } &[data-tile-w="4"] { - @include S(width, 4 * $iconSize); + width: 4 * $iconSize; } } diff --git a/src/css/ingame_hud/buildings_toolbar.scss b/src/css/ingame_hud/buildings_toolbar.scss index 4e9028b5..141c8a9a 100644 --- a/src/css/ingame_hud/buildings_toolbar.scss +++ b/src/css/ingame_hud/buildings_toolbar.scss @@ -1,185 +1,185 @@ -.ingame_buildingsToolbar { - position: absolute; - @include S(bottom, 10px); - left: 50%; - transform: translateX(-50%); - - display: grid; - grid-template-rows: auto auto; - justify-items: center; - @include S(grid-gap, 4px); - - background: transparent; - transition: transform 120ms ease-in-out; - will-change: transform; - - &:not(.visible) { - transform: translateX(-50%) translateY(#{D(100px)}); - } - - .buildings { - display: grid; - grid-auto-flow: column; - justify-items: center; - align-self: center; - grid-gap: D(2px); - grid-row: 2 / 3; - - @include S(border-radius, $globalBorderRadius); - @include S(padding, 2px); - background-color: rgba($ingameHudBg, 0.07); - - @include DarkThemeOverride { - background-color: rgba(darken($darkModeGameBackground, 15), 0.95); - } - - &.secondary { - grid-row: 1 / 2; - - .building { - @include S(width, 30px); - @include S(height, 30px); - background-size: 45%; - - &:not(.unlocked) { - &::before { - background-size: #{D(13px)}; - } - } - } - } - - .building { - display: flex; - @include S(width, 40px); - position: relative; - @include S(height, 40px); - - @at-root html[data-tutorial-step="1_1_extractor"] &[data-id="miner"]:not(.selected), - html[data-tutorial-step="1_2_conveyor"] &[data-id="belt"]:not(.selected), - html[data-tutorial-step="2_1_place_cutter"] &[data-id="cutter"]:not(.selected), - html[data-tutorial-step="2_2_place_trash"] &[data-id="trash"]:not(.selected) { - &::before { - content: ""; - - & { - /* load-async */ - background: uiResource("icons/tutorial_arrow.png") center center / contain no-repeat; - } - - @include S(width, 25px); - @include S(height, 25px); - position: absolute; - left: 50%; - bottom: 100%; - transform: translateX(-50%); - @include InlineAnimation(1s ease-in-out infinite) { - 50% { - transform: translateX(-50%) translateY(20%); - } - } - } - @include S(border-radius, $globalBorderRadius); - box-shadow: 0 0 D(10px) D(5px) rgba(74, 237, 134, 0.5) !important; - background: rgba(74, 237, 134, 0.5) !important; - } - - .icon { - color: $accentColorDark; - display: flex; - flex-direction: column-reverse; - position: relative; - align-items: center; - justify-content: center; - width: 100%; - height: 100%; - padding: 0; - margin: 0; - - background: center center / 70% no-repeat; - } - - &:not(.unlocked) { - @include S(width, 25px); - .icon { - opacity: 0.15; - } - &.editor { - .icon { - pointer-events: all; - cursor: pointer; - &:hover { - background-color: rgba(22, 30, 68, 0.1); - } - } - } - &:not(.editor) { - .icon { - background-image: uiResource("locked_building.png") !important; - } - } - } - - &.unlocked { - .icon { - @include S(border-radius, $globalBorderRadius * 0.9); - - pointer-events: all; - transition: all 0.12s ease-in-out; - transition-property: background-color, transform; - cursor: pointer; - - &:hover { - background-color: rgba(30, 40, 90, 0.1); - - @include DarkThemeOverride { - background-color: rgba(255, 255, 255, 0.07); - } - } - - &.pressed { - transform: scale(0.9) !important; - } - } - &.selected { - .icon { - background-color: rgba(lighten($colorBlueBright, 9), 0.4); - } - // transform: scale(1.05); - // @include S(border-radius, $globalBorderRadius); - - .keybinding { - color: #111; - } - } - - .puzzle-lock { - & { - /* @load-async */ - background: uiResource("locked_building.png") center center / 90% no-repeat; - } - - display: grid; - grid-auto-flow: column; - - position: absolute; - @include S(top, -15px); - left: 50%; - transform: translateX(-50%) !important; - transition: all 0.12s ease-in-out; - transition-property: opacity, transform; - - cursor: pointer; - pointer-events: all; - - @include S(width, 12px); - @include S(height, 12px); - - &:hover { - opacity: 0.5; - } - } - } - } - } -} +.ingame_buildingsToolbar { + position: absolute; + bottom: 1rem; + left: 50%; + transform: translateX(-50%); + + display: grid; + grid-template-rows: auto auto; + justify-items: center; + grid-gap: 0.4rem; + + background: transparent; + transition: transform 120ms ease-in-out; + will-change: transform; + + &:not(.visible) { + transform: translateX(-50%) translateY(10rem); + } + + .buildings { + display: grid; + grid-auto-flow: column; + justify-items: center; + align-self: center; + grid-gap: 0.2rem; + grid-row: 2 / 3; + + border-radius: $globalBorderRadius; + padding: 0.2rem; + background-color: rgba($ingameHudBg, 0.07); + + @include DarkThemeOverride { + background-color: rgba(darken($darkModeGameBackground, 15), 0.95); + } + + &.secondary { + grid-row: 1 / 2; + + .building { + width: 3rem; + height: 3rem; + background-size: 45%; + + &:not(.unlocked) { + &::before { + background-size: 1.3rem; + } + } + } + } + + .building { + display: flex; + width: 4rem; + position: relative; + height: 4rem; + + @at-root html[data-tutorial-step="1_1_extractor"] &[data-id="miner"]:not(.selected), + html[data-tutorial-step="1_2_conveyor"] &[data-id="belt"]:not(.selected), + html[data-tutorial-step="2_1_place_cutter"] &[data-id="cutter"]:not(.selected), + html[data-tutorial-step="2_2_place_trash"] &[data-id="trash"]:not(.selected) { + &::before { + content: ""; + + & { + /* load-async */ + background: uiResource("icons/tutorial_arrow.png") center center / contain no-repeat; + } + + width: 2.5rem; + height: 2.5rem; + position: absolute; + left: 50%; + bottom: 100%; + transform: translateX(-50%); + @include InlineAnimation(1s ease-in-out infinite) { + 50% { + transform: translateX(-50%) translateY(20%); + } + } + } + border-radius: $globalBorderRadius; + box-shadow: 0 0 1rem 0.5rem rgba(74, 237, 134, 0.5) !important; + background: rgba(74, 237, 134, 0.5) !important; + } + + .icon { + color: $accentColorDark; + display: flex; + flex-direction: column-reverse; + position: relative; + align-items: center; + justify-content: center; + width: 100%; + height: 100%; + padding: 0; + margin: 0; + + background: center center / 70% no-repeat; + } + + &:not(.unlocked) { + width: 2.5rem; + .icon { + opacity: 0.15; + } + &.editor { + .icon { + pointer-events: all; + cursor: pointer; + &:hover { + background-color: rgba(22, 30, 68, 0.1); + } + } + } + &:not(.editor) { + .icon { + background-image: uiResource("locked_building.png") !important; + } + } + } + + &.unlocked { + .icon { + border-radius: $globalBorderRadius * 0.9; + + pointer-events: all; + transition: all 0.12s ease-in-out; + transition-property: background-color, transform; + cursor: pointer; + + &:hover { + background-color: rgba(30, 40, 90, 0.1); + + @include DarkThemeOverride { + background-color: rgba(255, 255, 255, 0.07); + } + } + + &.pressed { + transform: scale(0.9) !important; + } + } + &.selected { + .icon { + background-color: rgba(lighten($colorBlueBright, 9), 0.4); + } + // transform: scale(1.05); + // border-radius: $globalBorderRadius; + + kbd { + color: #111; + } + } + + .puzzle-lock { + & { + /* @load-async */ + background: uiResource("locked_building.png") center center / 90% no-repeat; + } + + display: grid; + grid-auto-flow: column; + + position: absolute; + top: -1.5rem; + left: 50%; + transform: translateX(-50%) !important; + transition: all 0.12s ease-in-out; + transition-property: opacity, transform; + + cursor: pointer; + pointer-events: all; + + width: 1.2rem; + height: 1.2rem; + + &:hover { + opacity: 0.5; + } + } + } + } + } +} diff --git a/src/css/ingame_hud/color_blind_helper.scss b/src/css/ingame_hud/color_blind_helper.scss index 027b781b..0c1fc780 100644 --- a/src/css/ingame_hud/color_blind_helper.scss +++ b/src/css/ingame_hud/color_blind_helper.scss @@ -4,8 +4,8 @@ @include SuperSmallText; color: #fff; background: $ingameHudBg; - @include S(padding, 5px); - @include S(top, 20px); + padding: 0.5rem; + top: 2rem; left: 50%; transform: translateX(-50%); text-transform: uppercase; diff --git a/src/css/ingame_hud/debug_info.scss b/src/css/ingame_hud/debug_info.scss index 3a077d53..017e1f8f 100644 --- a/src/css/ingame_hud/debug_info.scss +++ b/src/css/ingame_hud/debug_info.scss @@ -1,7 +1,7 @@ #ingame_HUD_DebugInfo { position: absolute; - @include S(bottom, 5px); - @include S(right, 5px); + bottom: 0.5rem; + right: 0.5rem; text-align: right; font-size: 15px; @@ -28,7 +28,6 @@ font-size: 14px; line-height: 15px; padding: 1px; - font-family: "GameFont"; border-radius: 3px; } } diff --git a/src/css/ingame_hud/dialogs.scss b/src/css/ingame_hud/dialogs.scss index 412a1911..433c5aa3 100644 --- a/src/css/ingame_hud/dialogs.scss +++ b/src/css/ingame_hud/dialogs.scss @@ -1,307 +1,296 @@ -.ingameDialog { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - pointer-events: all; - background: $modalDialogBg; - - display: flex; - align-items: center; - justify-content: center; - - @include InlineAnimation(0.12s ease-in-out) { - 0% { - background-color: transparent; - opacity: 0.5; - } - 100% { - background-color: $modalDialogBg; - } - } - - $darkModeDialogBg: darken($darkModeGameBackground, 5); - - @include DarkThemeOverride { - background: rgba($darkModeDialogBg, 0.9); - @include InlineAnimation(0.12s ease-in-out) { - 0% { - background-color: transparent; - opacity: 0.5; - } - 100% { - background-color: rgba($darkModeDialogBg, 0.9); - } - } - - > .dialogInner.optionChooserDialog .optionParent { - .option { - background: $darkModeControlsBackground; - - &:hover { - background-color: lighten($darkModeControlsBackground, 5); - } - - &.active { - background: $colorBlueBright; - color: #fff; - } - } - } - } - - &.visible { - .dialogInner { - opacity: 1; - } - backdrop-filter: blur(D(3px)); - } - - .dialogInner { - transition: opacity 0.2s ease-in-out; - opacity: 0; - } - - &.loadingDialog { - * { - color: #fff; - } - - display: flex; - flex-direction: column; - - .text { - text-transform: uppercase; - @include S(margin-bottom, 10px); - } - } - - > .dialogInner { - background: #fff; - max-height: calc(100vh - #{D(40px)}); - @include S(border-radius, $globalBorderRadius); - display: flex; - flex-direction: column; - @include S(padding, 12px); - - box-shadow: 0 D(5px) D(15px) rgba(#000, 0.1); - pointer-events: all; - - @include DarkThemeOverride { - background: darken($darkModeControlsBackground, 5); - } - - &.optionChooserDialog { - .optionParent { - display: grid; - @include S(grid-gap, 5px); - @include S(padding-right, 5px); - grid-template-columns: 1fr 1fr; - .option { - @include S(border-radius, $globalBorderRadius); - - pointer-events: all; - cursor: pointer; - @include S(padding, 10px); - - background: #eee; - - transition: background-color 0.12s ease-in-out; - - &:hover { - background-color: #e7e7e7; - } - - &.active { - background-color: $colorBlueBright; - color: #fff; - } - } - } - } - - > .title { - @include Heading; - margin: 0; - text-transform: uppercase; - display: grid; - align-items: center; - grid-template-columns: 1fr auto; - @include S(margin-bottom, 10px); - - @include DarkThemeInvert(); - > .closeButton { - opacity: 0.7; - @include S(width, 20px); - @include S(height, 20px); - cursor: pointer; - pointer-events: all; - transition: opacity 0.2s ease-in-out; - &:hover { - opacity: 0.4; - } - & { - background: uiResource("icons/close.png") center center / 80% no-repeat; - } - } - } - - > .content { - @include PlainText; - overflow-y: auto; - pointer-events: all; - @include S(width, 350px); - - @include DarkThemeOverride { - color: #aaa; - } - - a { - color: $colorBlueBright; - } - - strong { - font-weight: bold; - } - - .keybinding { - position: relative; - background: #eee; - @include PlainText; - height: unset; - margin: 1px 0; - } - - h3 { - @include S(margin-top, 10px); - } - - input { - background: #eee; - color: #333438; - width: 100%; - - &.errored { - background-color: rgb(250, 206, 206); - - &::placeholder { - color: #fff; - opacity: 0.8; - } - } - } - - ul.bucketList { - padding-left: 30px; - - li { - display: list-item; - } - } - - .ingameItemChooser { - @include S(margin, 10px, 0); - display: grid; - @include S(grid-column-gap, 3px); - @include S(grid-row-gap, 5px); - grid-template-columns: repeat(10, 1fr); - align-items: center; - justify-items: center; - - canvas { - pointer-events: all; - @include S(width, 25px); - @include S(height, 25px); - position: relative; - cursor: pointer; - @include IncreasedClickArea(3px); - - &:hover { - opacity: 0.9; - } - } - } - - .dialogModsMod { - background: rgba(0, 0, 0, 0.05); - @include S(padding, 5px); - @include S(margin, 10px, 0); - @include S(border-radius, $globalBorderRadius); - display: grid; - grid-template-columns: 1fr D(100px); - - @include DarkThemeOverride { - background: rgba(0, 0, 0, 0.2); - } - - button { - grid-column: 2 / 3; - grid-row: 1 / 3; - align-self: start; - } - - .version { - @include SuperSmallText; - opacity: 0.5; - } - - .name { - } - } - } - - > .buttons { - @include S(margin-top, 15px); - display: flex; - justify-content: flex-end; - > button { - @include S(margin-left, 8px); - @include Text; - @include S(min-width, 60px); - @include S(padding, 5px, 15px); - - transition: opacity 0.12s ease-in-out; - &:hover { - opacity: 0.9; - } - - &.good { - background-color: $colorGreenBright; - color: #fff; - } - - &.bad { - background-color: $colorRedBright; - color: #fff; - } - - &.timedButton { - pointer-events: none; - cursor: default; - position: relative; - overflow: hidden; - &::after { - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: unset; - z-index: 5; - content: " "; - display: inline-block; - background: rgba(#fff, 0.6); - @include InlineAnimation(1s linear) { - 0% { - width: 100%; - } - 100% { - width: 0%; - } - } - } - } - } - } - } -} +.ingameDialog { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + pointer-events: all; + background: $modalDialogBg; + + display: flex; + align-items: center; + justify-content: center; + + @include InlineAnimation(0.12s ease-in-out) { + 0% { + background-color: transparent; + opacity: 0.5; + } + 100% { + background-color: $modalDialogBg; + } + } + + $darkModeDialogBg: darken($darkModeGameBackground, 5); + + @include DarkThemeOverride { + background: rgba($darkModeDialogBg, 0.9); + @include InlineAnimation(0.12s ease-in-out) { + 0% { + background-color: transparent; + opacity: 0.5; + } + 100% { + background-color: rgba($darkModeDialogBg, 0.9); + } + } + + > .dialogInner.optionChooserDialog .optionParent { + .option { + background: $darkModeControlsBackground; + + &:hover { + background-color: lighten($darkModeControlsBackground, 5); + } + + &.active { + background: $colorBlueBright; + color: #fff; + } + } + } + } + + &.visible { + .dialogInner { + opacity: 1; + } + backdrop-filter: blur(0.3rem); + } + + .dialogInner { + transition: opacity 0.2s ease-in-out; + opacity: 0; + } + + &.loadingDialog { + * { + color: #fff; + } + + display: flex; + flex-direction: column; + + .text { + text-transform: uppercase; + margin-bottom: 1rem; + } + } + + > .dialogInner { + background: #fff; + max-height: calc(100vh - 4rem); + border-radius: $globalBorderRadius; + display: flex; + flex-direction: column; + padding: 1.2rem; + + box-shadow: 0 0.5rem 1.5rem rgba(#000, 0.1); + pointer-events: all; + + @include DarkThemeOverride { + background: darken($darkModeControlsBackground, 5); + } + + &.optionChooserDialog { + .optionParent { + display: grid; + grid-gap: 0.5rem; + padding-right: 0.5rem; + grid-template-columns: 1fr 1fr; + .option { + border-radius: $globalBorderRadius; + + pointer-events: all; + cursor: pointer; + padding: 1rem; + + background: #eee; + + transition: background-color 0.12s ease-in-out; + + &:hover { + background-color: #e7e7e7; + } + + &.active { + background-color: $colorBlueBright; + color: #fff; + } + } + } + } + + > .title { + @include Heading; + margin: 0; + text-transform: uppercase; + display: grid; + align-items: center; + grid-template-columns: 1fr auto; + margin-bottom: 1rem; + + @include DarkThemeInvert(); + > .closeButton { + opacity: 0.7; + width: 2rem; + height: 2rem; + cursor: pointer; + pointer-events: all; + transition: opacity 0.2s ease-in-out; + &:hover { + opacity: 0.4; + } + & { + background: uiResource("icons/close.png") center center / 80% no-repeat; + } + } + } + + > .content { + @include PlainText; + overflow-y: auto; + pointer-events: all; + width: 35rem; + + @include DarkThemeOverride { + color: #aaa; + } + + a { + color: $colorBlueBright; + } + + strong { + font-weight: bold; + } + + // FIXME: Translations are still using code.keybinding instead of kbd + kbd, + .keybinding { + position: relative; + background: #eee; + @include PlainText; + height: unset; + margin: 1px 0; + } + + h3 { + margin-top: 1rem; + } + + input { + background: #eee; + color: #333438; + width: 100%; + + &.errored { + background-color: rgb(250, 206, 206); + + &::placeholder { + color: #fff; + opacity: 0.8; + } + } + } + + .ingameItemChooser { + margin: 1rem 0; + display: grid; + grid-column-gap: 0.3rem; + grid-row-gap: 0.5rem; + grid-template-columns: repeat(10, 1fr); + place-items: center; + + canvas { + pointer-events: all; + width: 2.5rem; + height: 2.5rem; + position: relative; + cursor: pointer; + + &:hover { + opacity: 0.9; + } + } + } + + .dialogModsMod { + background: rgba(0, 0, 0, 0.05); + padding: 0.5rem; + margin: 1rem 0; + border-radius: $globalBorderRadius; + display: grid; + grid-template-columns: 1fr 10rem; + + @include DarkThemeOverride { + background: rgba(0, 0, 0, 0.2); + } + + button { + grid-column: 2 / 3; + grid-row: 1 / 3; + align-self: start; + } + + .version { + @include SuperSmallText; + opacity: 0.5; + } + } + } + + > .buttons { + margin-top: 1.5rem; + display: flex; + justify-content: flex-end; + > button { + margin-left: 0.8rem; + @include Text; + min-width: 6rem; + padding: 0.5rem 1.5rem; + + transition: opacity 0.12s ease-in-out; + &:hover { + opacity: 0.9; + } + + &.good { + background-color: $colorGreenBright; + color: #fff; + } + + &.bad { + background-color: $colorRedBright; + color: #fff; + } + + &.timedButton { + pointer-events: none; + cursor: default; + position: relative; + overflow: hidden; + &::after { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: unset; + z-index: 5; + content: " "; + display: inline-block; + background: rgba(#fff, 0.6); + @include InlineAnimation(1s linear) { + 0% { + width: 100%; + } + 100% { + width: 0%; + } + } + } + } + } + } + } +} diff --git a/src/css/ingame_hud/entity_debugger.scss b/src/css/ingame_hud/entity_debugger.scss index 4cf7e5e9..3ce9c75c 100644 --- a/src/css/ingame_hud/entity_debugger.scss +++ b/src/css/ingame_hud/entity_debugger.scss @@ -1,8 +1,8 @@ #ingame_HUD_EntityDebugger { position: absolute; background: $ingameHudBg; - @include S(padding, 5px); - @include S(right, 30px); + padding: 0.5rem; + right: 3rem; top: 50%; transform: translateY(-50%); @@ -26,7 +26,7 @@ } .propertyTable { - @include S(margin-top, 8px); + margin-top: 0.8rem; } .propertyTable, @@ -34,16 +34,16 @@ .entityComponents .object > div { display: grid; grid-template-columns: 1fr auto; - @include S(column-gap, 10px); + column-gap: 1rem; } .entityComponents { grid-column: 1 / 3; - @include S(margin-top, 5px); + margin-top: 0.5rem; font-family: "Roboto Mono", "Fira Code", monospace; font-size: 90%; - @include S(letter-spacing, -0.5px); + letter-spacing: -0.05rem; label, span { @@ -56,8 +56,8 @@ &, * { @include SuperSmallText; - @include S(font-size, 7px, $important: true); - @include S(line-height, 12px, $important: true); + font-size: 0.7rem !important; + line-height: 1.2rem !important; } .object { @@ -72,7 +72,7 @@ } } > div { - @include S(margin-left, 4px); + margin-left: 0.4rem; cursor: pointer; } } diff --git a/src/css/ingame_hud/game_menu.scss b/src/css/ingame_hud/game_menu.scss index c95626f1..b8dedf2b 100644 --- a/src/css/ingame_hud/game_menu.scss +++ b/src/css/ingame_hud/game_menu.scss @@ -1,141 +1,139 @@ -#ingame_HUD_GameMenu { - position: absolute; - @include S(top, 10px); - @include S(right, 10px); - display: grid; - grid-template-columns: 1fr 1fr 1fr 1fr; - @include S(grid-gap, 6px); - - backdrop-filter: blur(D(1px)); - - > button, - > .button { - @include PlainText; - @include IncreasedClickArea(0px); - background: green; - @include S(width, 30px); - @include S(height, 30px); - - pointer-events: all; - cursor: pointer; - position: relative; - transition: all 0.12s ease-in-out; - transition-property: opacity, transform; - - display: inline-flex; - background: center center / 70% no-repeat; - grid-row: 1; - - &.pressed { - transform: scale(0.9) !important; - } - - opacity: 0.7; - &:hover { - opacity: 0.9 !important; - } - - @include DarkThemeInvert; - - &.shop { - grid-column: 1; - & { - /* @load-async */ - background-image: uiResource("icons/shop.png"); - } - } - - &.stats { - grid-column: 2; - & { - /* @load-async */ - background-image: uiResource("icons/statistics.png"); - } - } - - &.save { - & { - /* @load-async */ - background-image: uiResource("icons/save.png"); - } - grid-column: 3; - @include MakeAnimationWrappedEvenOdd(0.5s ease-in-out) { - 0% { - transform: scale(1, 1); - } - - 70% { - transform: scale(1.5, 1.5) rotate(20deg); - opacity: 0.2; - } - - 85% { - transform: scale(0.9, 0.9); - opacity: 1; - } - - 90% { - transform: scale(1.1, 1.1); - } - } - - &.saving { - @include InlineAnimation(0.4s ease-in-out infinite) { - 50% { - opacity: 0.5; - transform: scale(0.8); - } - } - pointer-events: none; - cursor: default; - } - } - - &.settings { - grid-column: 4; - & { - /* @load-async */ - background-image: uiResource("icons/settings_menu_settings.png"); - } - } - - &:hover { - opacity: 0.9; - transform: translateY(0); - } - - &:not(.hasBadge) .badge { - display: none; - } - - &.hasBadge { - &.shop { - filter: none; - opacity: 0.9; - - & { - /* @load-async */ - background-image: uiResource("icons/shop_active.png"); - } - } - - transform-origin: 50% 50%; - @include InlineAnimation(0.8s ease-in-out infinite) { - 50% { - transform: scale(1.3) rotate(6deg); - } - } - - .badge { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - @include PlainText; - display: flex; - justify-content: center; - align-items: center; - } - } - } -} +#ingame_HUD_GameMenu { + position: absolute; + top: 1rem; + right: 1rem; + display: grid; + grid-template-columns: 1fr 1fr 1fr 1fr; + grid-gap: 0.6rem; + + backdrop-filter: blur(0.1rem); + + > button, + > .button { + @include PlainText; + width: 3rem; + height: 3rem; + + pointer-events: all; + cursor: pointer; + position: relative; + transition: all 0.12s ease-in-out; + transition-property: opacity, transform; + + display: inline-flex; + background: center center / 70% no-repeat; + grid-row: 1; + + &.pressed { + transform: scale(0.9) !important; + } + + opacity: 0.7; + &:hover { + opacity: 0.9 !important; + } + + @include DarkThemeInvert; + + &.shop { + grid-column: 1; + & { + /* @load-async */ + background-image: uiResource("icons/shop.png"); + } + } + + &.stats { + grid-column: 2; + & { + /* @load-async */ + background-image: uiResource("icons/statistics.png"); + } + } + + &.save { + & { + /* @load-async */ + background-image: uiResource("icons/save.png"); + } + grid-column: 3; + @include MakeAnimationWrappedEvenOdd(0.5s ease-in-out) { + 0% { + transform: scale(1, 1); + } + + 70% { + transform: scale(1.5, 1.5) rotate(20deg); + opacity: 0.2; + } + + 85% { + transform: scale(0.9, 0.9); + opacity: 1; + } + + 90% { + transform: scale(1.1, 1.1); + } + } + + &.saving { + @include InlineAnimation(0.4s ease-in-out infinite) { + 50% { + opacity: 0.5; + transform: scale(0.8); + } + } + pointer-events: none; + cursor: default; + } + } + + &.settings { + grid-column: 4; + & { + /* @load-async */ + background-image: uiResource("icons/settings_menu_settings.png"); + } + } + + &:hover { + opacity: 0.9; + transform: translateY(0); + } + + &:not(.hasBadge) .badge { + display: none; + } + + &.hasBadge { + &.shop { + filter: none; + opacity: 0.9; + + & { + /* @load-async */ + background-image: uiResource("icons/shop_active.png"); + } + } + + transform-origin: 50% 50%; + @include InlineAnimation(0.8s ease-in-out infinite) { + 50% { + transform: scale(1.3) rotate(6deg); + } + } + + .badge { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + @include PlainText; + display: flex; + justify-content: center; + align-items: center; + } + } + } +} diff --git a/src/css/ingame_hud/interactive_tutorial.scss b/src/css/ingame_hud/interactive_tutorial.scss index dad52841..cb802935 100644 --- a/src/css/ingame_hud/interactive_tutorial.scss +++ b/src/css/ingame_hud/interactive_tutorial.scss @@ -1,21 +1,21 @@ #ingame_HUD_InteractiveTutorial { position: absolute; - @include S(left, 10px); - @include S(bottom, 10px); + left: 1rem; + bottom: 1rem; @include StyleBelowWidth(1430px) { - @include S(bottom, 10px + 40px); + bottom: 5rem; } - @include S(width, 150px); + width: 15rem; background: $ingameHudBg; - @include S(padding, 7px); + padding: 0.7rem; color: #eee; display: flex; flex-direction: column; - @include S(border-radius, $globalBorderRadius); + border-radius: $globalBorderRadius; @include MakeAnimationWrappedEvenOdd(0.5s ease-in-out) { 0% { @@ -49,11 +49,11 @@ } .helperGif { - @include S(margin-top, 5px); - @include S(width, 150px); - @include S(height, 150px); + margin-top: 0.5rem; + width: 15rem; + height: 15rem; background: center center / cover no-repeat; transition: opacity 0.1s ease-out; - @include S(border-radius, $globalBorderRadius); + border-radius: $globalBorderRadius; } } diff --git a/src/css/ingame_hud/keybindings_overlay.scss b/src/css/ingame_hud/keybindings_overlay.scss index b44dde4b..2f19bd9b 100644 --- a/src/css/ingame_hud/keybindings_overlay.scss +++ b/src/css/ingame_hud/keybindings_overlay.scss @@ -1,76 +1,77 @@ -#ingame_HUD_KeybindingOverlay { - position: absolute; - @include S(top, 10px); - @include S(left, 10px); - - display: flex; - flex-direction: column; - align-items: flex-start; - color: #333438; - backdrop-filter: blur(D(1px)); - padding: D(3px); - - @include DarkThemeOverride { - color: #fff; - } - - transition: opacity 0.1s ease-out; - &.hovered { - opacity: 0.1; - } - - > .binding { - &:not(.visible) { - display: none !important; - } - - display: inline-grid; - @include PlainText; - align-items: center; - @include S(margin-bottom, 3px); - grid-auto-flow: column; - @include S(grid-gap, 2px); - - i { - display: inline-block; - @include S(height, 10px); - width: 1px; - @include S(margin, 0, 3px); - background-color: #fff; - transform: rotate(10deg); - // @include S(margin, 0, 3px); - } - - code { - position: relative; - top: unset; - left: unset; - margin: 0; - &.rightMouse { - /* @load-async */ - background: #fff uiResource("icons/mouse_right.png") center center / 85% no-repeat; - } - - &.leftMouse { - /* @load-async */ - background: #fff uiResource("icons/mouse_left.png") center center / 85% no-repeat; - } - } - - label { - color: #333438; - @include SuperSmallText; - text-transform: uppercase; - // color: #fff; - @include DarkThemeOverride { - color: #fff; - } - - @include S(margin-left, 5px); - } - } -} - -body.uiHidden #ingame_HUD_KeybindingOverlay .binding:not(.hudToggle) { - display: none; -} +#ingame_HUD_KeybindingOverlay { + position: absolute; + top: 1rem; + left: 1rem; + + display: flex; + flex-direction: column; + align-items: flex-start; + color: #333438; + backdrop-filter: blur(0.1rem); + padding: 0.3rem; + + @include DarkThemeOverride { + color: #fff; + } + + transition: opacity 0.1s ease-out; + &.hovered { + opacity: 0.1; + } + + > .binding { + &:not(.visible) { + display: none !important; + } + + display: inline-grid; + @include PlainText; + align-items: center; + margin-bottom: 0.3rem; + grid-auto-flow: column; + grid-gap: 0.2rem; + + i { + display: inline-block; + height: 1rem; + width: 1px; + margin: 0 0.3rem; + background-color: #fff; + transform: rotate(10deg); + // margin: 0 .3rem; + } + + kbd { + position: relative; + top: unset; + left: unset; + margin: 0; + + &.rightMouse { + /* @load-async */ + background: #fff uiResource("icons/mouse_right.png") center center / 85% no-repeat; + } + + &.leftMouse { + /* @load-async */ + background: #fff uiResource("icons/mouse_left.png") center center / 85% no-repeat; + } + } + + label { + color: #333438; + @include SuperSmallText; + text-transform: uppercase; + // color: #fff; + @include DarkThemeOverride { + color: #fff; + } + + margin-left: 0.5rem; + } + } +} + +body.uiHidden #ingame_HUD_KeybindingOverlay .binding:not(.hudToggle) { + display: none; +} diff --git a/src/css/ingame_hud/notifications.scss b/src/css/ingame_hud/notifications.scss index 4b8ee767..f5db5fe9 100644 --- a/src/css/ingame_hud/notifications.scss +++ b/src/css/ingame_hud/notifications.scss @@ -1,27 +1,27 @@ #ingame_HUD_Notifications { position: absolute; - @include S(bottom, 60px); - @include S(right, 10px); + bottom: 6rem; + right: 1rem; .notification { background: rgba(#333438, 0.8); - @include S(border-radius, $globalBorderRadius); - @include S(margin-top, 3px); + border-radius: $globalBorderRadius; + margin-top: 0.3rem; color: #fff; @include SuperSmallText; - @include S(padding, 7px, 10px); - @include S(width, 150px); + padding: 0.7rem 1rem; + width: 15rem; @include DarkThemeOverride { background-color: rgba(#55595d, 0.8); } &[data-icon] { - @include S(background-position-x, 8px); + background-position-x: 0.8rem; background-position-y: center; - @include S(padding-left, 35px); + padding-left: 3.5rem; background-repeat: no-repeat; - @include S(background-size, 15px); + background-size: 1.5rem; } transform-origin: 100% 50%; diff --git a/src/css/ingame_hud/pinned_shapes.scss b/src/css/ingame_hud/pinned_shapes.scss index c9a39536..e7ef4691 100644 --- a/src/css/ingame_hud/pinned_shapes.scss +++ b/src/css/ingame_hud/pinned_shapes.scss @@ -1,156 +1,154 @@ -#ingame_HUD_PinnedShapes { - position: absolute; - @include S(left, 9px); - @include S(top, 150px); - @include PlainText; - display: flex; - flex-direction: column; - align-items: flex-start; - justify-content: flex-start; - - > .shape { - position: relative; - display: grid; - align-items: center; - justify-content: center; - grid-template-columns: auto 1fr; - grid-template-rows: 1fr 1fr; - @include S(margin-bottom, 4px); - color: #333438; - - &.removable { - pointer-events: all; - } - - > canvas { - @include S(width, 25px); - @include S(height, 25px); - grid-column: 1 / 2; - grid-row: 1 / 3; - pointer-events: none; - z-index: 20; - position: relative; - } - - > .amountLabel, - > .goalLabel { - @include S(margin-left, 5px); - @include SuperSmallText; - font-weight: bold; - display: inline-flex; - align-items: center; - flex-direction: row; - grid-column: 2 / 3; - @include S(height, 9px); - - @include DarkThemeOverride { - color: #eee; - } - } - - > .goalLabel { - @include S(font-size, 7px); - opacity: 0.9; - align-self: start; - justify-self: start; - font-weight: normal; - grid-row: 2 / 3; - } - - > .amountLabel { - align-self: end; - justify-self: start; - grid-row: 1 / 2; - } - - > .infoButton { - @include S(width, 8px); - @include S(height, 8px); - position: absolute; - opacity: 0.7; - @include S(top, 13px); - @include S(left, -7px); - @include DarkThemeInvert; - @include IncreasedClickArea(2px); - transition: opacity 0.12s ease-in-out; - z-index: 100; - - &:hover { - opacity: 0.8; - } - - & { - /* @load-async */ - background: uiResource("icons/info_button.png") center center / 95% no-repeat; - } - } - - > .unpinButton { - @include S(width, 8px); - @include S(height, 8px); - position: absolute; - opacity: 0.7; - @include S(top, 3px); - @include S(left, -7px); - @include DarkThemeInvert; - @include IncreasedClickArea(2px); - transition: opacity 0.12s ease-in-out; - z-index: 100; - - &:hover { - opacity: 0.8; - } - - & { - /* @load-async */ - background: uiResource("icons/unpin_shape.png") center center / 80% no-repeat; - } - } - - &.goal, - &.blueprint { - .amountLabel::after { - content: " "; - position: absolute; - display: inline-block; - @include S(width, 8px); - @include S(height, 8px); - @include S(top, 4px); - @include S(left, -7px); - background: center center / contain no-repeat; - } - - &.goal .amountLabel { - &::after { - /* @load-async */ - background-image: uiResource("icons/current_goal_marker.png"); - background-size: 90%; - } - @include DarkThemeOverride { - &::after { - /* @load-async */ - background-image: uiResource("icons/current_goal_marker_inverted.png") !important; - } - } - } - - &.blueprint .amountLabel { - &::after { - /* @load-async */ - background-image: uiResource("icons/blueprint_marker.png"); - background-size: 90%; - } - @include DarkThemeOverride { - &::after { - /* @load-async */ - background-image: uiResource("icons/blueprint_marker_inverted.png") !important; - } - } - } - } - - &.completed { - opacity: 0.5; - } - } -} +#ingame_HUD_PinnedShapes { + position: absolute; + left: 0.9rem; + top: 15rem; + @include PlainText; + display: flex; + flex-direction: column; + align-items: flex-start; + justify-content: flex-start; + + > .shape { + position: relative; + display: grid; + align-items: center; + justify-content: center; + grid-template-columns: auto 1fr; + grid-template-rows: 1fr 1fr; + margin-bottom: 0.4rem; + color: #333438; + + &.removable { + pointer-events: all; + } + + > canvas { + width: 2.5rem; + height: 2.5rem; + grid-column: 1 / 2; + grid-row: 1 / 3; + pointer-events: none; + z-index: 20; + position: relative; + } + + > .amountLabel, + > .goalLabel { + margin-left: 0.5rem; + @include SuperSmallText; + font-weight: bold; + display: inline-flex; + align-items: center; + flex-direction: row; + grid-column: 2 / 3; + height: 0.9rem; + + @include DarkThemeOverride { + color: #eee; + } + } + + > .goalLabel { + font-size: 0.7rem; + opacity: 0.9; + align-self: start; + justify-self: start; + font-weight: normal; + grid-row: 2 / 3; + } + + > .amountLabel { + align-self: end; + justify-self: start; + grid-row: 1 / 2; + } + + > .infoButton { + width: 0.8rem; + height: 0.8rem; + position: absolute; + opacity: 0.7; + top: 1.3rem; + left: -0.7rem; + @include DarkThemeInvert; + transition: opacity 0.12s ease-in-out; + z-index: 100; + + &:hover { + opacity: 0.8; + } + + & { + /* @load-async */ + background: uiResource("icons/info_button.png") center center / 95% no-repeat; + } + } + + > .unpinButton { + width: 0.8rem; + height: 0.8rem; + position: absolute; + opacity: 0.7; + top: 0.3rem; + left: -0.7rem; + @include DarkThemeInvert; + transition: opacity 0.12s ease-in-out; + z-index: 100; + + &:hover { + opacity: 0.8; + } + + & { + /* @load-async */ + background: uiResource("icons/unpin_shape.png") center center / 80% no-repeat; + } + } + + &.goal, + &.blueprint { + .amountLabel::after { + content: " "; + position: absolute; + display: inline-block; + width: 0.8rem; + height: 0.8rem; + top: 0.4rem; + left: -0.7rem; + background: center center / contain no-repeat; + } + + &.goal .amountLabel { + &::after { + /* @load-async */ + background-image: uiResource("icons/current_goal_marker.png"); + background-size: 90%; + } + @include DarkThemeOverride { + &::after { + /* @load-async */ + background-image: uiResource("icons/current_goal_marker_inverted.png") !important; + } + } + } + + &.blueprint .amountLabel { + &::after { + /* @load-async */ + background-image: uiResource("icons/blueprint_marker.png"); + background-size: 90%; + } + @include DarkThemeOverride { + &::after { + /* @load-async */ + background-image: uiResource("icons/blueprint_marker_inverted.png") !important; + } + } + } + } + + &.completed { + opacity: 0.5; + } + } +} diff --git a/src/css/ingame_hud/puzzle_back_to_menu.scss b/src/css/ingame_hud/puzzle_back_to_menu.scss index 564b592e..e103b183 100644 --- a/src/css/ingame_hud/puzzle_back_to_menu.scss +++ b/src/css/ingame_hud/puzzle_back_to_menu.scss @@ -1,13 +1,13 @@ #ingame_HUD_PuzzleBackToMenu { position: absolute; - @include S(top, 10px); - @include S(left, 0px); + top: 1rem; + left: 0rem; display: flex; flex-direction: column; align-items: flex-start; - backdrop-filter: blur(D(1px)); - padding: D(3px); + backdrop-filter: blur(0.1rem); + padding: 0.3rem; > .button { @include PlainText; @@ -19,8 +19,8 @@ transition-property: opacity, transform; text-transform: uppercase; @include PlainText; - @include S(width, 30px); - @include S(height, 30px); + width: 3rem; + height: 3rem; @include DarkThemeInvert; @@ -35,7 +35,7 @@ & { /* @load-async */ - background: uiResource("icons/state_back_button.png") center center / D(15px) no-repeat; + background: uiResource("icons/state_back_button.png") center center / 1.5rem no-repeat; } } } diff --git a/src/css/ingame_hud/puzzle_complete_notification.scss b/src/css/ingame_hud/puzzle_complete_notification.scss index a35da83d..e6e77844 100644 --- a/src/css/ingame_hud/puzzle_complete_notification.scss +++ b/src/css/ingame_hud/puzzle_complete_notification.scss @@ -13,7 +13,7 @@ & { /* @load-async */ - background: rgba(#333538, 0.95) uiResource("dialog_bg_pattern.png") top left / #{D(10px)} repeat; + background: rgba(#333538, 0.95) uiResource("dialog_bg_pattern.png") top left / 1rem repeat; } @include InlineAnimation(0.1s ease-in-out) { @@ -24,8 +24,8 @@ > .dialog { // background: rgba(#222428, 0.5); - @include S(border-radius, $globalBorderRadius); - @include S(padding, 30px); + border-radius: $globalBorderRadius; + padding: 3rem; @include InlineAnimation(0.5s ease-in-out) { 0% { @@ -43,8 +43,8 @@ > .title { @include SuperHeading; text-transform: uppercase; - @include S(font-size, 30px); - @include S(margin-bottom, 40px); + font-size: 3rem; + margin-bottom: 4rem; color: $colorGreenBright !important; @include InlineAnimation(0.5s ease-in-out) { @@ -82,23 +82,24 @@ > .stepLike { display: flex; flex-direction: column; - @include S(margin-bottom, 10px); + margin-bottom: 1rem; @include SuperSmallText; > .buttons { display: flex; align-items: center; justify-content: center; - @include S(margin, 10px, 0); + margin: 1rem 0; > button { - @include S(width, 60px); - @include S(height, 60px); - @include S(margin, 0, 10px); + width: 6rem; + height: 6rem; + margin: 0 1rem; box-sizing: border-box; border-radius: 50%; - transition: opacity 0.12s ease-in-out, background-color 0.12s ease-in-out; - @include IncreasedClickArea(0px); + transition: + opacity 0.12s ease-in-out, + background-color 0.12s ease-in-out; &.liked-yes { /* @load-async */ @@ -133,7 +134,7 @@ > .buttonBar { display: flex; - @include S(margin-top, 20px); + margin-top: 2rem; button.continue { background: #555; @@ -148,19 +149,18 @@ } > button { - @include S(min-width, 100px); - @include S(padding, 8px, 16px); - @include S(margin, 0, 6px); - @include IncreasedClickArea(0px); + min-width: 10rem; + padding: 0.8rem 1.6rem; + margin: 0 0.6rem; } } > .actions { position: absolute; - @include S(bottom, 40px); + bottom: 4rem; display: grid; - @include S(grid-gap, 15px); + grid-gap: 1.5rem; grid-auto-flow: column; button { diff --git a/src/css/ingame_hud/puzzle_dlc_logo.scss b/src/css/ingame_hud/puzzle_dlc_logo.scss index c2a64607..bab78f5d 100644 --- a/src/css/ingame_hud/puzzle_dlc_logo.scss +++ b/src/css/ingame_hud/puzzle_dlc_logo.scss @@ -1,9 +1,9 @@ #ingame_HUD_PuzzleDLCLogo { position: absolute; - @include S(width, 120px); - @include S(height, 40px); - @include S(left, 40px); - @include S(top, 7px); + width: 12rem; + height: 4rem; + left: 4rem; + top: 0.7rem; & { /* @load-async */ @@ -16,9 +16,4 @@ background: uiResource("puzzle_dlc_logo_inverse.png") center center / contain no-repeat; } } - - &.china { - /* @load-async */ - background: uiResource("puzzle_dlc_logo_china.png") center center / contain no-repeat !important; - } } diff --git a/src/css/ingame_hud/puzzle_editor_controls.scss b/src/css/ingame_hud/puzzle_editor_controls.scss index 7ce76b41..99f4cf5d 100644 --- a/src/css/ingame_hud/puzzle_editor_controls.scss +++ b/src/css/ingame_hud/puzzle_editor_controls.scss @@ -1,16 +1,16 @@ #ingame_HUD_PuzzleEditorControls { position: absolute; - @include S(top, 70px); - @include S(left, 10px); + top: 7rem; + left: 1rem; display: flex; flex-direction: column; @include SuperDuperSmallText; - @include S(width, 200px); + width: 20rem; > span { - @include S(margin-bottom, 10px); + margin-bottom: 1rem; strong { font-weight: bold; @@ -23,7 +23,7 @@ #ingame_HUD_PuzzleEditorTitle { position: absolute; - @include S(top, 18px); + top: 1.8rem; left: 50%; transform: translateX(-50%); text-transform: uppercase; diff --git a/src/css/ingame_hud/puzzle_editor_review.scss b/src/css/ingame_hud/puzzle_editor_review.scss index 523d8025..82ccbb24 100644 --- a/src/css/ingame_hud/puzzle_editor_review.scss +++ b/src/css/ingame_hud/puzzle_editor_review.scss @@ -1,17 +1,16 @@ #ingame_HUD_PuzzleEditorReview { position: absolute; - @include S(top, 17px); - @include S(right, 10px); + top: 1.7rem; + right: 1rem; display: flex; flex-direction: column; align-items: flex-end; - backdrop-filter: blur(D(1px)); - padding: D(3px); + backdrop-filter: blur(0.1rem); + padding: 0.3rem; > .button { @include ButtonText; - @include IncreasedClickArea(0px); pointer-events: all; cursor: pointer; position: relative; @@ -20,7 +19,7 @@ text-transform: uppercase; transition-property: opacity, transform; @include PlainText; - @include S(padding-right, 25px); + padding-right: 2.5rem; opacity: 1; @include DarkThemeInvert; @@ -35,14 +34,14 @@ & { /* @load-async */ - background: uiResource("icons/state_next_button.png") right center / D(15px) no-repeat; + background: uiResource("icons/state_next_button.png") right center / 1.5rem no-repeat; } } > .content { @include SuperDuperSmallText; - @include S(width, 180px); - @include S(padding-right, 25px); + width: 18rem; + padding-right: 2.5rem; text-align: right; text-transform: uppercase; color: $accentColorDark; diff --git a/src/css/ingame_hud/puzzle_editor_settings.scss b/src/css/ingame_hud/puzzle_editor_settings.scss index 9d093c42..a3276524 100644 --- a/src/css/ingame_hud/puzzle_editor_settings.scss +++ b/src/css/ingame_hud/puzzle_editor_settings.scss @@ -1,71 +1,70 @@ -#ingame_HUD_PuzzleEditorSettings { - position: absolute; - background: $ingameHudBg; - @include S(padding, 10px); - @include S(bottom, 60px); - @include S(left, 10px); - - @include SuperSmallText; - color: #eee; - display: flex; - flex-direction: column; - @include S(border-radius, $globalBorderRadius); - - > .section { - > label { - text-transform: uppercase; - } - - .plusMinus { - @include S(margin-top, 5px); - display: grid; - grid-template-columns: 1fr auto auto auto; - align-items: center; - @include S(grid-gap, 5px); - - label { - @include S(margin-right, 10px); - } - - button { - @include PlainText; - @include S(padding, 0); - display: flex; - align-items: center; - justify-content: center; - @include S(width, 15px); - @include S(height, 15px); - @include IncreasedClickArea(0px); - } - - .value { - text-align: center; - @include S(min-width, 15px); - } - } - - > .buttons { - > .buttonBar { - display: flex; - align-items: center; - @include S(margin-top, 10px); - > button { - @include S(margin-right, 4px); - @include SuperSmallText; - &:last-child { - margin-right: 0; - } - } - } - - > .buildingsButton { - display: grid; - align-items: center; - @include S(margin-top, 4px); - > button { - @include SuperSmallText; - } - } - } - } -} +#ingame_HUD_PuzzleEditorSettings { + position: absolute; + background: $ingameHudBg; + padding: 1rem; + bottom: 6rem; + left: 1rem; + + @include SuperSmallText; + color: #eee; + display: flex; + flex-direction: column; + border-radius: $globalBorderRadius; + + > .section { + > label { + text-transform: uppercase; + } + + .plusMinus { + margin-top: 0.5rem; + display: grid; + grid-template-columns: 1fr auto auto auto; + align-items: center; + grid-gap: 0.5rem; + + label { + margin-right: 1rem; + } + + button { + @include PlainText; + padding: 0; + display: flex; + align-items: center; + justify-content: center; + width: 1.5rem; + height: 1.5rem; + } + + .value { + text-align: center; + min-width: 1.5rem; + } + } + + > .buttons { + > .buttonBar { + display: flex; + align-items: center; + margin-top: 1rem; + > button { + margin-right: 0.4rem; + @include SuperSmallText; + &:last-child { + margin-right: 0; + } + } + } + + > .buildingsButton { + display: grid; + align-items: center; + margin-top: 0.4rem; + > button { + @include SuperSmallText; + } + } + } + } +} diff --git a/src/css/ingame_hud/puzzle_next.scss b/src/css/ingame_hud/puzzle_next.scss index ee0f664f..de415ed7 100644 --- a/src/css/ingame_hud/puzzle_next.scss +++ b/src/css/ingame_hud/puzzle_next.scss @@ -1,17 +1,16 @@ #ingame_HUD_PuzzleNextPuzzle { position: absolute; - @include S(top, 17px); - @include S(right, 10px); + top: 1.7rem; + right: 1rem; display: flex; flex-direction: column; align-items: flex-end; - backdrop-filter: blur(D(1px)); - padding: D(3px); + backdrop-filter: blur(0.1rem); + padding: 0.3rem; > .button { @include ButtonText; - @include IncreasedClickArea(0px); pointer-events: all; cursor: pointer; position: relative; @@ -20,7 +19,7 @@ text-transform: uppercase; transition-property: opacity, transform; @include PlainText; - @include S(padding-right, 25px); + padding-right: 2.5rem; opacity: 1; @include DarkThemeInvert; @@ -35,7 +34,7 @@ & { /* @load-async */ - background: uiResource("icons/state_next_button.png") right center / D(15px) no-repeat; + background: uiResource("icons/state_next_button.png") right center / 1.5rem no-repeat; } } } diff --git a/src/css/ingame_hud/puzzle_play_metadata.scss b/src/css/ingame_hud/puzzle_play_metadata.scss index d0675b13..66ffa7de 100644 --- a/src/css/ingame_hud/puzzle_play_metadata.scss +++ b/src/css/ingame_hud/puzzle_play_metadata.scss @@ -1,18 +1,18 @@ #ingame_HUD_PuzzlePlayMetadata { position: absolute; - @include S(top, 70px); - @include S(left, 10px); + top: 7rem; + left: 1rem; display: flex; flex-direction: column; - @include S(width, 200px); + width: 20rem; > .info { display: flex; flex-direction: column; @include SuperSmallText; - @include S(margin-bottom, 5px); + margin-bottom: 0.5rem; > label { text-transform: uppercase; @@ -35,7 +35,7 @@ justify-self: end; align-self: end; flex-direction: row; - @include S(margin-bottom, 10px); + margin-bottom: 1rem; opacity: 0.8; @include DarkThemeInvert; @include DarkThemeOverride { @@ -48,8 +48,8 @@ align-self: start; justify-self: start; font-weight: bold; - @include S(margin-right, 10px); - @include S(padding-left, 14px); + margin-right: 1rem; + padding-left: 1.4rem; opacity: 0.7; display: inline-flex; align-items: center; @@ -57,7 +57,7 @@ & { /* @load-async */ - background: uiResource("icons/puzzle_plays.png") #{D(2px)} center / #{D(8px)} #{D(8px)} no-repeat; + background: uiResource("icons/puzzle_plays.png") 0.2rem center / 0.8rem 0.8rem no-repeat; } } @@ -69,25 +69,25 @@ align-self: start; justify-self: start; font-weight: bold; - @include S(padding-left, 14px); + padding-left: 1.4rem; opacity: 0.7; & { /* @load-async */ - background: uiResource("icons/puzzle_upvotes.png") #{D(2px)} center / #{D(8px)} #{D(8px)} no-repeat; + background: uiResource("icons/puzzle_upvotes.png") 0.2rem center / 0.8rem 0.8rem no-repeat; } } } > .key { button { - @include S(margin-top, 2px); + margin-top: 0.2rem; } } button { @include SuperSmallText; align-self: start; - @include S(min-width, 50px); + min-width: 5rem; &.report { background-color: $accentColorDark; @@ -100,7 +100,7 @@ flex-direction: column; > button { - @include S(margin-bottom, 4px); + margin-bottom: 0.4rem; } } } @@ -108,7 +108,7 @@ #ingame_HUD_PuzzlePlayTitle { position: absolute; - @include S(top, 18px); + top: 1.8rem; left: 50%; transform: translateX(-50%); text-transform: uppercase; diff --git a/src/css/ingame_hud/puzzle_play_settings.scss b/src/css/ingame_hud/puzzle_play_settings.scss index b53d0829..e815732d 100644 --- a/src/css/ingame_hud/puzzle_play_settings.scss +++ b/src/css/ingame_hud/puzzle_play_settings.scss @@ -1,19 +1,19 @@ #ingame_HUD_PuzzlePlaySettings { position: absolute; background: $ingameHudBg; - @include S(padding, 10px); - @include S(bottom, 60px); - @include S(left, 10px); + padding: 1rem; + bottom: 6rem; + left: 1rem; @include SuperSmallText; color: #eee; display: flex; flex-direction: column; - @include S(border-radius, $globalBorderRadius); + border-radius: $globalBorderRadius; > .section { display: grid; - @include S(grid-gap, 5px); + grid-gap: 0.5rem; grid-auto-flow: row; > button { diff --git a/src/css/ingame_hud/sandbox_controller.scss b/src/css/ingame_hud/sandbox_controller.scss index e4680fe4..5a86de35 100644 --- a/src/css/ingame_hud/sandbox_controller.scss +++ b/src/css/ingame_hud/sandbox_controller.scss @@ -1,50 +1,48 @@ -#ingame_HUD_SandboxController { - position: absolute; - background: $ingameHudBg; - @include S(padding, 5px); - @include S(bottom, 10px); - @include S(left, 10px); - - @include SuperSmallText; - color: #eee; - display: flex; - flex-direction: column; - - > label { - text-transform: uppercase; - } - - .sandboxHint { - color: #aaa; - } - - .plusMinus { - @include S(margin-top, 4px); - display: grid; - grid-template-columns: 1fr auto auto; - align-items: center; - @include S(grid-gap, 4px); - - button { - @include PlainText; - @include S(padding, 0); - display: flex; - align-items: center; - justify-content: center; - @include S(width, 15px); - @include S(height, 15px); - @include IncreasedClickArea(0px); - } - } - - .additionalOptions { - display: flex; - flex-direction: column; - @include S(margin-top, 10px); - button { - @include S(margin-bottom, 2px); - @include IncreasedClickArea(0px); - @include SuperSmallText; - } - } -} +#ingame_HUD_SandboxController { + position: absolute; + background: $ingameHudBg; + padding: 0.5rem; + bottom: 1rem; + left: 1rem; + + @include SuperSmallText; + color: #eee; + display: flex; + flex-direction: column; + + > label { + text-transform: uppercase; + } + + .sandboxHint { + color: #aaa; + } + + .plusMinus { + margin-top: 0.4rem; + display: grid; + grid-template-columns: 1fr auto auto; + align-items: center; + grid-gap: 0.4rem; + + button { + @include PlainText; + padding: 0; + display: flex; + align-items: center; + justify-content: center; + width: 1.5rem; + height: 1.5rem; + } + } + + .additionalOptions { + display: flex; + flex-direction: column; + margin-top: 1rem; + button { + margin-bottom: 0.2rem; + @include SuperSmallText; + } + } +} diff --git a/src/css/ingame_hud/settings_menu.scss b/src/css/ingame_hud/settings_menu.scss index 55d5a9e4..e03e3fef 100644 --- a/src/css/ingame_hud/settings_menu.scss +++ b/src/css/ingame_hud/settings_menu.scss @@ -1,67 +1,66 @@ -#ingame_HUD_SettingsMenu { - .statsElement { - position: absolute; - @include S(left, 30px); - @include S(right, 30px); - @include S(bottom, 30px); - color: #fff; - display: grid; - grid-template-rows: auto auto; - grid-auto-columns: 1fr; - align-items: center; - justify-items: center; - - strong { - text-transform: uppercase; - @include PlainText; - opacity: 0.5; - grid-row: 1; - } - - span { - @include Heading; - grid-row: 2; - } - } - - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - - .buttons { - display: grid; - grid-auto-flow: column; - @include S(grid-gap, 50px); - @include S(margin-top, -10px); - - button { - background: transparent; - filter: invert(1); - - content: ""; - opacity: 0.8; - @include S(width, 35px); - @include S(height, 35px); - - &.settings { - /* @load-async */ - background-image: uiResource("icons/settings_menu_settings.png"); - } - - &.menu { - /* @load-async */ - background-image: uiResource("icons/settings_menu_exit.png"); - } - - &:hover { - opacity: 0.6; - } - - & { - /* @load-async */ - background: uiResource("icons/settings_menu_play.png") center top / contain no-repeat; - } - } - } -} +#ingame_HUD_SettingsMenu { + .statsElement { + position: absolute; + left: 3rem; + right: 3rem; + bottom: 3rem; + color: #fff; + display: grid; + grid-template-rows: auto auto; + grid-auto-columns: 1fr; + place-items: center; + + strong { + text-transform: uppercase; + @include PlainText; + opacity: 0.5; + grid-row: 1; + } + + span { + @include Heading; + grid-row: 2; + } + } + + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + + .buttons { + display: grid; + grid-auto-flow: column; + grid-gap: 5rem; + margin-top: -1rem; + + button { + background: transparent; + filter: invert(1); + + content: ""; + opacity: 0.8; + width: 3.5rem; + height: 3.5rem; + + &.settings { + /* @load-async */ + background-image: uiResource("icons/settings_menu_settings.png"); + } + + &.menu { + /* @load-async */ + background-image: uiResource("icons/settings_menu_exit.png"); + } + + &:hover { + opacity: 0.6; + } + + & { + /* @load-async */ + background: uiResource("icons/settings_menu_play.png") center top / contain no-repeat; + } + } + } +} diff --git a/src/css/ingame_hud/shape_viewer.scss b/src/css/ingame_hud/shape_viewer.scss index 2e07f1da..7b5f4beb 100644 --- a/src/css/ingame_hud/shape_viewer.scss +++ b/src/css/ingame_hud/shape_viewer.scss @@ -1,155 +1,156 @@ -#ingame_HUD_ShapeViewer { - $dims: 170px; - - .content { - display: flex; - @include S(width, $dims); - width: 100%; - flex-direction: column; - overflow-x: hidden; - - &[data-layers="3"], - &[data-layers="4"] { - @include S(width, 2 * $dims); - .renderArea { - display: grid; - grid-template-columns: 1fr 1fr; - @include S(grid-row-gap, 15px); - } - } - - .renderArea { - display: grid; - width: 100%; - @include S(grid-row-gap, 10px); - align-items: center; - justify-items: center; - } - - .infoArea { - align-self: flex-end; - @include S(margin-top, 10px); - display: flex; - flex-direction: column; - overflow: hidden; - - button { - @include S(margin, 0); - @include PlainText; - } - } - - .layer { - position: relative; - background: #eee; - - @include DarkThemeOverride { - background: rgba(0, 10, 20, 0.2); - } - @include S(width, 150px); - @include S(height, 100px); - display: flex; - align-items: center; - justify-content: center; - @include S(border-radius, $globalBorderRadius); - - > canvas { - @include S(width, 50px); - @include S(height, 50px); - } - - .quad { - position: absolute; - width: 50%; - height: 50%; - display: flex; - justify-content: center; - align-items: center; - box-sizing: border-box; - - $arrowDims: 23px; - $spacing: 9px; - @include S(padding, 6px); - - .colorLabel { - text-transform: uppercase; - @include SuperSmallText; - @include S(font-size, 9px); - } - - .emptyLabel { - text-transform: uppercase; - @include SuperSmallText; - @include S(font-size, 9px); - } - - &::after { - content: " "; - background: rgba(0, 10, 20, 0.5); - @include S(width, $arrowDims); - @include S(height, 1px); - position: absolute; - transform: rotate(45deg); - transform-origin: 50% 50%; - } - @include DarkThemeOverride { - &::after { - background: rgba(255, 255, 255, 0.5); - } - } - - &.quad-0 { - right: 0; - top: 0; - align-items: flex-start; - justify-content: flex-end; - - &::after { - @include S(left, $spacing); - @include S(bottom, $arrowDims / 2 + $spacing); - transform: rotate(-45deg); - } - } - &.quad-1 { - bottom: 0; - right: 0; - - align-items: flex-end; - justify-content: flex-end; - - &::after { - @include S(left, $spacing); - @include S(top, $arrowDims / 2 + $spacing); - transform: rotate(45deg); - } - } - &.quad-2 { - bottom: 0; - left: 0; - - align-items: flex-end; - justify-content: flex-start; - - &::after { - @include S(right, $spacing); - @include S(top, $arrowDims / 2 + $spacing); - transform: rotate(135deg); - } - } - &.quad-3 { - top: 0; - left: 0; - - align-items: flex-start; - justify-content: flex-start; - - &::after { - @include S(right, $spacing); - @include S(bottom, $arrowDims / 2 + $spacing); - transform: rotate(225deg); - } - } - } - } - } -} +@use "sass:math"; + +#ingame_HUD_ShapeViewer { + $dims: 17rem; + + .content { + display: flex; + width: $dims; + width: 100%; + flex-direction: column; + overflow-x: hidden; + + &[data-layers="3"], + &[data-layers="4"] { + width: 2 * $dims; + .renderArea { + display: grid; + grid-template-columns: 1fr 1fr; + grid-row-gap: 1.5rem; + } + } + + .renderArea { + display: grid; + width: 100%; + grid-row-gap: 1rem; + place-items: center; + } + + .infoArea { + align-self: flex-end; + margin-top: 1rem; + display: flex; + flex-direction: column; + overflow: hidden; + + button { + margin: 0; + @include PlainText; + } + } + + .layer { + position: relative; + background: #eee; + + @include DarkThemeOverride { + background: rgba(0, 10, 20, 0.2); + } + width: 15rem; + height: 10rem; + display: flex; + align-items: center; + justify-content: center; + border-radius: $globalBorderRadius; + + > canvas { + width: 5rem; + height: 5rem; + } + + .quad { + position: absolute; + width: 50%; + height: 50%; + display: flex; + justify-content: center; + align-items: center; + box-sizing: border-box; + + $arrowDims: 2.3rem; + $spacing: 0.9rem; + padding: 0.6rem; + + .colorLabel { + text-transform: uppercase; + @include SuperSmallText; + font-size: 0.9rem; + } + + .emptyLabel { + text-transform: uppercase; + @include SuperSmallText; + font-size: 0.9rem; + } + + &::after { + content: " "; + background: rgba(0, 10, 20, 0.5); + width: $arrowDims; + height: 0.1rem; + position: absolute; + transform: rotate(45deg); + transform-origin: 50% 50%; + } + @include DarkThemeOverride { + &::after { + background: rgba(255, 255, 255, 0.5); + } + } + + &.quad-0 { + right: 0; + top: 0; + align-items: flex-start; + justify-content: flex-end; + + &::after { + left: $spacing; + bottom: math.div($arrowDims, 2) + $spacing; + transform: rotate(-45deg); + } + } + &.quad-1 { + bottom: 0; + right: 0; + + align-items: flex-end; + justify-content: flex-end; + + &::after { + left: $spacing; + top: math.div($arrowDims, 2) + $spacing; + transform: rotate(45deg); + } + } + &.quad-2 { + bottom: 0; + left: 0; + + align-items: flex-end; + justify-content: flex-start; + + &::after { + right: $spacing; + top: math.div($arrowDims, 2) + $spacing; + transform: rotate(135deg); + } + } + &.quad-3 { + top: 0; + left: 0; + + align-items: flex-start; + justify-content: flex-start; + + &::after { + right: $spacing; + bottom: math.div($arrowDims, 2) + $spacing; + transform: rotate(225deg); + } + } + } + } + } +} diff --git a/src/css/ingame_hud/shop.scss b/src/css/ingame_hud/shop.scss index a9b27ebe..284a076c 100644 --- a/src/css/ingame_hud/shop.scss +++ b/src/css/ingame_hud/shop.scss @@ -1,335 +1,332 @@ -#ingame_HUD_Shop { - .content { - @include S(padding-right, 10px); - display: flex; - flex-direction: column; - @include S(width, 500px); - - .upgrade { - display: grid; - grid-template-columns: auto 1fr auto; - background: #eee; - @include S(border-radius, $globalBorderRadius); - @include S(margin-bottom, 4px); - @include S(padding, 5px, 10px); - @include S(grid-row-gap, 1px); - @include S(height, 85px); - grid-template-rows: #{D(20px)} auto; - - &:last-child { - margin-bottom: 0; - } - - @include DarkThemeOverride { - background: $darkModeControlsBackground; - } - - .title { - grid-column: 1 / 3; - grid-row: 1 / 2; - @include PlainText; - display: flex; - align-items: center; - flex-direction: row-reverse; - justify-content: flex-end; - - @include DarkThemeOverride { - color: #fff; - } - - .tier { - @include S(margin-right, 9px); - background: $colorGreenBright; - @include S(border-radius, $globalBorderRadius); - text-transform: uppercase; - @include PlainText; - color: #fff; - text-align: center; - font-weight: bold; - @include S(min-width, 50px); - @include S(padding, 0px, 5px); - - &[data-tier="0"] { - background-color: rgb(73, 186, 190); - } - &[data-tier="1"] { - background-color: rgb(88, 110, 207); - } - &[data-tier="2"] { - background-color: rgb(189, 100, 192); - } - &[data-tier="3"] { - background-color: rgb(117, 192, 98); - } - &[data-tier="4"] { - background-color: rgb(243, 77, 48); - } - &[data-tier="5"] { - background-color: rgb(255, 209, 6); - } - &[data-tier="6"] { - background-color: rgb(44, 41, 46); - } - } - } - - .icon { - @include S(width, 40px); - @include S(height, 40px); - background: center center / 80% no-repeat; - align-self: center; - justify-self: center; - grid-column: 1 / 2; - grid-row: 2 / 4; - @include S(margin-right, 30px); - @include S(margin-left, 10px); - opacity: 0.32; - display: none; - } - - .description { - grid-column: 2 / 4; - grid-row: 1 / 2; - @include PlainText; - color: #aaa; - align-self: start; - justify-self: end; - } - - .requirements { - grid-column: 2 / 3; - grid-row: 3 / 4; - display: grid; - grid-auto-flow: column; - @include S(grid-gap, 9px); - justify-content: start; - - .requirement { - position: relative; - display: flex; - flex-direction: column; - align-items: center; - @include S(width, 70px); - overflow: hidden; - - button.pin { - & { - /* @load-async */ - background: uiResource("icons/pin.png") center center / 95% no-repeat; - } - - @include S(width, 12px); - @include S(height, 12px); - position: absolute; - @include S(top, 2px); - @include S(right, 2px); - opacity: 0.6; - cursor: pointer; - pointer-events: all; - @include IncreasedClickArea(5px); - transition: opacity 0.12s ease-in-out; - - @include DarkThemeInvert; - - $disabledOpacity: 0.6; - $enabledOpacity: 0.6; - - &:hover { - opacity: $enabledOpacity + 0.1; - } - - &.alreadyPinned { - opacity: $disabledOpacity !important; - - &:hover { - opacity: $disabledOpacity + 0.1 !important; - } - } - - &.isGoal { - /* @load-async */ - background: uiResource("icons/current_goal_marker.png") center center / 95% - no-repeat; - opacity: $disabledOpacity !important; - cursor: default; - pointer-events: none; - } - - &.pinned { - opacity: $disabledOpacity; - - & { - /* @load-async */ - background: uiResource("icons/unpin_shape.png") center center / 75% no-repeat !important; - } - - @include InlineAnimation(0.3s ease-in-out) { - 0% { - opacity: 1; - transform: scale(0.8); - } - - 30% { - opacity: 1; - transform: scale(1.2); - } - - 100% { - transform: scale(1); - } - } - &:hover { - opacity: $disabledOpacity + 0.1; - } - } - - &.unpinned { - opacity: $enabledOpacity; - @include InlineAnimation(0.3s ease-in-out) { - 0% { - opacity: 1; - transform: scale(0.8); - } - - 30% { - opacity: 1; - transform: scale(1.2); - } - - 100% { - transform: scale(1); - } - } - &:hover { - opacity: $enabledOpacity + 0.1; - } - } - } - - button.showInfo { - @include S(width, 11px); - @include S(height, 11px); - position: absolute; - @include S(top, 17px); - @include S(right, 2.5px); - opacity: 0.5; - cursor: pointer; - pointer-events: all; - @include IncreasedClickArea(5px); - transition: opacity 0.12s ease-in-out; - @include DarkThemeInvert; - - &:hover { - opacity: 0.6; - } - } - button.showInfo { - /* @load-async */ - background: uiResource("icons/info_button.png") center center / 95% no-repeat; - } - - canvas { - @include S(width, 40px); - @include S(height, 40px); - } - - .amount { - @include S(margin-top, 4px); - z-index: 10; - @include SuperSmallText; - letter-spacing: 0; - background: #e2e4e6; - - @include S(line-height, 13px); - @include S(border-radius, $globalBorderRadius); - @include S(padding, 1px, 0px, 2px); - position: relative; - text-align: center; - @include S(min-width, 50px); - // @include S(max-width, 100px); - overflow: hidden; - width: 100%; - - @include DarkThemeOverride { - background: #333438; - color: #fff; - } - - .progressBar { - bottom: 0; - left: 0; - right: 0; - top: 0; - @include S(border-radius, $globalBorderRadius); - position: absolute; - display: inline-block; - z-index: -1; - transition: all 0.2s ease-in-out; - transition-property: width, background-color; - background: #bdbfca; - - @include DarkThemeOverride { - background: #8c8d96; - } - - &.complete { - background-color: $colorGreenBright; - - @include DarkThemeOverride { - background-color: $colorGreenBright; - } - } - } - } - } - } - - button.buy { - grid-column: 3 / 4; - grid-row: 3 / 4; - align-self: center; - justify-self: end; - // @include S(padding, 4px, 5px); - // @include PlainText; - background-color: $colorGreenBright; - color: #fff; - - transition: all 0.2s ease-in-out; - transition-property: background-color, opacity; - - &:not(.buyable) { - background-color: #aaa; - cursor: default; - pointer-events: none; - opacity: 0.3; - } - - &.buyable { - @include InlineAnimation(1s ease-in-out infinite) { - 0% { - } - - 50% { - background-color: lighten($colorGreenBright, 10); - transform: scale(1.02); - } - 100% { - } - } - } - } - - &.maxLevel { - button.buy { - opacity: 0 !important; - } - .requirements { - display: none; - } - .description { - color: $colorGreenBright; - } - } - } - } -} +#ingame_HUD_Shop { + .content { + padding-right: 1rem; + display: flex; + flex-direction: column; + width: 50rem; + + .upgrade { + display: grid; + grid-template-columns: auto 1fr auto; + background: #eee; + border-radius: $globalBorderRadius; + margin-bottom: 0.4rem; + padding: 0.5rem 1rem; + grid-row-gap: 0.1rem; + height: 8.5rem; + grid-template-rows: #{2rem} auto; + + &:last-child { + margin-bottom: 0; + } + + @include DarkThemeOverride { + background: $darkModeControlsBackground; + } + + .title { + grid-column: 1 / 3; + grid-row: 1 / 2; + @include PlainText; + display: flex; + align-items: center; + flex-direction: row-reverse; + justify-content: flex-end; + + @include DarkThemeOverride { + color: #fff; + } + + .tier { + margin-right: 0.9rem; + background: $colorGreenBright; + border-radius: $globalBorderRadius; + text-transform: uppercase; + @include PlainText; + color: #fff; + text-align: center; + font-weight: bold; + min-width: 5rem; + padding: 0rem 0.5rem; + + &[data-tier="0"] { + background-color: rgb(73, 186, 190); + } + &[data-tier="1"] { + background-color: rgb(88, 110, 207); + } + &[data-tier="2"] { + background-color: rgb(189, 100, 192); + } + &[data-tier="3"] { + background-color: rgb(117, 192, 98); + } + &[data-tier="4"] { + background-color: rgb(243, 77, 48); + } + &[data-tier="5"] { + background-color: rgb(255, 209, 6); + } + &[data-tier="6"] { + background-color: rgb(44, 41, 46); + } + } + } + + .icon { + width: 4rem; + height: 4rem; + background: center center / 80% no-repeat; + align-self: center; + justify-self: center; + grid-column: 1 / 2; + grid-row: 2 / 4; + margin-right: 3rem; + margin-left: 1rem; + opacity: 0.32; + display: none; + } + + .description { + grid-column: 2 / 4; + grid-row: 1 / 2; + @include PlainText; + color: #aaa; + align-self: start; + justify-self: end; + } + + .requirements { + grid-column: 2 / 3; + grid-row: 3 / 4; + display: grid; + grid-auto-flow: column; + grid-gap: 0.9rem; + justify-content: start; + + .requirement { + position: relative; + display: flex; + flex-direction: column; + align-items: center; + width: 7rem; + overflow: hidden; + + button.pin { + & { + /* @load-async */ + background: uiResource("icons/pin.png") center center / 95% no-repeat; + } + + width: 1.2rem; + height: 1.2rem; + position: absolute; + top: 0.2rem; + right: 0.2rem; + opacity: 0.6; + cursor: pointer; + pointer-events: all; + transition: opacity 0.12s ease-in-out; + + @include DarkThemeInvert; + + $disabledOpacity: 0.6; + $enabledOpacity: 0.6; + + &:hover { + opacity: $enabledOpacity + 0.1; + } + + &.alreadyPinned { + opacity: $disabledOpacity !important; + + &:hover { + opacity: $disabledOpacity + 0.1 !important; + } + } + + &.isGoal { + /* @load-async */ + background: uiResource("icons/current_goal_marker.png") center center / 95% + no-repeat; + opacity: $disabledOpacity !important; + cursor: default; + pointer-events: none; + } + + &.pinned { + opacity: $disabledOpacity; + + & { + /* @load-async */ + background: uiResource("icons/unpin_shape.png") center center / 75% no-repeat !important; + } + + @include InlineAnimation(0.3s ease-in-out) { + 0% { + opacity: 1; + transform: scale(0.8); + } + + 30% { + opacity: 1; + transform: scale(1.2); + } + + 100% { + transform: scale(1); + } + } + &:hover { + opacity: $disabledOpacity + 0.1; + } + } + + &.unpinned { + opacity: $enabledOpacity; + @include InlineAnimation(0.3s ease-in-out) { + 0% { + opacity: 1; + transform: scale(0.8); + } + + 30% { + opacity: 1; + transform: scale(1.2); + } + + 100% { + transform: scale(1); + } + } + &:hover { + opacity: $enabledOpacity + 0.1; + } + } + } + + button.showInfo { + width: 1.1rem; + height: 1.1rem; + position: absolute; + top: 1.7rem; + right: 0.25rem; + opacity: 0.5; + cursor: pointer; + pointer-events: all; + transition: opacity 0.12s ease-in-out; + @include DarkThemeInvert; + + &:hover { + opacity: 0.6; + } + } + button.showInfo { + /* @load-async */ + background: uiResource("icons/info_button.png") center center / 95% no-repeat; + } + + canvas { + width: 4rem; + height: 4rem; + } + + .amount { + margin-top: 0.4rem; + z-index: 10; + @include SuperSmallText; + background: #e2e4e6; + + line-height: 1.3rem; + border-radius: $globalBorderRadius; + padding: 0.1rem 0 0.2rem; + position: relative; + text-align: center; + min-width: 5rem; + // max-width: 10.0rem; + overflow: hidden; + width: 100%; + + @include DarkThemeOverride { + background: #333438; + color: #fff; + } + + .progressBar { + bottom: 0; + left: 0; + right: 0; + top: 0; + border-radius: $globalBorderRadius; + position: absolute; + display: inline-block; + z-index: -1; + transition: all 0.2s ease-in-out; + transition-property: width, background-color; + background: #bdbfca; + + @include DarkThemeOverride { + background: #8c8d96; + } + + &.complete { + background-color: $colorGreenBright; + + @include DarkThemeOverride { + background-color: $colorGreenBright; + } + } + } + } + } + } + + button.buy { + grid-column: 3 / 4; + grid-row: 3 / 4; + align-self: center; + justify-self: end; + // padding: .4rem .5rem; + // @include PlainText; + background-color: $colorGreenBright; + color: #fff; + + transition: all 0.2s ease-in-out; + transition-property: background-color, opacity; + + &:not(.buyable) { + background-color: #aaa; + cursor: default; + pointer-events: none; + opacity: 0.3; + } + + &.buyable { + @include InlineAnimation(1s ease-in-out infinite) { + 0% { + } + + 50% { + background-color: lighten($colorGreenBright, 10); + transform: scale(1.02); + } + 100% { + } + } + } + } + + &.maxLevel { + button.buy { + opacity: 0 !important; + } + .requirements { + display: none; + } + .description { + color: $colorGreenBright; + } + } + } + } +} diff --git a/src/css/ingame_hud/standalone_advantages.scss b/src/css/ingame_hud/standalone_advantages.scss deleted file mode 100644 index a75ace4c..00000000 --- a/src/css/ingame_hud/standalone_advantages.scss +++ /dev/null @@ -1,293 +0,0 @@ -#ingame_HUD_StandaloneAdvantages { - .content { - @include S(width, 500px); - @include S(min-height, 300px); - } - p { - @include PlainText; - } - - .points { - display: grid; - grid-template-columns: 1fr 1fr; - @include S(grid-column-gap, 5px); - @include S(grid-row-gap, 5px); - @include S(margin, 10px, 0, 10px); - align-items: center; - } - - .title { - @include Heading; - text-transform: none; - text-align: center; - color: $colorRedBright; - } - .subTitle { - @include PlainText; - text-transform: none; - text-align: center; - } - - .lowerBar { - width: 100%; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - - > button { - transition: opacity 0.12s ease-in-out; - &:hover { - opacity: 0.85; - } - } - - .otherCloseButton { - @include SuperSmallText; - @include S(margin-right, 30px); - color: #aaa; - @include S(margin, 0); - @include IncreasedClickArea(0px); - @include S(margin-top, 15px); - - &[data-btn-variant="prod"] { - @include InlineAnimation(11s ease-in-out) { - 0% { - opacity: 0.05; - } - 80% { - opacity: 0.05; - } - 100% { - opacity: 1; - } - } - } - - &[data-btn-variant="steam-demo"] { - @include InlineAnimation(5s ease-in-out) { - 0% { - opacity: 0.05; - } - 50% { - opacity: 0.05; - } - 100% { - opacity: 1; - } - } - } - } - - .playtimeDisclaimer { - @include S(margin-bottom, 15px); - @include PlainText; - } - - .playtimeDisclaimerDownload { - @include SuperSmallText; - box-sizing: border-box; - text-align: center; - @include S(border-radius, $globalBorderRadius); - @include S(padding, 8px, 10px, 8px, 20px); - @include S(margin-bottom, 15px); - pointer-events: all; - transition: background-color 0.12s ease-in-out; - color: rgba(#000, 0.5); - - width: 100%; - background: rgba($colorGreenBright, 0.2); - cursor: pointer; - - display: grid; - grid-template-columns: auto 1fr; - @include S(grid-gap, 5px); - - &::before { - content: ""; - display: inline-flex; - @include S(width, 15px); - @include S(height, 15px); - & { - /* load-async */ - background: uiResource("icons/savegame_correct.png") center center / contain no-repeat; - } - } - - strong { - color: #000; - } - } - - .steamLinkButton { - @include IncreasedClickArea(5px); - @include S(margin, 0); - @include S(width, 180px); - @include S(height, 40px); - background: #171a23 center center / contain no-repeat; - - box-shadow: 0 D(3px) D(10px) rgba(96, 163, 136, 0.5); - overflow: visible; - @include S(border-radius, $globalBorderRadius); - - @include InlineAnimation(1s ease-in-out infinite) { - 50% { - transform: scale(1.02, 1.03); - } - } - - &:hover { - opacity: 0.94 !important; - } - - > .discount { - position: absolute; - @include S(top, -12px); - @include S(right, -15px); - background: #4c6b22; - color: #c5ea3f; - @include S(border-radius, $globalBorderRadius); - @include S(padding, 1px, 3px, 1px, 4px); - @include PlainText; - text-transform: uppercase; - transform: rotate(3deg); - } - } - - .specialOffer { - color: #000000; - @include PlainText; - align-self: center; - text-align: center; - @include S(margin-top, 5px); - } - } - - .point { - display: grid; - grid-template-columns: #{D(50px)} auto; - grid-template-rows: D(20px) D(20px); - background: #eff2f4 #{D(12px)} center / #{D(30px)} no-repeat; - - @include S(border-radius, $globalBorderRadius); - align-items: center; - @include S(padding, 10px, 4px, 4px); - @include S(height, 40px); - - > strong { - grid-column: 2 / 3; - grid-row: 1 / 2; - @include PlainText; - text-transform: uppercase; - font-weight: bold; - } - - > p { - grid-column: 2 / 3; - grid-row: 2 / 3; - @include SuperSmallText; - line-height: 1em; - opacity: 0.8; - } - - &.levels { - & { - /* @load-async */ - background-image: uiResource("res/ui/icons/advantage_new_levels.png"); - } - > strong { - color: #f13555; - } - } - - &.upgrades { - & { - /* @load-async */ - background-image: uiResource("res/ui/icons/advantage_upgrades.png"); - } - > strong { - color: #8a00ff; - } - } - - &.buildings { - & { - /* @load-async */ - background-image: uiResource("res/ui/icons/advantage_buildings.png"); - } - > strong { - color: #3fce8b; - } - } - - &.wires { - & { - /* @load-async */ - background-image: uiResource("res/ui/icons/advantage_wires.png"); - } - > strong { - color: #ef2fdb; - } - } - - &.markers { - & { - /* @load-async */ - background-image: uiResource("res/ui/icons/advantage_markers.png"); - } - > strong { - color: #4294ff; - } - } - - &.mods { - & { - /* @load-async */ - background-image: uiResource("res/ui/icons/advantage_mods.png"); - } - > strong { - color: #8a00ff; - } - } - - &.savegames { - & { - /* @load-async */ - background-image: uiResource("res/ui/icons/advantage_savegames.png"); - } - > strong { - color: #ff9500; - } - } - - &.darkmode { - & { - /* @load-async */ - background-image: uiResource("res/ui/icons/advantage_dark_mode.png"); - } - > strong { - color: #292c32; - } - } - - &.support { - & { - /* @load-async */ - background-image: uiResource("res/ui/icons/advantage_support.png"); - } - > strong { - color: #e72d2d; - } - } - - &.achievements { - & { - /* @load-async */ - background-image: uiResource("res/ui/icons/advantage_achievements.png"); - } - > strong { - color: #ffac0f; - } - } - } -} diff --git a/src/css/ingame_hud/statistics.scss b/src/css/ingame_hud/statistics.scss index cdc428c1..ea307b89 100644 --- a/src/css/ingame_hud/statistics.scss +++ b/src/css/ingame_hud/statistics.scss @@ -1,6 +1,6 @@ #ingame_HUD_Statistics { .content { - @include S(width, 500px); + width: 50rem; } .filterHeader { @@ -10,32 +10,31 @@ justify-items: end; button { - @include S(height, 20px); - @include S(padding, 1px, 10px); + height: 2rem; + padding: 0.1rem 1rem; border: 0; box-shadow: none; - @include IncreasedClickArea(1px); - @include S(min-width, 30px); + min-width: 3rem; color: #fff; opacity: 0.25; - @include S(border-radius, $globalBorderRadius); + border-radius: $globalBorderRadius; border-radius: 0; &:first-child { - @include S(border-top-left-radius, $globalBorderRadius); - @include S(border-bottom-left-radius, $globalBorderRadius); + border-top-left-radius: $globalBorderRadius; + border-bottom-left-radius: $globalBorderRadius; } &:last-child { - @include S(border-top-right-radius, $globalBorderRadius); - @include S(border-bottom-right-radius, $globalBorderRadius); + border-top-right-radius: $globalBorderRadius; + border-bottom-right-radius: $globalBorderRadius; } &.displayIcons, &.displayDetailed, &.displaySorted, &.displayIterateUnit { - background: transparent center center / #{D(15px)} no-repeat; + background: transparent center center / 1.5rem no-repeat; } &.displayDetailed { @@ -46,12 +45,12 @@ &.displayIcons { /* @load-async */ background-image: uiResource("icons/display_icons.png"); - background-size: #{D(11.5px)}; + background-size: 1.15rem; } &.displayDetailed { - @include S(border-top-left-radius, $globalBorderRadius); - @include S(border-bottom-left-radius, $globalBorderRadius); + border-top-left-radius: $globalBorderRadius; + border-bottom-left-radius: $globalBorderRadius; } &.displaySorted { @@ -59,12 +58,12 @@ /* @load-async */ background-image: uiResource("icons/display_sorted.png"); } - background-size: #{D(11.5px)}; + background-size: 1.15rem; margin-right: 5px; - @include S(border-top-right-radius, $globalBorderRadius); - @include S(border-bottom-right-radius, $globalBorderRadius); + border-top-right-radius: $globalBorderRadius; + border-bottom-right-radius: $globalBorderRadius; - @include S(padding, 1px, 0); + padding: 0.1rem 0; } &.displayIterateUnit { @@ -73,7 +72,7 @@ background-image: uiResource("icons/toggle_unit.png"); } opacity: 0.8; - @include S(padding, 1px, 0); + padding: 0.1rem 0; } background-color: #44484a !important; @@ -101,20 +100,20 @@ .sourceExplanation { @include SuperSmallText(); - @include S(margin-top, 5px); + margin-top: 0.5rem; color: #aaa; } .content { - @include S(margin-top, 10px); - @include S(height, 350px); + margin-top: 1rem; + height: 35rem; overflow-y: scroll; display: flex; flex-direction: column; justify-content: flex-start; - @include S(padding-right, 4px); + padding-right: 0.4rem; > .noEntries { width: 100%; @@ -128,12 +127,12 @@ > div { background: #f4f4f4; - @include S(margin-bottom, 4px); + margin-bottom: 0.4rem; display: grid; - @include S(border-radius, $globalBorderRadius); + border-radius: $globalBorderRadius; grid-template-columns: 1fr auto; - @include S(padding, 5px); + padding: 0.5rem; &:last-child { margin-bottom: 0; } @@ -153,13 +152,13 @@ canvas.icon { grid-column: 1 / 2; grid-row: 1 / 2; - @include S(width, 40px); - @include S(height, 40px); + width: 4rem; + height: 4rem; } .counter { @include SuperSmallText; - @include S(padding, 0, 3px); + padding: 0, 0.3rem; } } } @@ -177,16 +176,15 @@ &[data-displaymode="icons"] .content.hasEntries { display: grid; grid-template-columns: repeat(6, 1fr); - grid-auto-rows: #{D(73px)}; + grid-auto-rows: 7.3rem; align-items: flex-start; - @include S(grid-column-gap, 3px); + grid-column-gap: 0.3rem; > div { - @include S(grid-row-gap, 5px); - @include S(height, 60px); + grid-row-gap: 0.5rem; + height: 6rem; grid-template-columns: 1fr; grid-template-rows: 1fr auto; - justify-items: center; - align-items: center; + place-items: center; .counter { grid-column: 1 / 2; @@ -202,10 +200,10 @@ } &[data-displaymode="detailed"] .content.hasEntries { > div { - @include S(padding, 10px); - @include S(height, 40px); + padding: 1rem; + height: 4rem; grid-template-columns: auto 1fr auto; - @include S(grid-column-gap, 15px); + grid-column-gap: 1.5rem; .counter { grid-column: 3 / 4; @@ -221,14 +219,13 @@ } canvas.graph { - @include S(width, 270px); - @include S(height, 40px); - @include S(border-radius, 0, 0, 2px, 2px); + width: 27rem; + height: 4rem; + border-radius: 0 0 0.2rem 0.2rem; $color: rgba(0, 10, 20, 0.04); // background: $color; - border: #{D(4px)} solid transparent; - // @include S(border-width, 1px, 0, 1px, 0); - @include S(margin-top, -3px); + border: 0.4rem solid transparent; + margin-top: -0.3rem; } } } diff --git a/src/css/ingame_hud/tutorial_hints.scss b/src/css/ingame_hud/tutorial_hints.scss index 27d85fe1..14d86377 100644 --- a/src/css/ingame_hud/tutorial_hints.scss +++ b/src/css/ingame_hud/tutorial_hints.scss @@ -1,120 +1,119 @@ -#ingame_HUD_TutorialHints { - position: absolute; - @include S(left, 10px); - @include S(bottom, 10px); - - @include StyleBelowWidth(1430px) { - @include S(bottom, 50px); - } - - display: flex; - flex-direction: column; - background: rgba(50, 60, 70, 0); - - transition: all 0.2s ease-in-out; - pointer-events: all; - - transition-property: background-color, transform, bottom, left; - - @include S(padding, 5px); - video { - transition: all 0.2s ease-in-out; - transition-property: opacity, width; - @include S(width, 0px); - opacity: 0; - z-index: 10; - position: relative; - } - - .header { - color: #333438; - display: grid; - align-items: center; - @include S(grid-gap, 2px); - grid-template-columns: 1fr; - @include S(margin-bottom, 3px); - z-index: 11; - position: relative; - - > span { - @include DarkThemeInvert; - - display: flex; - @include SuperSmallText; - justify-content: flex-start; - align-items: center; - &::before { - @include S(margin-right, 4px); - content: " "; - @include S(width, 12px); - @include S(height, 12px); - display: inline-block; - & { - /* @load-async */ - background: uiResource("icons/help.png") center center / 95% no-repeat; - } - } - } - - button.toggleHint { - @include PlainText; - @include IncreasedClickArea(0px); - } - } - - button.toggleHint { - .hide { - display: none; - } - } - - &.enlarged { - background: $ingameHudBg; - left: 50%; - bottom: 50%; - transform: translate(-50%, 50%); - - &::before { - pointer-events: all; - content: " "; - position: fixed; - top: -1000px; - left: -1000px; - right: -1000px; - bottom: -1000px; - z-index: 0; - - background: rgba($ingameHudBg, 0.3); - } - - .header { - grid-template-columns: 1fr auto; - > span { - display: none; - } - button.toggleHint { - grid-column: 2 / 3; - } - } - - video { - @include InlineAnimation(0.2s ease-in-out) { - 0% { - opacity: 0; - @include S(width, 0px); - } - } - - opacity: 1; - @include S(width, 500px); - } - button.toggleHint { - .hide { - display: block; - } - .show { - display: none; - } - } - } -} +#ingame_HUD_TutorialHints { + position: absolute; + left: 1rem; + bottom: 1rem; + + @include StyleBelowWidth(1430px) { + bottom: 5rem; + } + + display: flex; + flex-direction: column; + background: rgba(50, 60, 70, 0); + + transition: all 0.2s ease-in-out; + pointer-events: all; + + transition-property: background-color, transform, bottom, left; + + padding: 0.5rem; + video { + transition: all 0.2s ease-in-out; + transition-property: opacity, width; + width: 0rem; + opacity: 0; + z-index: 10; + position: relative; + } + + .header { + color: #333438; + display: grid; + align-items: center; + grid-gap: 0.2rem; + grid-template-columns: 1fr; + margin-bottom: 0.3rem; + z-index: 11; + position: relative; + + > span { + @include DarkThemeInvert; + + display: flex; + @include SuperSmallText; + justify-content: flex-start; + align-items: center; + &::before { + margin-right: 0.4rem; + content: " "; + width: 1.2rem; + height: 1.2rem; + display: inline-block; + & { + /* @load-async */ + background: uiResource("icons/help.png") center center / 95% no-repeat; + } + } + } + + button.toggleHint { + @include PlainText; + } + } + + button.toggleHint { + .hide { + display: none; + } + } + + &.enlarged { + background: $ingameHudBg; + left: 50%; + bottom: 50%; + transform: translate(-50%, 50%); + + &::before { + pointer-events: all; + content: " "; + position: fixed; + top: -1000px; + left: -1000px; + right: -1000px; + bottom: -1000px; + z-index: 0; + + background: rgba($ingameHudBg, 0.3); + } + + .header { + grid-template-columns: 1fr auto; + > span { + display: none; + } + button.toggleHint { + grid-column: 2 / 3; + } + } + + video { + @include InlineAnimation(0.2s ease-in-out) { + 0% { + opacity: 0; + width: 0rem; + } + } + + opacity: 1; + width: 50rem; + } + button.toggleHint { + .hide { + display: block; + } + .show { + display: none; + } + } + } +} diff --git a/src/css/ingame_hud/unlock_notification.scss b/src/css/ingame_hud/unlock_notification.scss index eeac3588..1cf1e59d 100644 --- a/src/css/ingame_hud/unlock_notification.scss +++ b/src/css/ingame_hud/unlock_notification.scss @@ -1,184 +1,178 @@ -#ingame_HUD_UnlockNotification { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - overflow: auto; - pointer-events: all; - - & { - /* @load-async */ - background: rgba(#333538, 0.98) uiResource("dialog_bg_pattern.png") top left / #{D(10px)} repeat; - } - - @include InlineAnimation(0.1s ease-in-out) { - 0% { - opacity: 0; - } - } - - &.withinDemo { - > .dialog { - @include S(padding-top, 60px); - } - } - - .dialog { - // background: rgba(#222428, 0.5); - @include S(border-radius, $globalBorderRadius); - @include S(padding, 30px); - - @include InlineAnimation(0.5s ease-in-out) { - 0% { - opacity: 0; - } - } - display: flex; - align-items: center; - flex-direction: column; - - color: #fff; - text-align: center; - .title, - .subTitle { - @include SuperHeading; - text-transform: uppercase; - @include S(font-size, 40px); - - @include InlineAnimation(0.5s ease-in-out) { - 0% { - transform: translateY(-50vh); - } - 50% { - transform: translateY(5vh); - } - 75% { - transform: translateY(-2vh); - } - } - } - - .subTitle { - @include PlainText; - display: inline-block; - @include S(margin, 5px, 0, 20px); - color: $colorGreenBright; - - @include S(border-radius, $globalBorderRadius); - @include InlineAnimation(0.5s ease-in-out) { - 0% { - transform: translateY(-60vh); - } - 50% { - transform: translateY(6vh); - } - 75% { - transform: translateY(-3vh); - } - } - } - - .contents { - @include S(width, 400px); - @include InlineAnimation(0.5s ease-in-out) { - 0% { - transform: translateX(-100vw); - } - 50% { - transform: translateX(5vw); - } - - 75% { - transform: translateX(-2vw); - } - } - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - @include S(grid-gap, 10px); - - .rewardName { - grid-column: 1 / 3; - display: none; - @include InlineAnimation(0.5s ease-in-out) { - 0% { - transform: translateX(200vw); - } - 50% { - transform: translateX(-10vw); - } - - 75% { - transform: translateX(4vw); - } - } - } - - .rewardDesc { - grid-column: 1 / 3; - @include PlainText; - @include S(margin-bottom, 15px); - color: #aaacaf; - @include S(width, 400px); - text-align: left; - strong { - color: #fff; - } - } - - .images { - display: flex; - .buildingExplanation { - @include S(width, 200px); - @include S(height, 200px); - display: inline-block; - background-position: center center; - background-size: cover; - background-repeat: no-repeat; - @include S(border-radius, $globalBorderRadius); - box-shadow: #{D(2px)} #{D(3px)} 0 0 rgba(0, 0, 0, 0.15); - } - } - } - - button.close { - border: 0; - position: relative; - @include S(margin-top, 30px); - - &:not(.unlocked) { - pointer-events: none; - opacity: 0.8; - cursor: default; - } - - &.unlocked { - &::after { - animation: none !important; - } - } - - &::after { - content: " "; - display: inline-block; - position: absolute; - top: 0; - left: 100%; - right: 0; - bottom: 0; - background: rgba(0, 10, 20, 0.8); - - @include InlineAnimation(1.5s linear) { - 0% { - left: 0; - } - 100% { - left: 100%; - } - } - } - } - } -} +#ingame_HUD_UnlockNotification { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + overflow: auto; + pointer-events: all; + + & { + /* @load-async */ + background: rgba(#333538, 0.98) uiResource("dialog_bg_pattern.png") top left / 1rem repeat; + } + + @include InlineAnimation(0.1s ease-in-out) { + 0% { + opacity: 0; + } + } + + .dialog { + // background: rgba(#222428, 0.5); + border-radius: $globalBorderRadius; + padding: 3rem; + + @include InlineAnimation(0.5s ease-in-out) { + 0% { + opacity: 0; + } + } + display: flex; + align-items: center; + flex-direction: column; + + color: #fff; + text-align: center; + .title, + .subTitle { + @include SuperHeading; + text-transform: uppercase; + font-size: 4rem; + + @include InlineAnimation(0.5s ease-in-out) { + 0% { + transform: translateY(-50vh); + } + 50% { + transform: translateY(5vh); + } + 75% { + transform: translateY(-2vh); + } + } + } + + .subTitle { + @include PlainText; + display: inline-block; + margin: 0.5rem 0 2rem; + color: $colorGreenBright; + + border-radius: $globalBorderRadius; + @include InlineAnimation(0.5s ease-in-out) { + 0% { + transform: translateY(-60vh); + } + 50% { + transform: translateY(6vh); + } + 75% { + transform: translateY(-3vh); + } + } + } + + .contents { + width: 40rem; + @include InlineAnimation(0.5s ease-in-out) { + 0% { + transform: translateX(-100vw); + } + 50% { + transform: translateX(5vw); + } + + 75% { + transform: translateX(-2vw); + } + } + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + grid-gap: 1rem; + + .rewardName { + grid-column: 1 / 3; + display: none; + @include InlineAnimation(0.5s ease-in-out) { + 0% { + transform: translateX(200vw); + } + 50% { + transform: translateX(-10vw); + } + + 75% { + transform: translateX(4vw); + } + } + } + + .rewardDesc { + grid-column: 1 / 3; + @include PlainText; + margin-bottom: 1.5rem; + color: #aaacaf; + width: 40rem; + text-align: left; + strong { + color: #fff; + } + } + + .images { + display: flex; + .buildingExplanation { + width: 20rem; + height: 20rem; + display: inline-block; + background-position: center center; + background-size: cover; + background-repeat: no-repeat; + border-radius: $globalBorderRadius; + box-shadow: 0.2rem 0.3rem 0 0 rgba(0, 0, 0, 0.15); + } + } + } + + button.close { + border: 0; + position: relative; + margin-top: 3rem; + + &:not(.unlocked) { + pointer-events: none; + opacity: 0.8; + cursor: default; + } + + &.unlocked { + &::after { + animation: none !important; + } + } + + &::after { + content: " "; + display: inline-block; + position: absolute; + top: 0; + left: 100%; + right: 0; + bottom: 0; + background: rgba(0, 10, 20, 0.8); + + @include InlineAnimation(1.5s linear) { + 0% { + left: 0; + } + 100% { + left: 100%; + } + } + } + } + } +} diff --git a/src/css/ingame_hud/watermark.scss b/src/css/ingame_hud/watermark.scss deleted file mode 100644 index f7a3d089..00000000 --- a/src/css/ingame_hud/watermark.scss +++ /dev/null @@ -1,107 +0,0 @@ -#ingame_HUD_Watermark { - position: absolute; - - @include S(border-radius, $globalBorderRadius); - @include S(top, 70px); - pointer-events: all; - cursor: pointer; - left: 50%; - text-align: center; - - background: rgba(207, 65, 65, 0.8); - color: #fff; - transform: translateX(-50%); - @include PlainText; - @include S(padding, 10px); - - &:hover { - transform: translateX(-50%) scale(1.02) !important; - } - - > strong { - @include PlainText; - text-transform: uppercase; - } - > p { - @include SuperSmallText; - opacity: 0.7; - } - - opacity: 0; - - &.visible { - @include InlineAnimation(0.5s ease-in-out) { - 0% { - opacity: 0; - } - 100% { - opacity: 1; - } - } - opacity: 1; - } - - &:not(.visible) { - @include InlineAnimation(0.5s ease-in-out) { - 0% { - opacity: 1; - } - 100% { - opacity: 0; - } - } - } -} - -#ingame_HUD_WatermarkClicker { - @include S(top, 55px); - position: absolute; - left: 50%; - transform: translateX(-50%) !important; - @include SuperSmallText; - color: $colorBlueBright; - text-transform: uppercase; - pointer-events: all; - cursor: pointer; - display: flex; - align-items: center; - - &:hover { - opacity: 0.9; - } - - &::after { - @include S(margin-left, 4px); - content: ""; - @include S(width, 10px); - @include S(height, 10px); - display: inline-flex; - background: center center / contain no-repeat; - & { - /* @load-async */ - background-image: uiResource("res/ui/icons/demo_steam_link_indicator.png"); - } - } - - &.withDiscount { - color: #4c6b22; - } - - > .discount { - // position: absolute; - @include S(margin, 0, 5px); - - background: rgba(#4c6b22, 1); - color: #c5ea3f; - @include S(border-radius, $globalBorderRadius); - @include S(padding, 0px, 2px, 0px, 3px); - @include SuperSmallText; - text-transform: uppercase; - transform: rotate(0deg); - @include InlineAnimation(1.3s ease-in-out infinite) { - 50% { - transform: rotate(0.5deg) scale(1.05); - } - } - } -} diff --git a/src/css/ingame_hud/waypoints.scss b/src/css/ingame_hud/waypoints.scss index f7566ee5..9537eb01 100644 --- a/src/css/ingame_hud/waypoints.scss +++ b/src/css/ingame_hud/waypoints.scss @@ -1,122 +1,122 @@ -#ingame_HUD_Waypoints_Hint { - position: absolute; - @include S(right, 10px); - @include S(bottom, 10px); - - display: flex; - flex-direction: column; - - @include PlainText; - @include S(width, 150px); - background: $ingameHudBg; - @include S(padding, 7px); - - color: #eee; - @include S(border-radius, $globalBorderRadius); - - .desc { - @include SuperSmallText; - color: #babcbf; - .keybinding { - position: relative; - } - - strong { - color: #fff; - } - } -} - -#ingame_HUD_Waypoints { - position: absolute; - @include S(right, 10px); - @include S(top, 45px); - display: flex; - flex-direction: column; - @include DarkThemeInvert(); - - max-height: 50vh; - overflow-x: hidden; - overflow-y: auto; - pointer-events: all; - @include S(padding-right, 5px); - @include S(padding-bottom, 5px); - @include S(padding-top, 5px); - - // Scrollbar - &::-webkit-scrollbar { - @include S(width, 2px); - @include S(height, 6px); - } - - .waypoint { - @include SuperSmallText; - pointer-events: all; - cursor: pointer; - color: #333438; - @include S(padding-left, 11px); - display: grid; - grid-template-columns: 1fr auto; - align-items: center; - & { - /* @load-async */ - background: uiResource("icons/waypoint.png") left 50% / #{D(8px)} no-repeat; - } - - &.layer--wires { - /* @load-async */ - background-image: uiResource("icons/waypoint_wires.png"); - } - - opacity: 0.7; - @include S(margin-bottom, 1px); - font-weight: bold; - - &:hover { - opacity: 0.8; - } - - .editButton { - @include S(width, 10px); - @include S(height, 10px); - @include S(margin-left, 4px); - & { - /* @load-async */ - background: uiResource("icons/edit_key.png") center center / 70% no-repeat; - } - pointer-events: all; - cursor: pointer; - position: relative; - @include IncreasedClickArea(2px); - transition: transform 0.04s ease-in-out; - - &:hover { - transform: scale(1.5); - } - } - - &.hub { - // Transform because there is a canvas before - @include S(margin-left, -2px); - - grid-template-columns: auto 1fr; - background: none !important; - @include S(padding-left, 0); - canvas { - @include S(width, 12px); - @include S(height, 12px); - @include S(margin-right, 1px); - } - } - - &.shapeIcon { - canvas { - @include S(width, 15px); - @include S(height, 15px); - pointer-events: none; - // Double invert, to make sure it has the right color - @include DarkThemeInvert(); - } - } - } -} +#ingame_HUD_Waypoints_Hint { + position: absolute; + right: 1rem; + bottom: 1rem; + + display: flex; + flex-direction: column; + + @include PlainText; + width: 15rem; + background: $ingameHudBg; + padding: 0.7rem; + + color: #eee; + border-radius: $globalBorderRadius; + + .desc { + @include SuperSmallText; + color: #babcbf; + + kbd { + position: relative; + } + + strong { + color: #fff; + } + } +} + +#ingame_HUD_Waypoints { + position: absolute; + right: 1rem; + top: 4.5rem; + display: flex; + flex-direction: column; + @include DarkThemeInvert(); + + max-height: 50vh; + overflow-x: hidden; + overflow-y: auto; + pointer-events: all; + padding-right: 0.5rem; + padding-bottom: 0.5rem; + padding-top: 0.5rem; + + // Scrollbar + &::-webkit-scrollbar { + width: 0.2rem; + height: 0.6rem; + } + + .waypoint { + @include SuperSmallText; + pointer-events: all; + cursor: pointer; + color: #333438; + padding-left: 1.1rem; + display: grid; + grid-template-columns: 1fr auto; + align-items: center; + & { + /* @load-async */ + background: uiResource("icons/waypoint.png") left 50% / 0.8rem no-repeat; + } + + &.layer--wires { + /* @load-async */ + background-image: uiResource("icons/waypoint_wires.png"); + } + + opacity: 0.7; + margin-bottom: 0.1rem; + font-weight: bold; + + &:hover { + opacity: 0.8; + } + + .editButton { + width: 1rem; + height: 1rem; + margin-left: 0.4rem; + & { + /* @load-async */ + background: uiResource("icons/edit_key.png") center center / 70% no-repeat; + } + pointer-events: all; + cursor: pointer; + position: relative; + transition: transform 0.04s ease-in-out; + + &:hover { + transform: scale(1.5); + } + } + + &.hub { + // Transform because there is a canvas before + margin-left: -0.2rem; + + grid-template-columns: auto 1fr; + background: none !important; + padding-left: 0; + canvas { + width: 1.2rem; + height: 1.2rem; + margin-right: 0.1rem; + } + } + + &.shapeIcon { + canvas { + width: 1.5rem; + height: 1.5rem; + pointer-events: none; + // Double invert, to make sure it has the right color + @include DarkThemeInvert(); + } + } + } +} diff --git a/src/css/main.scss b/src/css/main.scss index f1ec8570..74ad265c 100644 --- a/src/css/main.scss +++ b/src/css/main.scss @@ -1,135 +1,126 @@ -// Control here whether to inline all resources or instead load them -@function uiResource($pth) { - @if (str-index($string: $pth, $substring: ".noinline")) { - @return resolve($pth); - } - @return inline($pth); -} - -@import "resources"; -@import "trigonometry"; -@import "material_colors"; -@import "dynamic_ui"; -@import "variables"; - -@import "mixins"; -@import "common"; -@import "animations"; -@import "game_state"; -@import "textual_game_state"; -@import "adinplay"; -@import "changelog_skins"; - -@import "states/wegame_splash"; -@import "states/preload"; -@import "states/main_menu"; -@import "states/ingame"; -@import "states/keybindings"; -@import "states/settings"; -@import "states/about"; -@import "states/mobile_warning"; -@import "states/changelog"; -@import "states/puzzle_menu"; -@import "states/mods"; - -@import "ingame_hud/buildings_toolbar"; -@import "ingame_hud/building_placer"; -@import "ingame_hud/beta_overlay"; -@import "ingame_hud/keybindings_overlay"; -@import "ingame_hud/unlock_notification"; -@import "ingame_hud/shop"; -@import "ingame_hud/game_menu"; -@import "ingame_hud/dialogs"; -@import "ingame_hud/vignette_overlay"; -@import "ingame_hud/statistics"; -@import "ingame_hud/pinned_shapes"; -@import "ingame_hud/notifications"; -@import "ingame_hud/settings_menu"; -@import "ingame_hud/debug_info"; -@import "ingame_hud/entity_debugger"; -@import "ingame_hud/tutorial_hints"; -@import "ingame_hud/watermark"; -@import "ingame_hud/blueprint_placer"; -@import "ingame_hud/waypoints"; -@import "ingame_hud/interactive_tutorial"; -@import "ingame_hud/color_blind_helper"; -@import "ingame_hud/shape_viewer"; -@import "ingame_hud/sandbox_controller"; -@import "ingame_hud/standalone_advantages"; -@import "ingame_hud/puzzle_back_to_menu"; -@import "ingame_hud/puzzle_editor_review"; -@import "ingame_hud/puzzle_dlc_logo"; -@import "ingame_hud/puzzle_editor_controls"; -@import "ingame_hud/puzzle_editor_settings"; -@import "ingame_hud/puzzle_play_settings"; -@import "ingame_hud/puzzle_play_metadata"; -@import "ingame_hud/puzzle_complete_notification"; -@import "ingame_hud/puzzle_next"; - -// prettier-ignore -$elements: -// Base -ingame_Canvas, -ingame_VignetteOverlay, -ingame_HUD_PuzzleDLCLogo, - -// Ingame overlays -ingame_HUD_Waypoints, -ingame_HUD_PlacementHints, -ingame_HUD_PlacerVariants, - -// Regular hud -ingame_HUD_PinnedShapes, -ingame_HUD_GameMenu, -ingame_HUD_KeybindingOverlay, -ingame_HUD_PuzzleBackToMenu, -ingame_HUD_PuzzleNextPuzzle, -ingame_HUD_PuzzleEditorReview, -ingame_HUD_PuzzleEditorControls, -ingame_HUD_PuzzleEditorTitle, -ingame_HUD_PuzzleEditorSettings, -ingame_HUD_PuzzlePlaySettings, -ingame_HUD_PuzzlePlayMetadata, -ingame_HUD_PuzzlePlayTitle, -ingame_HUD_Notifications, -ingame_HUD_DebugInfo, -ingame_HUD_EntityDebugger, -ingame_HUD_TutorialHints, -ingame_HUD_InteractiveTutorial, -ingame_HUD_BuildingsToolbar, -ingame_HUD_wires_toolbar, -ingame_HUD_BlueprintPlacer, -ingame_HUD_Waypoints_Hint, -ingame_HUD_WatermarkClicker, -ingame_HUD_Watermark, -ingame_HUD_ColorBlindBelowTileHelper, -ingame_HUD_SandboxController, - -// Overlays -ingame_HUD_BetaOverlay, - -// Dialogs -ingame_HUD_Shop, -ingame_HUD_Statistics, -ingame_HUD_ShapeViewer, -ingame_HUD_StandaloneAdvantages, -ingame_HUD_UnlockNotification, -ingame_HUD_PuzzleCompleteNotification, -ingame_HUD_SettingsMenu, -ingame_HUD_ModalDialogs; - -$zindex: 100; - -@each $elem in $elements { - ##{$elem} { - z-index: $zindex; - } - - $zindex: $zindex + 10; -} - -body.uiHidden { - > div:not(.ingameDialog):not(#ingame_HUD_SettingsMenu):not(#ingame_HUD_ModalDialogs):not(#ingame_HUD_UnlockNotification):not(#ingame_HUD_PuzzleCompleteNotification) { - display: none !important; - } -} +// Control here whether to inline all resources or instead load them +@function uiResource($pth) { + @if (str-index($string: $pth, $substring: ".noinline")) { + @return resolve($pth); + } + @return inline($pth); +} + +@import "resources"; +@import "variables"; + +@import "mixins"; +@import "common"; +@import "game_state"; +@import "textual_game_state"; +@import "error_handler"; + +@import "states/preload"; +@import "states/main_menu"; +@import "states/ingame"; +@import "states/keybindings"; +@import "states/settings"; +@import "states/about"; +@import "states/changelog"; +@import "states/puzzle_menu"; +@import "states/mods"; + +@import "ingame_hud/buildings_toolbar"; +@import "ingame_hud/building_placer"; +@import "ingame_hud/beta_overlay"; +@import "ingame_hud/keybindings_overlay"; +@import "ingame_hud/unlock_notification"; +@import "ingame_hud/shop"; +@import "ingame_hud/game_menu"; +@import "ingame_hud/dialogs"; +@import "ingame_hud/vignette_overlay"; +@import "ingame_hud/statistics"; +@import "ingame_hud/pinned_shapes"; +@import "ingame_hud/notifications"; +@import "ingame_hud/settings_menu"; +@import "ingame_hud/debug_info"; +@import "ingame_hud/entity_debugger"; +@import "ingame_hud/tutorial_hints"; +@import "ingame_hud/blueprint_placer"; +@import "ingame_hud/waypoints"; +@import "ingame_hud/interactive_tutorial"; +@import "ingame_hud/color_blind_helper"; +@import "ingame_hud/shape_viewer"; +@import "ingame_hud/sandbox_controller"; +@import "ingame_hud/puzzle_back_to_menu"; +@import "ingame_hud/puzzle_editor_review"; +@import "ingame_hud/puzzle_dlc_logo"; +@import "ingame_hud/puzzle_editor_controls"; +@import "ingame_hud/puzzle_editor_settings"; +@import "ingame_hud/puzzle_play_settings"; +@import "ingame_hud/puzzle_play_metadata"; +@import "ingame_hud/puzzle_complete_notification"; +@import "ingame_hud/puzzle_next"; + +// prettier-ignore +$elements: +// Base +ingame_Canvas, +ingame_VignetteOverlay, +ingame_HUD_PuzzleDLCLogo, + +// Ingame overlays +ingame_HUD_Waypoints, +ingame_HUD_PlacementHints, +ingame_HUD_PlacerVariants, + +// Regular hud +ingame_HUD_PinnedShapes, +ingame_HUD_GameMenu, +ingame_HUD_KeybindingOverlay, +ingame_HUD_PuzzleBackToMenu, +ingame_HUD_PuzzleNextPuzzle, +ingame_HUD_PuzzleEditorReview, +ingame_HUD_PuzzleEditorControls, +ingame_HUD_PuzzleEditorTitle, +ingame_HUD_PuzzleEditorSettings, +ingame_HUD_PuzzlePlaySettings, +ingame_HUD_PuzzlePlayMetadata, +ingame_HUD_PuzzlePlayTitle, +ingame_HUD_Notifications, +ingame_HUD_DebugInfo, +ingame_HUD_EntityDebugger, +ingame_HUD_TutorialHints, +ingame_HUD_InteractiveTutorial, +ingame_HUD_BuildingsToolbar, +ingame_HUD_wires_toolbar, +ingame_HUD_BlueprintPlacer, +ingame_HUD_Waypoints_Hint, +ingame_HUD_WatermarkClicker, +ingame_HUD_ColorBlindBelowTileHelper, +ingame_HUD_SandboxController, + +// Overlays +ingame_HUD_BetaOverlay, + +// Dialogs +ingame_HUD_Shop, +ingame_HUD_Statistics, +ingame_HUD_ShapeViewer, +ingame_HUD_UnlockNotification, +ingame_HUD_PuzzleCompleteNotification, +ingame_HUD_SettingsMenu, +ingame_HUD_ModalDialogs; + +$zindex: 100; + +@each $elem in $elements { + ##{$elem} { + z-index: $zindex; + } + + $zindex: $zindex + 10; +} + +body.uiHidden { + > div:not(.ingameDialog):not(#ingame_HUD_SettingsMenu):not(#ingame_HUD_ModalDialogs):not( + #ingame_HUD_UnlockNotification + ):not(#ingame_HUD_PuzzleCompleteNotification) { + display: none !important; + } +} diff --git a/src/css/material_colors.scss b/src/css/material_colors.scss deleted file mode 100644 index dd4bdd1d..00000000 --- a/src/css/material_colors.scss +++ /dev/null @@ -1,319 +0,0 @@ -// -// color palette - -// sass-lint:disable hex-length - -@function color($color, $value: 500) { - @return map-get($color, $value); -} - -$red: ( - 50: #ffebee, - 100: #ffcdd2, - 200: #ef9a9a, - 300: #e57373, - 400: #ef5350, - 500: #f44336, - 600: #e53935, - 700: #d32f2f, - 800: #c62828, - 900: #b71c1c, - a100: #ff8a80, - a200: #ff5252, - a400: #ff1744, - a700: #d50000, -); - -$pink: ( - 50: #fce4ec, - 100: #f8bbd0, - 200: #f48fb1, - 300: #f06292, - 400: #ec407a, - 500: #e91e63, - 600: #d81b60, - 700: #c2185b, - 800: #ad1457, - 900: #880e4f, - a100: #ff80ab, - a200: #ff4081, - a400: #f50057, - a700: hsl(333, 84%, 42%), -); - -$purple: ( - 50: #f3e5f5, - 100: #e1bee7, - 200: #ce93d8, - 300: #ba68c8, - 400: #ab47bc, - 500: #9c27b0, - 600: #8e24aa, - 700: #7b1fa2, - 800: #6a1b9a, - 900: #4a148c, - a100: #ea80fc, - a200: #e040fb, - a400: #d500f9, - a700: #aa00ff, -); - -$deep-purple: ( - 50: #ede7f6, - 100: #d1c4e9, - 200: #b39ddb, - 300: #9575cd, - 400: #7e57c2, - 500: #673ab7, - 600: #5e35b1, - 700: #512da8, - 800: #4527a0, - 900: #311b92, - a100: #b388ff, - a200: #7c4dff, - a400: #651fff, - a700: #6200ea, -); - -$indigo: ( - 50: #e8eaf6, - 100: #c5cae9, - 200: #9fa8da, - 300: #7986cb, - 400: #5c6bc0, - 500: #3f51b5, - 600: #3949ab, - 700: #303f9f, - 800: #283593, - 900: #1a237e, - a100: #8c9eff, - a200: #536dfe, - a400: #3d5afe, - a700: #304ffe, -); - -$blue: ( - 50: #e3f2fd, - 100: #bbdefb, - 200: #90caf9, - 300: #64b5f6, - 400: #42a5f5, - 500: #2196f3, - 600: #1e88e5, - 700: #1976d2, - 800: #1565c0, - 900: #0d47a1, - a100: #82b1ff, - a200: #448aff, - a400: #2979ff, - a700: #2962ff, -); - -$light-blue: ( - 50: #e1f5fe, - 100: #b3e5fc, - 200: #81d4fa, - 300: #4fc3f7, - 400: #29b6f6, - 500: #03a9f4, - 600: #039be5, - 700: #0288d1, - 800: #0277bd, - 900: #01579b, - a100: #80d8ff, - a200: #40c4ff, - a400: #00b0ff, - a700: #0091ea, -); - -$cyan: ( - 50: #e0f7fa, - 100: #b2ebf2, - 200: #80deea, - 300: #4dd0e1, - 400: #26c6da, - 500: #00bcd4, - 600: #00acc1, - 700: #0097a7, - 800: #00838f, - 900: #006064, - a100: #84ffff, - a200: #18ffff, - a400: #00e5ff, - a700: #00b8d4, -); - -$teal: ( - 50: #e0f2f1, - 100: #b2dfdb, - 200: #80cbc4, - 300: #4db6ac, - 400: #26a69a, - 500: #009688, - 600: #00897b, - 700: #00796b, - 800: #00695c, - 900: #004d40, - a100: #a7ffeb, - a200: #64ffda, - a400: #1de9b6, - a700: #00bfa5, -); - -$green: ( - 50: #e8f5e9, - 100: #c8e6c9, - 200: #a5d6a7, - 300: #81c784, - 400: #66bb6a, - 500: #4caf50, - 600: #43a047, - 700: #388e3c, - 800: #2e7d32, - 900: #1b5e20, - a100: #b9f6ca, - a200: #69f0ae, - a400: #00e676, - a700: #00c853, -); - -$light-green: ( - 50: #f1f8e9, - 100: #dcedc8, - 200: #c5e1a5, - 300: #aed581, - 400: #9ccc65, - 500: #8bc34a, - 600: #7cb342, - 700: #689f38, - 800: #558b2f, - 900: #33691e, - a100: #ccff90, - a200: #b2ff59, - a400: #76ff03, - a700: #64dd17, -); - -$lime: ( - 50: #f9fbe7, - 100: #f0f4c3, - 200: #e6ee9c, - 300: #dce775, - 400: #d4e157, - 500: #cddc39, - 600: #c0ca33, - 700: #afb42b, - 800: #9e9d24, - 900: #827717, - a100: #f4ff81, - a200: #eeff41, - a400: #c6ff00, - a700: #aeea00, -); - -$yellow: ( - 50: #fffde7, - 100: #fff9c4, - 200: #fff59d, - 300: #fff176, - 400: #ffee58, - 500: #ffeb3b, - 600: #fdd835, - 700: #fbc02d, - 800: #f9a825, - 900: #f57f17, - a100: #ffff8d, - a200: #ffff00, - a400: #ffea00, - a700: #ffd600, -); - -$amber: ( - 50: #fff8e1, - 100: #ffecb3, - 200: #ffe082, - 300: #ffd54f, - 400: #ffca28, - 500: #ffc107, - 600: #ffb300, - 700: #ffa000, - 800: #ff8f00, - 900: #ff6f00, - a100: #ffe57f, - a200: #ffd740, - a400: #ffc400, - a700: #ffab00, -); - -$orange: ( - 50: #fff3e0, - 100: #ffe0b2, - 200: #ffcc80, - 300: #ffb74d, - 400: #ffa726, - 500: #ff9800, - 600: #fb8c00, - 700: #f57c00, - 800: #ef6c00, - 900: #e65100, - a100: #ffd180, - a200: #ffab40, - a400: #ff9100, - a700: #ff6d00, -); - -$deep-orange: ( - 50: #fbe9e7, - 100: #ffccbc, - 200: #ffab91, - 300: #ff8a65, - 400: #ff7043, - 500: #ff5722, - 600: #f4511e, - 700: #e64a19, - 800: #d84315, - 900: #bf360c, - a100: #ff9e80, - a200: #ff6e40, - a400: #ff3d00, - a700: #dd2c00, -); - -$brown: ( - 50: #efebe9, - 100: #d7ccc8, - 200: #bcaaa4, - 300: #a1887f, - 400: #8d6e63, - 500: #795548, - 600: #6d4c41, - 700: #5d4037, - 800: #4e342e, - 900: #3e2723, -); - -$grey: ( - 50: #fafafa, - 100: #f5f5f5, - 200: #eeeeee, - 300: #e0e0e0, - 400: #bdbdbd, - 500: #9e9e9e, - 600: #757575, - 700: #616161, - 800: #424242, - 900: #212121, -); - -$blue-grey: ( - 50: #eceff1, - 100: #cfd8dc, - 200: #b0bec5, - 300: #90a4ae, - 400: #78909c, - 500: #607d8b, - 600: #546e7a, - 700: #455a64, - 800: #37474f, - 900: #263238, -); diff --git a/src/css/mixins.scss b/src/css/mixins.scss index d0a4f367..7a4c0e1a 100644 --- a/src/css/mixins.scss +++ b/src/css/mixins.scss @@ -1,292 +1,80 @@ -// ---------------------------------------- -/* Forces an element to get rendered on its own layer, increasing -the performance when animated. Use only transform and opacity in animations! */ -@mixin FastAnimation { - will-change: transform, opacity, filter; - // transform: translateZ(0); - backface-visibility: hidden; - -webkit-backface-visibility: hidden; -} - -// Helper which includes the translateZ webkit fix, use together with Fast animation -// $hardwareAcc: translateZ(0); -$hardwareAcc: null; - -// ---------------------------------------- -/** Increased click area for this element, helpful on mobile */ -@mixin IncreasedClickArea($size) { - // &::after { - // content: ""; - // position: absolute; - // top: #{D(-$size)}; - // bottom: #{D(-$size)}; - // left: #{D(-$size)}; - // right: #{D(-$size)}; - // // background: rgba(255, 0, 0, 0.3); - // } -} -button, -.increasedClickArea { - position: relative; - @include IncreasedClickArea(15px); -} - -// ---------------------------------------- -/* Duplicates an animation and adds two classes .Even and .Odd which uses the - animation. This can be used to replay the animation by toggling between the classes, because - it is not possible to restart a css animation */ -@mixin MakeAnimationWrappedEvenOdd($duration, $classPrefix: "anim", $childSelector: "") { - $animName: autogen_anim_#{unique-id()}; - - @at-root { - @keyframes #{$animName}_even { - @content; - } - - @keyframes #{$animName}_odd { - @content; - } - } - - &.#{$classPrefix}Even #{$childSelector} { - animation: #{$animName}_even $duration; - } - - &.#{$classPrefix}Odd #{$childSelector} { - animation: #{$animName}_odd $duration; - } -} - -// ---------------------------------------- -/* Allows to use and define an animation without specifying its name */ -@mixin InlineAnimation($duration) { - $animName: autogen_anim_#{unique-id()}; - - @at-root { - @keyframes #{$animName} { - @content; - } - } - - animation: $animName $duration !important; -} - -// ---------------------------------------- -/* Animation prefab for a double bounce pop-in animation, useful for dialogs */ -@mixin DoubleBounceAnim($duration: 0.5s ease-in-out, $amount: 0.2, $initialOpacity: 0) { - @include InlineAnimation($duration) { - 0% { - opacity: $initialOpacity; - transform: scale(0) $hardwareAcc; - } - - 25% { - opacity: 0.5; - transform: scale(1 + $amount) $hardwareAcc; - } - - 50% { - opacity: 1; - transform: scale(1 - $amount * 0.5) $hardwareAcc; - } - - 75% { - transform: scale(1 + $amount * 0.25) $hardwareAcc; - } - - 100% { - transform: scale(1) $hardwareAcc; - } - } - - opacity: 1; -} - -// ---------------------------------------- -/* Automatically transforms the game state if a hardware keyboard is open */ -@mixin TransformToMatchKeyboard { - transition: transform 0.2s ease-in-out; - @include AndroidHwKeyboardOpen { - @include VerticalStyle { - transform: translateY(#{D(-125px)}) $hardwareAcc; - } - @include HorizontalStyle { - transform: translateY(#{D(-100px)}) $hardwareAcc; - } - } -} - -// ---------------------------------------- -/* Define a style which is only applied when the viewport is at least X pixels wide */ -@mixin StyleAtWidth($minW) { - @media (min-width: #{$minW}) { - @content; - } -} - -// ---------------------------------------- -/* Define a style which is only applied when the viewport is at least X pixels height */ -@mixin StyleAtHeight($minH) { - @media (min-height: #{$minH}) { - @content; - } -} - -// ---------------------------------------- -/* Define a style which is only applied when the viewport has at least the given dimensions */ -@mixin StyleAtDims($minW, $minH) { - @media (min-height: #{$minH}) and (min-width: #{$minW}) { - @content; - } -} - -// ---------------------------------------- -/* Define a style which is only applied when the viewport has at maximum the given dimensions */ -@mixin StyleBelowDims($maxW, $maxH) { - @media (max-height: #{$maxH}) and (max-width: #{$maxW}) { - @content; - } -} - -// ---------------------------------------- -/* Define a style which is only applied when the viewport has at maximum the given height */ -@mixin StyleBelowHeight($maxH) { - @media (max-height: #{$maxH}) { - @content; - } -} -// ---------------------------------------- -/* Define a style which is only applied when the viewport has at maximum the given width */ -@mixin StyleBelowWidth($maxW) { - @media (max-width: #{$maxW}) { - @content; - } -} - -// ---------------------------------------- -// Dynamic graphics quality styles - -@mixin BoxShadow3D($bgColor, $size: 3px, $pressEffect: true) { - background-color: $bgColor; - - $borderSize: 1.5px; - $borderColor: rgb(18, 20, 24); - - // box-shadow: 0 0 0 D($borderSize) $borderColor, 0 D($size) 0 0px rgba(mix(darken($bgColor, 9), #b0e2ff, 95%), 1), - // 0 D($size) 0 D($borderSize) $borderColor; - - // box-shadow: 0 0 0 D($borderSize) $borderColor, 0 D($size) 0 D($borderSize) $borderColor, - // D(-$size * 1.5) D($size * 2) 0 D($borderSize) rgba(0, 0, 0, 0.1); - - // transition: box-shadow 0.1s ease-in-out; - - // @if $pressEffect { - // &.pressed { - // transform: none !important; - // $pSize: max(0, $size - 1.5px); - // transition: none !important; - // box-shadow: 0 0 0 D($borderSize) $borderColor, 0 D($pSize) 0 0px rgba(mix(darken($bgColor, 9), #b0e2ff, 95%), 1), - // 0 D($pSize) 0 D($borderSize) $borderColor; - // top: D($size - $pSize); - // } - // } -} - -@mixin BorderRadius($v1: 2px, $v2: "", $v3: "", $v4: "") { - @include S(border-radius, $v1, $v2, $v3, $v4); -} - -@mixin BoxShadow($x, $y, $blur, $offset, $color) { - box-shadow: D($x) D($y) D($blur) D($offset) $color; -} - -@mixin DropShadow($yOffset: 2px, $blur: 2px, $amount: 0.2) { - @include BoxShadow(0, $yOffset, $blur, 0, rgba(#000, $amount)); -} - -@mixin TextShadow($yOffset: 2px, $blur: 1px, $amount: 0.6) { - text-shadow: 0 D($yOffset) D($blur) rgba(#000, $amount); -} - -@mixin Button3D($bgColor, $pressEffect: true) { - @include BoxShadow3D($bgColor, 2px, $pressEffect); -} - -@mixin ButtonDisabled3D($bgColor) { - @include BoxShadow3D($bgColor, 0.5px, false); -} - -@mixin BoxShadowInset($bgColor, $size: 3px) { - background-color: $bgColor; - - $borderSize: 1px; - $borderColor: rgb(15, 19, 24); - box-shadow: 0 0 0 D($borderSize) $borderColor, 0 D($size) 0 rgba(#fff, 0.07); - border-top: D($size) solid rgba(#000, 0.1); - - //, 0 D($size) 0 0px rgba(mix(darken($bgColor, 9), #b0e2ff, 95%), 1), - // 0 D($size + $borderSize) 0 0 $borderColor; -} - -@mixin TextShadow3D($color: rgb(222, 234, 238), $borderColor: #000) { - color: $color; -} - -// ---------------------------------------- -/* String replacement */ -@function str-replace($string, $search, $replace: "") { - $index: str-index($string, $search); - - @if $index { - @return str-slice($string, 1, $index - 1) + $replace + - str-replace(str-slice($string, $index + str-length($search)), $search, $replace); - } - - @return $string; -} - -@mixin BounceInFromSide($mul, $duration: 0.18s ease-in-out) { - @include InlineAnimation($duration) { - 0% { - transform: translateY(#{D(-100px * $mul)}) scale(0.9); - opacity: 0; - } - - 100% { - opacity: 1; - transform: none; - } - } - opacity: 1; - transform: none; -} - -@mixin BreakText { - word-wrap: break-word; - word-break: break-all; - overflow-wrap: break-all; -} - -@mixin SupportsAndroidNotchQuery { - @supports (color: constant(--notch-inset-left)) { - @content; - } -} -@mixin SupportsiOsNotchQuery { - @supports (color: env(safe-area-inset-left, 0px)) { - @content; - } -} - -@mixin DarkThemeOverride { - @at-root html[data-theme="dark"] &, - &[data-theme="dark"] { - @content; - } -} - -@mixin DarkThemeInvert { - @include DarkThemeOverride { - filter: invert(1); - } -} +// ---------------------------------------- +/* Duplicates an animation and adds two classes .Even and .Odd which uses the + animation. This can be used to replay the animation by toggling between the classes, because + it is not possible to restart a css animation */ +@mixin MakeAnimationWrappedEvenOdd($duration, $classPrefix: "anim", $childSelector: "") { + $animName: autogen_anim_#{unique-id()}; + + @at-root { + @keyframes #{$animName}_even { + @content; + } + + @keyframes #{$animName}_odd { + @content; + } + } + + &.#{$classPrefix}Even #{$childSelector} { + animation: #{$animName}_even $duration; + } + + &.#{$classPrefix}Odd #{$childSelector} { + animation: #{$animName}_odd $duration; + } +} + +// ---------------------------------------- +/* Allows to use and define an animation without specifying its name */ +@mixin InlineAnimation($duration) { + $animName: autogen_anim_#{unique-id()}; + + @at-root { + @keyframes #{$animName} { + @content; + } + } + + animation: $animName $duration !important; +} + +// ---------------------------------------- +/* Define a style which is only applied when the viewport has at maximum the given width */ +@mixin StyleBelowWidth($maxW) { + @media (max-width: #{$maxW}) { + @content; + } +} + +// ---------------------------------------- +// Dynamic graphics quality styles + +@mixin BoxShadow3D($bgColor, $size: 3px, $pressEffect: true) { + background-color: $bgColor; + + $borderSize: 1.5px; + $borderColor: rgb(18, 20, 24); +} + +@mixin DropShadow($yOffset: 0.2rem, $blur: 0.2rem, $amount: 0.2) { + box-shadow: 0 $yOffset $blur 0 rgba(#000, $amount); +} + +@mixin Button3D($bgColor, $pressEffect: true) { + @include BoxShadow3D($bgColor, 2px, $pressEffect); +} + +$legacyTextShadow3DColor: rgb(222, 234, 238); + +@mixin DarkThemeOverride { + @at-root html[data-theme="dark"] &, + &[data-theme="dark"] { + @content; + } +} + +@mixin DarkThemeInvert { + @include DarkThemeOverride { + filter: invert(1); + } +} diff --git a/src/css/resources.scss b/src/css/resources.scss index 2d1e46ae..de6ce65e 100644 --- a/src/css/resources.scss +++ b/src/css/resources.scss @@ -1,84 +1,80 @@ -$buildings: belt, cutter, miner, mixer, painter, rotater, balancer, stacker, trash, underground_belt, wire, - constant_signal, logic_gate, lever, filter, wire_tunnel, display, virtual_processor, reader, storage, - transistor, analyzer, comparator, item_producer, constant_producer, goal_acceptor, block; - -@each $building in $buildings { - [data-icon="building_icons/#{$building}.png"] { - /* @load-async */ - .icon { - background-image: uiResource("res/ui/building_icons/#{$building}.png") !important; - } - } -} - -$buildingsAndVariants: belt, balancer, underground_belt, underground_belt-tier2, miner, miner-chainable, - cutter, cutter-quad, rotater, rotater-ccw, stacker, mixer, painter-double, painter-quad, trash, storage, - reader, rotater-rotate180, display, constant_signal, wire, wire_tunnel, logic_gate-or, logic_gate-not, - logic_gate-xor, analyzer, virtual_processor-rotater, virtual_processor-unstacker, item_producer, - constant_producer, virtual_processor-stacker, virtual_processor-painter, wire-second, painter, - painter-mirrored, comparator, goal_acceptor, block; -@each $building in $buildingsAndVariants { - [data-icon="building_tutorials/#{$building}.png"] { - /* @load-async */ - background-image: uiResource("res/ui/building_tutorials/#{$building}.png") !important; - } -} - -[data-icon="building_tutorials/balancer-merger.png"], -[data-icon="building_tutorials/balancer-merger-inverse.png"] { - /* @load-async */ - background-image: uiResource("res/ui/building_tutorials/balancer-merger.png") !important; -} - -[data-icon="building_tutorials/balancer-splitter.png"], -[data-icon="building_tutorials/balancer-splitter-inverse.png"] { - /* @load-async */ - background-image: uiResource("res/ui/building_tutorials/balancer-splitter.png") !important; -} - -[data-icon="building_tutorials/transistor.png"], -[data-icon="building_tutorials/transistor-mirrored.png"] { - /* @load-async */ - background-image: uiResource("res/ui/building_tutorials/transistor.png") !important; -} - -// Filter and lever share tutorials -[data-icon="building_tutorials/filter.png"], -[data-icon="building_tutorials/lever.png"] { - /* @load-async */ - background-image: uiResource("res/ui/building_tutorials/lever.png") !important; -} - -// Logic gate -[data-icon="building_tutorials/logic_gate.png"] { - /* @load-async */ - background-image: uiResource("res/ui/building_tutorials/logic_gate-and.png") !important; -} - -// Virtual processor -[data-icon="building_tutorials/virtual_processor.png"] { - /* @load-async */ - background-image: uiResource("res/ui/building_tutorials/virtual_processor-cutter.png") !important; -} - -$icons: notification_saved, notification_success, notification_upgrade, notification_info, - notification_warning, notification_error; -@each $icon in $icons { - [data-icon="icons/#{$icon}.png"] { - /* @load-async */ - background-image: uiResource("res/ui/icons/#{$icon}.png") !important; - } -} - -$languages: en, de, cs, da, et, es-419, fr, it, pt-BR, sv, tr, el, ru, uk, zh-TW, zh-CN, nb, mt-MT, ar, nl, vi, - th, hu, pl, ja, kor, no, pt-PT, fi, ro, he; - -@each $language in $languages { - [data-languageicon="#{$language}"] { - background-image: uiResource("languages/#{$language}.svg") !important; - } -} - -.steam_dlbtn_0 { - background-image: uiResource("steam_link_btn/0.png") !important; -} +$buildings: belt, cutter, miner, mixer, painter, rotator, balancer, stacker, trash, underground_belt, wire, + constant_signal, logic_gate, lever, filter, wire_tunnel, display, virtual_processor, reader, storage, + transistor, analyzer, comparator, item_producer, constant_producer, goal_acceptor, block; + +@each $building in $buildings { + [data-icon="building_icons/#{$building}.png"] { + /* @load-async */ + .icon { + background-image: uiResource("res/ui/building_icons/#{$building}.png") !important; + } + } +} + +$buildingsAndVariants: belt, balancer, underground_belt, underground_belt-tier2, miner, miner-chainable, + cutter, cutter-quad, rotator, rotator-ccw, stacker, mixer, painter-double, painter-quad, trash, storage, + reader, rotator-rotate180, display, constant_signal, wire, wire_tunnel, logic_gate-or, logic_gate-not, + logic_gate-xor, analyzer, virtual_processor-rotator, virtual_processor-unstacker, item_producer, + constant_producer, virtual_processor-stacker, virtual_processor-painter, wire-second, painter, + painter-mirrored, comparator, goal_acceptor, block; +@each $building in $buildingsAndVariants { + [data-icon="building_tutorials/#{$building}.png"] { + /* @load-async */ + background-image: uiResource("res/ui/building_tutorials/#{$building}.png") !important; + } +} + +[data-icon="building_tutorials/balancer-merger.png"], +[data-icon="building_tutorials/balancer-merger-inverse.png"] { + /* @load-async */ + background-image: uiResource("res/ui/building_tutorials/balancer-merger.png") !important; +} + +[data-icon="building_tutorials/balancer-splitter.png"], +[data-icon="building_tutorials/balancer-splitter-inverse.png"] { + /* @load-async */ + background-image: uiResource("res/ui/building_tutorials/balancer-splitter.png") !important; +} + +[data-icon="building_tutorials/transistor.png"], +[data-icon="building_tutorials/transistor-mirrored.png"] { + /* @load-async */ + background-image: uiResource("res/ui/building_tutorials/transistor.png") !important; +} + +// Filter and lever share tutorials +[data-icon="building_tutorials/filter.png"], +[data-icon="building_tutorials/lever.png"] { + /* @load-async */ + background-image: uiResource("res/ui/building_tutorials/lever.png") !important; +} + +// Logic gate +[data-icon="building_tutorials/logic_gate.png"] { + /* @load-async */ + background-image: uiResource("res/ui/building_tutorials/logic_gate-and.png") !important; +} + +// Virtual processor +[data-icon="building_tutorials/virtual_processor.png"] { + /* @load-async */ + background-image: uiResource("res/ui/building_tutorials/virtual_processor-cutter.png") !important; +} + +$icons: notification_saved, notification_success, notification_upgrade, notification_info, + notification_warning, notification_error; +@each $icon in $icons { + [data-icon="icons/#{$icon}.png"] { + /* @load-async */ + background-image: uiResource("res/ui/icons/#{$icon}.png") !important; + } +} + +$languages: en, de, cs, da, et, es-419, fr, it, pt-BR, sv, tr, el, ru, uk, zh-TW, zh-CN, nb, mt-MT, ar, nl, vi, + th, hu, pl, ja, kor, no, pt-PT, fi, ro, he; + +@each $language in $languages { + [data-languageicon="#{$language}"] { + background-image: uiResource("languages/#{$language}.svg") !important; + } +} diff --git a/src/css/states/about.scss b/src/css/states/about.scss index 02025e03..5d1e2151 100644 --- a/src/css/states/about.scss +++ b/src/css/states/about.scss @@ -1,26 +1,26 @@ #state_AboutState { > .container .content { - @include S(max-width, 600px); + max-width: 60rem; @include PlainText; padding: 0; background: transparent; } .head { - @include S(padding, 20px); + padding: 2rem; img { display: block; margin: 0 auto; - @include S(max-width, 200px); + max-width: 20rem; } } .text { - @include S(margin, 10px); + margin: 1rem; } a { - @include S(margin, 0, 3px); + margin: 0 0.3rem; } } diff --git a/src/css/states/changelog.scss b/src/css/states/changelog.scss index 1daa5f04..2ecceadf 100644 --- a/src/css/states/changelog.scss +++ b/src/css/states/changelog.scss @@ -1,6 +1,6 @@ #state_ChangelogState { .content { - @include S(max-width, 800px); + max-width: 80rem; display: flex; flex-direction: column; } @@ -19,12 +19,12 @@ .changes { @include PlainText; - @include S(padding-left, 20px); + padding-left: 2rem; strong { color: #aaa; text-transform: uppercase; - @include S(padding, 1px, 2px); - @include S(margin-right, 3px); + padding: 0.1rem 0.2rem; + margin-right: 0.3rem; } } } diff --git a/src/css/states/ingame.scss b/src/css/states/ingame.scss index d67ee00f..381603a8 100644 --- a/src/css/states/ingame.scss +++ b/src/css/states/ingame.scss @@ -1,52 +1,52 @@ -#state_InGameState { - .gameLoadingOverlay { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - z-index: 9999; - align-items: center; - justify-content: center; - pointer-events: all; - display: flex; - background: $mainBgColor; - flex-direction: column; - } - - .prefab_GameHint { - position: absolute; - @include S(bottom, 40px); - @include S(left, 20px); - @include S(right, 20px); - @include PlainText; - text-align: center; - - color: #666; - - @include DarkThemeOverride() { - color: lighten($darkModeGameBackground, 50); - } - } - - #ingame_Canvas { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - } - #ingame_HUD_ModalDialogs { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - } - - @include DarkThemeOverride { - .gameLoadingOverlay { - background: $darkModeGameBackground; - } - } -} +#state_InGameState { + .gameLoadingOverlay { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 9999; + align-items: center; + justify-content: center; + pointer-events: all; + display: flex; + background: $mainBgColor; + flex-direction: column; + } + + .prefab_GameHint { + position: absolute; + bottom: 4rem; + left: 2rem; + right: 2rem; + @include PlainText; + text-align: center; + + color: #666; + + @include DarkThemeOverride() { + color: lighten($darkModeGameBackground, 50); + } + } + + #ingame_Canvas { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + } + #ingame_HUD_ModalDialogs { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + } + + @include DarkThemeOverride { + .gameLoadingOverlay { + background: $darkModeGameBackground; + } + } +} diff --git a/src/css/states/keybindings.scss b/src/css/states/keybindings.scss index 991d2af4..4111bf80 100644 --- a/src/css/states/keybindings.scss +++ b/src/css/states/keybindings.scss @@ -1,72 +1,71 @@ -#state_KeybindingsState { - .content { - .topEntries { - display: grid; - grid-template-columns: 1fr auto; - @include S(grid-gap, 5px); - @include S(margin-bottom, 10px); - } - - .hint { - display: block; - background: #eee; - @include S(padding, 6px, 10px); - @include PlainText; - @include S(border-radius, $globalBorderRadius); - } - - .category { - .entry { - display: grid; - @include S(margin-top, 2px); - @include S(padding-top, 2px); - @include S(grid-gap, 4px); - grid-template-columns: 1fr #{D(100px)} auto auto; - border-bottom: #{D(1px)} dotted #eee; - color: #888c8f; - .mapping { - color: $colorBlueBright; - text-align: center; - } - - button { - @include S(height, 15px); - @include S(width, 15px); - @include IncreasedClickArea(0px); - background: transparent center center / 40% no-repeat; - opacity: 0.9; - &.editKeybinding { - background-image: uiResource("icons/edit_key.png"); - } - - &.resetKeybinding { - background-image: uiResource("icons/reset_key.png"); - } - - &.disabled { - pointer-events: none; - cursor: default; - opacity: 0.1 !important; - } - } - } - } - } - - @include DarkThemeOverride { - .content { - .hint { - background: darken($darkModeControlsBackground, 4); - } - - .category .entry { - color: #c0c4c8; - border-bottom-color: #888; - - button { - filter: invert(1); - } - } - } - } -} +#state_KeybindingsState { + .content { + .topEntries { + display: grid; + grid-template-columns: 1fr auto; + grid-gap: 0.5rem; + margin-bottom: 1rem; + } + + .hint { + display: block; + background: #eee; + padding: 0.6rem 1rem; + @include PlainText; + border-radius: $globalBorderRadius; + } + + .category { + .entry { + display: grid; + margin-top: 0.2rem; + padding-top: 0.2rem; + grid-gap: 0.4rem; + grid-template-columns: 1fr 10rem auto auto; + border-bottom: 0.1rem dotted #eee; + color: #888c8f; + .mapping { + color: $colorBlueBright; + text-align: center; + } + + button { + height: 1.5rem; + width: 1.5rem; + background: transparent center center / 40% no-repeat; + opacity: 0.9; + &.editKeybinding { + background-image: uiResource("icons/edit_key.png"); + } + + &.resetKeybinding { + background-image: uiResource("icons/reset_key.png"); + } + + &.disabled { + pointer-events: none; + cursor: default; + opacity: 0.1 !important; + } + } + } + } + } + + @include DarkThemeOverride { + .content { + .hint { + background: darken($darkModeControlsBackground, 4); + } + + .category .entry { + color: #c0c4c8; + border-bottom-color: #888; + + button { + filter: invert(1); + } + } + } + } +} diff --git a/src/css/states/main_menu.scss b/src/css/states/main_menu.scss index 6d2f432e..e48549a4 100644 --- a/src/css/states/main_menu.scss +++ b/src/css/states/main_menu.scss @@ -1,1277 +1,717 @@ -#state_MainMenuState { - display: grid; - align-items: center; - justify-content: center; - grid-template-rows: D(95px) 1fr D(100px); - grid-template-columns: 1fr; - - // background: #aaacb4 center center / cover !important; - background: #bbc2cf center center / cover !important; - - .topButtons { - position: absolute; - @include S(top, 25px); - @include S(right, 25px); - display: flex; - flex-direction: row; - z-index: 20; - @include S(gap, 9px); - - .settingsButton, - .exitAppButton, - .languageChoose { - @include S(width, 35px); - @include S(height, 35px); - pointer-events: all; - border-radius: 50%; - box-shadow: 0 D(2px) D(3px) rgba(#000, 0.1); - background: rgba(#fff, 1) uiResource("icons/main_menu_settings.png") center center / 70% no-repeat; - - cursor: pointer; - transition: opacity 0.12s ease-in-out; - @include IncreasedClickArea(2px); - opacity: 0.85; - &:hover { - opacity: 1; - } - } - - .exitAppButton { - background-image: uiResource("icons/main_menu_exit.png"); - background-size: 56%; - } - - .languageChoose { - background: rgba(#fff, 1) center center / contain no-repeat; - border-radius: 50%; - background-color: #222428 !important; - background-size: contain !important; - background-position: center center !important; - opacity: 0.8; - transform: scale(0.98); - } - } - - &::after { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: uiResource("vignette-strong.lossless.png") center center / cover no-repeat; - pointer-events: none; - z-index: 2; - content: ""; - } - - .fullscreenBackgroundVideo { - // display: none !important; - z-index: 1; - position: fixed; - right: 50%; - bottom: 50%; - min-width: 100%; - min-height: 100%; - - opacity: 0; - display: none; - transform: translate(50%, 50%); - filter: blur(D(10px)); - - $opacity: 0.4; - &.loaded { - display: block; - opacity: $opacity; - - @include InlineAnimation(0.1s ease-in-out) { - 0% { - opacity: 0; - } - 100% { - opacity: $opacity; - } - } - } - } - - .mainWrapper { - @include S(margin-top, 15px); - align-items: start; - justify-items: center; - align-self: center; - justify-self: center; - @include S(grid-column-gap, 20px); - display: grid; - - position: relative; - z-index: 10; - grid-template-rows: 1fr; - - &[data-columns="1"] { - grid-template-columns: 1fr; - } - &[data-columns="2"] { - grid-template-columns: D(290px) 1fr; - } - } - - .logo { - display: flex; - flex-grow: 1; - align-items: center; - justify-content: center; - z-index: 20; - - flex-direction: column; - @include S(padding-top, 0px); - - img { - @include S(width, 710px / 3); - @include S(height, 180px / 3); - } - position: relative; - @include S(left, -8px); - - .updateLabel { - position: absolute; - transform: translateX(50%) rotate(-5deg); - color: #fff; - @include PlainText; - font-weight: bold; - @include S(right, 40px); - @include S(bottom, 20px); - background: $modsColor; - @include S(border-radius, $globalBorderRadius); - @include S(padding, 0, 5px, 1px, 5px); - - @include InlineAnimation(1.3s ease-in-out infinite) { - 50% { - transform: translateX(50%) rotate(-7deg) scale(1.1); - } - } - } - } - - .betaWarning { - @include S(width, 400px); - @include PlainText; - background: $colorRedBright; - @include S(padding, 10px); - @include S(border-radius, $globalBorderRadius); - color: #fff; - @include S(margin-top, 10px); - border: #{D(2px)} solid rgba(0, 10, 20, 0.1); - } - - .sideContainer { - display: flex; - width: 100%; - grid-column: 2 / 3; - flex-direction: column; - @include S(grid-gap, 20px); - - .mainNews { - background: linear-gradient(220deg, rgb(74, 93, 201), rgb(93, 255, 150)); - &::before { - background: uiResource("shapez2.png") center center / 100% no-repeat; - content: ""; - @include S(width, 100px); - @include S(height, 100px); - position: absolute; - top: 55%; - right: 8%; - pointer-events: none; - transform: translateY(-50%); - transition: transform 0.5s ease-in-out; - } - - // .close { - // position: absolute; - // pointer-events: all; - // background: uiResource("icons/main_menu_exit.png") center center / 50% no-repeat; - // display: inline-flex; - // @include S(width, 15px); - // @include S(height, 15px); - // @include S(top, 2px); - // opacity: 0.3; - // @include S(right, 2px); - // z-index: 200; - // transition: opacity 0.12s ease-in-out; - // &:hover { - // opacity: 0.7; - // } - // } - - &:hover::before { - transform: translate(0, -51%); - } - - box-shadow: 0 D(9px) D(15px) rgba(#000, 0.2); - width: 100%; - display: flex; - flex-direction: column; - @include S(height, 60px); - - pointer-events: all; - flex-grow: 1; - z-index: 100; - position: relative; - grid-row: 2 / 3; - @include S(border-radius, $globalBorderRadius); - justify-content: center; - @include S(padding, 10px); - @include S(padding-right, 100px); - box-sizing: border-box; - transition: opacity 0.12s ease-in-out; - cursor: pointer; - &:hover { - opacity: 0.85; - } - - .text { - @include SuperSmallText; - @include S(width, 100px); - color: rgba(#000, 1); - } - } - - .standaloneBanner { - background: transparent; - @include S(border-radius, $globalBorderRadius); - @include S(min-width, 320px); - @include S(max-width, 370px); - width: 100%; - box-sizing: border-box; - grid-row: 1 / 3; - - display: flex; - flex-direction: column; - margin: 0; - - strong { - font-weight: 700 !important; - } - - .onlinePlayerCount { - color: #333; - display: none; - @include S(margin-top, 15px); - @include SuperSmallText; - @include S(height, 15px); - text-align: center; - } - - h3 { - @include Heading; - font-weight: bold; - @include S(margin-bottom, 20px); - text-align: center; - color: #222; - text-shadow: 0 D(1px) D(5px) rgba(#fff, 0.7); - } - - p { - @include Text; - color: #333; - } - - ul { - @include S(margin-top, 5px); - @include S(padding-left, 20px); - li { - @include Text; - color: #fff; - } - } - - .playtimeDisclaimer { - color: #333; - @include S(margin-top, 15px); - @include SuperSmallText; - text-align: center; - } - - .steamLink { - align-self: center; - justify-self: center; - width: 100%; - @include S(height, 40px * 1.2); - @include S(width, 180px * 1.2); - background: #171a23 center center / contain no-repeat; - // overflow: hidden; - display: block; - cursor: pointer; - @include S(margin-top, 15px); - pointer-events: all; - transition: all 0.12s ease-in; - transition-property: opacity, transform; - position: relative; - @include S(border-radius, $globalBorderRadius); - color: transparent; - - box-shadow: 0 D(3px) D(7px) rgba(#000, 0.3); - &:hover { - opacity: 0.9; - } - - @include InlineAnimation(1s ease-in-out infinite) { - 50% { - transform: scale(1.02, 1.03); - } - } - - > .discount { - position: absolute; - @include S(top, -7px); - @include S(right, -5px); - background: #4c6b22; - color: #c5ea3f; - @include S(border-radius, $globalBorderRadius); - @include S(padding, 1px, 3px, 1px, 4px); - @include PlainText; - text-transform: uppercase; - transform: rotate(5deg); - } - } - .specialOffer { - color: #000000; - @include PlainText; - align-self: center; - text-align: center; - @include S(margin-top, 5px); - @include InlineAnimation(1s ease-in-out infinite) { - 50% { - transform: scale(1.02, 1.03) translateY(2%); - } - } - } - - .points { - display: grid; - grid-template-columns: 1fr 1fr; - width: 100%; - @include S(grid-gap, 5px); - } - - .point { - display: grid; - grid-template-columns: #{D(27px)} auto; - grid-template-rows: 1fr 1fr; - background: #fff #{D(10px)} center / #{D(17px)} no-repeat; - @include S(grid-row-gap, 2px); - align-items: center; - @include S(padding, 6px); - @include S(border-radius, $globalBorderRadius); - @include S(height, 26px); - box-shadow: 0 D(5px) D(10px) rgba(#000, 0.2); - - > strong { - grid-column: 2 / 3; - grid-row: 1 / 2; - @include PlainText; - @include S(font-size, 12px); - line-height: 0.8em; - white-space: nowrap; - text-transform: uppercase; - font-weight: bold; - align-self: end; - } - - > p { - grid-column: 2 / 3; - grid-row: 2 / 3; - @include SuperSmallText; - @include S(font-size, 8px); - line-height: 1em; - align-self: center; - - opacity: 0.8; - } - - &.levels { - background-image: uiResource("res/ui/icons/advantage_new_levels.png"); - > strong { - color: #f13555; - } - } - - &.upgrades { - background-image: uiResource("res/ui/icons/advantage_upgrades.png"); - > strong { - color: #8a00ff; - } - } - - &.buildings { - background-image: uiResource("res/ui/icons/advantage_buildings.png"); - > strong { - color: #3fce8b; - } - } - - &.wires { - background-image: uiResource("res/ui/icons/advantage_wires.png"); - > strong { - color: #ef2fdb; - } - } - - &.markers { - background-image: uiResource("res/ui/icons/advantage_markers.png"); - > strong { - color: #4294ff; - } - } - - &.mods { - background-image: uiResource("res/ui/icons/advantage_mods.png"); - > strong { - color: #8a00ff; - } - } - - &.savegames { - background-image: uiResource("res/ui/icons/advantage_savegames.png"); - > strong { - color: #ff9500; - } - } - - &.darkmode { - background-image: uiResource("res/ui/icons/advantage_dark_mode.png"); - > strong { - color: #292c32; - } - } - - &.support { - background-image: uiResource("res/ui/icons/advantage_support.png"); - > strong { - color: #e72d2d; - } - } - - &.achievements { - background-image: uiResource("res/ui/icons/advantage_achievements.png"); - > strong { - color: #ffac0f; - } - } - } - } - - .puzzleContainer { - display: flex; - align-items: center; - justify-content: center; - flex-direction: column; - background: $colorBlueBright; - grid-row: 1 / 2; - width: 100%; - box-sizing: border-box; - position: relative; - @include S(border-radius, $globalBorderRadius); - box-shadow: 0 D(5px) D(10px) rgba(#000, 0.4); - border: D(1px) solid rgba(#000, 0.1); - overflow: hidden; - - > .badge { - color: #fff; - text-transform: uppercase; - font-weight: bold; - position: absolute; - @include S(top, 10px); - @include S(right, 10px); - - transform: translateX(50%) rotate(10deg); - @include Heading; - font-weight: bold; - - @include InlineAnimation(1.3s ease-in-out infinite) { - 50% { - transform: translateX(50%) rotate(12deg) scale(1.1); - } - } - } - - > .hint { - @include SuperDuperSmallText; - @include S(margin-top, 10px); - @include S(width, 200px); - } - - > .dlcLogo { - @include S(width, 120px); - } - - > button { - @include S(margin-top, 20px); - @include Heading; - @include S(padding, 10px, 30px); - background-color: #333; - color: #fff; - } - - &.owned { - @include S(height, 118px); - @include S(width, 250px); - background: uiResource("puzzle_460x215_15.png") center D(-5px) / cover repeat; - .dlcLogo { - display: none; - } - - > button { - pointer-events: all; - @include S(padding, 4px, 10px); - margin: 0; - background: #47c599; - box-sizing: border-box; - position: absolute; - @include S(top, 10px); - @include S(right, 10px); - z-index: 100; - @include PlainText; - z-index: 200; - color: #111215; - box-shadow: 0 D(2px) D(9px) rgba(#000, 0.4); - } - } - - &.notOwned { - @include S(height, 200px); - @include S(width, 250px); - border: 0; - background: uiResource("puzzle_460x215_15.png") center D(-5px) / 100% repeat; - .dlcLogo { - display: none; - } - &::before { - content: ""; - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - background: linear-gradient(180deg, rgba(#222529, 0.1) 40%, rgba(#222529, 1) 70%); - } - - p { - position: absolute; - left: 0; - right: 0; - @include S(bottom, 35px); - z-index: 50; - box-sizing: border-box; - display: block; - @include PlainText; - color: rgba(#fff, 0.9); - @include S(padding, 10px); - @include SuperSmallText; - } - > button { - pointer-events: all; - @include S(padding, 4px, 10px); - margin: 0; - background: #47c599; - box-sizing: border-box; - position: absolute; - @include S(bottom, 10px); - @include S(right, 10px); - z-index: 100; - @include PlainText; - z-index: 200; - color: #111215; - box-shadow: 0 D(2px) D(4px) rgba(#000, 1); - } - } - } - - .modsOverview { - display: flex; - align-items: center; - justify-content: center; - flex-direction: column; - background: #fff; - grid-row: 1 / 2; - position: relative; - text-align: left; - align-items: flex-start; - @include S(width, 250px); - @include S(padding, 15px); - @include S(padding-bottom, 10px); - @include S(border-radius, $globalBorderRadius); - box-shadow: 0 D(5px) D(15px) rgba(#000, 0.2); - - .header { - display: flex; - width: 100%; - align-items: center; - text-transform: uppercase; - - @include S(margin-bottom, 10px); - - .editMods { - margin-left: auto; - @include S(width, 20px); - @include S(height, 20px); - padding: 0; - opacity: 0.5; - background: transparent center center/ 80% no-repeat; - & { - background-image: uiResource("icons/edit_key.png") !important; - } - @include DarkThemeInvert; - &:hover { - opacity: 0.6; - } - } - } - - h3 { - @include Heading; - color: $modsColor; - margin: 0; - } - - .dlcHint { - @include SuperSmallText; - @include S(margin-top, 10px); - width: 100%; - - display: grid; - grid-template-columns: 1fr auto; - grid-gap: 20px; - align-items: center; - } - - .mod { - background: #eee; - width: 100%; - @include S(border-radius, $globalBorderRadius); - @include S(padding, 5px); - box-sizing: border-box; - @include PlainText; - @include S(margin-bottom, 5px); - display: flex; - flex-direction: column; - - .author, - .version { - @include SuperSmallText; - align-self: end; - opacity: 0.4; - } - .name { - overflow: hidden; - } - } - - .modsList { - box-sizing: border-box; - @include S(height, 100px); - @include S(padding, 5px); - border: D(1px) solid #eee; - overflow-y: scroll; - width: 100%; - display: flex; - flex-direction: column; - @include S(border-radius, $globalBorderRadius); - pointer-events: all; - - :last-child { - margin-bottom: auto; - } - } - } - } - - .mainContainer { - display: flex; - align-items: center; - justify-content: center; - flex-direction: column; - background: rgba(#fff, 0.9); - @include S(border-radius, $globalBorderRadius); - height: 100%; - box-shadow: 0 D(9px) D(15px) rgba(#000, 0.2); - width: 100%; - position: relative; - align-self: center; - justify-self: center; - grid-column: 1 / 2; - @include S(max-width, 400px); - overflow: hidden; - box-sizing: border-box; - - &[data-savegames="0"] .buttons .outer { - grid-template-rows: 1fr 1fr; - } - - .buttons { - display: grid; - grid-template-columns: auto auto; - grid-template-rows: 1fr; - // flex-direction: column; - // align-items: center; - width: 100%; - background: rgba(0, 20, 40, 0.05); - - @include S(padding, 10px); - @include S(grid-gap, 10px); - max-width: 100%; - box-sizing: border-box; - - .playButton, - .continueButton { - @include SuperHeading; - width: 100%; - @include S(padding, 15px, 10px); - letter-spacing: 0.1em !important; - @include IncreasedClickArea(0px); - box-sizing: border-box; - font-weight: bold; - color: #fff; - background-color: $colorGreenBright; - transition: transform 0.12s ease-in-out, background-color 0.12s ease-in-out; - - grid-column: 1 / 2; - min-width: auto; - grid-row: 1 / 1; - - &:hover { - background-color: darken($colorGreenBright, 4); - opacity: 1; - } - - &.continueButton { - @include Heading; - } - } - - .outer { - display: grid; - grid-template-columns: 1fr; - grid-template-rows: 1fr 1fr 1fr; - height: 100%; - - grid-column: 2 / 3; - grid-row: 1 / 1; - min-width: auto; - - @include S(grid-gap, 5px); - width: 100%; - - > button { - @include S(padding, 3px, 6px); - width: 100%; - white-space: nowrap; - margin: 0 !important; - box-sizing: border-box; - } - - .importButton { - @include IncreasedClickArea(0px); - } - - .newGameButton { - @include IncreasedClickArea(0px); - } - - .modsButton { - @include IncreasedClickArea(0px); - background-color: $modsColor !important; - } - } - } - - .modeButtons { - display: grid; - grid-template-columns: repeat(2, 1fr); - @include S(grid-column-gap, 10px); - align-items: start; - height: 100%; - width: 100%; - } - - .steamSso { - cursor: default; - pointer-events: all; - display: inline-flex; - @include S(padding, 10px); - color: #000; - flex-direction: column; - line-height: 1em; - @include S(gap, 3px); - position: relative; - background: rgba(0, 20, 40, 0.05); - width: 100%; - box-sizing: border-box; - margin-top: auto; - display: grid; - grid-template-columns: 1fr auto; - align-items: center; - - .description { - @include SuperSmallText; - color: rgba(0, 10, 20, 0.5); - @include DarkThemeOverride { - color: rgba(#fff, 0.7); - } - } - // &:hover { - // .tooltip { - // opacity: 1; - // } - // } - - a.ssoSignIn { - background: #171a23 uiResource("steam_signin.png") center center / contain no-repeat; - width: 100%; - box-sizing: border-box; - - @include S(height, 19px); - @include S(width, 110px); - - display: inline-flex; - @include S(border-radius, $globalBorderRadius * 0.5); - transition: opacity 0.12s ease-in-out; - overflow: hidden; - text-indent: -999em; - &:hover { - opacity: 0.9; - } - - box-shadow: 0 D(1.5px) D(4px) rgba(#000, 0.21); - } - - a.ssoSignOut { - width: 100%; - background: $colorRedBright; - color: #fff !important; - display: flex; - align-items: center; - box-sizing: border-box; - justify-content: center; - @include SuperSmallText; - text-transform: uppercase; - - @include S(border-radius, $globalBorderRadius * 0.5); - - box-shadow: 0 D(1.5px) D(4px) rgba(#000, 0.21); - @include S(padding, 2px, 9px); - &:hover { - opacity: 0.95; - } - } - } - - .savegamesMount { - width: 100%; - display: flex; - flex-grow: 1; - flex-direction: column; - @include S(padding, 10px); - box-sizing: border-box; - @include S(min-height, 150px); - - .savegamesNone { - align-items: center; - justify-content: center; - display: flex; - @include PlainText; - flex-grow: 1; - text-align: center; - @include S(padding, 0, 20px); - opacity: 0.5; - - @include DarkThemeOverride { - color: #fff; - } - } - - .savegames { - overflow-y: auto; - width: 100%; - pointer-events: all; - @include S(padding-right, 5px); - margin-right: D(-5px); - @include S(max-height, 150px); - display: grid; - grid-auto-flow: row; - @include S(grid-gap, 5px); - - .savegame { - background: #eee; - @include S(border-radius, $globalBorderRadius); - @include S(padding, 5px); - display: grid; - grid-template-columns: 1fr 1fr auto auto; - grid-template-rows: auto auto; - @include S(grid-column-gap, 4px); - @include S(grid-row-gap, 1px); - - .playtime { - grid-column: 2 / 3; - grid-row: 2 / 3; - @include SuperSmallText; - opacity: 0.5; - } - - .level { - grid-column: 1 / 2; - grid-row: 2 / 3; - @include SuperSmallText; - opacity: 0.5; - } - - .name { - grid-column: 1 / 3; - grid-row: 1 / 2; - @include PlainText; - display: inline-flex; - align-items: center; - - > span { - display: inline-flex; - @include S(max-width, 140px); - overflow: hidden; - } - } - - button.resumeGame, - button.downloadGame, - button.deleteGame, - button.renameGame { - padding: 0; - align-self: center; - justify-self: center; - @include IncreasedClickArea(0px); - background: #44484a center center / 40% no-repeat; - } - - button.resumeGame { - background-color: #44484a; - & { - background-image: uiResource("icons/play.png"); - } - } - - button.downloadGame { - grid-column: 3 / 4; - grid-row: 1 / 2; - background-color: transparent; - - & { - background-image: uiResource("icons/download.png"); - } - @include S(width, 15px); - @include IncreasedClickArea(0px); - @include S(height, 15px); - background-size: 80%; - align-self: start; - border-radius: 0; - opacity: 0.4; - - &:hover { - opacity: 0.5; - } - - @include DarkThemeInvert; - } - - button.deleteGame { - grid-column: 3 / 4; - grid-row: 2 / 3; - background-color: transparent; - @include IncreasedClickArea(0px); - - & { - background-image: uiResource("icons/delete.png"); - } - @include S(width, 15px); - @include S(height, 15px); - align-self: end; - background-size: 80%; - border-radius: 0; - opacity: 0.4; - - &:hover { - opacity: 0.5; - } - - @include DarkThemeInvert; - } - - button.renameGame { - background-color: transparent; - @include IncreasedClickArea(2px); - - & { - background-image: uiResource("icons/edit_key.png"); - } - @include S(width, 10px); - @include S(height, 10px); - align-self: center; - justify-self: center; - border-radius: 0; - background-size: 90%; - opacity: 0.4; - @include S(margin-left, 4px); - - &:hover { - opacity: 0.5; - } - - @include DarkThemeInvert; - } - - button.resumeGame { - grid-column: 4 / 5; - grid-row: 1 / 3; - margin: 0; - @include S(width, 32px); - height: 100%; - @include S(margin-left, 4px); - - @include DarkThemeOverride { - background-color: lighten($darkModeControlsBackground, 10); - } - } - } - } - } - } - - .bottomContainer { - display: flex; - align-items: center; - justify-content: center; - flex-direction: row; - @include S(padding-top, 10px); - height: 100%; - width: 100%; - box-sizing: border-box; - - .buttons { - display: grid; - grid-template-columns: repeat(2, 1fr); - @include S(grid-column-gap, 10px); - align-items: start; - height: 100%; - width: 100%; - box-sizing: border-box; - } - } - - .socialLinks { - position: fixed; - z-index: 100; - display: flex; - flex-direction: row; - @include S(gap, 9px); - @include S(top, 25px); - @include S(left, 25px); - - @media (max-aspect-ratio: 1460/1000) { - position: unset; - top: unset; - left: unset; - .label { - display: none; - } - } - - > .boxLink { - pointer-events: all; - display: flex; - flex-direction: column; - align-items: center; - position: relative; - cursor: pointer; - @include S(gap, 3px); - @include S(width, 35px); - - .label { - @include SuperSmallText; - font-weight: bold; - box-sizing: border-box; - text-transform: uppercase; - opacity: 0; - transition: opacity 0.12s ease-in-out; - } - - &:hover { - .label { - opacity: 1; - .thirdpartyLogo { - background-color: #fff; - } - } - } - - .thirdpartyLogo { - display: inline-flex; - @include S(width, 35px); - @include S(height, 35px); - background: rgba(#fff, 0.9) center center / contain no-repeat; - border-radius: 50%; - box-shadow: 0 D(2px) D(3px) rgba(#000, 0.1); - - transition: background-color 0.12s ease-in-out; - - &.githubLogo { - background-image: uiResource("main_menu/github.png"); - background-size: 66%; - background-position: 54% 50%; - } - - &.discordLogo { - background-image: uiResource("main_menu/discord.svg"); - background-size: 66%; - background-position: 50% 53%; - } - - &.redditLogo { - background-image: uiResource("main_menu/reddit.svg"); - background-size: 65%; - } - &.twitterLogo { - background-image: uiResource("main_menu/twitter.svg"); - background-size: 60%; - background-position: 60% 58%; - } - - &.patreonLogo { - background-image: uiResource("main_menu/patreon.svg"); - background-size: 60%; - background-position: 60% 60%; - } - - &.steamLogo { - background-image: uiResource("main_menu/steam.svg"); - background-size: 105%; - } - } - } - } - - .footer { - display: flex; - justify-content: center; - align-self: end; - align-items: center; - position: relative; - z-index: 20; - width: 100%; - box-sizing: border-box; - @include S(gap, 30px); - @include S(padding, 15px, 25px, 15px, 20px); - - &.wegameDisclaimer { - @include SuperSmallText; - display: grid; - justify-content: center; - text-align: center; - - > .disclaimer { - grid-column: 2 / 3; - - @include DarkThemeOverride { - color: #fff; - } - } - - > .rating { - grid-column: 3 / 4; - justify-self: end; - align-self: end; - - @include S(width, 32px); - @include S(height, 40px); - background: green; - cursor: pointer !important; - pointer-events: all; - @include S(border-radius, 4px); - overflow: hidden; - - & { - background: #fff uiResource("wegame_isbn_rating.jpg") center center / contain no-repeat; - } - } - } - - .author { - margin-left: auto; - display: flex; - @include S(width, 110px); - justify-content: flex-end; - - a { - &:hover img { - opacity: 0.85; - } - position: relative; - display: flex; - align-items: center; - justify-content: center; - - img { - transition: opacity 0.12s ease-in-out; - @include S(width, 82px * 0.8); - @include S(height, 25px * 0.8); - filter: invert(100%); - opacity: 0.75; - } - } - } - - .footerGrow { - flex-grow: 1; - display: flex; - align-items: center; - justify-content: flex-start; - @include S(gap, 15px); - > a { - @include SuperSmallText; - @include S(padding, 2px); - font-weight: bold; - color: #000; - opacity: 0.6; - transition: opacity 0.12s ease-in-out; - &:hover { - opacity: 1; - } - } - } - } - - @include DarkThemeOverride { - background: rgba($darkModeGameBackground, 0.5) center center / cover !important; - - .mainContainer { - background: $darkModeControlsBackground; - - .savegames .savegame { - background: darken($darkModeControlsBackground, 5); - color: white; - } - } - - .modsOverview { - background: $darkModeControlsBackground; - - .modsList { - border-color: darken($darkModeControlsBackground, 5); - - .mod { - background: darken($darkModeControlsBackground, 5); - color: white; - } - } - - .dlcHint { - color: $accentColorBright; - } - } - } -} +#state_MainMenuState { + display: grid; + align-items: center; + justify-content: center; + grid-template-rows: 9.5rem 1fr 10rem; + grid-template-columns: 1fr; + + // background: #aaacb4 center center / cover !important; + background: #bbc2cf center center / cover !important; + + .topButtons { + position: absolute; + top: 2.5rem; + right: 2.5rem; + display: flex; + flex-direction: row; + z-index: 20; + gap: 0.9rem; + + .settingsButton, + .exitAppButton, + .languageChoose { + width: 3.5rem; + height: 3.5rem; + pointer-events: all; + border-radius: 50%; + box-shadow: 0 0.2rem 0.3rem rgba(#000, 0.1); + background: rgba(#fff, 1) uiResource("icons/main_menu_settings.png") center center / 70% no-repeat; + + cursor: pointer; + transition: opacity 0.12s ease-in-out; + opacity: 0.85; + &:hover { + opacity: 1; + } + } + + .exitAppButton { + background-image: uiResource("icons/main_menu_exit.png"); + background-size: 56%; + } + + .languageChoose { + background: rgba(#fff, 1) center center / contain no-repeat; + border-radius: 50%; + background-color: #222428 !important; + background-size: contain !important; + background-position: center center !important; + opacity: 0.8; + transform: scale(0.98); + } + } + + &::after { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: uiResource("vignette-strong.lossless.png") center center / cover no-repeat; + pointer-events: none; + z-index: 2; + content: ""; + } + + .fullscreenBackgroundVideo { + // display: none !important; + z-index: 1; + position: fixed; + right: 50%; + bottom: 50%; + min-width: 100%; + min-height: 100%; + + opacity: 0; + display: none; + transform: translate(50%, 50%); + filter: blur(1rem); + + $opacity: 0.4; + &.loaded { + display: block; + opacity: $opacity; + + @include InlineAnimation(0.1s ease-in-out) { + 0% { + opacity: 0; + } + 100% { + opacity: $opacity; + } + } + } + } + + .mainWrapper { + margin-top: 1.5rem; + align-items: start; + justify-items: center; + align-self: center; + justify-self: center; + grid-column-gap: 2rem; + display: grid; + + position: relative; + z-index: 10; + grid-template-rows: 1fr; + + &[data-columns="1"] { + grid-template-columns: 1fr; + } + &[data-columns="2"] { + grid-template-columns: 29rem 1fr; + } + } + + .logo { + display: flex; + flex-grow: 1; + align-items: center; + justify-content: center; + z-index: 20; + + flex-direction: column; + padding-top: 0rem; + + img { + width: 710px / 3; + height: 180px / 3; + } + position: relative; + left: -0.8rem; + } + + .sideContainer { + display: flex; + width: 100%; + grid-column: 2 / 3; + flex-direction: column; + grid-gap: 2rem; + + .puzzleContainer { + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + background: $colorBlueBright; + grid-row: 1 / 2; + width: 100%; + box-sizing: border-box; + position: relative; + border-radius: $globalBorderRadius; + box-shadow: 0 0.5rem 1rem rgba(#000, 0.4); + border: 0.1rem solid rgba(#000, 0.1); + overflow: hidden; + + > button { + margin-top: 2rem; + @include Heading; + padding: 1rem 3rem; + background-color: #333; + color: #fff; + } + + &.owned { + height: 11.8rem; + width: 25rem; + background: uiResource("puzzle_460x215_15.png") center -0.5rem / cover repeat; + + > button { + pointer-events: all; + padding: 0.4rem 1rem; + margin: 0; + background: #47c599; + box-sizing: border-box; + position: absolute; + top: 1rem; + right: 1rem; + z-index: 100; + @include PlainText; + z-index: 200; + color: #111215; + box-shadow: 0 0.2rem 0.9rem rgba(#000, 0.4); + } + } + } + + .modsOverview { + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + background: #fff; + grid-row: 1 / 2; + position: relative; + text-align: left; + align-items: flex-start; + width: 25rem; + padding: 1.5rem; + padding-bottom: 1rem; + border-radius: $globalBorderRadius; + box-shadow: 0 0.5rem 1.5rem rgba(#000, 0.2); + + .header { + display: flex; + width: 100%; + align-items: center; + text-transform: uppercase; + + margin-bottom: 1rem; + + .editMods { + margin-left: auto; + width: 2rem; + height: 2rem; + padding: 0; + opacity: 0.5; + background: transparent center center/ 80% no-repeat; + & { + background-image: uiResource("icons/edit_key.png") !important; + } + @include DarkThemeInvert; + &:hover { + opacity: 0.6; + } + } + } + + h3 { + @include Heading; + color: $modsColor; + margin: 0; + } + + .dlcHint { + @include SuperSmallText; + margin-top: 1rem; + width: 100%; + + display: grid; + grid-template-columns: 1fr auto; + grid-gap: 20px; + align-items: center; + } + + .mod { + background: #eee; + width: 100%; + border-radius: $globalBorderRadius; + padding: 0.5rem; + box-sizing: border-box; + @include PlainText; + margin-bottom: 0.5rem; + display: flex; + flex-direction: column; + + .author, + .version { + @include SuperSmallText; + align-self: end; + opacity: 0.4; + } + .name { + overflow: hidden; + } + } + + .modsList { + box-sizing: border-box; + height: 10rem; + padding: 0.5rem; + border: 0.1rem solid #eee; + overflow-y: scroll; + width: 100%; + display: flex; + flex-direction: column; + border-radius: $globalBorderRadius; + pointer-events: all; + + :last-child { + margin-bottom: auto; + } + } + } + } + + .mainContainer { + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + background: rgba(#fff, 0.9); + border-radius: $globalBorderRadius; + height: 100%; + box-shadow: 0 0.9rem 1.5rem rgba(#000, 0.2); + width: 100%; + position: relative; + align-self: center; + justify-self: center; + grid-column: 1 / 2; + max-width: 40rem; + overflow: hidden; + box-sizing: border-box; + + &[data-savegames="0"] .buttons .outer { + grid-template-rows: 1fr 1fr; + } + + .buttons { + display: grid; + grid-template-columns: auto auto; + grid-template-rows: 1fr; + // flex-direction: column; + // align-items: center; + width: 100%; + background: rgba(0, 20, 40, 0.05); + + padding: 1rem; + grid-gap: 1rem; + max-width: 100%; + box-sizing: border-box; + + .playButton, + .continueButton { + @include SuperHeading; + width: 100%; + padding: 1.5rem 1rem; + letter-spacing: 0.1em; + box-sizing: border-box; + font-weight: bold; + background-color: $colorGreenBright; + transition: + transform 0.12s ease-in-out, + background-color 0.12s ease-in-out; + + grid-column: 1 / 2; + min-width: auto; + grid-row: 1 / 1; + + &:hover { + background-color: darken($colorGreenBright, 4); + opacity: 1; + } + + &.continueButton { + @include Heading; + } + } + + .outer { + display: grid; + grid-template-columns: 1fr; + grid-template-rows: 1fr 1fr 1fr; + height: 100%; + + grid-column: 2 / 3; + grid-row: 1 / 1; + min-width: auto; + + grid-gap: 0.5rem; + width: 100%; + + > button { + padding: 0.3rem 0.6rem; + width: 100%; + white-space: nowrap; + margin: 0 !important; + box-sizing: border-box; + } + + .modsButton { + background-color: $modsColor !important; + } + } + } + + .savegamesMount { + width: 100%; + display: flex; + flex-grow: 1; + flex-direction: column; + padding: 1rem; + box-sizing: border-box; + min-height: 15rem; + + .savegamesNone { + align-items: center; + justify-content: center; + display: flex; + @include PlainText; + flex-grow: 1; + text-align: center; + padding: 0 2rem; + opacity: 0.5; + + @include DarkThemeOverride { + color: #fff; + } + } + + .savegames { + overflow-y: auto; + width: 100%; + pointer-events: all; + padding-right: 0.5rem; + margin-right: -0.5rem; + max-height: 15rem; + display: grid; + grid-auto-flow: row; + grid-gap: 0.5rem; + + .savegame { + background: #eee; + border-radius: $globalBorderRadius; + padding: 0.5rem; + display: grid; + grid-template-columns: 1fr 1fr auto auto; + grid-template-rows: auto auto; + grid-column-gap: 0.4rem; + grid-row-gap: 0.1rem; + + .playtime { + grid-column: 2 / 3; + grid-row: 2 / 3; + @include SuperSmallText; + opacity: 0.5; + } + + .level { + grid-column: 1 / 2; + grid-row: 2 / 3; + @include SuperSmallText; + opacity: 0.5; + } + + .name { + grid-column: 1 / 3; + grid-row: 1 / 2; + @include PlainText; + display: inline-flex; + align-items: center; + + > span { + display: inline-flex; + max-width: 14rem; + overflow: hidden; + } + } + + button.resumeGame, + button.downloadGame, + button.deleteGame, + button.renameGame { + padding: 0; + align-self: center; + justify-self: center; + background: #44484a center center / 40% no-repeat; + } + + button.resumeGame { + background-color: #44484a; + & { + background-image: uiResource("icons/play.png"); + } + } + + button.downloadGame { + grid-column: 3 / 4; + grid-row: 1 / 2; + background-color: transparent; + + & { + background-image: uiResource("icons/download.png"); + } + width: 1.5rem; + height: 1.5rem; + background-size: 80%; + align-self: start; + border-radius: 0; + opacity: 0.4; + + &:hover { + opacity: 0.5; + } + + @include DarkThemeInvert; + } + + button.deleteGame { + grid-column: 3 / 4; + grid-row: 2 / 3; + background-color: transparent; + + & { + background-image: uiResource("icons/delete.png"); + } + width: 1.5rem; + height: 1.5rem; + align-self: end; + background-size: 80%; + border-radius: 0; + opacity: 0.4; + + &:hover { + opacity: 0.5; + } + + @include DarkThemeInvert; + } + + button.renameGame { + background-color: transparent; + + & { + background-image: uiResource("icons/edit_key.png"); + } + width: 1rem; + height: 1rem; + align-self: center; + justify-self: center; + border-radius: 0; + background-size: 90%; + opacity: 0.4; + margin-left: 0.4rem; + + &:hover { + opacity: 0.5; + } + + @include DarkThemeInvert; + } + + button.resumeGame { + grid-column: 4 / 5; + grid-row: 1 / 3; + margin: 0; + width: 3.2rem; + height: 100%; + margin-left: 0.4rem; + + @include DarkThemeOverride { + background-color: lighten($darkModeControlsBackground, 10); + } + } + } + } + } + } + + .socialLinks { + position: fixed; + z-index: 100; + display: flex; + flex-direction: row; + gap: 0.9rem; + top: 2.5rem; + left: 2.5rem; + + @media (max-aspect-ratio: 1460/1000) { + position: unset; + top: unset; + left: unset; + .label { + display: none; + } + } + + > .boxLink { + pointer-events: all; + display: flex; + flex-direction: column; + align-items: center; + position: relative; + cursor: pointer; + gap: 0.3rem; + width: 3.5rem; + + .label { + @include SuperSmallText; + font-weight: bold; + box-sizing: border-box; + text-transform: uppercase; + opacity: 0; + transition: opacity 0.12s ease-in-out; + } + + &:hover { + .label { + opacity: 1; + .thirdpartyLogo { + background-color: #fff; + } + } + } + + .thirdpartyLogo { + display: inline-flex; + width: 3.5rem; + height: 3.5rem; + background: rgba(#fff, 0.9) center center / contain no-repeat; + border-radius: 50%; + box-shadow: 0 0.2rem 0.3rem rgba(#000, 0.1); + + transition: background-color 0.12s ease-in-out; + + &.githubLogo { + background-image: uiResource("main_menu/github.png"); + background-size: 66%; + background-position: 54% 50%; + } + + &.discordLogo { + background-image: uiResource("main_menu/discord.svg"); + background-size: 66%; + background-position: 50% 53%; + } + + &.redditLogo { + background-image: uiResource("main_menu/reddit.svg"); + background-size: 65%; + } + + &.patreonLogo { + background-image: uiResource("main_menu/patreon.svg"); + background-size: 60%; + background-position: 60% 60%; + } + } + } + } + + .footer { + display: flex; + justify-content: center; + align-self: end; + align-items: center; + position: relative; + z-index: 20; + width: 100%; + box-sizing: border-box; + gap: 3rem; + padding: 1.5rem 2.5rem 1.5rem 2rem; + + .author { + margin-left: auto; + display: flex; + width: 11rem; + justify-content: flex-end; + + a { + &:hover img { + opacity: 0.85; + } + position: relative; + display: flex; + align-items: center; + justify-content: center; + + img { + transition: opacity 0.12s ease-in-out; + width: 6rem; + filter: invert(100%); + opacity: 0.75; + } + } + } + + .footerGrow { + flex-grow: 1; + display: flex; + align-items: center; + justify-content: flex-start; + gap: 1.5rem; + > a { + @include SuperSmallText; + padding: 0.2rem; + font-weight: bold; + color: #000; + opacity: 0.6; + transition: opacity 0.12s ease-in-out; + &:hover { + opacity: 1; + } + } + } + } + + @include DarkThemeOverride { + background: rgba($darkModeGameBackground, 0.5) center center / cover !important; + + .mainContainer { + background: $darkModeControlsBackground; + + .savegames .savegame { + background: darken($darkModeControlsBackground, 5); + color: white; + } + } + + .modsOverview { + background: $darkModeControlsBackground; + + .modsList { + border-color: darken($darkModeControlsBackground, 5); + + .mod { + background: darken($darkModeControlsBackground, 5); + color: white; + } + } + + .dlcHint { + color: $accentColorBright; + } + } + } +} diff --git a/src/css/states/mobile_warning.scss b/src/css/states/mobile_warning.scss deleted file mode 100644 index 947d530c..00000000 --- a/src/css/states/mobile_warning.scss +++ /dev/null @@ -1,51 +0,0 @@ -#state_MobileWarningState { - display: flex; - align-items: center; - background: #555b75 !important; - @include S(padding, 20px); - box-sizing: border-box; - justify-content: center; - flex-direction: column; - - .logo { - width: 80%; - max-width: 200px; - margin-bottom: 10px; - } - - p { - color: rgba(#fff, 0.5); - display: block; - margin-bottom: 13px; - font-size: 16px; - line-height: 20px; - max-width: 300px; - text-align: left; - a { - color: $colorBlueBright; - } - } - - .standaloneLink { - width: 200px; - height: 48px; - min-height: 40px; - & { - background: #000 uiResource("steam_link_btn/0.png") center center / contain no-repeat; - } - display: block; - text-indent: -999em; - cursor: pointer; - margin-top: 10px; - pointer-events: all; - transition: all 0.12s ease-in; - transition-property: opacity, transform; - @include S(border-radius, $globalBorderRadius); - overflow: hidden; - - &:hover { - transform: skewX(-1deg) scale(1.02); - opacity: 0.9; - } - } -} diff --git a/src/css/states/mods.scss b/src/css/states/mods.scss index 60912510..3935e390 100644 --- a/src/css/states/mods.scss +++ b/src/css/states/mods.scss @@ -1,144 +1,41 @@ #state_ModsState { - .mainContent { - display: flex; - flex-direction: column; - } - - > .headerBar { + // TODO: Update dimensions to use less build-time logic + .modsGrid { display: grid; - grid-template-columns: 1fr auto; - align-items: center; + grid-template-columns: repeat(2, 1fr); + gap: 1rem; + } - > h1 { - justify-self: start; - } + .modsGrid.noMods { + grid-template-columns: unset; + place-items: center; + } - .openModsFolder { - background-color: $modsColor; + .mod { + width: 100%; + box-sizing: border-box; + padding: 1rem; + + border-radius: 0.4rem; + background: #eee; + + @include DarkThemeOverride { + background: lighten($darkModeControlsBackground, 5); } } - .noModSupport { - display: flex; - align-items: center; - justify-content: center; - height: 100%; - flex-direction: column; - text-align: center; - max-width: 80%; - align-self: center; - - .steamLink { - @include S(height, 50px); - @include S(width, 220px); - background: #171a23 center center / contain no-repeat; - overflow: hidden; - display: block; - text-indent: -999em; - cursor: pointer; - @include S(margin-top, 30px); - pointer-events: all; - transition: all 0.12s ease-in; - transition-property: opacity, transform; - - box-shadow: 0 D(3px) D(10px) rgba(96, 163, 136, 0.5); - @include S(border-radius, $globalBorderRadius); - - &:hover { - opacity: 0.9; - } - } + .mod > .title { + text-wrap: nowrap; + text-overflow: ellipsis; + overflow: hidden; } - .modsStats { - @include PlainText; + .mod > .description { + @include PlainText(); + } + + .mod > .advanced { + @include PlainText(); color: $accentColorDark; - - &.noMods { - @include S(width, 400px); - align-self: center; - justify-self: center; - text-align: center; - display: flex; - flex-direction: column; - align-items: center; - @include Text; - @include S(margin-top, 100px); - color: lighten($accentColorDark, 15); - - button { - @include S(margin-top, 10px); - @include S(padding, 10px, 20px); - } - - &::before { - @include S(margin-bottom, 15px); - content: ""; - @include S(width, 50px); - @include S(height, 50px); - background-position: center center; - background-size: contain; - opacity: 0.2; - } - &::before { - background-image: uiResource("res/ui/icons/mods.png") !important; - } - } - } - - .modsList { - @include S(margin-top, 10px); - overflow-y: scroll; - pointer-events: all; - @include S(padding-right, 5px); - flex-grow: 1; - - .mod { - @include S(border-radius, $globalBorderRadius); - background: #eeeff4; - @include S(margin-bottom, 4px); - @include S(padding, 7px, 10px); - @include S(grid-gap, 15px); - display: grid; - grid-template-columns: 1fr D(100px) D(80px) D(50px); - - @include DarkThemeOverride { - background: darken($darkModeControlsBackground, 5); - } - - .checkbox { - align-self: center; - justify-self: center; - } - - .mainInfo { - display: flex; - flex-direction: column; - - .description { - @include PlainText; - @include S(margin-top, 5px); - color: $accentColorDark; - } - .website { - text-transform: uppercase; - align-self: start; - @include PlainText; - @include S(margin-top, 5px); - } - } - - .version, - .author { - display: flex; - flex-direction: column; - align-self: center; - strong { - text-transform: uppercase; - color: $accentColorDark; - @include SuperSmallText; - } - } - } } } diff --git a/src/css/states/preload.scss b/src/css/states/preload.scss index dee33c06..caae6be7 100644 --- a/src/css/states/preload.scss +++ b/src/css/states/preload.scss @@ -1,153 +1,152 @@ -#state_PreloadState { - .changelogDialogEntry { - @include S(margin-top, 10px); - - width: 100%; - flex-direction: column; - text-align: left; - padding: 10px; - box-sizing: border-box; - background: #eef1f4; - @include S(border-radius, $globalBorderRadius); - - @include DarkThemeOverride { - background: #33343c; - } - - .version { - @include Heading; - } - .date { - @include PlainText; - &::before { - content: " | "; - } - color: #aaabaf; - } - - .changes { - @include PlainText; - @include S(padding-left, 15px); - @include S(margin-top, 10px); - strong { - background: $colorBlueBright; - color: #fff; - text-transform: uppercase; - @include S(padding, 1px, 2px); - @include S(margin-right, 3px); - } - a { - color: $colorBlueBright; - } - li { - @include SuperSmallText; - @include S(margin-bottom, 5px); - } - } - } - - .failureBox { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - z-index: 999999; - background: #d5d8de; - display: flex; - align-items: center; - justify-content: center; - flex-direction: column; - - .logo { - img { - @include S(width, 240px); - } - - @include S(margin-bottom, 30px); - } - - @include InlineAnimation(0.3s ease-in-out) { - 0% { - opacity: 0; - } - 100% { - opacity: 1; - } - } - - .failureInner { - // background: darken($mainBgColor, 6); - @include S(max-width, 350px); - margin: 0 20px; - text-align: left; - - @include BoxShadow3D(#fff); - @include S(padding, 15px); - @include S(border-radius, $globalBorderRadius); - @include DropShadow; - - .errorHeader { - color: #ef5072; - } - - .errorMessage { - @include PlainText; - display: block; - color: #666; - text-align: left; - @include BreakText; - hyphens: auto; - // border: dotted #666; - // @include S(border-width, 1px, 0); - @include S(padding, 10px, 0); - @include S(margin-top, 10px); - } - - .supportHelp { - @include S(margin-top, 10px); - @include PlainText; - - .email { - color: $themeColor; - cursor: pointer; - pointer-events: all; - } - } - - .lower { - display: flex; - align-items: center; - @include S(margin-top, 16px); - - i { - flex-grow: 1; - text-align: right; - color: #777; - @include PlainText; - } - - button.resetApp { - @include Button3D($colorRedBright); - @include PlainText; - @include S(padding, 5px, 8px, 4px); - color: #fff; - } - } - } - } - - /* Animations */ - .status { - transform: scale(0.7) $hardwareAcc; - opacity: 0; - @include StateAnim(transform, opacity); - } - - &.arrived { - .status { - opacity: 1; - transform: none; - } - } -} +#state_PreloadState { + .changelogDialogEntry { + margin-top: 1rem; + + width: 100%; + flex-direction: column; + text-align: left; + padding: 10px; + box-sizing: border-box; + background: #eef1f4; + border-radius: $globalBorderRadius; + + @include DarkThemeOverride { + background: #33343c; + } + + .version { + @include Heading; + } + .date { + @include PlainText; + &::before { + content: " | "; + } + color: #aaabaf; + } + + .changes { + @include PlainText; + padding-left: 1.5rem; + margin-top: 1rem; + strong { + background: $colorBlueBright; + color: #fff; + text-transform: uppercase; + padding: 0.1rem 0.2rem; + margin-right: 0.3rem; + } + a { + color: $colorBlueBright; + } + li { + @include SuperSmallText; + margin-bottom: 0.5rem; + } + } + } + + .failureBox { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 999999; + background: #d5d8de; + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + + .logo { + img { + width: 24rem; + } + + margin-bottom: 3rem; + } + + @include InlineAnimation(0.3s ease-in-out) { + 0% { + opacity: 0; + } + 100% { + opacity: 1; + } + } + + .failureInner { + // background: darken($mainBgColor, 6); + max-width: 35rem; + margin: 0 20px; + text-align: left; + + @include BoxShadow3D(#fff); + padding: 1.5rem; + border-radius: $globalBorderRadius; + @include DropShadow; + + .errorHeader { + color: #ef5072; + } + + .errorMessage { + @include PlainText; + display: block; + color: #666; + text-align: left; + overflow-wrap: break-word; + hyphens: auto; + // border: dotted #666; + padding: 1rem 0; + margin-top: 1rem; + } + + .supportHelp { + margin-top: 1rem; + @include PlainText; + + .email { + color: $themeColor; + cursor: pointer; + pointer-events: all; + } + } + + .lower { + display: flex; + align-items: center; + margin-top: 1.6rem; + + i { + flex-grow: 1; + text-align: right; + color: #777; + @include PlainText; + } + + button.resetApp { + @include Button3D($colorRedBright); + @include PlainText; + padding: 0.5rem 0.8rem 0.4rem; + color: #fff; + } + } + } + } + + /* Animations */ + .status { + transform: scale(0.7); + opacity: 0; + @include StateAnim(transform, opacity); + } + + &.arrived { + .status { + opacity: 1; + transform: none; + } + } +} diff --git a/src/css/states/puzzle_menu.scss b/src/css/states/puzzle_menu.scss index ad326c49..94bcfc72 100644 --- a/src/css/states/puzzle_menu.scss +++ b/src/css/states/puzzle_menu.scss @@ -10,7 +10,7 @@ .createPuzzle { background-color: $colorGreenBright; - @include S(margin-left, 5px); + margin-left: 0.5rem; } } @@ -22,8 +22,8 @@ color: #333; background: $accentColorBright; - @include S(padding, 5px); - @include S(border-radius, $globalBorderRadius); + padding: 0.5rem; + border-radius: $globalBorderRadius; flex-wrap: wrap; @include DarkThemeOverride { @@ -35,8 +35,8 @@ margin: 0; display: inline-block; flex-grow: 1; - @include S(padding, 5px, 10px); - @include S(min-width, 50px); + padding: 0.5rem 1rem; + min-width: 5rem; &::placeholder { color: #aaa; @@ -46,15 +46,15 @@ select { color: #333; border: 0; - @include S(padding, 5px); - @include S(border-radius, $globalBorderRadius); - @include S(padding, 7px, 10px); - @include S(margin-left, 5px); + padding: 0.5rem; + border-radius: $globalBorderRadius; + padding: 0.7rem 1rem; + margin-left: 0.5rem; @include PlainText; } .filterCompleted { - @include S(margin-left, 20px); + margin-left: 2rem; pointer-events: all; cursor: pointer; display: flex; @@ -62,26 +62,26 @@ justify-content: center; text-transform: uppercase; @include PlainText; - @include S(margin-right, 10px); + margin-right: 1rem; @include DarkThemeOverride { color: #bbbbc4; } input { - @include S(width, 15px); - @include S(height, 15px); - @include S(margin-right, 5px); - @include S(border-radius, $globalBorderRadius); + width: 1.5rem; + height: 1.5rem; + margin-right: 0.5rem; + border-radius: $globalBorderRadius; border: 0; } } button[type="submit"] { - @include S(padding, 7px, 10px, 5px); - @include S(margin-left, 20px); - @include S(margin-top, 4px); - @include S(margin-bottom, 4px); + padding: 0.7rem 1rem 0.5rem; + margin-left: 2rem; + margin-top: 0.4rem; + margin-bottom: 0.4rem; margin-left: auto; } } @@ -96,9 +96,9 @@ display: grid; grid-auto-columns: 1fr; grid-auto-flow: column; - @include S(grid-gap, 2px); - @include S(padding-right, 10px); - @include S(margin-bottom, 5px); + grid-gap: 0.2rem; + padding-right: 1rem; + margin-bottom: 0.5rem; .category { background: $accentColorBright; @@ -108,8 +108,8 @@ transition-property: opacity, background-color, color; &:first-child { - @include S(border-top-left-radius, $globalBorderRadius); - @include S(border-bottom-left-radius, $globalBorderRadius); + border-top-left-radius: $globalBorderRadius; + border-bottom-left-radius: $globalBorderRadius; } &:last-child { border-top-right-radius: $globalBorderRadius; @@ -134,8 +134,8 @@ } &.root { - @include S(padding-top, 10px); - @include S(padding-bottom, 10px); + padding-top: 1rem; + padding-bottom: 1rem; @include Text; } &.child { @@ -147,11 +147,11 @@ > .puzzles { display: grid; - grid-template-columns: repeat(auto-fit, minmax(D(240px), 1fr)); - @include S(grid-auto-rows, 65px); - @include S(grid-gap, 7px); - @include S(margin-top, 10px); - @include S(padding-right, 4px); + grid-template-columns: repeat(auto-fit, minmax(24rem, 1fr)); + grid-auto-rows: 6.5rem; + grid-gap: 0.7rem; + margin-top: 1rem; + padding-right: 0.4rem; overflow-y: scroll; flex-grow: 1; pointer-events: all; @@ -159,20 +159,20 @@ > .puzzle { width: 100%; - @include S(height, 65px); + height: 6.5rem; background: #f3f3f8; - @include S(border-radius, $globalBorderRadius); + border-radius: $globalBorderRadius; display: grid; grid-template-columns: auto 1fr; - grid-template-rows: D(15px) D(15px) 1fr; - @include S(padding, 5px); - @include S(grid-column-gap, 5px); + grid-template-rows: 1.5rem 1.5rem 1fr; + padding: 0.5rem; + grid-column-gap: 0.5rem; box-sizing: border-box; pointer-events: all; cursor: pointer; position: relative; - @include S(padding-left, 10px); + padding-left: 1rem; @include DarkThemeOverride { background: rgba(0, 0, 10, 0.2); @@ -202,8 +202,8 @@ justify-self: start; width: 100%; box-sizing: border-box; - @include S(padding, 2px, 5px); - @include S(height, 17px); + padding: 0.2rem 0.5rem; + height: 1.7rem; } > .author { @@ -215,7 +215,7 @@ overflow: hidden; text-overflow: ellipsis; white-space: nowrap; - @include S(padding, 2px, 5px); + padding: 0.2rem 0.5rem; } > .icon { @@ -223,8 +223,8 @@ grid-row: 1 / 4; align-self: center; justify-self: center; - @include S(width, 45px); - @include S(height, 45px); + width: 4.5rem; + height: 4.5rem; canvas { width: 100%; @@ -234,14 +234,14 @@ > button.delete { position: absolute; - @include S(top, 5px); - @include S(right, 5px); + top: 0.5rem; + right: 0.5rem; background-repeat: no-repeat; background-position: center center; background-size: 70%; background-color: transparent !important; - @include S(width, 20px); - @include S(height, 20px); + width: 2rem; + height: 2rem; padding: 0; opacity: 0.7; @include DarkThemeInvert; @@ -259,14 +259,14 @@ justify-self: end; justify-content: center; align-self: end; - @include S(height, 14px); + height: 1.4rem; > .downloads { @include SuperSmallText; color: #000; font-weight: bold; - @include S(margin-right, 5px); - @include S(padding-left, 12px); + margin-right: 0.5rem; + padding-left: 1.2rem; opacity: 0.7; display: inline-flex; align-items: center; @@ -274,9 +274,8 @@ @include DarkThemeInvert; & { - background: uiResource("icons/puzzle_plays.png") #{D(2px)} #{D(2.5px)} / #{D( - 8px - )} #{D(8px)} no-repeat; + background: uiResource("icons/puzzle_plays.png") 0.2rem 0.25rem / 0.8rem + 0.8rem no-repeat; } } @@ -286,14 +285,13 @@ justify-content: center; color: #000; font-weight: bold; - @include S(padding-left, 14px); + padding-left: 1.4rem; opacity: 0.7; @include DarkThemeInvert; & { - background: uiResource("icons/puzzle_upvotes.png") #{D(2px)} #{D(2.4px)} / #{D( - 9px - )} #{D(9px)} no-repeat; + background: uiResource("icons/puzzle_upvotes.png") 0.2rem 0.24rem / 0.9rem + 0.9rem no-repeat; } } @@ -303,7 +301,7 @@ justify-content: center; color: #000; font-weight: bold; - @include S(margin-right, 3px); + margin-right: 0.3rem; opacity: 0.7; text-transform: uppercase; @@ -339,10 +337,10 @@ &::after { content: ""; position: absolute; - @include S(top, 10px); - @include S(right, 10px); - @include S(width, 30px); - @include S(height, 30px); + top: 1rem; + right: 1rem; + width: 3rem; + height: 3rem; opacity: 0.1; & { diff --git a/src/css/states/settings.scss b/src/css/states/settings.scss index 98135909..619a1772 100644 --- a/src/css/states/settings.scss +++ b/src/css/states/settings.scss @@ -1,272 +1,248 @@ -#state_SettingsState { - $colorCategoryButton: #eeeff5; - $colorCategoryButtonSelected: $colorBlueBright; - - $layoutBreak: 1000px; - - .container .content { - display: grid; - grid-template-columns: auto 1fr; - @include S(grid-gap, 10px); - - @include StyleBelowWidth($layoutBreak) { - grid-template-columns: 1fr; - grid-template-rows: auto 1fr; - } - - .sidebar { - display: flex; - @include S(min-width, 210px); - @include S(max-width, 320px); - flex-direction: column; - - @include StyleBelowWidth($layoutBreak) { - display: grid; - grid-template-columns: 1fr 1fr 1fr; - @include S(grid-gap, 5px); - max-width: unset !important; - } - - button { - text-align: left; - @include S(margin-bottom, 3px); - &::after { - content: unset; - } - width: 100%; - box-sizing: border-box; - - @include StyleBelowWidth($layoutBreak) { - text-align: center; - height: D(30px) !important; - padding: D(5px) !important; - } - } - - .other { - align-self: end; - margin-top: auto; - - &.noabout { - align-self: start; - } - - @include StyleBelowWidth($layoutBreak) { - margin-top: 0; - display: grid; - grid-template-columns: 1fr 1fr; - @include S(grid-gap, 5px); - max-width: unset !important; - grid-column: 1 / 3; - - button { - margin: 0 !important; - } - } - } - - button.categoryButton, - button.about, - button.privacy { - background-color: $colorCategoryButton; - color: #777a7f; - - &.active { - background-color: $colorCategoryButtonSelected; - color: #fff; - - &:hover { - opacity: 1; - } - } - - &.pressed { - transform: none !important; - } - } - - button.manageMods { - background-color: lighten($modsColor, 38); - color: $modsColor; - display: flex; - @include S(padding-right, 5px); - .newBadge { - color: #fff; - @include S(border-radius, $globalBorderRadius); - background: $modsColor; - margin-left: auto; - @include S(padding, 0, 3px, 0, 3px); - - @include InlineAnimation(1.3s ease-in-out infinite) { - 50% { - transform: rotate(0deg) scale(1.1); - } - } - } - - &.active { - background-color: $colorGreenBright; - } - } - - .versionbar { - @include S(margin-top, 10px); - - @include StyleBelowWidth($layoutBreak) { - display: none; - } - - @include SuperSmallText; - display: grid; - align-items: center; - grid-template-columns: 1fr auto; - .buildVersion { - display: flex; - flex-direction: column; - color: #aaadaf; - } - } - } - - .categoryContainer { - overflow-y: scroll; - pointer-events: all; - @include S(padding-right, 10px); - - .category { - display: none; - - &.active { - display: block; - } - - .setting { - @include S(padding, 10px); - background: #eeeff5; - @include S(border-radius, $globalBorderRadius); - @include S(margin-bottom, 5px); - - .desc { - @include S(margin-top, 5px); - @include SuperSmallText; - color: #aaadb2; - } - - > .row { - display: grid; - align-items: center; - grid-template-columns: 1fr auto; - - > label { - text-transform: uppercase; - @include Text; - } - } - - &.disabled { - // opacity: 0.3; - pointer-events: none; - * { - pointer-events: none !important; - cursor: default !important; - } - position: relative; - .standaloneOnlyHint { - @include PlainText; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - pointer-events: all; - display: flex; - align-items: center; - z-index: 100; - justify-content: center; - background: rgba(#fff, 0.5); - text-transform: uppercase; - color: $colorRedBright; - @include S(border-radius, $globalBorderRadius); - - @include DarkThemeOverride { - background: rgba(#55585f, 0.95); - } - } - } - - .value.enum { - background: #fff; - @include PlainText; - display: flex; - align-items: flex-start; - pointer-events: all; - cursor: pointer; - justify-content: center; - @include S(min-width, 100px); - @include S(border-radius, $globalBorderRadius); - @include S(padding, 4px); - @include S(padding-right, 15px); - - & { - background: #fff uiResource("icons/enum_selector.png") calc(100% - #{D(5px)}) - calc(50% + #{D(1px)}) / #{D(15px)} no-repeat; - } - - transition: background-color 0.12s ease-in-out; - &:hover { - background-color: #fafafa; - } - } - } - } - } - } - - @include DarkThemeOverride { - .container .content { - .sidebar { - button.categoryButton, - button.about, - button.privacy { - color: #ccc; - background-color: darken($darkModeControlsBackground, 5); - - &.active { - color: #fff; - background-color: $colorCategoryButtonSelected; - } - } - } - - .categoryContainer { - .category { - .setting { - background: darken($darkModeGameBackground, 10); - - .value.enum { - // dirty but works - // color: #222; - background-color: $darkModeControlsBackground; - - & { - background-image: uiResource("icons/enum_selector_white.png"); - } - color: #ddd; - &:hover { - background-color: darken($darkModeControlsBackground, 2); - } - } - - .value.checkbox { - background-color: #74767b; - - &.checked { - background-color: $colorBlueBright; - } - } - } - } - } - } - } -} +#state_SettingsState { + $colorCategoryButton: #eeeff5; + $colorCategoryButtonSelected: $colorBlueBright; + + $layoutBreak: 1000px; + + .container .content { + display: grid; + grid-template-columns: auto 1fr; + grid-gap: 1rem; + + @include StyleBelowWidth($layoutBreak) { + grid-template-columns: 1fr; + grid-template-rows: auto 1fr; + } + + .sidebar { + display: flex; + min-width: 21rem; + max-width: 32rem; + flex-direction: column; + + @include StyleBelowWidth($layoutBreak) { + display: grid; + grid-template-columns: 1fr 1fr 1fr; + grid-gap: 0.5rem; + max-width: unset !important; + } + + button { + text-align: left; + margin-bottom: 0.3rem; + &::after { + content: unset; + } + width: 100%; + box-sizing: border-box; + + @include StyleBelowWidth($layoutBreak) { + text-align: center; + height: 3rem !important; + padding: 0.5rem !important; + } + } + + .other { + align-self: end; + margin-top: auto; + + &.noabout { + align-self: start; + } + + @include StyleBelowWidth($layoutBreak) { + margin-top: 0; + display: grid; + grid-template-columns: 1fr 1fr; + grid-gap: 0.5rem; + max-width: unset !important; + grid-column: 1 / 3; + + button { + margin: 0 !important; + } + } + } + + button.categoryButton, + button.about, + button.privacy { + background-color: $colorCategoryButton; + color: #777a7f; + + &.active { + background-color: $colorCategoryButtonSelected; + color: #fff; + + &:hover { + opacity: 1; + } + } + + &.pressed { + transform: none !important; + } + } + + .versionbar { + margin-top: 1rem; + + @include StyleBelowWidth($layoutBreak) { + display: none; + } + + @include SuperSmallText; + display: grid; + align-items: center; + grid-template-columns: 1fr auto; + .buildVersion { + display: flex; + flex-direction: column; + color: #aaadaf; + } + } + } + + .categoryContainer { + overflow-y: scroll; + pointer-events: all; + padding-right: 1rem; + + .category { + display: none; + + &.active { + display: block; + } + + .setting { + padding: 1rem; + background: #eeeff5; + border-radius: $globalBorderRadius; + margin-bottom: 0.5rem; + + .desc { + margin-top: 0.5rem; + @include SuperSmallText; + color: #aaadb2; + } + + > .row { + display: grid; + align-items: center; + grid-template-columns: 1fr auto; + + > label { + text-transform: uppercase; + @include Text; + } + } + + &.disabled { + // opacity: 0.3; + pointer-events: none; + * { + pointer-events: none !important; + cursor: default !important; + } + position: relative; + .standaloneOnlyHint { + @include PlainText; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + pointer-events: all; + display: flex; + align-items: center; + z-index: 100; + justify-content: center; + background: rgba(#fff, 0.5); + text-transform: uppercase; + color: $colorRedBright; + border-radius: $globalBorderRadius; + + @include DarkThemeOverride { + background: rgba(#55585f, 0.95); + } + } + } + + .value.enum { + background: #fff; + @include PlainText; + display: flex; + align-items: flex-start; + pointer-events: all; + cursor: pointer; + justify-content: center; + min-width: 10rem; + border-radius: $globalBorderRadius; + padding: 0.4rem; + padding-right: 1.5rem; + + & { + background: #fff uiResource("icons/enum_selector.png") calc(100% - 0.5rem) + calc(50% + 0.1rem) / 1.5rem no-repeat; + } + + transition: background-color 0.12s ease-in-out; + &:hover { + background-color: #fafafa; + } + } + } + } + } + } + + @include DarkThemeOverride { + .container .content { + .sidebar { + button.categoryButton, + button.about, + button.privacy { + color: #ccc; + background-color: darken($darkModeControlsBackground, 5); + + &.active { + color: #fff; + background-color: $colorCategoryButtonSelected; + } + } + } + + .categoryContainer { + .category { + .setting { + background: darken($darkModeGameBackground, 10); + + .value.enum { + // dirty but works + // color: #222; + background-color: $darkModeControlsBackground; + + & { + background-image: uiResource("icons/enum_selector_white.png"); + } + color: #ddd; + &:hover { + background-color: darken($darkModeControlsBackground, 2); + } + } + + .value.checkbox { + background-color: #74767b; + + &.checked { + background-color: $colorBlueBright; + } + } + } + } + } + } + } +} diff --git a/src/css/states/wegame_splash.scss b/src/css/states/wegame_splash.scss deleted file mode 100644 index 961cfa67..00000000 --- a/src/css/states/wegame_splash.scss +++ /dev/null @@ -1,38 +0,0 @@ -#state_WegameSplashState { - background: #000 !important; - display: flex; - align-items: center; - justify-content: center; - - .wrapper { - opacity: 0; - @include InlineAnimation(5.9s ease-in-out) { - 0% { - opacity: 0; - } - 20% { - opacity: 1; - } - 90% { - opacity: 1; - } - 100% { - opacity: 0; - } - } - - text-align: center; - color: #fff; - @include Heading; - - strong { - display: block; - @include SuperHeading; - @include S(margin-bottom, 20px); - } - - div { - @include S(margin-bottom, 10px); - } - } -} diff --git a/src/css/textual_game_state.scss b/src/css/textual_game_state.scss index 6e3fad30..bb637ddd 100644 --- a/src/css/textual_game_state.scss +++ b/src/css/textual_game_state.scss @@ -1,82 +1,81 @@ -.gameState.textualState { - display: grid; - grid-template-rows: auto 1fr; - box-sizing: border-box; - @include S(padding, 32px); - height: 100vh; - - .headerBar { - display: flex; - - h1 { - display: grid; - grid-template-columns: auto 1fr; - align-items: center; - pointer-events: all; - cursor: pointer; - @include SuperHeading; - text-transform: uppercase; - color: #333438; - position: relative; - @include IncreasedClickArea(10px); - } - - .backButton { - @include S(width, 30px); - @include S(height, 30px); - @include S(margin-right, 10px); - @include S(margin-left, -5px); - & { - background: uiResource("icons/state_back_button.png") center center / 70% no-repeat; - } - } - @include S(margin-bottom, 20px); - } - - > .container { - display: flex; - justify-content: center; - width: 100%; - overflow-y: auto; - - > .content { - width: 100%; - background: #fff; - @include S(border-radius, $globalBorderRadius); - @include S(padding, 10px); - height: 100%; - overflow-y: auto; - box-sizing: border-box; - pointer-events: all; - - a { - color: $colorBlueBright; - } - - .categoryLabel { - display: block; - text-transform: uppercase; - @include S(margin-top, 15px); - @include S(margin-bottom, 15px); - @include Heading; - } - } - } - - @include DarkThemeOverride { - .headerBar { - h1 { - color: #e2e0db; - } - - .backButton { - filter: invert(1); - } - } - - > .container > .content { - background: $darkModeControlsBackground; - color: #eee; - } - } -} +.gameState.textualState { + display: grid; + grid-template-rows: auto 1fr; + box-sizing: border-box; + padding: 3.2rem; + height: 100vh; + + .headerBar { + display: flex; + + h1 { + display: grid; + grid-template-columns: auto 1fr; + align-items: center; + pointer-events: all; + cursor: pointer; + @include SuperHeading; + text-transform: uppercase; + color: #333438; + position: relative; + } + + .backButton { + width: 3rem; + height: 3rem; + margin-right: 1rem; + margin-left: -0.5rem; + & { + background: uiResource("icons/state_back_button.png") center center / 70% no-repeat; + } + } + margin-bottom: 2rem; + } + + > .container { + display: flex; + justify-content: center; + width: 100%; + overflow-y: auto; + + > .content { + width: 100%; + background: #fff; + border-radius: $globalBorderRadius; + padding: 1rem; + height: 100%; + overflow-y: auto; + box-sizing: border-box; + pointer-events: all; + + a { + color: $colorBlueBright; + } + + .categoryLabel { + display: block; + text-transform: uppercase; + margin-top: 1.5rem; + margin-bottom: 1.5rem; + @include Heading; + } + } + } + + @include DarkThemeOverride { + .headerBar { + h1 { + color: #e2e0db; + } + + .backButton { + filter: invert(1); + } + } + + > .container > .content { + background: $darkModeControlsBackground; + color: #eee; + } + } +} diff --git a/src/css/trigonometry.scss b/src/css/trigonometry.scss deleted file mode 100644 index 97d2e730..00000000 --- a/src/css/trigonometry.scss +++ /dev/null @@ -1,66 +0,0 @@ -/////////////////////////////////////////////////////////// -// Plain SASS Trigonometry Algorithm in Taylor Expansion // -// // -// Based on // -// http://japborst.net/posts/sass-sines-and-cosines // -/////////////////////////////////////////////////////////// - -$pi: 3.14159265359; -$_precision: 10; - -@function pow($base, $exp) { - $value: $base; - @if $exp > 1 { - @for $i from 2 through $exp { - $value: $value * $base; - } - } - @if $exp < 1 { - @for $i from 0 through -$exp { - $value: $value / $base; - } - } - @return $value; -} - -@function fact($num) { - $fact: 1; - @if $num > 0 { - @for $i from 1 through $num { - $fact: $fact * $i; - } - } - @return $fact; -} - -@function _to_unitless_rad($angle) { - @if unit($angle) == "deg" { - $angle: $angle / 180deg * $pi; - } - @if unit($angle) == "rad" { - $angle: $angle / 1rad; - } - @return $angle; -} - -@function sin($angle) { - $a: _to_unitless_rad($angle); - $sin: $a; - @for $n from 1 through $_precision { - $sin: $sin + (pow(-1, $n) / fact(2 * $n + 1)) * pow($a, (2 * $n + 1)); - } - @return $sin; -} - -@function cos($angle) { - $a: _to_unitless_rad($angle); - $cos: 1; - @for $n from 1 through $_precision { - $cos: $cos + (pow(-1, $n) / fact(2 * $n)) * pow($a, 2 * $n); - } - @return $cos; -} - -@function tan($angle) { - @return sin($angle) / cos($angle); -} diff --git a/src/css/variables.scss b/src/css/variables.scss index 3ae8878d..60e0a13c 100644 --- a/src/css/variables.scss +++ b/src/css/variables.scss @@ -1,180 +1,103 @@ -$globalBorderRadius: 6px; - -// When to reduce control elements size for small devices -$layoutExpandMinWidth: 340px; - -// Font sizes and line heights -$superHeadingFontSize: 25px; -$superHeadingLineHeight: 24px; - -$breakTooltipShowStatsPx: 1023px; - -$headingFontSize: 19px; -$headingLineHeight: 21px; - -$textFontSize: 16px; -$textLineHeight: 21px; - -$plainTextFontSize: 13px; -$plainTextLineHeight: 17px; - -$superDuperSmallTextFontSize: 8px; -$superDuperSmallTextLineHeight: 9px; -$superSmallTextFontSize: 10px; -$superSmallTextLineHeight: 13px; -$buttonFontSize: 14px; -$buttonLineHeight: 18px; - -// Main background color -$mainBgColor: #dee1ea; - -// Accent colors - -$accentColorBright: #e1e4ed; -$accentColorDark: #7d808a; -$colorGreenBright: #66bb6a; -$colorBlueBright: rgb(74, 151, 223); -$colorRedBright: #ef5072; -$colorOrangeBright: #ef9d50; -$themeColor: #393747; -$ingameHudBg: rgba(#33343b, 0.9); -$modsColor: rgb(214, 60, 228); - -$text3dColor: #f4ffff; - -$darkModeGameBackground: #535866; -$darkModeControlsBackground: darken($darkModeGameBackground, 5); - -// Dialog properties -$modalDialogBg: rgba(160, 165, 180, 0.8); -$dialogBgColor: lighten($mainBgColor, 10); - -$lightFontWeight: normal; -$boldFontWeight: 600; - -$iconSizeSmall: 30px; -$iconSizeMedium: 40px; -$iconSizeLarge: 60px; - -// Poppins 500 -// Rubik 400 -// Cairo 400 -// Viga 400 -// Sniglet 400 - -$mainFont: "GameFont", sans-serif; -// $mainFont: "DK Canoodle"; -// $mainFont: "MADE Florence Sans"; -$numberFont: $mainFont; -$textFont: $mainFont; - -$mainFontWeight: 400; -$mainFontSpacing: 0.01em; -$mainFontScale: 1; - -@mixin DebugText($color) { - // font-size: 3px; - // &, - // * { - // color: $color !important; - // } -} - -@mixin SuperDuperSmallText { - @include ScaleFont($superDuperSmallTextFontSize, $superDuperSmallTextLineHeight); - font-weight: $mainFontWeight; - font-family: $mainFont; - letter-spacing: $mainFontSpacing; - @include DebugText(green); -} - -@mixin SuperSmallText { - @include ScaleFont($superSmallTextFontSize, $superSmallTextLineHeight); - font-weight: $mainFontWeight; - font-family: $mainFont; - letter-spacing: $mainFontSpacing; - @include DebugText(green); -} - -@mixin PlainText { - @include ScaleFont($plainTextFontSize, $plainTextLineHeight); - font-weight: $mainFontWeight; - font-family: $mainFont; - letter-spacing: $mainFontSpacing; - - @include DebugText(red); -} - -@mixin Text { - @include ScaleFont($textFontSize, $textLineHeight); - font-weight: $mainFontWeight; - font-family: $mainFont; - - letter-spacing: $mainFontSpacing; - - @include DebugText(blue); -} - -@mixin Heading { - @include ScaleFont($headingFontSize, $headingLineHeight); - font-weight: $mainFontWeight; - font-family: $mainFont; - letter-spacing: $mainFontSpacing; - - @include DebugText(yellow); -} - -@mixin SuperHeading { - @include ScaleFont($superHeadingFontSize, $superHeadingLineHeight); - font-weight: $mainFontWeight; - font-family: $mainFont; - letter-spacing: $mainFontSpacing; - - @include DebugText(orange); -} - -@mixin ButtonText { - @include ScaleFont($buttonFontSize, $buttonLineHeight); - font-weight: $mainFontWeight; - font-family: $mainFont; - letter-spacing: $mainFontSpacing; - @include DebugText(purple); -} - -@function str-split($string, $separator) { - // empty array/list - $split-arr: (); - // first index of separator in string - $index: str-index($string, $separator); - // loop through string - @while $index != null { - // get the substring from the first character to the separator - $item: str-slice($string, 1, $index - 1); - // push item to array - $split-arr: append($split-arr, $item); - // remove item and separator from string - $string: str-slice($string, $index + 1); - // find new index of separator - $index: str-index($string, $separator); - } - // add the remaining string to list (the last item) - $split-arr: append($split-arr, $string); - - @return $split-arr; -} - -@function _first-index($string, $direction: "left") { - @for $i from 1 through str-length($string) { - $index: if($direction == "left", $i, -$i); - - @if str-slice($string, $index, $index) != " " { - @return $index; - } - } - - @return 0; -} - -@function trim($string) { - @return str-slice($string, _first-index($string, "left"), _first-index($string, "right")); -} +$globalBorderRadius: 0.6rem; + +// Font sizes and line heights +$superHeadingFontSize: 2.5rem; +$superHeadingLineHeight: 2.4rem; + +$headingFontSize: 1.9rem; +$headingLineHeight: 2.1rem; + +$textFontSize: 1.6rem; +$textLineHeight: 2.1rem; + +$plainTextFontSize: 1.3rem; +$plainTextLineHeight: 1.7rem; + +$superDuperSmallTextFontSize: 0.8rem; +$superDuperSmallTextLineHeight: 0.9rem; +$superSmallTextFontSize: 1rem; +$superSmallTextLineHeight: 1.3rem; +$buttonFontSize: 1.4rem; +$buttonLineHeight: 1.8rem; + +// Main background color +$mainBgColor: #dee1ea; + +// Accent colors + +$accentColorBright: #e1e4ed; +$accentColorDark: #7d808a; +$colorGreenBright: #66bb6a; +$colorBlueBright: rgb(74, 151, 223); +$colorRedBright: #ef5072; +$colorOrangeBright: #ef9d50; +$themeColor: #393747; +$ingameHudBg: rgba(#33343b, 0.9); +$modsColor: rgb(214, 60, 228); + +$darkModeGameBackground: #535866; +$darkModeControlsBackground: darken($darkModeGameBackground, 5); + +// Dialog properties +$modalDialogBg: rgba(160, 165, 180, 0.8); +$dialogBgColor: lighten($mainBgColor, 10); + +// Poppins 500 +// Rubik 400 +// Cairo 400 +// Viga 400 +// Sniglet 400 + +$mainFont: "GameFont", sans-serif; +// $mainFont: "DK Canoodle"; +// $mainFont: "MADE Florence Sans"; + +@mixin DebugText($color) { + // font-size: 3px; + // &, + // * { + // color: $color !important; + // } +} + +@mixin SuperDuperSmallText { + font-size: $superDuperSmallTextFontSize; + line-height: $superDuperSmallTextLineHeight; + @include DebugText(green); +} + +@mixin SuperSmallText { + font-size: $superSmallTextFontSize; + line-height: $superSmallTextLineHeight; + @include DebugText(green); +} + +@mixin PlainText { + font-size: $plainTextFontSize; + line-height: $plainTextLineHeight; + @include DebugText(red); +} + +@mixin Text { + font-size: $textFontSize; + line-height: $textLineHeight; + @include DebugText(blue); +} + +@mixin Heading { + font-size: $headingFontSize; + line-height: $headingLineHeight; + @include DebugText(yellow); +} + +@mixin SuperHeading { + font-size: $superHeadingFontSize; + line-height: $superHeadingLineHeight; + @include DebugText(orange); +} + +@mixin ButtonText { + font-size: $buttonFontSize; + line-height: $buttonLineHeight; + @include DebugText(purple); +} diff --git a/src/html/index.html b/src/html/index.html index ab32169c..88ec6b32 100644 --- a/src/html/index.html +++ b/src/html/index.html @@ -1,46 +1,25 @@ - - + + - shapez Demo - Factory Automation Game - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + +
_
+
+ +
+
+
+ +
Downloading Game
+
+
+
+ diff --git a/src/html/index.standalone.html b/src/html/index.standalone.html deleted file mode 100644 index 9209c2fc..00000000 --- a/src/html/index.standalone.html +++ /dev/null @@ -1,22 +0,0 @@ - - - - shapez - - - - - - - - - - - - - - - diff --git a/src/js/.gitignore b/src/js/.gitignore deleted file mode 100644 index d71c2f1e..00000000 --- a/src/js/.gitignore +++ /dev/null @@ -1 +0,0 @@ -built-temp diff --git a/src/js/application.js b/src/js/application.js index d31e6e3f..088020e9 100644 --- a/src/js/application.js +++ b/src/js/application.js @@ -1,440 +1,345 @@ -import { AnimationFrame } from "./core/animation_frame"; -import { BackgroundResourcesLoader } from "./core/background_resources_loader"; -import { IS_MOBILE } from "./core/config"; -import { GameState } from "./core/game_state"; -import { GLOBAL_APP, setGlobalApp } from "./core/globals"; -import { InputDistributor } from "./core/input_distributor"; -import { Loader } from "./core/loader"; -import { createLogger, logSection } from "./core/logging"; -import { StateManager } from "./core/state_manager"; -import { TrackedState } from "./core/tracked_state"; -import { getPlatformName, waitNextFrame } from "./core/utils"; -import { Vector } from "./core/vector"; -import { AdProviderInterface } from "./platform/ad_provider"; -import { NoAdProvider } from "./platform/ad_providers/no_ad_provider"; -import { NoAchievementProvider } from "./platform/browser/no_achievement_provider"; -import { AnalyticsInterface } from "./platform/analytics"; -import { GoogleAnalyticsImpl } from "./platform/browser/google_analytics"; -import { SoundImplBrowser } from "./platform/browser/sound"; -import { PlatformWrapperImplBrowser } from "./platform/browser/wrapper"; -import { PlatformWrapperImplElectron } from "./platform/electron/wrapper"; -import { PlatformWrapperInterface } from "./platform/wrapper"; -import { ApplicationSettings } from "./profile/application_settings"; -import { SavegameManager } from "./savegame/savegame_manager"; -import { AboutState } from "./states/about"; -import { ChangelogState } from "./states/changelog"; -import { InGameState } from "./states/ingame"; -import { KeybindingsState } from "./states/keybindings"; -import { MainMenuState } from "./states/main_menu"; -import { MobileWarningState } from "./states/mobile_warning"; -import { PreloadState } from "./states/preload"; -import { SettingsState } from "./states/settings"; -import { ShapezGameAnalytics } from "./platform/browser/game_analytics"; -import { RestrictionManager } from "./core/restriction_manager"; -import { PuzzleMenuState } from "./states/puzzle_menu"; -import { ClientAPI } from "./platform/api"; -import { LoginState } from "./states/login"; -import { WegameSplashState } from "./states/wegame_splash"; -import { MODS } from "./mods/modloader"; -import { MOD_SIGNALS } from "./mods/mod_signals"; -import { ModsState } from "./states/mods"; - -/** - * @typedef {import("./platform/achievement_provider").AchievementProviderInterface} AchievementProviderInterface - * @typedef {import("./platform/sound").SoundInterface} SoundInterface - * @typedef {import("./platform/storage").StorageInterface} StorageInterface - */ - -const logger = createLogger("application"); - -// Set the name of the hidden property and the change event for visibility -let pageHiddenPropName, pageVisibilityEventName; -if (typeof document.hidden !== "undefined") { - // Opera 12.10 and Firefox 18 and later support - pageHiddenPropName = "hidden"; - pageVisibilityEventName = "visibilitychange"; - // @ts-ignore -} else if (typeof document.msHidden !== "undefined") { - pageHiddenPropName = "msHidden"; - pageVisibilityEventName = "msvisibilitychange"; - // @ts-ignore -} else if (typeof document.webkitHidden !== "undefined") { - pageHiddenPropName = "webkitHidden"; - pageVisibilityEventName = "webkitvisibilitychange"; -} - -export class Application { - /** - * Boots the application - */ - async boot() { - console.log("Booting ..."); - - assert(!GLOBAL_APP, "Tried to construct application twice"); - logger.log("Creating application, platform =", getPlatformName()); - setGlobalApp(this); - MODS.app = this; - - // MODS - - try { - await MODS.initMods(); - } catch (ex) { - alert("Failed to load mods (launch with --dev for more info): \n\n" + ex); - } - - this.unloaded = false; - - // Global stuff - this.settings = new ApplicationSettings(this); - this.ticker = new AnimationFrame(); - this.stateMgr = new StateManager(this); - this.savegameMgr = new SavegameManager(this); - this.inputMgr = new InputDistributor(this); - this.backgroundResourceLoader = new BackgroundResourcesLoader(this); - this.clientApi = new ClientAPI(this); - - // Restrictions (Like demo etc) - this.restrictionMgr = new RestrictionManager(this); - - // Platform dependent stuff - - /** @type {StorageInterface} */ - this.storage = null; - - /** @type {SoundInterface} */ - this.sound = null; - - /** @type {PlatformWrapperInterface} */ - this.platformWrapper = null; - - /** @type {AchievementProviderInterface} */ - this.achievementProvider = null; - - /** @type {AdProviderInterface} */ - this.adProvider = null; - - /** @type {AnalyticsInterface} */ - this.analytics = null; - - /** @type {ShapezGameAnalytics} */ - this.gameAnalytics = null; - - this.initPlatformDependentInstances(); - - // Track if the window is focused (only relevant for browser) - this.focused = true; - - // Track if the window is visible - this.pageVisible = true; - - // Track if the app is paused (cordova) - this.applicationPaused = false; - - /** @type {TypedTrackedState} */ - this.trackedIsRenderable = new TrackedState(this.onAppRenderableStateChanged, this); - - /** @type {TypedTrackedState} */ - this.trackedIsPlaying = new TrackedState(this.onAppPlayingStateChanged, this); - - // Dimensions - this.screenWidth = 0; - this.screenHeight = 0; - - // Store the timestamp where we last checked for a screen resize, since orientationchange is unreliable with cordova - this.lastResizeCheck = null; - - // Store the mouse position, or null if not available - /** @type {Vector|null} */ - this.mousePosition = null; - - this.registerStates(); - this.registerEventListeners(); - - Loader.linkAppAfterBoot(this); - - if (G_WEGAME_VERSION) { - this.stateMgr.moveToState("WegameSplashState"); - } - - // Check for mobile - else if (IS_MOBILE) { - this.stateMgr.moveToState("MobileWarningState"); - } else { - this.stateMgr.moveToState("PreloadState"); - } - - // Starting rendering - this.ticker.frameEmitted.add(this.onFrameEmitted, this); - this.ticker.bgFrameEmitted.add(this.onBackgroundFrame, this); - this.ticker.start(); - - window.focus(); - - MOD_SIGNALS.appBooted.dispatch(); - } - - /** - * Initializes all platform instances - */ - initPlatformDependentInstances() { - logger.log("Creating platform dependent instances (standalone=", G_IS_STANDALONE, ")"); - - if (G_IS_STANDALONE) { - this.platformWrapper = new PlatformWrapperImplElectron(this); - } else { - this.platformWrapper = new PlatformWrapperImplBrowser(this); - } - - // Start with empty ad provider - this.adProvider = new NoAdProvider(this); - this.sound = new SoundImplBrowser(this); - this.analytics = new GoogleAnalyticsImpl(this); - this.gameAnalytics = new ShapezGameAnalytics(this); - this.achievementProvider = new NoAchievementProvider(this); - } - - /** - * Registers all game states - */ - registerStates() { - /** @type {Array} */ - const states = [ - WegameSplashState, - PreloadState, - MobileWarningState, - MainMenuState, - InGameState, - SettingsState, - KeybindingsState, - AboutState, - ChangelogState, - PuzzleMenuState, - LoginState, - ModsState, - ]; - - for (let i = 0; i < states.length; ++i) { - this.stateMgr.register(states[i]); - } - } - - /** - * Registers all event listeners - */ - registerEventListeners() { - window.addEventListener("focus", this.onFocus.bind(this)); - window.addEventListener("blur", this.onBlur.bind(this)); - - window.addEventListener("resize", () => this.checkResize(), true); - window.addEventListener("orientationchange", () => this.checkResize(), true); - - window.addEventListener("mousemove", this.handleMousemove.bind(this)); - window.addEventListener("mouseout", this.handleMousemove.bind(this)); - window.addEventListener("mouseover", this.handleMousemove.bind(this)); - window.addEventListener("mouseleave", this.handleMousemove.bind(this)); - - // Unload events - window.addEventListener("beforeunload", this.onBeforeUnload.bind(this), true); - - document.addEventListener(pageVisibilityEventName, this.handleVisibilityChange.bind(this), false); - - // Track touches so we can update the focus appropriately - document.addEventListener("touchstart", this.updateFocusAfterUserInteraction.bind(this), true); - document.addEventListener("touchend", this.updateFocusAfterUserInteraction.bind(this), true); - } - - /** - * Checks the focus after a touch - * @param {TouchEvent} event - */ - updateFocusAfterUserInteraction(event) { - const target = /** @type {HTMLElement} */ (event.target); - if (!target || !target.tagName) { - // Safety check - logger.warn("Invalid touchstart/touchend event:", event); - return; - } - - // When clicking an element which is not the currently focused one, defocus it - if (target !== document.activeElement) { - // @ts-ignore - if (document.activeElement.blur) { - // @ts-ignore - document.activeElement.blur(); - } - } - - // If we click an input field, focus it now - if (target.tagName.toLowerCase() === "input") { - // We *really* need the focus - waitNextFrame().then(() => target.focus()); - } - } - - /** - * Handles a page visibility change event - * @param {Event} event - */ - handleVisibilityChange(event) { - window.focus(); - const pageVisible = !document[pageHiddenPropName]; - if (pageVisible !== this.pageVisible) { - this.pageVisible = pageVisible; - logger.log("Visibility changed:", this.pageVisible); - this.trackedIsRenderable.set(this.isRenderable()); - } - } - - /** - * Handles a mouse move event - * @param {MouseEvent} event - */ - handleMousemove(event) { - this.mousePosition = new Vector(event.clientX, event.clientY); - } - - /** - * Internal on focus handler - */ - onFocus() { - this.focused = true; - } - - /** - * Internal blur handler - */ - onBlur() { - this.focused = false; - } - - /** - * Returns if the app is currently visible - */ - isRenderable() { - return !this.applicationPaused && this.pageVisible; - } - - onAppRenderableStateChanged(renderable) { - logger.log("Application renderable:", renderable); - window.focus(); - const currentState = this.stateMgr.getCurrentState(); - if (!renderable) { - if (currentState) { - currentState.onAppPause(); - } - } else { - if (currentState) { - currentState.onAppResume(); - } - this.checkResize(); - } - - this.sound.onPageRenderableStateChanged(renderable); - } - - onAppPlayingStateChanged(playing) { - try { - this.adProvider.setPlayStatus(playing); - } catch (ex) { - console.warn("Play status changed"); - } - } - - /** - * Internal before-unload handler - */ - onBeforeUnload(event) { - logSection("BEFORE UNLOAD HANDLER", "#f77"); - const currentState = this.stateMgr.getCurrentState(); - - if (!G_IS_DEV && currentState && currentState.getHasUnloadConfirmation()) { - if (!G_IS_STANDALONE) { - // Need to show a "Are you sure you want to exit" - event.preventDefault(); - event.returnValue = "Are you sure you want to exit?"; - } - } - } - - /** - * Deinitializes the application - */ - deinitialize() { - return this.sound.deinitialize(); - } - - /** - * Background frame update callback - * @param {number} dt - */ - onBackgroundFrame(dt) { - if (this.isRenderable()) { - return; - } - - const currentState = this.stateMgr.getCurrentState(); - if (currentState) { - currentState.onBackgroundTick(dt); - } - } - - /** - * Frame update callback - * @param {number} dt - */ - onFrameEmitted(dt) { - if (!this.isRenderable()) { - return; - } - - const time = performance.now(); - - // Periodically check for resizes, this is expensive (takes 2-3ms so only do it once in a while!) - if (!this.lastResizeCheck || time - this.lastResizeCheck > 1000) { - this.checkResize(); - this.lastResizeCheck = time; - } - - const currentState = this.stateMgr.getCurrentState(); - this.trackedIsPlaying.set(currentState && currentState.getIsIngame()); - if (currentState) { - currentState.onRender(dt); - } - } - - /** - * Checks if the app resized. Only does this once in a while - * @param {boolean} forceUpdate Forced update of the dimensions - */ - checkResize(forceUpdate = false) { - const w = window.innerWidth; - const h = window.innerHeight; - if (this.screenWidth !== w || this.screenHeight !== h || forceUpdate) { - this.screenWidth = w; - this.screenHeight = h; - const currentState = this.stateMgr.getCurrentState(); - if (currentState) { - currentState.onResized(this.screenWidth, this.screenHeight); - } - - const scale = this.getEffectiveUiScale(); - waitNextFrame().then(() => document.documentElement.style.setProperty("--ui-scale", `${scale}`)); - window.focus(); - } - } - - /** - * Returns the effective ui sclae - */ - getEffectiveUiScale() { - return this.platformWrapper.getUiScale() * this.settings.getInterfaceScaleValue(); - } - - /** - * Callback after ui scale has changed - */ - updateAfterUiScaleChanged() { - this.checkResize(true); - } -} +import { AnimationFrame } from "./core/animation_frame"; +import { BackgroundResourcesLoader } from "./core/background_resources_loader"; +import { ErrorHandler } from "./core/error_handler"; +import { GameState } from "./core/game_state"; +import { setGlobalApp } from "./core/globals"; +import { InputDistributor } from "./core/input_distributor"; +import { Loader } from "./core/loader"; +import { createLogger } from "./core/logging"; +import { StateManager } from "./core/state_manager"; +import { TrackedState } from "./core/tracked_state"; +import { getPlatformName, round2Digits, waitNextFrame } from "./core/utils"; +import { Vector } from "./core/vector"; +import { MOD_SIGNALS } from "./mods/mod_signals"; +import { MODS } from "./mods/modloader"; +import { ClientAPI } from "./platform/api"; +import { Sound } from "./platform/sound"; +import { Storage, STORAGE_SAVES } from "./platform/storage"; +import { PlatformWrapperImplElectron } from "./platform/wrapper"; +import { ApplicationSettings } from "./profile/application_settings"; +import { SavegameManager } from "./savegame/savegame_manager"; +import { AboutState } from "./states/about"; +import { ChangelogState } from "./states/changelog"; +import { InGameState } from "./states/ingame"; +import { KeybindingsState } from "./states/keybindings"; +import { LoginState } from "./states/login"; +import { MainMenuState } from "./states/main_menu"; +import { ModsState } from "./states/mods"; +import { PreloadState } from "./states/preload"; +import { PuzzleMenuState } from "./states/puzzle_menu"; +import { SettingsState } from "./states/settings"; + +/** + * @typedef {import("./platform/sound").SoundInterface} SoundInterface + */ + +const logger = createLogger("application"); + +export class Application { + /** + * Boots the application + */ + async boot() { + console.log("Booting ..."); + + this.errorHandler = new ErrorHandler(); + + logger.log("Creating application, platform =", getPlatformName()); + setGlobalApp(this); + + // MODS + + try { + await MODS.initMods(); + } catch (ex) { + throw new Error("Failed to initialize mods", { cause: ex }); + } + + this.unloaded = false; + + // Platform stuff + this.storage = new Storage(this, STORAGE_SAVES); + await this.storage.initialize(); + + this.platformWrapper = new PlatformWrapperImplElectron(this); + + // Global stuff + this.settings = new ApplicationSettings(this, this.storage); + this.ticker = new AnimationFrame(); + this.stateMgr = new StateManager(this); + // NOTE: SavegameManager uses the passed storage, but savegames always + // use Application#storage + this.savegameMgr = new SavegameManager(this, this.storage); + this.inputMgr = new InputDistributor(this); + this.backgroundResourceLoader = new BackgroundResourcesLoader(this); + this.clientApi = new ClientAPI(this); + + this.sound = new Sound(this); + + // Track if the window is focused (only relevant for browser) + this.focused = true; + + // Track if the window is visible + this.pageVisible = true; + + /** @type {TypedTrackedState} */ + this.trackedIsRenderable = new TrackedState(this.onAppRenderableStateChanged, this); + + /** @type {TypedTrackedState} */ + this.trackedIsPlaying = new TrackedState(this.onAppPlayingStateChanged, this); + + // Dimensions + this.screenWidth = 0; + this.screenHeight = 0; + + // Store the timestamp where we last checked for a screen resize, since orientationchange is unreliable with cordova + this.lastResizeCheck = null; + + // Store the mouse position, or null if not available + /** @type {Vector|null} */ + this.mousePosition = null; + + this.registerStates(); + this.registerEventListeners(); + + Loader.linkAppAfterBoot(this); + + this.stateMgr.moveToState("PreloadState"); + + // Starting rendering + this.ticker.frameEmitted.add(this.onFrameEmitted, this); + this.ticker.bgFrameEmitted.add(this.onBackgroundFrame, this); + this.ticker.start(); + + window.focus(); + + MOD_SIGNALS.appBooted.dispatch(); + } + + /** + * Registers all game states + */ + registerStates() { + /** @type {Array} */ + const states = [ + PreloadState, + MainMenuState, + InGameState, + SettingsState, + KeybindingsState, + AboutState, + ChangelogState, + PuzzleMenuState, + LoginState, + ModsState, + ]; + + for (let i = 0; i < states.length; ++i) { + this.stateMgr.register(states[i]); + } + } + + /** + * Registers all event listeners + */ + registerEventListeners() { + window.addEventListener("focus", this.onFocus.bind(this)); + window.addEventListener("blur", this.onBlur.bind(this)); + + window.addEventListener("resize", () => this.checkResize(), true); + window.addEventListener("orientationchange", () => this.checkResize(), true); + + window.addEventListener("mousemove", this.handleMousemove.bind(this)); + window.addEventListener("mouseout", this.handleMousemove.bind(this)); + window.addEventListener("mouseover", this.handleMousemove.bind(this)); + window.addEventListener("mouseleave", this.handleMousemove.bind(this)); + + // Unload events + window.addEventListener("beforeunload", this.onBeforeUnload.bind(this), true); + + document.addEventListener("visibilitychange", this.handleVisibilityChange.bind(this), false); + + // Track touches so we can update the focus appropriately + document.addEventListener("touchstart", this.updateFocusAfterUserInteraction.bind(this), true); + document.addEventListener("touchend", this.updateFocusAfterUserInteraction.bind(this), true); + } + + /** + * Checks the focus after a touch + * @param {TouchEvent} event + */ + updateFocusAfterUserInteraction(event) { + const target = /** @type {HTMLElement} */ (event.target); + if (!target || !target.tagName) { + // Safety check + logger.warn("Invalid touchstart/touchend event:", event); + return; + } + + // When clicking an element which is not the currently focused one, defocus it + if (target !== document.activeElement) { + // @ts-ignore + if (document.activeElement.blur) { + // @ts-ignore + document.activeElement.blur(); + } + } + + // If we click an input field, focus it now + if (target.tagName.toLowerCase() === "input") { + // We *really* need the focus + waitNextFrame().then(() => target.focus()); + } + } + + /** + * Handles a page visibility change event + * @param {Event} event + */ + handleVisibilityChange(event) { + window.focus(); + const pageVisible = !document.hidden; + if (pageVisible !== this.pageVisible) { + this.pageVisible = pageVisible; + logger.log("Visibility changed:", this.pageVisible); + this.trackedIsRenderable.set(this.isRenderable()); + } + } + + /** + * Handles a mouse move event + * @param {MouseEvent} event + */ + handleMousemove(event) { + this.mousePosition = new Vector(event.clientX, event.clientY); + } + + /** + * Internal on focus handler + */ + onFocus() { + this.focused = true; + } + + /** + * Internal blur handler + */ + onBlur() { + this.focused = false; + } + + /** + * Returns if the app is currently visible + */ + isRenderable() { + return this.pageVisible; + } + + onAppRenderableStateChanged(renderable) { + logger.log("Application renderable:", renderable); + window.focus(); + const currentState = this.stateMgr.getCurrentState(); + if (!renderable) { + if (currentState) { + currentState.onAppPause(); + } + } else { + if (currentState) { + currentState.onAppResume(); + } + this.checkResize(); + } + + this.sound.onPageRenderableStateChanged(renderable); + } + + onAppPlayingStateChanged(playing) { + // TODO: Check for usages and alternatives. This can be turned into a singal. + } + + /** + * Internal before-unload handler + */ + onBeforeUnload(event) {} + + /** + * Deinitializes the application + */ + deinitialize() { + return this.sound.deinitialize(); + } + + /** + * Background frame update callback + * @param {number} dt + */ + onBackgroundFrame(dt) { + if (this.isRenderable()) { + return; + } + + const currentState = this.stateMgr.getCurrentState(); + if (currentState) { + currentState.onBackgroundTick(dt); + } + } + + /** + * Frame update callback + * @param {number} dt + */ + onFrameEmitted(dt) { + if (!this.isRenderable()) { + return; + } + + const time = performance.now(); + + // Periodically check for resizes, this is expensive (takes 2-3ms so only do it once in a while!) + if (!this.lastResizeCheck || time - this.lastResizeCheck > 1000) { + this.checkResize(); + this.lastResizeCheck = time; + } + + const currentState = this.stateMgr.getCurrentState(); + this.trackedIsPlaying.set(currentState && currentState.getIsIngame()); + if (currentState) { + currentState.onRender(dt); + } + } + + /** + * Checks if the app resized. Only does this once in a while + * @param {boolean} forceUpdate Forced update of the dimensions + */ + checkResize(forceUpdate = false) { + const w = window.innerWidth; + const h = window.innerHeight; + if (this.screenWidth !== w || this.screenHeight !== h || forceUpdate) { + this.screenWidth = w; + this.screenHeight = h; + const currentState = this.stateMgr.getCurrentState(); + if (currentState) { + currentState.onResized(this.screenWidth, this.screenHeight); + } + + const scale = this.getEffectiveUiScale(); + document.documentElement.style.fontSize = `${round2Digits(scale * 10)}px`; + window.focus(); + } + } + + /** + * Returns the effective ui sclae + */ + getEffectiveUiScale() { + return this.platformWrapper.getUiScale() * this.settings.getInterfaceScaleValue(); + } + + /** + * Callback after ui scale has changed + */ + updateAfterUiScaleChanged() { + this.checkResize(true); + } +} diff --git a/src/js/changelog.js b/src/js/changelog.js index de7cd029..a80a2047 100644 --- a/src/js/changelog.js +++ b/src/js/changelog.js @@ -1,443 +1,423 @@ -export const CHANGELOG = [ - { - version: "1.5.6", - date: "09.12.2022", - entries: [ - "⚠️ We are currently prototyping Shapez 2! Click here to find out more. ⚠️ ", - "Minor fixes & improvements", - "Updated translations", - ], - }, - { - version: "1.5.5", - date: "20.06.2022", - entries: [ - "You can now play the full version in your browser! Click here to read all details.", - "Reworked the tutorial to be simpler and more interactive", - "General polishing", - "Fix being unable to delete savegame when the savegame file was deleted externally", - "New sfx when unlocking upgrades", - "Updated translations", - ], - }, - { - version: "1.5.3", - date: "05.06.2022", - entries: [ - "Fixed buildings not being lockable in the Puzzle DLC Editor", - "Fixed issues launching the game with proton", - "Updated translations", - ], - }, - { - version: "1.5.2", - date: "02.06.2022", - entries: [ - "Attempted to fix the 'vram glitch', where the map background would not redraw anymore, especially in fullscreen. If the issue still persists, please let me know in the discord server!", - "The game has been renamed from 'shapez.io' to 'shapez', since it is not really an .io game", - "Various performance improvements", - "Upgrades should now show the full precision", - "UI Polishing & Cleanup", - "Updated translations", - "PS: We are already working on shapez 2, more information will follow in the discord soon!", - ], - }, - { - version: "1.5.1", - date: "25.02.2022", - entries: [ - "This version adds an official modloader! You can now load mods by extracting them and placing the .js file in the mods/ folder of the game.", - "Mods can be found here", - "When holding shift while placing a belt, the indicator now becomes red when crossing buildings", - "Lots of performance improvements, leading to up to 50% more FPS", - ], - }, - { - version: "1.4.4", - date: "29.08.2021", - entries: [ - "Hotfix: Fixed the balancer not distributing items evenly, caused by the 1.4.3 update. Sorry for any inconveniences!", - ], - }, - { - version: "1.4.3", - date: "28.08.2021", - entries: [ - "You can now hold 'ALT' while hovering a building to see its output! (Thanks to Sense101) (PS: There is now a setting to have it always on!)", - "The map overview should now be much more performant! As a consequence, you can now zoom out farther! (Thanks to PFedak)", - "Puzzle DLC: There is now a 'next puzzle' button!", - "Puzzle DLC: There is now a search function!", - "Edit signal dialog now has the previous signal filled (Thanks to EmeraldBlock)", - "Further performance improvements (Thanks to PFedak)", - "Improved puzzle validation (Thanks to Sense101)", - "Input fields in dialogs should now automatically focus", - "Fix selected building being deselected at level up (Thanks to EmeraldBlock)", - "Updated translations", - ], - }, - { - version: "1.4.2", - date: "24.06.2021", - entries: [ - "Puzzle DLC: Goal acceptors now reset after getting no items for a while (This should prevent being able to 'cheat' puzzles) (by Sense101)", - "Puzzle DLC: Added button to clear all buildings / reset the puzzle (by Sense101)", - "Puzzle DLC: Allow copy-paste in puzzle mode (by Sense101)", - "Fixed level achievements being given on the wrong level (by DJ1TJOO)", - "Fixed blueprint not properly clearing on right click", - "Updated translations", - ], - }, - { - version: "1.4.1", - date: "22.06.2021", - entries: [ - "The Puzzle DLC is now available on Steam!", - "The Soundtrack is now also available to wishlist and will be released within the next days, including the new music from the Puzzle DLC!", - ], - }, - { - version: "1.4.0", - date: "04.06.2021", - entries: [ - "Belts in blueprints should now always paste correctly", - "You can now clear belts by selecting them and then pressing 'B'", - "Preparations for the Puzzle DLC, coming June 22nd!", - ], - }, - { - version: "1.3.1", - date: "16.04.2021", - entries: G_CHINA_VERSION - ? [ - "第13关的交付目标更改为:中国古代指南针。(感谢玩家:凯风入心 创作并提供", - "第17关的交付目标更改为:永乐通宝。(感谢玩家:金天赐 创作并提供", - "第22关的交付目标更改为:凤凰。(感谢玩家:我没得眼镜 创作并提供", - "第23关的交付目标更改为:古代车轮。(感谢玩家:我没得眼镜 创作并提供", - "第24关的交付目标更改为:大熊猫。(感谢玩家:窝囸倪现任 创作并提供", - - "修复了一些特定情况下偶尔会发生的存档损坏问题", - "修复了成就更新后有时候游戏崩溃的问题", - ] - : [ - "Fixed savegames getting corrupt in rare conditions", - "Fixed game crashing sometimes since the achievements update", - ], - }, - { - version: "1.3.0", - date: "12.03.2020", - skin: "achievements", - entries: [ - "There are now 45 Steam Achievements!", - "Fixed constant signals being editable from the regular layer", - "Fixed items still overlapping sometimes between buildings and belts", - "The game is now available in finnish, italian, romanian and ukrainian! (Thanks to all contributors!)", - "Updated translations (Thanks to all contributors!)", - ], - }, - { - version: "1.2.2", - date: "07.12.2020", - entries: [ - "Fix item readers and some other buildings slowing up belts, especially if they stalled (inspired by Keterr's fix)", - "Added the ability to edit constant signals by left clicking them", - "Prevent items from being rendered on each other when a belt stalls (inspired by Keterr)", - "You can now add markers in the wire layer (partially by daanbreur)", - "Allow to cycle backwards in the toolbar with SHIFT + Tab (idea by EmeraldBlock)", - "Allow to cycle variants backwards with SHIFT + T", - "Upgrade numbers now use roman numerals until tier 50 (by LeopoldTal)", - "Add button to unpin shapes from the left side (by artemisSystem)", - "Fix middle mouse button also placing blueprints (by Eiim)", - "Hide wires grid when using the 'Disable Grid' setting (by EmeraldBlock)", - "Fix UI using multiple different save icons", - "Updated translations (Thanks to all contributors!)", - ], - }, - { - version: "1.2.1", - date: "31.10.2020", - entries: [ - "Fixed stacking bug for level 26 which required restarting the game", - "Fix reward notification being too long sometimes (by LeopoldTal)", - "Use locale decimal separator on belt reader display (by LeopoldTal)", - "Vastly improved performance when saving games (by LeopoldTal)", - "Prevent some antivirus programs blocking the opening of external links (by LeopoldTal)", - "Match tutorials to the correct painter variants (by LeopoldTal)", - "Prevent throughput goals containing fractional numbers (by CEbbinghaus)", - "Updated translations and added Hungarian", - ], - }, - { - version: "1.2.0", - date: "09.10.2020", - entries: [ - "⚠️⚠️This update is HUGE, view the full changelog here! ⚠️⚠️", - ], - }, - { - version: "1.1.18", - date: "27.06.2020", - entries: [ - "Huge performance improvements - up to double fps and tick-rate! This will wipe out all current items on belts.", - "Reduce story shapes required until unlocking blueprints", - "Allow clicking on variants to select them", - "Add 'copy key' button to shape viewer", - "Add more FPS to the belt animation and fix belt animation seeming to go 'backwards' on high belt speeds", - "Fix deconstruct sound being played when right clicking hub", - "Allow clicking 'Q' over a shape or color patch to automatically select the miner building (by Gerdon262)", - "Update belt placement performance on huge factories (by Phlosioneer)", - "Fix duplicate waypoints with a shape not rendering (by hexy)", - "Fix smart tunnel placement deleting wrong tunnels (by mordof)", - "Add setting (on by default) to store the last used rotation per building instead of globally storing it (by Magos)", - "Added chinese (traditional) translation", - "Updated translations", - ], - }, - { - version: "1.1.17", - date: "22.06.2020", - entries: [ - "Color blind mode! You can now activate it in the settings and it will show you which color is below your cursor (Either resource or on the belt)", - "Add info buttons to all shapes so you can figure out how they are built! (And also, which colors they have)", - "Allow configuring autosave interval and disabling it in the settings", - "The smart-tunnel placement has been reworked to properly replace belts. Thus the setting has been turned on again by default", - "The soundtrack now has a higher quality on the standalone version than the web version", - "Add setting to disable cut/delete warnings (by hexy)", - "Fix bug where belts in blueprints don't orient correctly (by hexy)", - "Fix camera moving weird after dragging and holding (by hexy)", - "Fix keybinding for pipette showing while pasting blueprints", - "Improve visibility of shape background in dark mode", - "Added sound when destroying a building", - "Added swedish translation", - "Update tutorial image for tier 2 tunnels to explain mix/match (by jimmyshadow1)", - ], - }, - { - version: "1.1.16", - date: "21.06.2020", - entries: [ - "You can now pickup buildings below your cursor with 'Q'!", - "The game soundtrack has been extended! There are now 4 songs with over 13 minutes of playtime from Peppsen!", - "Refactor keybindings overlay to show more appropriate keybindings", - "Show keybindings for area-select in the upper left instead", - "Automatically deselect area when selecting a new building", - "Raise markers limit from 14 characters to 71 (by Joker-vD)", - "Optimize performance by caching extractor items (by Phlosioneer)", - "Added setting to enable compact building infos, which only show ratios and hide the image / description", - "Apply dark theme to menu as well (by dengr1065)", - "Fix belt planner not placing the last belt", - "Fix buildings getting deleted when right clicking while placing a blueprint", - "Fix for exporting screenshots for huge bases (It was showing an empty file) (by xSparfuchs)", - "Fix buttons not responding when using right click directly after left click (by davidburhans)", - "Fix hub marker being hidden by building info panel", - "Disable dialog background blur since it can cause performance issues", - "Added simplified chinese translations", - "Update translations (Thanks to all translators!)", - ], - }, - { - version: "1.1.15", - date: "17.06.2020", - entries: [ - "You can now place straight belts (and tunnels) by holding SHIFT! (For you, @giantwaffle ❤️)", - "Added continue button to main menu and add separate 'New game' button (by jaysc)", - "Added setting to disable smart tunnel placement introduced with the last update", - "Added setting to disable vignette", - "Update translations", - ], - }, - { - version: "1.1.14", - date: "16.06.2020", - entries: [ - "There is now an indicator (compass) to the HUB for the HUB Marker!", - "You can now include shape short keys in markers to render shape icons instead of text!", - "Added mirrored variant of the painter", - "When placing tunnels, unnecessary belts inbetween are now removed!", - "You can now drag tunnels and they will automatically expand! (Just try it out, its intuitive)", - ], - }, - { - version: "1.1.13", - date: "15.06.2020", - entries: [ - "Added shift modifier for faster pan (by jaysc)", - "Added Japanese translations", - "Added Portuguese (Portugal) translations", - "Updated icon for Spanish (Latin America) - It was showing a Spanish flag before", - "Updated existing translations", - ], - }, - { - version: "1.1.12", - date: "14.06.2020", - entries: [ - "Huge performance improvements! The game should now run up to 60% faster!", - "Added norwegian translation", - ], - }, - { - version: "1.1.11", - date: "13.06.2020", - entries: [ - "Pinned shapes are now smart, they dynamically update their goal and also unpin when no longer required. Completed objectives are now rendered transparent.", - "You can now cut areas, and also paste the last blueprint again! (by hexy)", - "You can now export your whole base as an image by pressing F3!", - "Improve upgrade number rounding, so there are no goals like '37.4k', instead it will now be '35k'", - "You can now configure the camera movement speed when using WASD (by mini-bomba)", - "Selecting an area now is relative to the world and thus does not move when moving the screen (by Dimava)", - "Allow higher tick-rates up to 500hz (This will burn your PC!)", - "Fix bug regarding number rounding", - "Fix dialog text being hardly readable in dark theme", - "Fix app not starting when the savegames were corrupted - there is now a better error message as well.", - "Further translation updates - Big thanks to all contributors!", - ], - }, - { - version: "1.1.10", - date: "12.06.2020", - entries: [ - "There are now linux builds on steam! Please report any issues in the Discord!", - "Steam cloud saves are now available!", - "Added and update more translations (Big thank you to all translators!)", - "Prevent invalid connection if existing underground tunnel entrance exists (by jaysc)", - ], - }, - { - version: "1.1.9", - date: "11.06.2020", - entries: [ - "Support for translations! Interested in helping out? Check out the translation guide!", - "Update stacker artwork to clarify how it works", - "Update keybinding hints on the top left to be more accurate", - "Make it more clear when blueprints are unlocked when trying to use them", - "Fix pinned shape icons not being visible in dark mode", - "Fix being able to select buildings via hotkeys in map overview mode", - "Make shapes unpinnable in the upgrades tab (By hexy)", - ], - }, - { - version: "1.1.8", - date: "07.06.2020", - entries: [ - "You can now purchase the standalone on steam! View steam page", - "Added ability to create markers in the demo, but only two.", - "Contest #01 has ended! I'll now work through the entries, select the 5 I like most and present them to the community to vote for!", - ], - }, - { - version: "1.1.7", - date: "04.06.2020", - entries: ["HOTFIX: Fix savegames not showing up on the standalone version"], - }, - { - version: "1.1.6", - date: "04.06.2020", - entries: [ - "The steam release will happen on the 7th of June - Be sure to add it to your wishlist! View on steam", - "Fixed level complete dialog being blurred when the shop was opened before", - "Standalone: Increased icon visibility for windows builds", - "Web version: Fixed firefox not loading the game when browsing in private mode", - ], - }, - - { - version: "1.1.5", - date: "03.06.2020", - entries: ["Added weekly contests!"], - }, - { - version: "1.1.4", - date: "01.06.2020", - entries: ["Add 'interactive' tutorial for the first level to improve onboarding experience"], - }, - { - version: "1.1.3", - date: "01.06.2020", - entries: [ - "Added setting to configure zoom / mouse wheel / touchpad sensitivity", - "Fix belts being too slow when copied via blueprint (by Dimava)", - "Allow binding mouse buttons to actions (by Dimava)", - "Increase readability of certain HUD elements", - ], - }, - { - version: "1.1.2", - date: "30.05.2020", - entries: [ - "The official trailer is now ready! Check it out here!", - "The steam page is now live!", - "Experimental linux builds are now available! Please give me feedback on them in the Discord", - "Allow hovering pinned shapes to enlarge them", - "Allow deselecting blueprints with right click and 'Q'", - "Move default key for deleting from 'X' to 'DEL'", - "Show confirmation when deleting more than 100 buildings", - "Reintroduce 'SPACE' keybinding to center on map", - "Improved keybinding hints", - "Fixed some keybindings showing as 'undefined'", - ], - }, - { - version: "1.1.1", - date: "28.05.2020", - entries: ["Fix crash when 'Show Hints' setting was turned off"], - }, - { - version: "1.1.0", - date: "28.05.2020", - entries: [ - "BLUEPRINTS! They are unlocked at level 12 and cost a special shape to build.", - "MAP MARKERS! Press 'M' to create a waypoint and be able to jump to it", - "Savegame levels are now shown in the main menu. For existing games, save them again to make the level show up.", - "Allow holding SHIFT to rotate counter clockwise", - "Added confirmation when deleting more than 500 buildings at a time", - "Added background to toolbar to increase contrast", - "Further decerase requirements of first levels", - "Pinned shapes now are saved", - "Allow placing extractors anywhere again, but they don't work at all if not placed on a resource", - "Show dialog explaining some keybindings after completing level 4", - "Fix keys being stuck when opening a dialog", - "Swapped shape order for painting upgrades", - "Allow changing all keybindings, including CTRL, ALT and SHIFT (by Dimava)", - "Fix cycling through keybindings selecting locked buildings as well (by Dimava)", - "There is now a github action, checking all pull requests with eslint. (by mrHedgehog)", - ], - }, - { - version: "1.0.4", - date: "26.05.2020", - entries: [ - "Reduce cost of first painting upgrade, and change 'Shape Processing' to 'Cutting, Rotating & Stacking'", - "Add dialog after completing level 2 to check out the upgrades tab.", - "Allow changing the keybindings in the demo version", - ], - }, - { - version: "1.0.3", - date: "24.05.2020", - entries: [ - "Reduced the amount of shapes required for the first 5 levels to make it easier to get into the game.", - ], - }, - { - version: "1.0.2", - date: "23.05.2020", - entries: [ - "Introduced changelog", - "Removed 'early access' label because the game isn't actually early access - its in a pretty good state already! (No worries, a lot more updates will follow!)", - "Added a 'Show hint' button which shows a small video for almost all levels to help out", - "Now showing proper descriptions when completing levels, with instructions on what the gained reward does.", - "Show a landing page on mobile devices about the game not being ready to be played on mobile yet", - "Fix painters and mixers being affected by the shape processors upgrade and not the painter one", - "Added 'multiplace' setting which is equivalent to holding SHIFT all the time", - "Added keybindings to zoom in / zoom out", - "Tunnels now also show connection lines to tunnel exits, instead of just tunnel entries", - "Lots of minor fixes and improvements", - ], - }, - { - version: "1.0.1", - date: "21.05.2020", - entries: ["Initial release!"], - }, -]; +export const CHANGELOG = [ + { + version: "1.5.6", + date: "09.12.2022", + entries: [ + "⚠️ We are currently prototyping Shapez 2! Click here to find out more. ⚠️ ", + "Minor fixes & improvements", + "Updated translations", + ], + }, + { + version: "1.5.5", + date: "20.06.2022", + entries: [ + "You can now play the full version in your browser! Click here to read all details.", + "Reworked the tutorial to be simpler and more interactive", + "General polishing", + "Fix being unable to delete savegame when the savegame file was deleted externally", + "New sfx when unlocking upgrades", + "Updated translations", + ], + }, + { + version: "1.5.3", + date: "05.06.2022", + entries: [ + "Fixed buildings not being lockable in the Puzzle DLC Editor", + "Fixed issues launching the game with proton", + "Updated translations", + ], + }, + { + version: "1.5.2", + date: "02.06.2022", + entries: [ + "Attempted to fix the 'vram glitch', where the map background would not redraw anymore, especially in fullscreen. If the issue still persists, please let me know in the discord server!", + "The game has been renamed from 'shapez.io' to 'shapez', since it is not really an .io game", + "Various performance improvements", + "Upgrades should now show the full precision", + "UI Polishing & Cleanup", + "Updated translations", + "PS: We are already working on shapez 2, more information will follow in the discord soon!", + ], + }, + { + version: "1.5.1", + date: "25.02.2022", + entries: [ + "This version adds an official modloader! You can now load mods by extracting them and placing the .js file in the mods/ folder of the game.", + "Mods can be found here", + "When holding shift while placing a belt, the indicator now becomes red when crossing buildings", + "Lots of performance improvements, leading to up to 50% more FPS", + ], + }, + { + version: "1.4.4", + date: "29.08.2021", + entries: [ + "Hotfix: Fixed the balancer not distributing items evenly, caused by the 1.4.3 update. Sorry for any inconveniences!", + ], + }, + { + version: "1.4.3", + date: "28.08.2021", + entries: [ + "You can now hold 'ALT' while hovering a building to see its output! (Thanks to Sense101) (PS: There is now a setting to have it always on!)", + "The map overview should now be much more performant! As a consequence, you can now zoom out farther! (Thanks to PFedak)", + "Puzzle DLC: There is now a 'next puzzle' button!", + "Puzzle DLC: There is now a search function!", + "Edit signal dialog now has the previous signal filled (Thanks to EmeraldBlock)", + "Further performance improvements (Thanks to PFedak)", + "Improved puzzle validation (Thanks to Sense101)", + "Input fields in dialogs should now automatically focus", + "Fix selected building being deselected at level up (Thanks to EmeraldBlock)", + "Updated translations", + ], + }, + { + version: "1.4.2", + date: "24.06.2021", + entries: [ + "Puzzle DLC: Goal acceptors now reset after getting no items for a while (This should prevent being able to 'cheat' puzzles) (by Sense101)", + "Puzzle DLC: Added button to clear all buildings / reset the puzzle (by Sense101)", + "Puzzle DLC: Allow copy-paste in puzzle mode (by Sense101)", + "Fixed level achievements being given on the wrong level (by DJ1TJOO)", + "Fixed blueprint not properly clearing on right click", + "Updated translations", + ], + }, + { + version: "1.4.1", + date: "22.06.2021", + entries: [ + "The Puzzle DLC is now available on Steam!", + "The Soundtrack is now also available to wishlist and will be released within the next days, including the new music from the Puzzle DLC!", + ], + }, + { + version: "1.4.0", + date: "04.06.2021", + entries: [ + "Belts in blueprints should now always paste correctly", + "You can now clear belts by selecting them and then pressing 'B'", + "Preparations for the Puzzle DLC, coming June 22nd!", + ], + }, + { + version: "1.3.0", + date: "12.03.2020", + entries: [ + "There are now 45 Steam Achievements!", + "Fixed constant signals being editable from the regular layer", + "Fixed items still overlapping sometimes between buildings and belts", + "The game is now available in finnish, italian, romanian and ukrainian! (Thanks to all contributors!)", + "Updated translations (Thanks to all contributors!)", + ], + }, + { + version: "1.2.2", + date: "07.12.2020", + entries: [ + "Fix item readers and some other buildings slowing up belts, especially if they stalled (inspired by Keterr's fix)", + "Added the ability to edit constant signals by left clicking them", + "Prevent items from being rendered on each other when a belt stalls (inspired by Keterr)", + "You can now add markers in the wire layer (partially by daanbreur)", + "Allow to cycle backwards in the toolbar with SHIFT + Tab (idea by EmeraldBlock)", + "Allow to cycle variants backwards with SHIFT + T", + "Upgrade numbers now use roman numerals until tier 50 (by LeopoldTal)", + "Add button to unpin shapes from the left side (by artemisSystem)", + "Fix middle mouse button also placing blueprints (by Eiim)", + "Hide wires grid when using the 'Disable Grid' setting (by EmeraldBlock)", + "Fix UI using multiple different save icons", + "Updated translations (Thanks to all contributors!)", + ], + }, + { + version: "1.2.1", + date: "31.10.2020", + entries: [ + "Fixed stacking bug for level 26 which required restarting the game", + "Fix reward notification being too long sometimes (by LeopoldTal)", + "Use locale decimal separator on belt reader display (by LeopoldTal)", + "Vastly improved performance when saving games (by LeopoldTal)", + "Prevent some antivirus programs blocking the opening of external links (by LeopoldTal)", + "Match tutorials to the correct painter variants (by LeopoldTal)", + "Prevent throughput goals containing fractional numbers (by CEbbinghaus)", + "Updated translations and added Hungarian", + ], + }, + { + version: "1.2.0", + date: "09.10.2020", + entries: [ + "⚠️⚠️This update is HUGE, view the full changelog here! ⚠️⚠️", + ], + }, + { + version: "1.1.18", + date: "27.06.2020", + entries: [ + "Huge performance improvements - up to double fps and tick-rate! This will wipe out all current items on belts.", + "Reduce story shapes required until unlocking blueprints", + "Allow clicking on variants to select them", + "Add 'copy key' button to shape viewer", + "Add more FPS to the belt animation and fix belt animation seeming to go 'backwards' on high belt speeds", + "Fix deconstruct sound being played when right clicking hub", + "Allow clicking 'Q' over a shape or color patch to automatically select the miner building (by Gerdon262)", + "Update belt placement performance on huge factories (by Phlosioneer)", + "Fix duplicate waypoints with a shape not rendering (by hexy)", + "Fix smart tunnel placement deleting wrong tunnels (by mordof)", + "Add setting (on by default) to store the last used rotation per building instead of globally storing it (by Magos)", + "Added chinese (traditional) translation", + "Updated translations", + ], + }, + { + version: "1.1.17", + date: "22.06.2020", + entries: [ + "Color blind mode! You can now activate it in the settings and it will show you which color is below your cursor (Either resource or on the belt)", + "Add info buttons to all shapes so you can figure out how they are built! (And also, which colors they have)", + "Allow configuring autosave interval and disabling it in the settings", + "The smart-tunnel placement has been reworked to properly replace belts. Thus the setting has been turned on again by default", + "The soundtrack now has a higher quality on the standalone version than the web version", + "Add setting to disable cut/delete warnings (by hexy)", + "Fix bug where belts in blueprints don't orient correctly (by hexy)", + "Fix camera moving weird after dragging and holding (by hexy)", + "Fix keybinding for pipette showing while pasting blueprints", + "Improve visibility of shape background in dark mode", + "Added sound when destroying a building", + "Added swedish translation", + "Update tutorial image for tier 2 tunnels to explain mix/match (by jimmyshadow1)", + ], + }, + { + version: "1.1.16", + date: "21.06.2020", + entries: [ + "You can now pickup buildings below your cursor with 'Q'!", + "The game soundtrack has been extended! There are now 4 songs with over 13 minutes of playtime from Peppsen!", + "Refactor keybindings overlay to show more appropriate keybindings", + "Show keybindings for area-select in the upper left instead", + "Automatically deselect area when selecting a new building", + "Raise markers limit from 14 characters to 71 (by Joker-vD)", + "Optimize performance by caching extractor items (by Phlosioneer)", + "Added setting to enable compact building infos, which only show ratios and hide the image / description", + "Apply dark theme to menu as well (by dengr1065)", + "Fix belt planner not placing the last belt", + "Fix buildings getting deleted when right clicking while placing a blueprint", + "Fix for exporting screenshots for huge bases (It was showing an empty file) (by xSparfuchs)", + "Fix buttons not responding when using right click directly after left click (by davidburhans)", + "Fix hub marker being hidden by building info panel", + "Disable dialog background blur since it can cause performance issues", + "Added simplified chinese translations", + "Update translations (Thanks to all translators!)", + ], + }, + { + version: "1.1.15", + date: "17.06.2020", + entries: [ + "You can now place straight belts (and tunnels) by holding SHIFT! (For you, @giantwaffle ❤️)", + "Added continue button to main menu and add separate 'New game' button (by jaysc)", + "Added setting to disable smart tunnel placement introduced with the last update", + "Added setting to disable vignette", + "Update translations", + ], + }, + { + version: "1.1.14", + date: "16.06.2020", + entries: [ + "There is now an indicator (compass) to the HUB for the HUB Marker!", + "You can now include shape short keys in markers to render shape icons instead of text!", + "Added mirrored variant of the painter", + "When placing tunnels, unnecessary belts inbetween are now removed!", + "You can now drag tunnels and they will automatically expand! (Just try it out, its intuitive)", + ], + }, + { + version: "1.1.13", + date: "15.06.2020", + entries: [ + "Added shift modifier for faster pan (by jaysc)", + "Added Japanese translations", + "Added Portuguese (Portugal) translations", + "Updated icon for Spanish (Latin America) - It was showing a Spanish flag before", + "Updated existing translations", + ], + }, + { + version: "1.1.12", + date: "14.06.2020", + entries: [ + "Huge performance improvements! The game should now run up to 60% faster!", + "Added norwegian translation", + ], + }, + { + version: "1.1.11", + date: "13.06.2020", + entries: [ + "Pinned shapes are now smart, they dynamically update their goal and also unpin when no longer required. Completed objectives are now rendered transparent.", + "You can now cut areas, and also paste the last blueprint again! (by hexy)", + "You can now export your whole base as an image by pressing F3!", + "Improve upgrade number rounding, so there are no goals like '37.4k', instead it will now be '35k'", + "You can now configure the camera movement speed when using WASD (by mini-bomba)", + "Selecting an area now is relative to the world and thus does not move when moving the screen (by Dimava)", + "Allow higher tick-rates up to 500hz (This will burn your PC!)", + "Fix bug regarding number rounding", + "Fix dialog text being hardly readable in dark theme", + "Fix app not starting when the savegames were corrupted - there is now a better error message as well.", + "Further translation updates - Big thanks to all contributors!", + ], + }, + { + version: "1.1.10", + date: "12.06.2020", + entries: [ + "There are now linux builds on steam! Please report any issues in the Discord!", + "Steam cloud saves are now available!", + "Added and update more translations (Big thank you to all translators!)", + "Prevent invalid connection if existing underground tunnel entrance exists (by jaysc)", + ], + }, + { + version: "1.1.9", + date: "11.06.2020", + entries: [ + "Support for translations! Interested in helping out? Check out the translation guide!", + "Update stacker artwork to clarify how it works", + "Update keybinding hints on the top left to be more accurate", + "Make it more clear when blueprints are unlocked when trying to use them", + "Fix pinned shape icons not being visible in dark mode", + "Fix being able to select buildings via hotkeys in map overview mode", + "Make shapes unpinnable in the upgrades tab (By hexy)", + ], + }, + { + version: "1.1.8", + date: "07.06.2020", + entries: [ + "You can now purchase the standalone on steam! View steam page", + "Added ability to create markers in the demo, but only two.", + "Contest #01 has ended! I'll now work through the entries, select the 5 I like most and present them to the community to vote for!", + ], + }, + { + version: "1.1.7", + date: "04.06.2020", + entries: ["HOTFIX: Fix savegames not showing up on the standalone version"], + }, + { + version: "1.1.6", + date: "04.06.2020", + entries: [ + "The steam release will happen on the 7th of June - Be sure to add it to your wishlist! View on steam", + "Fixed level complete dialog being blurred when the shop was opened before", + "Standalone: Increased icon visibility for windows builds", + "Web version: Fixed firefox not loading the game when browsing in private mode", + ], + }, + + { + version: "1.1.5", + date: "03.06.2020", + entries: ["Added weekly contests!"], + }, + { + version: "1.1.4", + date: "01.06.2020", + entries: ["Add 'interactive' tutorial for the first level to improve onboarding experience"], + }, + { + version: "1.1.3", + date: "01.06.2020", + entries: [ + "Added setting to configure zoom / mouse wheel / touchpad sensitivity", + "Fix belts being too slow when copied via blueprint (by Dimava)", + "Allow binding mouse buttons to actions (by Dimava)", + "Increase readability of certain HUD elements", + ], + }, + { + version: "1.1.2", + date: "30.05.2020", + entries: [ + "The official trailer is now ready! Check it out here!", + "The steam page is now live!", + "Experimental linux builds are now available! Please give me feedback on them in the Discord", + "Allow hovering pinned shapes to enlarge them", + "Allow deselecting blueprints with right click and 'Q'", + "Move default key for deleting from 'X' to 'DEL'", + "Show confirmation when deleting more than 100 buildings", + "Reintroduce 'SPACE' keybinding to center on map", + "Improved keybinding hints", + "Fixed some keybindings showing as 'undefined'", + ], + }, + { + version: "1.1.1", + date: "28.05.2020", + entries: ["Fix crash when 'Show Hints' setting was turned off"], + }, + { + version: "1.1.0", + date: "28.05.2020", + entries: [ + "BLUEPRINTS! They are unlocked at level 12 and cost a special shape to build.", + "MAP MARKERS! Press 'M' to create a waypoint and be able to jump to it", + "Savegame levels are now shown in the main menu. For existing games, save them again to make the level show up.", + "Allow holding SHIFT to rotate counter clockwise", + "Added confirmation when deleting more than 500 buildings at a time", + "Added background to toolbar to increase contrast", + "Further decerase requirements of first levels", + "Pinned shapes now are saved", + "Allow placing extractors anywhere again, but they don't work at all if not placed on a resource", + "Show dialog explaining some keybindings after completing level 4", + "Fix keys being stuck when opening a dialog", + "Swapped shape order for painting upgrades", + "Allow changing all keybindings, including CTRL, ALT and SHIFT (by Dimava)", + "Fix cycling through keybindings selecting locked buildings as well (by Dimava)", + "There is now a github action, checking all pull requests with eslint. (by mrHedgehog)", + ], + }, + { + version: "1.0.4", + date: "26.05.2020", + entries: [ + "Reduce cost of first painting upgrade, and change 'Shape Processing' to 'Cutting, Rotating & Stacking'", + "Add dialog after completing level 2 to check out the upgrades tab.", + "Allow changing the keybindings in the demo version", + ], + }, + { + version: "1.0.3", + date: "24.05.2020", + entries: [ + "Reduced the amount of shapes required for the first 5 levels to make it easier to get into the game.", + ], + }, + { + version: "1.0.2", + date: "23.05.2020", + entries: [ + "Introduced changelog", + "Removed 'early access' label because the game isn't actually early access - its in a pretty good state already! (No worries, a lot more updates will follow!)", + "Added a 'Show hint' button which shows a small video for almost all levels to help out", + "Now showing proper descriptions when completing levels, with instructions on what the gained reward does.", + "Show a landing page on mobile devices about the game not being ready to be played on mobile yet", + "Fix painters and mixers being affected by the shape processors upgrade and not the painter one", + "Added 'multiplace' setting which is equivalent to holding SHIFT all the time", + "Added keybindings to zoom in / zoom out", + "Tunnels now also show connection lines to tunnel exits, instead of just tunnel entries", + "Lots of minor fixes and improvements", + ], + }, + { + version: "1.0.1", + date: "21.05.2020", + entries: ["Initial release!"], + }, +]; diff --git a/src/js/core/animation_frame.js b/src/js/core/animation_frame.js index 6aa629a5..dc846dd8 100644 --- a/src/js/core/animation_frame.js +++ b/src/js/core/animation_frame.js @@ -1,8 +1,5 @@ import { Signal } from "./signal"; -// @ts-ignore -import BackgroundAnimationFrameEmitterWorker from "../webworkers/background_animation_frame_emittter.worker"; - import { createLogger } from "./logging"; const logger = createLogger("animation_frame"); @@ -11,7 +8,9 @@ const resetDtMs = 16; export class AnimationFrame { constructor() { + /** @type {Signal<[number]>} */ this.frameEmitted = new Signal(); + /** @type {Signal<[number]>} */ this.bgFrameEmitted = new Signal(); this.lastTime = performance.now(); @@ -19,7 +18,9 @@ export class AnimationFrame { this.boundMethod = this.handleAnimationFrame.bind(this); - this.backgroundWorker = new BackgroundAnimationFrameEmitterWorker(); + this.backgroundWorker = new Worker( + new URL("../webworkers/background_animation_frame_emittter", import.meta.url) + ); this.backgroundWorker.addEventListener("error", err => { logger.error("Error in background fps worker:", err); }); @@ -40,7 +41,6 @@ export class AnimationFrame { } start() { - assertAlways(window.requestAnimationFrame, "requestAnimationFrame is not supported!"); this.handleAnimationFrame(); } @@ -51,11 +51,7 @@ export class AnimationFrame { dt = resetDtMs; } - try { - this.frameEmitted.dispatch(dt); - } catch (ex) { - console.error(ex); - } + this.frameEmitted.dispatch(dt); this.lastTime = time; window.requestAnimationFrame(this.boundMethod); } diff --git a/src/js/core/async_compression.js b/src/js/core/async_compression.js deleted file mode 100644 index ddc780cc..00000000 --- a/src/js/core/async_compression.js +++ /dev/null @@ -1,120 +0,0 @@ -// @ts-ignore -import CompressionWorker from "../webworkers/compression.worker"; - -import { createLogger } from "./logging"; -import { round2Digits } from "./utils"; - -const logger = createLogger("async_compression"); - -export let compressionPrefix = String.fromCodePoint(1); - -function checkCryptPrefix(prefix) { - try { - window.localStorage.setItem("prefix_test", prefix); - window.localStorage.removeItem("prefix_test"); - return true; - } catch (ex) { - logger.warn("Prefix '" + prefix + "' not available"); - return false; - } -} - -if (!checkCryptPrefix(compressionPrefix)) { - logger.warn("Switching to basic prefix"); - compressionPrefix = " "; - if (!checkCryptPrefix(compressionPrefix)) { - logger.warn("Prefix not available, ls seems to be unavailable"); - } -} - -/** - * @typedef {{ - * errorHandler: function(any) : void, - * resolver: function(any) : void, - * startTime: number - * }} JobEntry - */ - -class AsynCompression { - constructor() { - this.worker = new CompressionWorker(); - - this.currentJobId = 1000; - - /** @type {Object.} */ - this.currentJobs = {}; - - this.worker.addEventListener("message", event => { - const { jobId, result } = event.data; - const jobData = this.currentJobs[jobId]; - if (!jobData) { - logger.error("Failed to resolve job result, job id", jobId, "is not known"); - return; - } - - const duration = performance.now() - jobData.startTime; - logger.log( - "Got job", - jobId, - "response within", - round2Digits(duration), - "ms: ", - result.length, - "bytes" - ); - const resolver = jobData.resolver; - delete this.currentJobs[jobId]; - resolver(result); - }); - - this.worker.addEventListener("error", err => { - logger.error("Got error from webworker:", err, "aborting all jobs"); - const failureCalls = []; - for (const jobId in this.currentJobs) { - failureCalls.push(this.currentJobs[jobId].errorHandler); - } - this.currentJobs = {}; - for (let i = 0; i < failureCalls.length; ++i) { - failureCalls[i](err); - } - }); - } - - /** - * Compresses any object - * @param {any} obj - */ - compressObjectAsync(obj) { - logger.log("Compressing object async (optimized)"); - return this.internalQueueJob("compressObject", { - obj, - compressionPrefix, - }); - } - - /** - * Queues a new job - * @param {string} job - * @param {any} data - * @returns {Promise} - */ - internalQueueJob(job, data) { - const jobId = ++this.currentJobId; - return new Promise((resolve, reject) => { - const errorHandler = err => { - logger.error("Failed to compress job", jobId, ":", err); - reject(err); - }; - this.currentJobs[jobId] = { - errorHandler, - resolver: resolve, - startTime: performance.now(), - }; - - logger.log("Posting job", job, "/", jobId); - this.worker.postMessage({ jobId, job, data }); - }); - } -} - -export const asyncCompressor = new AsynCompression(); diff --git a/src/js/core/atlas_definitions.js b/src/js/core/atlas_definitions.js index 38d36b59..176b2364 100644 --- a/src/js/core/atlas_definitions.js +++ b/src/js/core/atlas_definitions.js @@ -41,10 +41,13 @@ export class AtlasDefinition { } /** @type {AtlasDefinition[]} **/ -export const atlasFiles = require - // @ts-ignore - .context("../../../res_built/atlas/", false, /.*\.json/i) - .keys() - .map(f => f.replace(/^\.\//gi, "")) - .map(f => require("../../../res_built/atlas/" + f)) - .map(data => new AtlasDefinition(data)); +export const atlasFiles = ( + await Promise.all( + import.meta + // @ts-ignore + .webpackContext("../../../res_built/atlas/", { recursive: false, regExp: /.*\.json/i }) + .keys() + .map(f => f.replace(/^\.\//gi, "")) + .map(f => import("../../../res_built/atlas/" + f)) + ) +).map(data => new AtlasDefinition(data.default)); diff --git a/src/js/core/background_resources_loader.js b/src/js/core/background_resources_loader.js index 01e74479..d0489503 100644 --- a/src/js/core/background_resources_loader.js +++ b/src/js/core/background_resources_loader.js @@ -1,257 +1,214 @@ -/* typehints:start */ -import { Application } from "../application"; -/* typehints:end */ - -import { initSpriteCache } from "../game/meta_building_registry"; -import { MUSIC, SOUNDS } from "../platform/sound"; -import { T } from "../translations"; -import { AtlasDefinition, atlasFiles } from "./atlas_definitions"; -import { cachebust } from "./cachebust"; -import { Loader } from "./loader"; -import { createLogger } from "./logging"; -import { Signal } from "./signal"; -import { clamp, getLogoSprite, timeoutPromise } from "./utils"; - -const logger = createLogger("background_loader"); - -const MAIN_MENU_ASSETS = { - sprites: [getLogoSprite()], - sounds: [SOUNDS.uiClick, SOUNDS.uiError, SOUNDS.dialogError, SOUNDS.dialogOk], - atlas: [], - css: [], -}; - -const INGAME_ASSETS = { - sprites: [], - sounds: [ - ...Array.from(Object.values(MUSIC)), - ...Array.from(Object.values(SOUNDS)).filter(sound => !MAIN_MENU_ASSETS.sounds.includes(sound)), - ], - atlas: atlasFiles, - css: ["async-resources.css"], -}; - -if (G_IS_STANDALONE) { - MAIN_MENU_ASSETS.sounds = [...Array.from(Object.values(MUSIC)), ...Array.from(Object.values(SOUNDS))]; - INGAME_ASSETS.sounds = []; -} - -const LOADER_TIMEOUT_PER_RESOURCE = 180000; - -// Cloudflare does not send content-length headers with brotli compression, -// so store the actual (compressed) file sizes so we can show a progress bar. -const HARDCODED_FILE_SIZES = { - "async-resources.css": 2216145, -}; - -export class BackgroundResourcesLoader { - /** - * - * @param {Application} app - */ - constructor(app) { - this.app = app; - - this.mainMenuPromise = null; - this.ingamePromise = null; - - this.resourceStateChangedSignal = new Signal(); - } - - getMainMenuPromise() { - if (this.mainMenuPromise) { - return this.mainMenuPromise; - } - - logger.log("⏰ Loading main menu assets"); - return (this.mainMenuPromise = this.loadAssets(MAIN_MENU_ASSETS)); - } - - getIngamePromise() { - if (this.ingamePromise) { - return this.ingamePromise; - } - logger.log("⏰ Loading ingame assets"); - const promise = this.loadAssets(INGAME_ASSETS).then(() => initSpriteCache()); - return (this.ingamePromise = promise); - } - - /** - * - * @param {object} param0 - * @param {string[]} param0.sprites - * @param {string[]} param0.sounds - * @param {AtlasDefinition[]} param0.atlas - * @param {string[]} param0.css - */ - async loadAssets({ sprites, sounds, atlas, css }) { - /** - * @type {((progressHandler: (progress: number) => void) => Promise)[]} - */ - let promiseFunctions = []; - - // CSS - for (let i = 0; i < css.length; ++i) { - promiseFunctions.push(progress => - timeoutPromise(this.internalPreloadCss(css[i], progress), LOADER_TIMEOUT_PER_RESOURCE).catch( - err => { - logger.error("Failed to load css:", css[i], err); - throw new Error("HUD Stylesheet " + css[i] + " failed to load: " + err); - } - ) - ); - } - - // ATLAS FILES - for (let i = 0; i < atlas.length; ++i) { - promiseFunctions.push(progress => - timeoutPromise(Loader.preloadAtlas(atlas[i], progress), LOADER_TIMEOUT_PER_RESOURCE).catch( - err => { - logger.error("Failed to load atlas:", atlas[i].sourceFileName, err); - throw new Error("Atlas " + atlas[i].sourceFileName + " failed to load: " + err); - } - ) - ); - } - - // HUD Sprites - for (let i = 0; i < sprites.length; ++i) { - promiseFunctions.push(progress => - timeoutPromise( - Loader.preloadCSSSprite(sprites[i], progress), - LOADER_TIMEOUT_PER_RESOURCE - ).catch(err => { - logger.error("Failed to load css sprite:", sprites[i], err); - throw new Error("HUD Sprite " + sprites[i] + " failed to load: " + err); - }) - ); - } - - // SFX & Music - for (let i = 0; i < sounds.length; ++i) { - promiseFunctions.push(progress => - timeoutPromise(this.app.sound.loadSound(sounds[i]), LOADER_TIMEOUT_PER_RESOURCE).catch( - err => { - logger.warn("Failed to load sound, will not be available:", sounds[i], err); - } - ) - ); - } - - const originalAmount = promiseFunctions.length; - const start = performance.now(); - - logger.log("⏰ Preloading", originalAmount, "assets"); - - let progress = 0; - this.resourceStateChangedSignal.dispatch({ progress }); - let promises = []; - - for (let i = 0; i < promiseFunctions.length; i++) { - let lastIndividualProgress = 0; - const progressHandler = individualProgress => { - const delta = clamp(individualProgress) - lastIndividualProgress; - lastIndividualProgress = clamp(individualProgress); - progress += delta / originalAmount; - this.resourceStateChangedSignal.dispatch({ progress }); - }; - promises.push( - promiseFunctions[i](progressHandler).then(() => { - progressHandler(1); - }) - ); - } - await Promise.all(promises); - - logger.log("⏰ Preloaded assets in", Math.round(performance.now() - start), "ms"); - } - - /** - * Shows an error when a resource failed to load and allows to reload the game - */ - showLoaderError(dialogs, err) { - if (G_IS_STANDALONE) { - dialogs - .showWarning( - T.dialogs.resourceLoadFailed.title, - T.dialogs.resourceLoadFailed.descSteamDemo + "
" + err, - ["retry"] - ) - .retry.add(() => window.location.reload()); - } else { - dialogs - .showWarning( - T.dialogs.resourceLoadFailed.title, - T.dialogs.resourceLoadFailed.descWeb.replace( - "", - `${T.dialogs.resourceLoadFailed.demoLinkText}` - ) + - "
" + - err, - ["retry"] - ) - .retry.add(() => window.location.reload()); - } - } - - preloadWithProgress(src, progressHandler) { - return new Promise((resolve, reject) => { - const xhr = new XMLHttpRequest(); - let notifiedNotComputable = false; - - const fullUrl = cachebust(src); - xhr.open("GET", fullUrl, true); - xhr.responseType = "arraybuffer"; - xhr.onprogress = function (ev) { - if (ev.lengthComputable) { - progressHandler(ev.loaded / ev.total); - } else { - if (window.location.search.includes("alwaysLogFileSize")) { - console.warn("Progress:", src, ev.loaded); - } - - if (HARDCODED_FILE_SIZES[src]) { - progressHandler(clamp(ev.loaded / HARDCODED_FILE_SIZES[src])); - } else { - if (!notifiedNotComputable) { - notifiedNotComputable = true; - console.warn("Progress not computable:", src, ev.loaded); - progressHandler(0); - } - } - } - }; - - xhr.onloadend = function () { - if (!xhr.status.toString().match(/^2/)) { - reject(fullUrl + ": " + xhr.status + " " + xhr.statusText); - } else { - if (!notifiedNotComputable) { - progressHandler(1); - } - - const options = {}; - const headers = xhr.getAllResponseHeaders(); - const contentType = headers.match(/^Content-Type:\s*(.*?)$/im); - if (contentType && contentType[1]) { - options.type = contentType[1].split(";")[0]; - } - const blob = new Blob([this.response], options); - resolve(window.URL.createObjectURL(blob)); - } - }; - xhr.send(); - }); - } - - internalPreloadCss(src, progressHandler) { - return this.preloadWithProgress(src, progressHandler).then(blobSrc => { - var styleElement = document.createElement("link"); - styleElement.href = blobSrc; - styleElement.rel = "stylesheet"; - styleElement.setAttribute("media", "all"); - styleElement.type = "text/css"; - document.head.appendChild(styleElement); - }); - } -} +/* typehints:start */ +import { Application } from "../application"; +/* typehints:end */ + +import { initSpriteCache } from "../game/meta_building_registry"; +import { MUSIC, SOUNDS } from "../platform/sound"; +import { T } from "../translations"; +import { AtlasDefinition, atlasFiles } from "./atlas_definitions"; +import { Loader } from "./loader"; +import { createLogger } from "./logging"; +import { Signal } from "./signal"; +import { clamp, timeoutPromise } from "./utils"; + +const logger = createLogger("background_loader"); + +const MAIN_MENU_ASSETS = { + sprites: ["logo.png"], + sounds: [SOUNDS.uiClick, SOUNDS.uiError, SOUNDS.dialogError, SOUNDS.dialogOk], + atlas: [], + css: [], +}; + +const INGAME_ASSETS = { + sprites: [], + sounds: [ + ...Array.from(Object.values(MUSIC)), + ...Array.from(Object.values(SOUNDS)).filter(sound => !MAIN_MENU_ASSETS.sounds.includes(sound)), + ], + atlas: atlasFiles, + css: ["async-resources.css"], +}; + +MAIN_MENU_ASSETS.sounds = [...Array.from(Object.values(MUSIC)), ...Array.from(Object.values(SOUNDS))]; +INGAME_ASSETS.sounds = []; + +const LOADER_TIMEOUT_PER_RESOURCE = 180000; + +export class BackgroundResourcesLoader { + /** + * + * @param {Application} app + */ + constructor(app) { + this.app = app; + + this.mainMenuPromise = null; + this.ingamePromise = null; + + /** @type {Signal<[{ progress: number }]>} */ + this.resourceStateChangedSignal = new Signal(); + } + + getMainMenuPromise() { + if (this.mainMenuPromise) { + return this.mainMenuPromise; + } + + logger.log("⏰ Loading main menu assets"); + return (this.mainMenuPromise = this.loadAssets(MAIN_MENU_ASSETS)); + } + + getIngamePromise() { + if (this.ingamePromise) { + return this.ingamePromise; + } + logger.log("⏰ Loading ingame assets"); + const promise = this.loadAssets(INGAME_ASSETS).then(() => initSpriteCache()); + return (this.ingamePromise = promise); + } + + /** + * + * @param {object} param0 + * @param {string[]} param0.sprites + * @param {string[]} param0.sounds + * @param {AtlasDefinition[]} param0.atlas + * @param {string[]} param0.css + */ + async loadAssets({ sprites, sounds, atlas, css }) { + /** + * @type {((progressHandler: (progress: number) => void) => Promise)[]} + */ + const promiseFunctions = []; + + // CSS + for (let i = 0; i < css.length; ++i) { + promiseFunctions.push(progress => + timeoutPromise(this.internalPreloadCss(css[i], progress), LOADER_TIMEOUT_PER_RESOURCE).catch( + err => { + logger.error("Failed to load css:", css[i], err); + throw new Error("HUD Stylesheet " + css[i] + " failed to load: " + err); + } + ) + ); + } + + // ATLAS FILES + for (let i = 0; i < atlas.length; ++i) { + promiseFunctions.push(progress => + timeoutPromise(Loader.preloadAtlas(atlas[i], progress), LOADER_TIMEOUT_PER_RESOURCE).catch( + err => { + logger.error("Failed to load atlas:", atlas[i].sourceFileName, err); + throw new Error("Atlas " + atlas[i].sourceFileName + " failed to load: " + err); + } + ) + ); + } + + // HUD Sprites + for (let i = 0; i < sprites.length; ++i) { + promiseFunctions.push(progress => + timeoutPromise( + Loader.preloadCSSSprite(sprites[i], progress), + LOADER_TIMEOUT_PER_RESOURCE + ).catch(err => { + logger.error("Failed to load css sprite:", sprites[i], err); + throw new Error("HUD Sprite " + sprites[i] + " failed to load: " + err); + }) + ); + } + + // SFX & Music + for (let i = 0; i < sounds.length; ++i) { + promiseFunctions.push(() => + timeoutPromise(this.app.sound.loadSound(sounds[i]), LOADER_TIMEOUT_PER_RESOURCE).catch( + err => { + logger.warn("Failed to load sound, will not be available:", sounds[i], err); + } + ) + ); + } + + const originalAmount = promiseFunctions.length; + const start = performance.now(); + + logger.log("⏰ Preloading", originalAmount, "assets"); + + let progress = 0; + this.resourceStateChangedSignal.dispatch({ progress }); + const promises = []; + + for (let i = 0; i < promiseFunctions.length; i++) { + let lastIndividualProgress = 0; + const progressHandler = individualProgress => { + const delta = clamp(individualProgress) - lastIndividualProgress; + lastIndividualProgress = clamp(individualProgress); + progress += delta / originalAmount; + this.resourceStateChangedSignal.dispatch({ progress }); + }; + promises.push( + promiseFunctions[i](progressHandler).then(() => { + progressHandler(1); + }) + ); + } + await Promise.all(promises); + + logger.log("⏰ Preloaded assets in", Math.round(performance.now() - start), "ms"); + } + + /** + * Shows an error when a resource failed to load and allows to reload the game + */ + showLoaderError(dialogs, err) { + dialogs + .showWarning( + T.dialogs.resourceLoadFailed.title, + T.dialogs.resourceLoadFailed.descSteamDemo + "
" + err, + ["retry"] + ) + .retry.add(() => window.location.reload()); + } + + preloadWithProgress(src, progressHandler) { + return new Promise((resolve, reject) => { + const xhr = new XMLHttpRequest(); + + xhr.open("GET", src, true); + xhr.responseType = "arraybuffer"; + xhr.onprogress = function (ev) { + if (ev.lengthComputable) { + progressHandler(ev.loaded / ev.total); + } + }; + + xhr.onloadend = function () { + if (!xhr.status.toString().match(/^2/)) { + reject(src + ": " + xhr.status + " " + xhr.statusText); + } else { + const options = {}; + const headers = xhr.getAllResponseHeaders(); + const contentType = headers.match(/^Content-Type:\s*(.*?)$/im); + if (contentType && contentType[1]) { + options.type = contentType[1].split(";")[0]; + } + const blob = new Blob([this.response], options); + resolve(window.URL.createObjectURL(blob)); + } + }; + xhr.send(); + }); + } + + internalPreloadCss(src, progressHandler) { + return this.preloadWithProgress(src, progressHandler).then(blobSrc => { + const styleElement = document.createElement("link"); + styleElement.href = blobSrc; + styleElement.rel = "stylesheet"; + styleElement.setAttribute("media", "all"); + styleElement.type = "text/css"; + document.head.appendChild(styleElement); + }); + } +} diff --git a/src/js/core/buffer_maintainer.js b/src/js/core/buffer_maintainer.js index 1d6bffb9..410a8244 100644 --- a/src/js/core/buffer_maintainer.js +++ b/src/js/core/buffer_maintainer.js @@ -1,7 +1,6 @@ import { GameRoot } from "../game/root"; -import { clearBufferBacklog, freeCanvas, getBufferStats, makeOffscreenBuffer } from "./buffer_utils"; +import { clearBufferBacklog, freeCanvas, makeOffscreenBuffer } from "./buffer_utils"; import { createLogger } from "./logging"; -import { round1Digit } from "./utils"; /** * @typedef {{ @@ -35,7 +34,7 @@ export class BufferMaintainer { * Returns the buffer stats */ getStats() { - let stats = { + const stats = { rootKeys: 0, subKeys: 0, vramBytes: 0, @@ -59,25 +58,16 @@ export class BufferMaintainer { * for a few iterations */ garbargeCollect() { - let totalKeys = 0; - let deletedKeys = 0; const minIteration = this.iterationIndex; this.cache.forEach((subCache, key) => { - let unusedSubKeys = []; + const unusedSubKeys = []; // Filter sub cache subCache.forEach((cacheEntry, subKey) => { - if ( - cacheEntry.lastUse < minIteration || - // @ts-ignore - cacheEntry.canvas._contextLost - ) { + if (cacheEntry.lastUse < minIteration) { unusedSubKeys.push(subKey); freeCanvas(cacheEntry.canvas); - ++deletedKeys; - } else { - ++totalKeys; } }); @@ -90,30 +80,6 @@ export class BufferMaintainer { // Make sure our backlog never gets too big clearBufferBacklog(); - // if (G_IS_DEV) { - // const bufferStats = getBufferStats(); - // const mbUsed = round1Digit(bufferStats.vramUsage / (1024 * 1024)); - // logger.log( - // "GC: Remove", - // (deletedKeys + "").padStart(4), - // ", Remain", - // (totalKeys + "").padStart(4), - // "(", - // (bufferStats.bufferCount + "").padStart(4), - // "total", - // ")", - - // "(", - // (bufferStats.backlogSize + "").padStart(4), - // "backlog", - // ")", - - // "VRAM:", - // mbUsed, - // "MB" - // ); - // } - ++this.iterationIndex; } @@ -180,7 +146,7 @@ export class BufferMaintainer { * */ getForKeyOrNullNoUpdate({ key, subKey }) { - let parent = this.cache.get(key); + const parent = this.cache.get(key); if (!parent) { return null; } diff --git a/src/js/core/buffer_utils.js b/src/js/core/buffer_utils.js index 0048b214..9434a5e8 100644 --- a/src/js/core/buffer_utils.js +++ b/src/js/core/buffer_utils.js @@ -1,30 +1,9 @@ import { globalConfig } from "./config"; -import { fastArrayDelete } from "./utils"; import { createLogger } from "./logging"; +import { fastArrayDelete } from "./utils"; const logger = createLogger("buffer_utils"); -/** - * Enables images smoothing on a context - * @param {CanvasRenderingContext2D} context - */ -export function enableImageSmoothing(context) { - context.imageSmoothingEnabled = true; - context.webkitImageSmoothingEnabled = true; - - // @ts-ignore - context.imageSmoothingQuality = globalConfig.smoothing.quality; -} - -/** - * Disables image smoothing on a context - * @param {CanvasRenderingContext2D} context - */ -export function disableImageSmoothing(context) { - context.imageSmoothingEnabled = false; - context.webkitImageSmoothingEnabled = false; -} - /** * @typedef {{ * canvas: HTMLCanvasElement, @@ -103,15 +82,7 @@ export function clearBufferBacklog() { * @returns {[HTMLCanvasElement, CanvasRenderingContext2D]} */ export function makeOffscreenBuffer(w, h, { smooth = true, reusable = true, label = "buffer" }) { - assert(w > 0 && h > 0, "W or H < 0"); - if (w % 1 !== 0 || h % 1 !== 0) { - // console.warn("Subpixel offscreen buffer size:", w, h); - } - if (w < 1 || h < 1) { - logger.error("Offscreen buffer size < 0:", w, "x", h); - w = Math.max(1, w); - h = Math.max(1, h); - } + assert(w >= 1 && h >= 1, "Invalid offscreen buffer size: W or H < 1"); const recommendedSize = 1024 * 1024; if (w * h > recommendedSize) { @@ -161,29 +132,13 @@ export function makeOffscreenBuffer(w, h, { smooth = true, reusable = true, labe // Initial state context.save(); - - canvas.addEventListener("webglcontextlost", () => { - console.warn("canvas::webglcontextlost", canvas); - // @ts-ignore - canvas._contextLost = true; - }); - canvas.addEventListener("contextlost", () => { - console.warn("canvas::contextlost", canvas); - // @ts-ignore - canvas._contextLost = true; - }); } - // @ts-ignore - canvas._contextLost = false; // @ts-ignore canvas.label = label; - if (smooth) { - enableImageSmoothing(context); - } else { - disableImageSmoothing(context); - } + context.imageSmoothingEnabled = smooth; + context.imageSmoothingQuality = globalConfig.smoothing.quality; if (reusable) { registerCanvas(canvas, context); diff --git a/src/js/core/cachebust.js b/src/js/core/cachebust.js deleted file mode 100644 index 26be0449..00000000 --- a/src/js/core/cachebust.js +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Generates a cachebuster string. This only modifies the path in the browser version - * @param {string} path - */ -export function cachebust(path) { - if (G_IS_BROWSER && !G_IS_STANDALONE && !G_IS_DEV) { - return "/v/" + G_BUILD_COMMIT_HASH + "/" + path; - } - return path; -} diff --git a/src/js/core/click_detector.js b/src/js/core/click_detector.js index fb62f0f1..9b1b52c9 100644 --- a/src/js/core/click_detector.js +++ b/src/js/core/click_detector.js @@ -1,465 +1,474 @@ -import { createLogger } from "../core/logging"; -import { Signal } from "../core/signal"; -import { fastArrayDelete, fastArrayDeleteValueIfContained } from "./utils"; -import { Vector } from "./vector"; -import { IS_MOBILE, SUPPORT_TOUCH } from "./config"; -import { SOUNDS } from "../platform/sound"; -import { GLOBAL_APP } from "./globals"; - -const logger = createLogger("click_detector"); - -export const MAX_MOVE_DISTANCE_PX = IS_MOBILE ? 20 : 80; - -// For debugging -const registerClickDetectors = G_IS_DEV && true; -if (registerClickDetectors) { - /** @type {Array} */ - window.activeClickDetectors = []; -} - -// Store active click detectors so we can cancel them -/** @type {Array} */ -const ongoingClickDetectors = []; - -// Store when the last touch event was registered, to avoid accepting a touch *and* a click event - -export let clickDetectorGlobals = { - lastTouchTime: -1000, -}; - -/** - * Click detector creation payload typehints - * @typedef {{ - * consumeEvents?: boolean, - * preventDefault?: boolean, - * applyCssClass?: string, - * captureTouchmove?: boolean, - * targetOnly?: boolean, - * maxDistance?: number, - * clickSound?: string, - * preventClick?: boolean, - * }} ClickDetectorConstructorArgs - */ - -// Detects clicks -export class ClickDetector { - /** - * - * @param {Element} element - * @param {object} param1 - * @param {boolean=} param1.consumeEvents Whether to call stopPropagation - * (Useful for nested elements where the parent has a click handler as wel) - * @param {boolean=} param1.preventDefault Whether to call preventDefault (Usually makes the handler faster) - * @param {string=} param1.applyCssClass The css class to add while the element is pressed - * @param {boolean=} param1.captureTouchmove Whether to capture touchmove events as well - * @param {boolean=} param1.targetOnly Whether to also accept clicks on child elements (e.target !== element) - * @param {number=} param1.maxDistance The maximum distance in pixels to accept clicks - * @param {string=} param1.clickSound Sound key to play on touchdown - * @param {boolean=} param1.preventClick Whether to prevent click events - */ - constructor( - element, - { - consumeEvents = false, - preventDefault = true, - applyCssClass = "pressed", - captureTouchmove = false, - targetOnly = false, - maxDistance = MAX_MOVE_DISTANCE_PX, - clickSound = SOUNDS.uiClick, - preventClick = false, - } - ) { - assert(element, "No element given!"); - this.clickDownPosition = null; - - this.consumeEvents = consumeEvents; - this.preventDefault = preventDefault; - this.applyCssClass = applyCssClass; - this.captureTouchmove = captureTouchmove; - this.targetOnly = targetOnly; - this.clickSound = clickSound; - this.maxDistance = maxDistance; - this.preventClick = preventClick; - - // Signals - this.click = new Signal(); - this.rightClick = new Signal(); - this.touchstart = new Signal(); - this.touchmove = new Signal(); - this.touchend = new Signal(); - this.touchcancel = new Signal(); - - // Simple signals which just receive the touch position - this.touchstartSimple = new Signal(); - this.touchmoveSimple = new Signal(); - this.touchendSimple = new Signal(); - - // Store time of touch start - this.clickStartTime = null; - - // A click can be cancelled if another detector registers a click - this.cancelled = false; - - this.internalBindTo(/** @type {HTMLElement} */ (element)); - } - - /** - * Cleans up all event listeners of this detector - */ - cleanup() { - if (this.element) { - if (registerClickDetectors) { - const index = window.activeClickDetectors.indexOf(this); - if (index < 0) { - logger.error("Click detector cleanup but is not active"); - } else { - window.activeClickDetectors.splice(index, 1); - } - } - const options = this.internalGetEventListenerOptions(); - - if (SUPPORT_TOUCH) { - this.element.removeEventListener("touchstart", this.handlerTouchStart, options); - this.element.removeEventListener("touchend", this.handlerTouchEnd, options); - this.element.removeEventListener("touchcancel", this.handlerTouchCancel, options); - } - - this.element.removeEventListener("mouseup", this.handlerTouchStart, options); - this.element.removeEventListener("mousedown", this.handlerTouchEnd, options); - this.element.removeEventListener("mouseout", this.handlerTouchCancel, options); - - if (this.captureTouchmove) { - if (SUPPORT_TOUCH) { - this.element.removeEventListener("touchmove", this.handlerTouchMove, options); - } - this.element.removeEventListener("mousemove", this.handlerTouchMove, options); - } - - if (this.preventClick) { - this.element.removeEventListener("click", this.handlerPreventClick, options); - } - - this.click.removeAll(); - this.touchstart.removeAll(); - this.touchmove.removeAll(); - this.touchend.removeAll(); - this.touchcancel.removeAll(); - - this.element = null; - } - } - - // INTERNAL METHODS - - /** - * - * @param {Event} event - */ - internalPreventClick(event) { - window.focus(); - event.preventDefault(); - } - - /** - * Internal method to get the options to pass to an event listener - */ - internalGetEventListenerOptions() { - return { - capture: this.consumeEvents, - passive: !this.preventDefault, - }; - } - - /** - * Binds the click detector to an element - * @param {HTMLElement} element - */ - internalBindTo(element) { - const options = this.internalGetEventListenerOptions(); - - this.handlerTouchStart = this.internalOnPointerDown.bind(this); - this.handlerTouchEnd = this.internalOnPointerEnd.bind(this); - this.handlerTouchMove = this.internalOnPointerMove.bind(this); - this.handlerTouchCancel = this.internalOnTouchCancel.bind(this); - - if (this.preventClick) { - this.handlerPreventClick = this.internalPreventClick.bind(this); - element.addEventListener("click", this.handlerPreventClick, options); - } - - if (SUPPORT_TOUCH) { - element.addEventListener("touchstart", this.handlerTouchStart, options); - element.addEventListener("touchend", this.handlerTouchEnd, options); - element.addEventListener("touchcancel", this.handlerTouchCancel, options); - } - - element.addEventListener("mousedown", this.handlerTouchStart, options); - element.addEventListener("mouseup", this.handlerTouchEnd, options); - element.addEventListener("mouseout", this.handlerTouchCancel, options); - - if (this.captureTouchmove) { - if (SUPPORT_TOUCH) { - element.addEventListener("touchmove", this.handlerTouchMove, options); - } - element.addEventListener("mousemove", this.handlerTouchMove, options); - } - - if (registerClickDetectors) { - window.activeClickDetectors.push(this); - } - this.element = element; - } - - /** - * Returns if the bound element is currently in the DOM. - */ - internalIsDomElementAttached() { - return this.element && document.documentElement.contains(this.element); - } - - /** - * Checks if the given event is relevant for this detector - * @param {TouchEvent|MouseEvent} event - */ - internalEventPreHandler(event, expectedRemainingTouches = 1) { - if (!this.element) { - // Already cleaned up - return false; - } - - if (this.targetOnly && event.target !== this.element) { - // Clicked a child element - return false; - } - - // Stop any propagation and defaults if configured - if (this.consumeEvents && event.cancelable) { - event.stopPropagation(); - } - - if (this.preventDefault && event.cancelable) { - event.preventDefault(); - } - - if (window.TouchEvent && event instanceof TouchEvent) { - clickDetectorGlobals.lastTouchTime = performance.now(); - - // console.log("Got touches", event.targetTouches.length, "vs", expectedRemainingTouches); - if (event.targetTouches.length !== expectedRemainingTouches) { - return false; - } - } - - if (event instanceof MouseEvent) { - if (performance.now() - clickDetectorGlobals.lastTouchTime < 1000.0) { - return false; - } - } - - return true; - } - - /** - * Extracts the mous position from an event - * @param {TouchEvent|MouseEvent} event - * @returns {Vector} The client space position - */ - static extractPointerPosition(event) { - if (window.TouchEvent && event instanceof TouchEvent) { - if (event.changedTouches.length !== 1) { - logger.warn( - "Got unexpected target touches:", - event.targetTouches.length, - "->", - event.targetTouches - ); - return new Vector(0, 0); - } - - const touch = event.changedTouches[0]; - return new Vector(touch.clientX, touch.clientY); - } - - if (event instanceof MouseEvent) { - return new Vector(event.clientX, event.clientY); - } - - assertAlways(false, "Got unknown event: " + event); - - return new Vector(0, 0); - } - - /** - * Cacnels all ongoing events on this detector - */ - cancelOngoingEvents() { - if (this.applyCssClass && this.element) { - this.element.classList.remove(this.applyCssClass); - } - this.clickDownPosition = null; - this.clickStartTime = null; - this.cancelled = true; - fastArrayDeleteValueIfContained(ongoingClickDetectors, this); - } - - /** - * Internal pointer down handler - * @param {TouchEvent|MouseEvent} event - */ - internalOnPointerDown(event) { - window.focus(); - - if (!this.internalEventPreHandler(event, 1)) { - return false; - } - - const position = /** @type {typeof ClickDetector} */ (this.constructor).extractPointerPosition(event); - - if (event instanceof MouseEvent) { - const isRightClick = event.button === 2; - if (isRightClick) { - // Ignore right clicks - this.rightClick.dispatch(position, event); - this.cancelled = true; - this.clickDownPosition = null; - return; - } - } - - if (this.clickDownPosition) { - logger.warn("Ignoring double click"); - return false; - } - - this.cancelled = false; - this.touchstart.dispatch(event); - - // Store where the touch started - this.clickDownPosition = position; - this.clickStartTime = performance.now(); - this.touchstartSimple.dispatch(this.clickDownPosition.x, this.clickDownPosition.y); - - // If we are not currently within a click, register it - if (ongoingClickDetectors.indexOf(this) < 0) { - ongoingClickDetectors.push(this); - } else { - logger.warn("Click detector got pointer down of active pointer twice"); - } - - // If we should apply any classes, do this now - if (this.applyCssClass) { - this.element.classList.add(this.applyCssClass); - } - - // If we should play any sound, do this - if (this.clickSound) { - GLOBAL_APP.sound.playUiSound(this.clickSound); - } - - return false; - } - - /** - * Internal pointer move handler - * @param {TouchEvent|MouseEvent} event - */ - internalOnPointerMove(event) { - if (!this.internalEventPreHandler(event, 1)) { - return false; - } - this.touchmove.dispatch(event); - const pos = /** @type {typeof ClickDetector} */ (this.constructor).extractPointerPosition(event); - this.touchmoveSimple.dispatch(pos.x, pos.y); - return false; - } - - /** - * Internal pointer end handler - * @param {TouchEvent|MouseEvent} event - */ - internalOnPointerEnd(event) { - window.focus(); - - if (!this.internalEventPreHandler(event, 0)) { - return false; - } - - if (this.cancelled) { - // warn(this, "Not dispatching touchend on cancelled listener"); - return false; - } - - if (event instanceof MouseEvent) { - const isRightClick = event.button === 2; - if (isRightClick) { - return; - } - } - - const index = ongoingClickDetectors.indexOf(this); - if (index < 0) { - logger.warn("Got pointer end but click detector is not in pressed state"); - } else { - fastArrayDelete(ongoingClickDetectors, index); - } - - let dispatchClick = false; - let dispatchClickPos = null; - - // Check for correct down position, otherwise must have pinched or so - if (this.clickDownPosition) { - const pos = /** @type {typeof ClickDetector} */ (this.constructor).extractPointerPosition(event); - const distance = pos.distance(this.clickDownPosition); - if (!IS_MOBILE || distance <= this.maxDistance) { - dispatchClick = true; - dispatchClickPos = pos; - } else { - console.warn("[ClickDetector] Touch does not count as click:", "(was", distance, ")"); - } - } - - this.clickDownPosition = null; - this.clickStartTime = null; - - if (this.applyCssClass) { - this.element.classList.remove(this.applyCssClass); - } - - // Dispatch in the end to avoid the element getting invalidated - // Also make sure that the element is still in the dom - if (this.internalIsDomElementAttached()) { - this.touchend.dispatch(event); - this.touchendSimple.dispatch(); - - if (dispatchClick) { - const detectors = ongoingClickDetectors.slice(); - for (let i = 0; i < detectors.length; ++i) { - detectors[i].cancelOngoingEvents(); - } - this.click.dispatch(dispatchClickPos, event); - } - } - return false; - } - - /** - * Internal touch cancel handler - * @param {TouchEvent|MouseEvent} event - */ - internalOnTouchCancel(event) { - if (!this.internalEventPreHandler(event, 0)) { - return false; - } - - if (this.cancelled) { - // warn(this, "Not dispatching touchcancel on cancelled listener"); - return false; - } - - this.cancelOngoingEvents(); - this.touchcancel.dispatch(event); - this.touchendSimple.dispatch(event); - return false; - } -} +import { createLogger } from "../core/logging"; +import { Signal } from "../core/signal"; +import { fastArrayDelete, fastArrayDeleteValueIfContained } from "./utils"; +import { Vector } from "./vector"; +import { IS_MOBILE, SUPPORT_TOUCH } from "./config"; +import { SOUNDS } from "../platform/sound"; +import { GLOBAL_APP } from "./globals"; + +const logger = createLogger("click_detector"); + +export const MAX_MOVE_DISTANCE_PX = IS_MOBILE ? 20 : 80; + +// For debugging +const registerClickDetectors = G_IS_DEV && true; +if (registerClickDetectors) { + /** @type {Array} */ + window.activeClickDetectors = []; +} + +// Store active click detectors so we can cancel them +/** @type {Array} */ +const ongoingClickDetectors = []; + +// Store when the last touch event was registered, to avoid accepting a touch *and* a click event + +export let clickDetectorGlobals = { + lastTouchTime: -1000, +}; + +/** + * Click detector creation payload typehints + * @typedef {{ + * consumeEvents?: boolean, + * preventDefault?: boolean, + * applyCssClass?: string, + * captureTouchmove?: boolean, + * targetOnly?: boolean, + * maxDistance?: number, + * clickSound?: string, + * preventClick?: boolean, + * }} ClickDetectorConstructorArgs + */ + +// Detects clicks +export class ClickDetector { + /** + * + * @param {Element} element + * @param {object} param1 + * @param {boolean=} param1.consumeEvents Whether to call stopPropagation + * (Useful for nested elements where the parent has a click handler as wel) + * @param {boolean=} param1.preventDefault Whether to call preventDefault (Usually makes the handler faster) + * @param {string=} param1.applyCssClass The css class to add while the element is pressed + * @param {boolean=} param1.captureTouchmove Whether to capture touchmove events as well + * @param {boolean=} param1.targetOnly Whether to also accept clicks on child elements (e.target !== element) + * @param {number=} param1.maxDistance The maximum distance in pixels to accept clicks + * @param {string=} param1.clickSound Sound key to play on touchdown + * @param {boolean=} param1.preventClick Whether to prevent click events + */ + constructor( + element, + { + consumeEvents = false, + preventDefault = true, + applyCssClass = "pressed", + captureTouchmove = false, + targetOnly = false, + maxDistance = MAX_MOVE_DISTANCE_PX, + clickSound = SOUNDS.uiClick, + preventClick = false, + } + ) { + assert(element, "No element given!"); + this.clickDownPosition = null; + + this.consumeEvents = consumeEvents; + this.preventDefault = preventDefault; + this.applyCssClass = applyCssClass; + this.captureTouchmove = captureTouchmove; + this.targetOnly = targetOnly; + this.clickSound = clickSound; + this.maxDistance = maxDistance; + this.preventClick = preventClick; + + // Signals + /** @type {Signal<[Vector, TouchEvent | MouseEvent]>} */ + this.click = new Signal(); + /** @type {Signal<[Vector, MouseEvent]>} */ + this.rightClick = new Signal(); + /** @type {Signal<[TouchEvent | MouseEvent]>} */ + this.touchstart = new Signal(); + /** @type {Signal<[TouchEvent | MouseEvent]>} */ + this.touchmove = new Signal(); + /** @type {Signal<[TouchEvent | MouseEvent]>} */ + this.touchend = new Signal(); + /** @type {Signal<[TouchEvent | MouseEvent]>} */ + this.touchcancel = new Signal(); + + // Simple signals which just receive the touch position + /** @type {Signal<[number, number]>} */ + this.touchstartSimple = new Signal(); + /** @type {Signal<[number, number]>} */ + this.touchmoveSimple = new Signal(); + /** @type {Signal<[(TouchEvent | MouseEvent)?]>} */ + this.touchendSimple = new Signal(); + + // Store time of touch start + this.clickStartTime = null; + + // A click can be cancelled if another detector registers a click + this.cancelled = false; + + this.internalBindTo(/** @type {HTMLElement} */ (element)); + } + + /** + * Cleans up all event listeners of this detector + */ + cleanup() { + if (this.element) { + if (registerClickDetectors) { + const index = window.activeClickDetectors.indexOf(this); + if (index < 0) { + logger.error("Click detector cleanup but is not active"); + } else { + window.activeClickDetectors.splice(index, 1); + } + } + const options = this.internalGetEventListenerOptions(); + + if (SUPPORT_TOUCH) { + this.element.removeEventListener("touchstart", this.handlerTouchStart, options); + this.element.removeEventListener("touchend", this.handlerTouchEnd, options); + this.element.removeEventListener("touchcancel", this.handlerTouchCancel, options); + } + + this.element.removeEventListener("mouseup", this.handlerTouchStart, options); + this.element.removeEventListener("mousedown", this.handlerTouchEnd, options); + this.element.removeEventListener("mouseout", this.handlerTouchCancel, options); + + if (this.captureTouchmove) { + if (SUPPORT_TOUCH) { + this.element.removeEventListener("touchmove", this.handlerTouchMove, options); + } + this.element.removeEventListener("mousemove", this.handlerTouchMove, options); + } + + if (this.preventClick) { + this.element.removeEventListener("click", this.handlerPreventClick, options); + } + + this.click.removeAll(); + this.touchstart.removeAll(); + this.touchmove.removeAll(); + this.touchend.removeAll(); + this.touchcancel.removeAll(); + + this.element = null; + } + } + + // INTERNAL METHODS + + /** + * + * @param {Event} event + */ + internalPreventClick(event) { + window.focus(); + event.preventDefault(); + } + + /** + * Internal method to get the options to pass to an event listener + */ + internalGetEventListenerOptions() { + return { + capture: this.consumeEvents, + passive: !this.preventDefault, + }; + } + + /** + * Binds the click detector to an element + * @param {HTMLElement} element + */ + internalBindTo(element) { + const options = this.internalGetEventListenerOptions(); + + this.handlerTouchStart = this.internalOnPointerDown.bind(this); + this.handlerTouchEnd = this.internalOnPointerEnd.bind(this); + this.handlerTouchMove = this.internalOnPointerMove.bind(this); + this.handlerTouchCancel = this.internalOnTouchCancel.bind(this); + + if (this.preventClick) { + this.handlerPreventClick = this.internalPreventClick.bind(this); + element.addEventListener("click", this.handlerPreventClick, options); + } + + if (SUPPORT_TOUCH) { + element.addEventListener("touchstart", this.handlerTouchStart, options); + element.addEventListener("touchend", this.handlerTouchEnd, options); + element.addEventListener("touchcancel", this.handlerTouchCancel, options); + } + + element.addEventListener("mousedown", this.handlerTouchStart, options); + element.addEventListener("mouseup", this.handlerTouchEnd, options); + element.addEventListener("mouseout", this.handlerTouchCancel, options); + + if (this.captureTouchmove) { + if (SUPPORT_TOUCH) { + element.addEventListener("touchmove", this.handlerTouchMove, options); + } + element.addEventListener("mousemove", this.handlerTouchMove, options); + } + + if (registerClickDetectors) { + window.activeClickDetectors.push(this); + } + this.element = element; + } + + /** + * Returns if the bound element is currently in the DOM. + */ + internalIsDomElementAttached() { + return this.element && document.documentElement.contains(this.element); + } + + /** + * Checks if the given event is relevant for this detector + * @param {TouchEvent|MouseEvent} event + */ + internalEventPreHandler(event, expectedRemainingTouches = 1) { + if (!this.element) { + // Already cleaned up + return false; + } + + if (this.targetOnly && event.target !== this.element) { + // Clicked a child element + return false; + } + + // Stop any propagation and defaults if configured + if (this.consumeEvents && event.cancelable) { + event.stopPropagation(); + } + + if (this.preventDefault && event.cancelable) { + event.preventDefault(); + } + + if (window.TouchEvent && event instanceof TouchEvent) { + clickDetectorGlobals.lastTouchTime = performance.now(); + + // console.log("Got touches", event.targetTouches.length, "vs", expectedRemainingTouches); + if (event.targetTouches.length !== expectedRemainingTouches) { + return false; + } + } + + if (event instanceof MouseEvent) { + if (performance.now() - clickDetectorGlobals.lastTouchTime < 1000.0) { + return false; + } + } + + return true; + } + + /** + * Extracts the mous position from an event + * @param {TouchEvent|MouseEvent} event + * @returns {Vector} The client space position + */ + static extractPointerPosition(event) { + if (window.TouchEvent && event instanceof TouchEvent) { + if (event.changedTouches.length !== 1) { + logger.warn( + "Got unexpected target touches:", + event.targetTouches.length, + "->", + event.targetTouches + ); + return new Vector(0, 0); + } + + const touch = event.changedTouches[0]; + return new Vector(touch.clientX, touch.clientY); + } + + if (event instanceof MouseEvent) { + return new Vector(event.clientX, event.clientY); + } + + assertAlways(false, "Got unknown event: " + event); + + return new Vector(0, 0); + } + + /** + * Cacnels all ongoing events on this detector + */ + cancelOngoingEvents() { + if (this.applyCssClass && this.element) { + this.element.classList.remove(this.applyCssClass); + } + this.clickDownPosition = null; + this.clickStartTime = null; + this.cancelled = true; + fastArrayDeleteValueIfContained(ongoingClickDetectors, this); + } + + /** + * Internal pointer down handler + * @param {TouchEvent|MouseEvent} event + */ + internalOnPointerDown(event) { + window.focus(); + + if (!this.internalEventPreHandler(event, 1)) { + return false; + } + + const position = /** @type {typeof ClickDetector} */ (this.constructor).extractPointerPosition(event); + + if (event instanceof MouseEvent) { + const isRightClick = event.button === 2; + if (isRightClick) { + // Ignore right clicks + this.rightClick.dispatch(position, event); + this.cancelled = true; + this.clickDownPosition = null; + return; + } + } + + if (this.clickDownPosition) { + logger.warn("Ignoring double click"); + return false; + } + + this.cancelled = false; + this.touchstart.dispatch(event); + + // Store where the touch started + this.clickDownPosition = position; + this.clickStartTime = performance.now(); + this.touchstartSimple.dispatch(this.clickDownPosition.x, this.clickDownPosition.y); + + // If we are not currently within a click, register it + if (ongoingClickDetectors.indexOf(this) < 0) { + ongoingClickDetectors.push(this); + } else { + logger.warn("Click detector got pointer down of active pointer twice"); + } + + // If we should apply any classes, do this now + if (this.applyCssClass) { + this.element.classList.add(this.applyCssClass); + } + + // If we should play any sound, do this + if (this.clickSound) { + GLOBAL_APP.sound.playUiSound(this.clickSound); + } + + return false; + } + + /** + * Internal pointer move handler + * @param {TouchEvent|MouseEvent} event + */ + internalOnPointerMove(event) { + if (!this.internalEventPreHandler(event, 1)) { + return false; + } + this.touchmove.dispatch(event); + const pos = /** @type {typeof ClickDetector} */ (this.constructor).extractPointerPosition(event); + this.touchmoveSimple.dispatch(pos.x, pos.y); + return false; + } + + /** + * Internal pointer end handler + * @param {TouchEvent|MouseEvent} event + */ + internalOnPointerEnd(event) { + window.focus(); + + if (!this.internalEventPreHandler(event, 0)) { + return false; + } + + if (this.cancelled) { + // warn(this, "Not dispatching touchend on cancelled listener"); + return false; + } + + if (event instanceof MouseEvent) { + const isRightClick = event.button === 2; + if (isRightClick) { + return; + } + } + + const index = ongoingClickDetectors.indexOf(this); + if (index < 0) { + logger.warn("Got pointer end but click detector is not in pressed state"); + } else { + fastArrayDelete(ongoingClickDetectors, index); + } + + let dispatchClick = false; + let dispatchClickPos = null; + + // Check for correct down position, otherwise must have pinched or so + if (this.clickDownPosition) { + const pos = /** @type {typeof ClickDetector} */ (this.constructor).extractPointerPosition(event); + const distance = pos.distance(this.clickDownPosition); + if (!IS_MOBILE || distance <= this.maxDistance) { + dispatchClick = true; + dispatchClickPos = pos; + } else { + console.warn("[ClickDetector] Touch does not count as click:", "(was", distance, ")"); + } + } + + this.clickDownPosition = null; + this.clickStartTime = null; + + if (this.applyCssClass) { + this.element.classList.remove(this.applyCssClass); + } + + // Dispatch in the end to avoid the element getting invalidated + // Also make sure that the element is still in the dom + if (this.internalIsDomElementAttached()) { + this.touchend.dispatch(event); + this.touchendSimple.dispatch(); + + if (dispatchClick) { + const detectors = ongoingClickDetectors.slice(); + for (let i = 0; i < detectors.length; ++i) { + detectors[i].cancelOngoingEvents(); + } + this.click.dispatch(dispatchClickPos, event); + } + } + return false; + } + + /** + * Internal touch cancel handler + * @param {TouchEvent|MouseEvent} event + */ + internalOnTouchCancel(event) { + if (!this.internalEventPreHandler(event, 0)) { + return false; + } + + if (this.cancelled) { + // warn(this, "Not dispatching touchcancel on cancelled listener"); + return false; + } + + this.cancelOngoingEvents(); + this.touchcancel.dispatch(event); + this.touchendSimple.dispatch(event); + return false; + } +} diff --git a/src/js/core/compression.ts b/src/js/core/compression.ts new file mode 100644 index 00000000..e131332b --- /dev/null +++ b/src/js/core/compression.ts @@ -0,0 +1,56 @@ +export interface Compression { + compress(data: unknown): Promise; + decompress(data: Uint8Array): Promise; +} + +type CompressionWorkerResponse = { result: T; error: null } | { result: null; error: Error }; + +export class DefaultCompression implements Compression { + compress(data: unknown): Promise { + const { promise, reject, resolve } = Promise.withResolvers(); + + // NOTE: new URL(...) has to be inlined for webpack to process it correctly + const worker = new Worker(new URL("../webworkers/compression", import.meta.url)); + worker.addEventListener("error", ev => reject(ev.message)); + + worker.addEventListener("message", ev => { + const response = ev.data as CompressionWorkerResponse; + if (response.error !== null) { + reject(response.error); + return; + } + + resolve(response.result); + }); + + this.scheduleWorkerTermination(worker); + worker.postMessage(data); + return promise; + } + + decompress(data: Uint8Array): Promise { + const { promise, reject, resolve } = Promise.withResolvers(); + + const worker = new Worker(new URL("../webworkers/decompression", import.meta.url)); + worker.addEventListener("error", ev => reject(new Error(ev.message))); + + worker.addEventListener("message", ev => { + const response = ev.data as CompressionWorkerResponse; + if (response.error !== null) { + reject(response.error); + return; + } + + resolve(response.result); + }); + + this.scheduleWorkerTermination(worker); + worker.postMessage(data, [data.buffer]); + return promise; + } + + private scheduleWorkerTermination(worker: Worker): void { + worker.addEventListener("message", () => worker.terminate(), { once: true }); + worker.addEventListener("error", () => worker.terminate(), { once: true }); + } +} diff --git a/src/js/core/config.js b/src/js/core/config.js deleted file mode 100644 index d5a04832..00000000 --- a/src/js/core/config.js +++ /dev/null @@ -1,179 +0,0 @@ -/* typehints:start */ -import { Application } from "../application"; -/* typehints:end */ - -export const IS_DEBUG = - G_IS_DEV && - typeof window !== "undefined" && - window.location.port === "3005" && - (window.location.host.indexOf("localhost:") >= 0 || window.location.host.indexOf("192.168.0.") >= 0) && - window.location.search.indexOf("nodebug") < 0; - -export const SUPPORT_TOUCH = false; - -const smoothCanvas = true; - -export const THIRDPARTY_URLS = { - discord: "https://discord.gg/HN7EVzV", - github: "https://github.com/tobspr-games/shapez.io", - reddit: "https://www.reddit.com/r/shapezio", - shapeViewer: "https://viewer.shapez.io", - - twitter: "https://twitter.com/tobspr", - patreon: "https://www.patreon.com/tobsprgames", - privacyPolicy: "https://tobspr.io/privacy.html", - - standaloneCampaignLink: "https://get.shapez.io/bundle/$campaign", - puzzleDlcStorePage: "https://get.shapez.io/mm_puzzle_dlc?target=dlc", - - levelTutorialVideos: { - 21: "https://www.youtube.com/watch?v=0nUfRLMCcgo&", - 25: "https://www.youtube.com/watch?v=7OCV1g40Iew&", - 26: "https://www.youtube.com/watch?v=gfm6dS1dCoY", - }, - - modBrowser: "https://shapez.mod.io/", -}; - -/** - * @param {Application} app - * @param {string} campaign - */ -export function openStandaloneLink(app, campaign) { - const discount = globalConfig.currentDiscount > 0 ? "_discount" + globalConfig.currentDiscount : ""; - const steamSuffix = G_IS_STEAM_DEMO ? "_steamdemo" : ""; - const event = campaign + discount + steamSuffix; - app.platformWrapper.openExternalLink(THIRDPARTY_URLS.standaloneCampaignLink.replace("$campaign", event)); - app.gameAnalytics.noteMinor("g.stdlink." + event); -} - -export const globalConfig = { - // Size of a single tile in Pixels. - // NOTICE: Update webpack.production.config too! - tileSize: 32, - halfTileSize: 16, - - // Which dpi the assets have - assetsDpi: 192 / 32, - assetsSharpness: 1.5, - shapesSharpness: 1.3, - - // Achievements - achievementSliceDuration: 10, // Seconds - - // Production analytics - statisticsGraphDpi: 2.5, - statisticsGraphSlices: 100, - analyticsSliceDurationSeconds: G_IS_DEV ? 1 : 10, - - minimumTickRate: 25, - maximumTickRate: 500, - - // Map - mapChunkSize: 16, - chunkAggregateSize: 4, - mapChunkOverviewMinZoom: 0.9, - mapChunkWorldSize: null, // COMPUTED - - maxBeltShapeBundleSize: 20, - - // Belt speeds - // NOTICE: Update webpack.production.config too! - beltSpeedItemsPerSecond: 2, - minerSpeedItemsPerSecond: 0, // COMPUTED - - defaultItemDiameter: 20, - - itemSpacingOnBelts: 0.63, - - wiresSpeedItemsPerSecond: 6, - - undergroundBeltMaxTilesByTier: [5, 9], - - readerAnalyzeIntervalSeconds: 10, - - goalAcceptorItemsRequired: 12, - goalAcceptorsPerProducer: 5, - puzzleModeSpeed: 3, - puzzleMinBoundsSize: 2, - puzzleMaxBoundsSize: 20, - puzzleValidationDurationSeconds: 30, - - buildingSpeeds: { - cutter: 1 / 4, - cutterQuad: 1 / 4, - rotater: 1 / 1, - rotaterCCW: 1 / 1, - rotater180: 1 / 1, - painter: 1 / 6, - painterDouble: 1 / 8, - painterQuad: 1 / 2, - mixer: 1 / 5, - stacker: 1 / 8, - }, - - // Zooming - initialZoom: 1.9, - minZoomLevel: 0.1, - maxZoomLevel: 3, - - // Global game speed - gameSpeed: 1, - - warmupTimeSecondsFast: 0.25, - warmupTimeSecondsRegular: 0.25, - - smoothing: { - smoothMainCanvas: smoothCanvas && true, - quality: "low", // Low is CRUCIAL for mobile performance! - }, - - rendering: {}, - debug: require("./config.local").default, - - currentDiscount: 0, - - // Secret vars - info: { - // Binary file salt - file: "Ec'])@^+*9zMevK3uMV4432x9%iK'=", - - // Savegame salt - sgSalt: "}95Q3%8/.837Lqym_BJx%q7)pAHJbF", - - // Analytics key - analyticsApiKey: "baf6a50f0cc7dfdec5a0e21c88a1c69a4b34bc4a", - }, -}; - -export const IS_MOBILE = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent); - -// Automatic calculations -globalConfig.minerSpeedItemsPerSecond = globalConfig.beltSpeedItemsPerSecond / 5; - -globalConfig.mapChunkWorldSize = globalConfig.mapChunkSize * globalConfig.tileSize; - -// Dynamic calculations -if (globalConfig.debug.disableMapOverview) { - globalConfig.mapChunkOverviewMinZoom = 0; -} - -// Stuff for making the trailer -if (G_IS_DEV && globalConfig.debug.renderForTrailer) { - globalConfig.debug.framePausesBetweenTicks = 32; - // globalConfig.mapChunkOverviewMinZoom = 0.0; - // globalConfig.debug.instantBelts = true; - // globalConfig.debug.instantProcessors = true; - // globalConfig.debug.instantMiners = true; - globalConfig.debug.disableSavegameWrite = true; - // globalConfig.beltSpeedItemsPerSecond *= 2; -} - -if (globalConfig.debug.fastGameEnter) { - globalConfig.debug.noArtificialDelays = true; -} - -if (G_IS_DEV && globalConfig.debug.noArtificialDelays) { - globalConfig.warmupTimeSecondsFast = 0; - globalConfig.warmupTimeSecondsRegular = 0; -} diff --git a/src/js/core/config.local.template.js b/src/js/core/config.local.template.js index 9a432b56..d1f6cea0 100644 --- a/src/js/core/config.local.template.js +++ b/src/js/core/config.local.template.js @@ -1,126 +1,114 @@ -export default { - // You can set any debug options here! - /* dev:start */ - // ----------------------------------------------------------------------------------- - // Quickly enters the game and skips the main menu - good for fast iterating - // fastGameEnter: true, - // ----------------------------------------------------------------------------------- - // Skips any delays like transitions between states and such - // noArtificialDelays: true, - // ----------------------------------------------------------------------------------- - // Disables writing of savegames, useful for testing the same savegame over and over - // disableSavegameWrite: true, - // ----------------------------------------------------------------------------------- - // Shows bounds of all entities - // showEntityBounds: true, - // ----------------------------------------------------------------------------------- - // Shows arrows for every ejector / acceptor - // showAcceptorEjectors: true, - // ----------------------------------------------------------------------------------- - // Disables the music (Overrides any setting, can cause weird behaviour) - // disableMusic: true, - // ----------------------------------------------------------------------------------- - // Do not render static map entities (=most buildings) - // doNotRenderStatics: true, - // ----------------------------------------------------------------------------------- - // Allow to zoom freely without limits - // disableZoomLimits: true, - // ----------------------------------------------------------------------------------- - // All rewards can be unlocked by passing just 1 of any shape - // rewardsInstant: true, - // ----------------------------------------------------------------------------------- - // Unlocks all buildings - // allBuildingsUnlocked: true, - // ----------------------------------------------------------------------------------- - // Disables cost of blueprints - // blueprintsNoCost: true, - // ----------------------------------------------------------------------------------- - // Disables cost of upgrades - // upgradesNoCost: true, - // ----------------------------------------------------------------------------------- - // Disables the dialog when completing a level - // disableUnlockDialog: true, - // ----------------------------------------------------------------------------------- - // Disables the simulation - This effectively pauses the game. - // disableLogicTicks: true, - // ----------------------------------------------------------------------------------- - // Test the rendering if everything is clipped out properly - // testClipping: true, - // ----------------------------------------------------------------------------------- - // Allows to render slower, useful for recording at half speed to avoid stuttering - // framePausesBetweenTicks: 250, - // ----------------------------------------------------------------------------------- - // Replace all translations with emojis to see which texts are translateable - // testTranslations: true, - // ----------------------------------------------------------------------------------- - // Enables an inspector which shows information about the entity below the cursor - // enableEntityInspector: true, - // ----------------------------------------------------------------------------------- - // Enables ads in the local build (normally they are deactivated there) - // testAds: true, - // ----------------------------------------------------------------------------------- - // Allows unlocked achievements to be logged to console in the local build - // testAchievements: true, - // ----------------------------------------------------------------------------------- - // Enables use of (some) existing flags within the puzzle mode context - // testPuzzleMode: true, - // ----------------------------------------------------------------------------------- - // Disables the automatic switch to an overview when zooming out - // disableMapOverview: true, - // ----------------------------------------------------------------------------------- - // Disables the notification when there are new entries in the changelog since last played - // disableUpgradeNotification: true, - // ----------------------------------------------------------------------------------- - // Makes belts almost infinitely fast - // instantBelts: true, - // ----------------------------------------------------------------------------------- - // Makes item processors almost infinitely fast - // instantProcessors: true, - // ----------------------------------------------------------------------------------- - // Makes miners almost infinitely fast - // instantMiners: true, - // ----------------------------------------------------------------------------------- - // When using fastGameEnter, controls whether a new game is started or the last one is resumed - // resumeGameOnFastEnter: true, - // ----------------------------------------------------------------------------------- - // Special option used to render the trailer - // renderForTrailer: true, - // ----------------------------------------------------------------------------------- - // Whether to render changes - // renderChanges: true, - // ----------------------------------------------------------------------------------- - // Whether to render belt paths - // renderBeltPaths: true, - // ----------------------------------------------------------------------------------- - // Whether to check belt paths - // checkBeltPaths: true, - // ----------------------------------------------------------------------------------- - // Whether to items / s instead of items / m in stats - // detailedStatistics: true, - // ----------------------------------------------------------------------------------- - // Shows detailed information about which atlas is used - // showAtlasInfo: true, - // ----------------------------------------------------------------------------------- - // Renders the rotation of all wires - // renderWireRotations: true, - // ----------------------------------------------------------------------------------- - // Renders information about wire networks - // renderWireNetworkInfos: true, - // ----------------------------------------------------------------------------------- - // Disables ejector animations and processing - // disableEjectorProcessing: true, - // ----------------------------------------------------------------------------------- - // Allows manual ticking - // manualTickOnly: true, - // ----------------------------------------------------------------------------------- - // Disables slow asserts, useful for debugging performance - // disableSlowAsserts: true, - // ----------------------------------------------------------------------------------- - // Allows to load a mod from an external source for developing it - // externalModUrl: "http://localhost:3005/combined.js", - // ----------------------------------------------------------------------------------- - // Visualizes the shape grouping on belts - // showShapeGrouping: true - // ----------------------------------------------------------------------------------- - /* dev:end */ -}; +export default { + // You can set any debug options here! + /* dev:start */ + // ----------------------------------------------------------------------------------- + // Quickly enters the game and skips the main menu - good for fast iterating + // fastGameEnter: true, + // ----------------------------------------------------------------------------------- + // Skips any delays like transitions between states and such + // noArtificialDelays: true, + // ----------------------------------------------------------------------------------- + // Disables writing of savegames, useful for testing the same savegame over and over + // disableSavegameWrite: true, + // ----------------------------------------------------------------------------------- + // Shows bounds of all entities + // showEntityBounds: true, + // ----------------------------------------------------------------------------------- + // Shows arrows for every ejector / acceptor + // showAcceptorEjectors: true, + // ----------------------------------------------------------------------------------- + // Disables the music (Overrides any setting, can cause weird behaviour) + // disableMusic: true, + // ----------------------------------------------------------------------------------- + // Do not render static map entities (=most buildings) + // doNotRenderStatics: true, + // ----------------------------------------------------------------------------------- + // Allow to zoom freely without limits + // disableZoomLimits: true, + // ----------------------------------------------------------------------------------- + // All rewards can be unlocked by passing just 1 of any shape + // rewardsInstant: true, + // ----------------------------------------------------------------------------------- + // Unlocks all buildings + // allBuildingsUnlocked: true, + // ----------------------------------------------------------------------------------- + // Disables cost of blueprints + // blueprintsNoCost: true, + // ----------------------------------------------------------------------------------- + // Disables cost of upgrades + // upgradesNoCost: true, + // ----------------------------------------------------------------------------------- + // Disables the dialog when completing a level + // disableUnlockDialog: true, + // ----------------------------------------------------------------------------------- + // Disables the simulation - This effectively pauses the game. + // disableLogicTicks: true, + // ----------------------------------------------------------------------------------- + // Test the rendering if everything is clipped out properly + // testClipping: true, + // ----------------------------------------------------------------------------------- + // Allows to render slower, useful for recording at half speed to avoid stuttering + // framePausesBetweenTicks: 250, + // ----------------------------------------------------------------------------------- + // Replace all translations with emojis to see which texts are translateable + // testTranslations: true, + // ----------------------------------------------------------------------------------- + // Enables an inspector which shows information about the entity below the cursor + // enableEntityInspector: true, + // ----------------------------------------------------------------------------------- + // Enables use of (some) existing flags within the puzzle mode context + // testPuzzleMode: true, + // ----------------------------------------------------------------------------------- + // Disables the automatic switch to an overview when zooming out + // disableMapOverview: true, + // ----------------------------------------------------------------------------------- + // Disables the notification when there are new entries in the changelog since last played + // disableUpgradeNotification: true, + // ----------------------------------------------------------------------------------- + // Makes belts almost infinitely fast + // instantBelts: true, + // ----------------------------------------------------------------------------------- + // Makes item processors almost infinitely fast + // instantProcessors: true, + // ----------------------------------------------------------------------------------- + // Makes miners almost infinitely fast + // instantMiners: true, + // ----------------------------------------------------------------------------------- + // When using fastGameEnter, controls whether a new game is started or the last one is resumed + // resumeGameOnFastEnter: true, + // ----------------------------------------------------------------------------------- + // Whether to render changes + // renderChanges: true, + // ----------------------------------------------------------------------------------- + // Whether to render belt paths + // renderBeltPaths: true, + // ----------------------------------------------------------------------------------- + // Whether to check belt paths + // checkBeltPaths: true, + // ----------------------------------------------------------------------------------- + // Whether to items / s instead of items / m in stats + // detailedStatistics: true, + // ----------------------------------------------------------------------------------- + // Shows detailed information about which atlas is used + // showAtlasInfo: true, + // ----------------------------------------------------------------------------------- + // Renders the rotation of all wires + // renderWireRotations: true, + // ----------------------------------------------------------------------------------- + // Renders information about wire networks + // renderWireNetworkInfos: true, + // ----------------------------------------------------------------------------------- + // Disables ejector animations and processing + // disableEjectorProcessing: true, + // ----------------------------------------------------------------------------------- + // Allows manual ticking + // manualTickOnly: true, + // ----------------------------------------------------------------------------------- + // Disables slow asserts, useful for debugging performance + // disableSlowAsserts: true, + // ----------------------------------------------------------------------------------- + // Visualizes the shape grouping on belts + // showShapeGrouping: true + // ----------------------------------------------------------------------------------- + /* dev:end */ +}; diff --git a/src/js/core/config.ts b/src/js/core/config.ts new file mode 100644 index 00000000..aaa116c6 --- /dev/null +++ b/src/js/core/config.ts @@ -0,0 +1,105 @@ +import debug from "./config.local"; + +export const THIRDPARTY_URLS = { + discord: "https://discord.gg/HN7EVzV", + github: "https://github.com/tobspr-games/shapez.io", + reddit: "https://www.reddit.com/r/shapezio", + shapeViewer: "https://viewer.shapez.io", + + patreon: "https://www.patreon.com/tobsprgames", + privacyPolicy: "https://tobspr.io/privacy.html", + + levelTutorialVideos: { + 21: "https://www.youtube.com/watch?v=0nUfRLMCcgo&", + 25: "https://www.youtube.com/watch?v=7OCV1g40Iew&", + 26: "https://www.youtube.com/watch?v=gfm6dS1dCoY", + }, +}; + +export const globalConfig = { + // Size of a single tile in Pixels. + tileSize: 32, + halfTileSize: 16, + + // Which dpi the assets have + assetsDpi: 192 / 32, + assetsSharpness: 1.5, + shapesSharpness: 1.3, + + // Production analytics + statisticsGraphDpi: 2.5, + statisticsGraphSlices: 100, + analyticsSliceDurationSeconds: G_IS_DEV ? 1 : 10, + + // Map + mapChunkSize: 16, + chunkAggregateSize: 4, + mapChunkOverviewMinZoom: 0.9, + mapChunkWorldSize: null, // COMPUTED + + maxBeltShapeBundleSize: 20, + + // Belt speeds + beltSpeedItemsPerSecond: 2, + minerSpeedItemsPerSecond: 0, // COMPUTED + + defaultItemDiameter: 20, + + itemSpacingOnBelts: 0.63, + + undergroundBeltMaxTilesByTier: [5, 9], + + readerAnalyzeIntervalSeconds: 10, + + goalAcceptorItemsRequired: 12, + goalAcceptorsPerProducer: 5, + puzzleModeSpeed: 3, + puzzleMinBoundsSize: 2, + puzzleMaxBoundsSize: 20, + puzzleValidationDurationSeconds: 30, + + buildingSpeeds: { + cutter: 1 / 4, + cutterQuad: 1 / 4, + rotator: 1 / 1, + rotatorCCW: 1 / 1, + rotator180: 1 / 1, + painter: 1 / 6, + painterDouble: 1 / 8, + painterQuad: 1 / 2, + mixer: 1 / 5, + stacker: 1 / 8, + }, + + warmupTimeSecondsFast: 0.25, + warmupTimeSecondsRegular: 0.25, + + smoothing: { + smoothMainCanvas: true, + quality: "low" as ImageSmoothingQuality, // Low is CRUCIAL for mobile performance! + }, + + debug, +}; + +export const IS_MOBILE = navigator.userAgentData.mobile; +export const SUPPORT_TOUCH = IS_MOBILE; + +// Automatic calculations +globalConfig.minerSpeedItemsPerSecond = globalConfig.beltSpeedItemsPerSecond / 5; + +globalConfig.mapChunkWorldSize = globalConfig.mapChunkSize * globalConfig.tileSize; + +// Dynamic calculations +if (globalConfig.debug.disableMapOverview) { + globalConfig.mapChunkOverviewMinZoom = 0; +} + +if (globalConfig.debug.fastGameEnter) { + globalConfig.debug.noArtificialDelays = true; +} + +if (G_IS_DEV && globalConfig.debug.noArtificialDelays) { + globalConfig.warmupTimeSecondsFast = 0; + globalConfig.warmupTimeSecondsRegular = 0; +} diff --git a/src/js/core/dpi_manager.js b/src/js/core/dpi_manager.js index 4fb792c0..842dd779 100644 --- a/src/js/core/dpi_manager.js +++ b/src/js/core/dpi_manager.js @@ -41,13 +41,9 @@ export function prepareHighDPIContext(context, smooth = true) { if (smooth) { context.imageSmoothingEnabled = true; - context.webkitImageSmoothingEnabled = true; - - // @ts-ignore context.imageSmoothingQuality = globalConfig.smoothing.quality; } else { context.imageSmoothingEnabled = false; - context.webkitImageSmoothingEnabled = false; } } diff --git a/src/js/core/draw_parameters.js b/src/js/core/draw_parameters.js index 95e71bf2..193c788d 100644 --- a/src/js/core/draw_parameters.js +++ b/src/js/core/draw_parameters.js @@ -1,25 +1,25 @@ -import { globalConfig } from "./config"; - -/** - * @typedef {import("../game/root").GameRoot} GameRoot - * @typedef {import("./rectangle").Rectangle} Rectangle - */ - -export class DrawParameters { - constructor({ context, visibleRect, desiredAtlasScale, zoomLevel, root }) { - /** @type {CanvasRenderingContext2D} */ - this.context = context; - - /** @type {Rectangle} */ - this.visibleRect = visibleRect; - - /** @type {string} */ - this.desiredAtlasScale = desiredAtlasScale; - - /** @type {number} */ - this.zoomLevel = zoomLevel; - - /** @type {GameRoot} */ - this.root = root; - } -} +import { globalConfig } from "./config"; + +/** + * @typedef {import("../game/root").GameRoot} GameRoot + * @typedef {import("./rectangle").Rectangle} Rectangle + */ + +export class DrawParameters { + constructor({ context, visibleRect, desiredAtlasScale, zoomLevel, root }) { + /** @type {CanvasRenderingContext2D} */ + this.context = context; + + /** @type {Rectangle} */ + this.visibleRect = visibleRect; + + /** @type {string} */ + this.desiredAtlasScale = desiredAtlasScale; + + /** @type {number} */ + this.zoomLevel = zoomLevel; + + /** @type {GameRoot} */ + this.root = root; + } +} diff --git a/src/js/core/draw_utils.js b/src/js/core/draw_utils.js index d5183cfb..93652d11 100644 --- a/src/js/core/draw_utils.js +++ b/src/js/core/draw_utils.js @@ -9,42 +9,6 @@ import { Rectangle } from "./rectangle"; const logger = createLogger("draw_utils"); -export function initDrawUtils() { - CanvasRenderingContext2D.prototype.beginRoundedRect = function (x, y, w, h, r) { - this.beginPath(); - - if (r < 0.05) { - this.rect(x, y, w, h); - return; - } - - if (w < 2 * r) { - r = w / 2; - } - - if (h < 2 * r) { - r = h / 2; - } - - this.moveTo(x + r, y); - this.arcTo(x + w, y, x + w, y + h, r); - this.arcTo(x + w, y + h, x, y + h, r); - this.arcTo(x, y + h, x, y, r); - this.arcTo(x, y, x + w, y, r); - }; - - CanvasRenderingContext2D.prototype.beginCircle = function (x, y, r) { - this.beginPath(); - - if (r < 0.05) { - this.rect(x, y, 1, 1); - return; - } - - this.arc(x, y, r, 0, 2.0 * Math.PI); - }; -} - /** * * @param {object} param0 @@ -83,8 +47,20 @@ let warningsShown = 0; * @param {number} param0.h * @param {number} param0.originalW * @param {number} param0.originalH + * @param {boolean=} param0.pixelAligned + * Whether to round the canvas coordinates, to avoid issues with transparency between tiling images */ -export function drawSpriteClipped({ parameters, sprite, x, y, w, h, originalW, originalH }) { +export function drawSpriteClipped({ + parameters, + sprite, + x, + y, + w, + h, + originalW, + originalH, + pixelAligned = false, +}) { const rect = new Rectangle(x, y, w, h); const intersection = rect.getIntersection(parameters.visibleRect); if (!intersection) { @@ -103,6 +79,39 @@ export function drawSpriteClipped({ parameters, sprite, x, y, w, h, originalW, o return; } + if (!pixelAligned) { + parameters.context.drawImage( + sprite, + + // src pos and size + ((intersection.x - x) / w) * originalW, + ((intersection.y - y) / h) * originalH, + (originalW * intersection.w) / w, + (originalH * intersection.h) / h, + + // dest pos and size + intersection.x, + intersection.y, + intersection.w, + intersection.h + ); + return; + } + + const matrix = parameters.context.getTransform(); + let { x: x1, y: y1 } = matrix.transformPoint(new DOMPoint(intersection.x, intersection.y)); + let { x: x2, y: y2 } = matrix.transformPoint( + new DOMPoint(intersection.x + intersection.w, intersection.y + intersection.h) + ); + x1 = Math.round(x1); + y1 = Math.round(y1); + x2 = Math.round(x2); + y2 = Math.round(y2); + if (x2 - x1 == 0 || y2 - y1 == 0) { + return; + } + + parameters.context.resetTransform(); parameters.context.drawImage( sprite, @@ -113,9 +122,10 @@ export function drawSpriteClipped({ parameters, sprite, x, y, w, h, originalW, o (originalH * intersection.h) / h, // dest pos and size - intersection.x, - intersection.y, - intersection.w, - intersection.h + x1, + y1, + x2 - x1, + y2 - y1 ); + parameters.context.setTransform(matrix); } diff --git a/src/js/core/error_handler.tsx b/src/js/core/error_handler.tsx new file mode 100644 index 00000000..e2e3bd38 --- /dev/null +++ b/src/js/core/error_handler.tsx @@ -0,0 +1,160 @@ +import { MODS } from "@/mods/modloader"; +import { T } from "@/translations"; +import copy from "clipboard-copy"; +import { BUILD_OPTIONS } from "./globals"; +import { removeAllChildren } from "./utils"; + +export class ErrorHandler { + isActive = true; + + constructor() { + window.addEventListener("error", this.onError.bind(this)); + window.addEventListener("unhandledrejection", this.onUnhandledRejection.bind(this)); + } + + private onError(ev: ErrorEvent) { + if (!this.isActive) { + return; + } + + // Don't trigger more than once + this.isActive = false; + + const screen = new ErrorScreen(ev.error, ev.filename, ev.lineno, ev.colno); + screen.show(); + } + + private onUnhandledRejection(ev: PromiseRejectionEvent) { + const error = ev.reason instanceof Error ? ev.reason : new Error(ev.reason); + + // Avoid logging the error twice + ev.preventDefault(); + + // Turn the unhandled rejection into a regular error event + throw new Error("Unhandled Promise rejection", { cause: error }); + } +} + +export class ErrorScreen { + private error: Error; + private file?: string; + private line?: number; + private column?: number; + + constructor(error: Error, file?: string, line?: number, column?: number) { + this.error = error; + this.file = file; + this.line = line; + this.column = column; + } + + show() { + // Set the global to stop future callback handlers + window.APP_ERROR_OCCURED = true; + + removeAllChildren(document.body); + document.body.id = "errorHandler"; + document.body.className = ""; + + const layout = this.createLayout(); + if (Array.isArray(layout)) { + document.body.append(...layout); + } else { + document.body.append(layout); + } + } + + private createLayout(): HTMLElement | HTMLElement[] { + const btnCopy = ; + const btnRestart = ; + + btnCopy.addEventListener("click", this.copyErrorLog.bind(this)); + btnRestart.addEventListener("click", this.restart.bind(this)); + + return ( + <> +
+

{T.errorHandler.title}

+
{this.source}
+
+
{this.recursiveStack}
+
+ {T.errorHandler.labels.loadedMods} +
{this.loadedMods}
+
+
+ {T.errorHandler.labels.buildInformation} +
{this.buildInformation}
+
+
+ {btnCopy} + {btnRestart} +
+ + ); + } + + private copyErrorLog(ev: MouseEvent) { + let log = `shapez Error Log - ${new Date().toISOString()}\n\n`; + + log += this.recursiveStack; + log += `\n\nLoaded Mods:\n${this.loadedMods}`; + log += `\n\nBuild Information:\n${this.buildInformation}`; + + copy(log); + + if (ev.target instanceof HTMLButtonElement) { + ev.target.innerText = T.errorHandler.actions.copyDone; + ev.target.classList.add("success"); + } + } + + private restart() { + // performRestart may not be available yet + location.reload(); + } + + private get source(): string { + return `${this.file} (${this.line}:${this.column})`; + } + + private get recursiveStack(): string { + // Follow the error cause chain + let current = this.error; + let stack = current.stack; + + while (current.cause instanceof Error) { + current = current.cause; + stack += `\nCaused by: ${current.stack}`; + } + + return stack; + } + + private get loadedMods(): string { + const mods: string[] = []; + const activeMods = MODS.activeMods; + + for (const mod of MODS.allMods) { + const isActive = activeMods.includes(mod); + const prefix = isActive ? "*" : ""; + + const id = mod.mod.id; + const version = mod.mod.metadata.version; + + mods.push(`${prefix}${id}@${version} (${mod.source})`); + } + + return mods.join("\n"); + } + + private get buildInformation(): string { + const info: string[] = []; + + for (const [key, value] of Object.entries(BUILD_OPTIONS)) { + info.push(`${key}: ${JSON.stringify(value)}`); + } + + return info.join("\n"); + } +} diff --git a/src/js/core/factory.js b/src/js/core/factory.ts similarity index 69% rename from src/js/core/factory.js rename to src/js/core/factory.ts index f2587f69..b30513b4 100644 --- a/src/js/core/factory.js +++ b/src/js/core/factory.ts @@ -3,21 +3,19 @@ import { createLogger } from "./logging"; const logger = createLogger("factory"); // simple factory pattern -export class Factory { - constructor(id) { - this.id = id; +export class Factory { + // Store array as well as dictionary, to speed up lookups + public entries: Class[] = []; + public entryIds: string[] = []; + public idToEntry: Record> = {}; - // Store array as well as dictionary, to speed up lookups - this.entries = []; - this.entryIds = []; - this.idToEntry = {}; - } + constructor(public id: string) {} getId() { return this.id; } - register(entry) { + register(entry: Class & { getId(): string }) { // Extract id const id = entry.getId(); assert(id, "Factory: Invalid id for class: " + entry); @@ -33,19 +31,15 @@ export class Factory { /** * Checks if a given id is registered - * @param {string} id - * @returns {boolean} */ - hasId(id) { + hasId(id: string): boolean { return !!this.idToEntry[id]; } /** * Finds an instance by a given id - * @param {string} id - * @returns {object} */ - findById(id) { + findById(id: string): Class { const entry = this.idToEntry[id]; if (!entry) { logger.error("Object with id", id, "is not registered on factory", this.id, "!"); @@ -57,25 +51,22 @@ export class Factory { /** * Returns all entries - * @returns {Array} */ - getEntries() { + getEntries(): Class[] { return this.entries; } /** * Returns all registered ids - * @returns {Array} */ - getAllIds() { + getAllIds(): string[] { return this.entryIds; } /** * Returns amount of stored entries - * @returns {number} */ - getNumEntries() { + getNumEntries(): number { return this.entries.length; } } diff --git a/src/js/core/game_state.js b/src/js/core/game_state.ts similarity index 68% rename from src/js/core/game_state.js rename to src/js/core/game_state.ts index 6f276e99..1a452ca1 100644 --- a/src/js/core/game_state.js +++ b/src/js/core/game_state.ts @@ -1,15 +1,12 @@ -/* typehints:start */ -import { Application } from "../application"; -import { StateManager } from "./state_manager"; -/* typehints:end */ - -import { globalConfig } from "./config"; +import { MUSIC } from "@/platform/sound"; +import type { Application } from "../application"; import { ClickDetector } from "./click_detector"; -import { logSection, createLogger } from "./logging"; +import { globalConfig } from "./config"; import { InputReceiver } from "./input_receiver"; -import { waitNextFrame } from "./utils"; +import { createLogger, logSection } from "./logging"; import { RequestChannel } from "./request_channel"; -import { MUSIC } from "../platform/sound"; +import type { StateManager } from "./state_manager"; +import { waitNextFrame } from "./utils"; const logger = createLogger("game_state"); @@ -17,49 +14,41 @@ const logger = createLogger("game_state"); * Basic state of the game state machine. This is the base of the whole game */ export class GameState { + public app: Application = null; + public readonly key: string; + public inputReceiver: InputReceiver; + + /** A channel we can use to perform async ops */ + protected asyncChannel = new RequestChannel(); + protected clickDetectors: ClickDetector[] = []; + + /** @todo review this */ + protected htmlElement: HTMLElement | undefined; + + private stateManager: StateManager = null; + + /** Store if we are currently fading out */ + private fadingOut = false; + /** * Constructs a new state with the given id - * @param {string} key The id of the state. We use ids to refer to states because otherwise we get - * circular references + * @param key The id of the state. We use ids to refer to states because otherwise we get + * circular references */ - constructor(key) { + constructor(key: string) { this.key = key; - /** @type {StateManager} */ - this.stateManager = null; - - /** @type {Application} */ - this.app = null; - - // Store if we are currently fading out - this.fadingOut = false; - - /** @type {Array} */ - this.clickDetectors = []; - // Every state captures keyboard events by default - this.inputReciever = new InputReceiver("state-" + key); - this.inputReciever.backButton.add(this.onBackButton, this); - - // A channel we can use to perform async ops - this.asyncChannel = new RequestChannel(); + this.inputReceiver = new InputReceiver("state-" + key); + this.inputReceiver.backButton.add(this.onBackButton, this); } //// GETTERS / HELPER METHODS //// - /** - * Returns the states key - * @returns {string} - */ - getKey() { - return this.key; - } - /** * Returns the html element of the state - * @returns {HTMLElement} */ - getDivElement() { + getDivElement(): HTMLElement { return document.getElementById("state_" + this.key); } @@ -120,9 +109,9 @@ export class GameState { /** * Callback when entering the state, to be overriddemn - * @param {any} payload Arbitrary data passed from the state which we are transferring from + * @param payload Arbitrary data passed from the state which we are transferring from */ - onEnter(payload) {} + onEnter(payload: {}) {} /** * Callback when leaving the state @@ -141,22 +130,22 @@ export class GameState { /** * Render callback - * @param {number} dt Delta time in ms since last render + * @param dt Delta time in ms since last render */ - onRender(dt) {} + onRender(dt: number) {} /** * Background tick callback, called while the game is inactiev - * @param {number} dt Delta time in ms since last tick + * @param dt Delta time in ms since last tick */ - onBackgroundTick(dt) {} + onBackgroundTick(dt: number) {} /** * Called when the screen resized - * @param {number} w window/screen width - * @param {number} h window/screen height + * @param w window/screen width + * @param h window/screen height */ - onResized(w, h) {} + onResized(w: number, h: number) {} /** * Internal backbutton handler, called when the hardware back button is pressed or @@ -168,9 +157,9 @@ export class GameState { /** * Should return how many mulliseconds to fade in / out the state. Not recommended to override! - * @returns {number} Time in milliseconds to fade out + * @returns Time in milliseconds to fade out */ - getInOutFadeTime() { + getInOutFadeTime(): number { if (globalConfig.debug.noArtificialDelays) { return 0; } @@ -180,39 +169,45 @@ export class GameState { /** * Should return whether to fade in the game state. This will then apply the right css classes * for the fadein. - * @returns {boolean} */ - getHasFadeIn() { + getHasFadeIn(): boolean { return true; } /** * Should return whether to fade out the game state. This will then apply the right css classes * for the fadeout and wait the delay before moving states - * @returns {boolean} */ - getHasFadeOut() { + getHasFadeOut(): boolean { return true; } /** * Returns if this state should get paused if it does not have focus - * @returns {boolean} true to pause the updating of the game + * @returns true to pause the updating of the game */ - getPauseOnFocusLost() { + getPauseOnFocusLost(): boolean { return true; } /** * Should return the html code of the state. - * @returns {string} - * @abstract + * @deprecated use {@link getContentLayout} instead */ - getInnerHTML() { - abstract; + getInnerHTML(): string { return ""; } + /** + * Should return the element(s) to be displayed in the state. + * If not overridden, {@link getInnerHTML} will be used to provide the layout. + */ + protected getContentLayout(): Node { + const template = document.createElement("template"); + template.innerHTML = this.getInnerHTML(); + return template.content; + } + /** * Returns if the state has an unload confirmation, this is the * "Are you sure you want to leave the page" message. @@ -223,25 +218,22 @@ export class GameState { /** * Should return the theme music for this state - * @returns {string|null} */ - getThemeMusic() { + getThemeMusic(): string | null { return MUSIC.menu; } /** * Should return true if the player is currently ingame - * @returns {boolean} */ - getIsIngame() { + getIsIngame(): boolean { return false; } /** * Should return whether to clear the whole body content before entering the state. - * @returns {boolean} */ - getRemovePreviousContent() { + getRemovePreviousContent(): boolean { return true; } @@ -251,9 +243,8 @@ export class GameState { /** * Internal callback from the manager. Do not override! - * @param {StateManager} stateManager */ - internalRegisterCallback(stateManager, app) { + internalRegisterCallback(stateManager: StateManager, app: Application) { assert(stateManager, "No state manager"); assert(app, "No app"); this.stateManager = stateManager; @@ -262,12 +253,12 @@ export class GameState { /** * Internal callback when entering the state. Do not override! - * @param {any} payload Arbitrary data passed from the state which we are transferring from - * @param {boolean} callCallback Whether to call the onEnter callback + * @param payload Arbitrary data passed from the state which we are transferring from + * @param callCallback Whether to call the onEnter callback */ - internalEnterCallback(payload, callCallback = true) { + internalEnterCallback(payload: any, callCallback = true) { logSection(this.key, "#26a69a"); - this.app.inputMgr.pushReciever(this.inputReciever); + this.app.inputMgr.pushReceiver(this.inputReceiver); this.htmlElement = this.getDivElement(); this.htmlElement.classList.add("active"); @@ -293,7 +284,7 @@ export class GameState { this.onLeave(); this.htmlElement.classList.remove("active"); - this.app.inputMgr.popReciever(this.inputReciever); + this.app.inputMgr.popReceiver(this.inputReceiver); this.internalCleanUpClickDetectors(); this.asyncChannel.cancelAll(); } @@ -325,18 +316,27 @@ export class GameState { } /** - * Internal method to get the HTML of the game state. - * @returns {string} + * Internal method to get all elements of the game state. Can be + * called from subclasses to provide support for both HTMLElements + * and HTML strings. */ - internalGetFullHtml() { - return this.getInnerHTML(); + internalGetWrappedContent(): Node { + const elements = this.getContentLayout(); + + if (Array.isArray(elements)) { + const fragment = document.createDocumentFragment(); + fragment.append(...(elements as Node[])); + return fragment; + } + + return elements; } /** * Internal method to compute the time to fade in / out - * @returns {number} time to fade in / out in ms + * @returns time to fade in / out in ms */ - internalGetFadeInOutTime() { + internalGetFadeInOutTime(): number { if (G_IS_DEV && globalConfig.debug.fastGameEnter) { return 1; } diff --git a/src/js/core/global_registries.js b/src/js/core/global_registries.js deleted file mode 100644 index 723bf567..00000000 --- a/src/js/core/global_registries.js +++ /dev/null @@ -1,39 +0,0 @@ -import { SingletonFactory } from "./singleton_factory"; -import { Factory } from "./factory"; - -/** - * @typedef {import("../game/time/base_game_speed").BaseGameSpeed} BaseGameSpeed - * @typedef {import("../game/component").Component} Component - * @typedef {import("../game/base_item").BaseItem} BaseItem - * @typedef {import("../game/game_mode").GameMode} GameMode - * @typedef {import("../game/meta_building").MetaBuilding} MetaBuilding - - -// These factories are here to remove circular dependencies - -/** @type {SingletonFactoryTemplate} */ -export let gMetaBuildingRegistry = new SingletonFactory(); - -/** @type {Object.>>} */ -export let gBuildingsByCategory = null; - -/** @type {FactoryTemplate} */ -export let gComponentRegistry = new Factory("component"); - -/** @type {FactoryTemplate} */ -export let gGameModeRegistry = new Factory("gameMode"); - -/** @type {FactoryTemplate} */ -export let gGameSpeedRegistry = new Factory("gamespeed"); - -/** @type {FactoryTemplate} */ -export let gItemRegistry = new Factory("item"); - -// Helpers - -/** - * @param {Object.>>} buildings - */ -export function initBuildingsByCategory(buildings) { - gBuildingsByCategory = buildings; -} diff --git a/src/js/core/global_registries.ts b/src/js/core/global_registries.ts new file mode 100644 index 00000000..10780474 --- /dev/null +++ b/src/js/core/global_registries.ts @@ -0,0 +1,20 @@ +import type { BaseGameSpeed } from "../game/time/base_game_speed"; +import type { Component } from "../game/component"; +import type { BaseItem } from "../game/base_item"; +import type { GameMode } from "../game/game_mode"; +import type { MetaBuilding } from "../game/meta_building"; + +import { SingletonFactory } from "./singleton_factory"; +import { Factory } from "./factory"; + +// These factories are here to remove circular dependencies + +export const gMetaBuildingRegistry = new SingletonFactory("metaBuilding"); + +export const gComponentRegistry = new Factory("component"); + +export const gGameModeRegistry = new Factory("gameMode"); + +export const gGameSpeedRegistry = new Factory("gameSpeed"); + +export const gItemRegistry = new Factory("item"); diff --git a/src/js/core/globals.js b/src/js/core/globals.js index 55238e85..4d901996 100644 --- a/src/js/core/globals.js +++ b/src/js/core/globals.js @@ -12,19 +12,14 @@ export let GLOBAL_APP = null; * @param {Application} app */ export function setGlobalApp(app) { - assert(!GLOBAL_APP, "Create application twice!"); + assert(!GLOBAL_APP, "Tried to set GLOBAL_APP twice"); GLOBAL_APP = app; } export const BUILD_OPTIONS = { - HAVE_ASSERT: G_HAVE_ASSERT, APP_ENVIRONMENT: G_APP_ENVIRONMENT, - CHINA_VERSION: G_CHINA_VERSION, - WEGAME_VERSION: G_WEGAME_VERSION, IS_DEV: G_IS_DEV, IS_RELEASE: G_IS_RELEASE, - IS_BROWSER: G_IS_BROWSER, - IS_STANDALONE: G_IS_STANDALONE, BUILD_TIME: G_BUILD_TIME, BUILD_COMMIT_HASH: G_BUILD_COMMIT_HASH, BUILD_VERSION: G_BUILD_VERSION, diff --git a/src/js/core/input_distributor.js b/src/js/core/input_distributor.ts similarity index 52% rename from src/js/core/input_distributor.js rename to src/js/core/input_distributor.ts index be5440a9..dad0417b 100644 --- a/src/js/core/input_distributor.js +++ b/src/js/core/input_distributor.ts @@ -1,139 +1,104 @@ -/* typehints:start */ -import { Application } from "../application"; -import { InputReceiver } from "./input_receiver"; -/* typehints:end */ +import type { Application } from "../application"; +import type { InputReceiver, ReceiverId } from "./input_receiver"; -import { Signal, STOP_PROPAGATION } from "./signal"; import { createLogger } from "./logging"; +import { Signal, STOP_PROPAGATION } from "./signal"; import { arrayDeleteValue, fastArrayDeleteValue } from "./utils"; const logger = createLogger("input_distributor"); export class InputDistributor { + public receiverStack: InputReceiver[] = []; + public filters: ((arg: string) => boolean)[] = []; + /** - * - * @param {Application} app + * All keys which are currently down */ - constructor(app) { - this.app = app; - - /** @type {Array} */ - this.recieverStack = []; - - /** @type {Array} */ - this.filters = []; - - /** - * All keys which are currently down - */ - this.keysDown = new Set(); + public keysDown = new Set(); + constructor(public app: Application) { this.bindToEvents(); } /** * Attaches a new filter which can filter and reject events - * @param {function(any): boolean} filter */ - installFilter(filter) { + installFilter(filter: (arg: string) => boolean) { this.filters.push(filter); } /** * Removes an attached filter - * @param {function(any) : boolean} filter */ - dismountFilter(filter) { + dismountFilter(filter: (arg: string) => boolean) { fastArrayDeleteValue(this.filters, filter); } - /** - * @param {InputReceiver} reciever - */ - pushReciever(reciever) { - if (this.isRecieverAttached(reciever)) { - assert(false, "Can not add reciever " + reciever.context + " twice"); - logger.error("Can not add reciever", reciever.context, "twice"); + pushReceiver(receiver: InputReceiver) { + if (this.isReceiverAttached(receiver)) { + assert(false, "Can not add receiver " + receiver.context + " twice"); + logger.error("Can not add receiver", receiver.context, "twice"); return; } - this.recieverStack.push(reciever); + this.receiverStack.push(receiver); - if (this.recieverStack.length > 10) { + if (this.receiverStack.length > 10) { logger.error( - "Reciever stack is huge, probably some dead receivers arround:", - this.recieverStack.map(x => x.context) + "Receiver stack is huge, probably some dead receivers arround:", + this.receiverStack.map(x => x.context) ); } } - /** - * @param {InputReceiver} reciever - */ - popReciever(reciever) { - if (this.recieverStack.indexOf(reciever) < 0) { - assert(false, "Can not pop reciever " + reciever.context + " since its not contained"); - logger.error("Can not pop reciever", reciever.context, "since its not contained"); + popReceiver(receiver: InputReceiver) { + if (this.receiverStack.indexOf(receiver) < 0) { + assert(false, "Can not pop receiver " + receiver.context + " since its not contained"); + logger.error("Can not pop receiver", receiver.context, "since its not contained"); return; } - if (this.recieverStack[this.recieverStack.length - 1] !== reciever) { + if (this.receiverStack[this.receiverStack.length - 1] !== receiver) { logger.warn( - "Popping reciever", - reciever.context, + "Popping receiver", + receiver.context, "which is not on top of the stack. Stack is: ", - this.recieverStack.map(x => x.context) + this.receiverStack.map(x => x.context) ); } - arrayDeleteValue(this.recieverStack, reciever); + arrayDeleteValue(this.receiverStack, receiver); } - /** - * @param {InputReceiver} reciever - */ - isRecieverAttached(reciever) { - return this.recieverStack.indexOf(reciever) >= 0; + isReceiverAttached(receiver: InputReceiver) { + return this.receiverStack.indexOf(receiver) >= 0; } - /** - * @param {InputReceiver} reciever - */ - isRecieverOnTop(reciever) { + isReceiverOnTop(receiver: InputReceiver) { return ( - this.isRecieverAttached(reciever) && - this.recieverStack[this.recieverStack.length - 1] === reciever + this.isReceiverAttached(receiver) && + this.receiverStack[this.receiverStack.length - 1] === receiver ); } - /** - * @param {InputReceiver} reciever - */ - makeSureAttachedAndOnTop(reciever) { - this.makeSureDetached(reciever); - this.pushReciever(reciever); + makeSureAttachedAndOnTop(receiver: InputReceiver) { + this.makeSureDetached(receiver); + this.pushReceiver(receiver); } - /** - * @param {InputReceiver} reciever - */ - makeSureDetached(reciever) { - if (this.isRecieverAttached(reciever)) { - arrayDeleteValue(this.recieverStack, reciever); + makeSureDetached(receiver: InputReceiver) { + if (this.isReceiverAttached(receiver)) { + arrayDeleteValue(this.receiverStack, receiver); } } - /** - * - * @param {InputReceiver} reciever - */ - destroyReceiver(reciever) { - this.makeSureDetached(reciever); - reciever.cleanup(); + destroyReceiver(receiver: InputReceiver) { + this.makeSureDetached(receiver); + receiver.cleanup(); } // Internal - getTopReciever() { - if (this.recieverStack.length > 0) { - return this.recieverStack[this.recieverStack.length - 1]; + getTopReceiver() { + if (this.receiverStack.length > 0) { + return this.receiverStack[this.receiverStack.length - 1]; } return null; } @@ -153,7 +118,10 @@ export class InputDistributor { document.addEventListener("paste", this.handlePaste.bind(this)); } - forwardToReceiver(eventId, payload = null) { + forwardToReceiver( + eventId: T, + payload: Parameters[0] = null + ) { // Check filters for (let i = 0; i < this.filters.length; ++i) { if (!this.filters[i](eventId)) { @@ -161,20 +129,18 @@ export class InputDistributor { } } - const reciever = this.getTopReciever(); - if (!reciever) { - logger.warn("Dismissing event because not reciever was found:", eventId); + const receiver = this.getTopReceiver(); + if (!receiver) { + logger.warn("Dismissing event because not receiver was found:", eventId); return; } - const signal = reciever[eventId]; + const signal = receiver[eventId]; assert(signal instanceof Signal, "Not a valid event id"); - return signal.dispatch(payload); + // probably not possible to type properly, since the types of `signal` and `payload` are correlated + return signal.dispatch(payload as never); } - /** - * @param {Event} event - */ - handleBackButton(event) { + handleBackButton(event: Event) { event.preventDefault(); event.stopPropagation(); this.forwardToReceiver("backButton"); @@ -184,21 +150,15 @@ export class InputDistributor { * Handles when the page got blurred */ handleBlur() { - this.forwardToReceiver("pageBlur", {}); + this.forwardToReceiver("pageBlur"); this.keysDown.clear(); } - /** - * - */ - handlePaste(ev) { + handlePaste(ev: ClipboardEvent) { this.forwardToReceiver("paste", ev); } - /** - * @param {KeyboardEvent | MouseEvent} event - */ - handleKeyMouseDown(event) { + handleKeyMouseDown(event: KeyboardEvent | MouseEvent) { const keyCode = event instanceof MouseEvent ? event.button + 1 : event.keyCode; if ( keyCode === 4 || // MB4 @@ -236,10 +196,7 @@ export class InputDistributor { } } - /** - * @param {KeyboardEvent | MouseEvent} event - */ - handleKeyMouseUp(event) { + handleKeyMouseUp(event: KeyboardEvent | MouseEvent) { const keyCode = event instanceof MouseEvent ? event.button + 1 : event.keyCode; this.keysDown.delete(keyCode); diff --git a/src/js/core/input_receiver.js b/src/js/core/input_receiver.js deleted file mode 100644 index 164ab84b..00000000 --- a/src/js/core/input_receiver.js +++ /dev/null @@ -1,27 +0,0 @@ -import { Signal } from "./signal"; - -export class InputReceiver { - constructor(context = "unknown") { - this.context = context; - - this.backButton = new Signal(); - - this.keydown = new Signal(); - this.keyup = new Signal(); - this.pageBlur = new Signal(); - - // Dispatched on destroy - this.destroyed = new Signal(); - - this.paste = new Signal(); - } - - cleanup() { - this.backButton.removeAll(); - this.keydown.removeAll(); - this.keyup.removeAll(); - this.paste.removeAll(); - - this.destroyed.dispatch(); - } -} diff --git a/src/js/core/input_receiver.ts b/src/js/core/input_receiver.ts new file mode 100644 index 00000000..da9a4934 --- /dev/null +++ b/src/js/core/input_receiver.ts @@ -0,0 +1,47 @@ +import { Signal } from "./signal"; + +export type KeydownEvent = { + keyCode: number; + shift: boolean; + alt: boolean; + ctrl: boolean; + initial: boolean; + event: KeyboardEvent | MouseEvent; +}; +export type KeyupEvent = { + keyCode: number; + shift: boolean; + alt: boolean; +}; + +export class InputReceiver { + public backButton = new Signal(); + + public keydown = new Signal<[KeydownEvent]>(); + public keyup = new Signal<[KeyupEvent]>(); + public pageBlur = new Signal(); + + // Dispatched on destroy + public destroyed = new Signal(); + + public paste = new Signal<[ClipboardEvent]>(); + + constructor(public context: string = "unknown") {} + + cleanup() { + this.backButton.removeAll(); + this.keydown.removeAll(); + this.keyup.removeAll(); + this.paste.removeAll(); + + this.destroyed.dispatch(); + } +} + +export type ReceiverId = keyof { + [K in keyof InputReceiver as InputReceiver[K] extends Signal + ? K extends "destroyed" + ? never + : K + : never]: unknown; +}; diff --git a/src/js/core/keycodes.ts b/src/js/core/keycodes.ts new file mode 100644 index 00000000..d2e1df33 --- /dev/null +++ b/src/js/core/keycodes.ts @@ -0,0 +1,194 @@ +import { T } from "@/translations"; + +export function keyToKeyCode(key: string) { + return key.toUpperCase().charCodeAt(0); +} + +export const KEYCODES = { + Tab: 9, + Enter: 13, + + Shift: 16, + Ctrl: 17, + Alt: 18, + + Escape: 27, + + Space: 32, + + ArrowLeft: 37, + ArrowUp: 38, + ArrowRight: 39, + ArrowDown: 40, + + Delete: 46, + + F1: 112, + F2: 113, + F3: 114, + F4: 115, + F5: 116, + F6: 117, + F7: 118, + F8: 119, + F9: 120, + F10: 121, + F11: 122, + F12: 123, + + Plus: 187, + Minus: 189, +}; + +export const KEYCODE_LMB = 1; +export const KEYCODE_MMB = 2; +export const KEYCODE_RMB = 3; + +export function getStringForKeyCode(code: number): string { + // @todo: Refactor into dictionary + switch (code) { + case KEYCODE_LMB: + return "LMB"; + case KEYCODE_MMB: + return "MMB"; + case KEYCODE_RMB: + return "RMB"; + case 4: + return "MB4"; + case 5: + return "MB5"; + case 8: + return "⌫"; + case KEYCODES.Tab: + return T.global.keys.tab; + case KEYCODES.Enter: + return "⏎"; + case KEYCODES.Shift: + return "⇪"; + case KEYCODES.Ctrl: + return T.global.keys.control; + case KEYCODES.Alt: + return T.global.keys.alt; + case 19: + return "PAUSE"; + case 20: + return "CAPS"; + case KEYCODES.Escape: + return T.global.keys.escape; + case KEYCODES.Space: + return T.global.keys.space; + case 33: + return "PGUP"; + case 34: + return "PGDOWN"; + case 35: + return "END"; + case 36: + return "HOME"; + case KEYCODES.ArrowLeft: + return "⬅"; + case KEYCODES.ArrowUp: + return "⬆"; + case KEYCODES.ArrowRight: + return "➡"; + case KEYCODES.ArrowDown: + return "⬇"; + case 44: + return "PRNT"; + case 45: + return "INS"; + case 46: + return "DEL"; + case 93: + return "SEL"; + case 96: + return "NUM 0"; + case 97: + return "NUM 1"; + case 98: + return "NUM 2"; + case 99: + return "NUM 3"; + case 100: + return "NUM 4"; + case 101: + return "NUM 5"; + case 102: + return "NUM 6"; + case 103: + return "NUM 7"; + case 104: + return "NUM 8"; + case 105: + return "NUM 9"; + case 106: + return "*"; + case 107: + return "+"; + case 109: + return "-"; + case 110: + return "."; + case 111: + return "/"; + case KEYCODES.F1: + return "F1"; + case KEYCODES.F2: + return "F2"; + case KEYCODES.F3: + return "F3"; + case KEYCODES.F4: + return "F4"; + case KEYCODES.F5: + return "F5"; + case KEYCODES.F6: + return "F6"; + case KEYCODES.F7: + return "F7"; + case KEYCODES.F8: + return "F8"; + case KEYCODES.F9: + return "F9"; + case KEYCODES.F10: + return "F10"; + case KEYCODES.F11: + return "F11"; + case KEYCODES.F12: + return "F12"; + + case 144: + return "NUMLOCK"; + case 145: + return "SCRLOCK"; + case 182: + return "COMP"; + case 183: + return "CALC"; + case 186: + return ";"; + case 187: + return "+"; + case 188: + return ","; + case 189: + return "-"; + case 190: + return "."; + case 191: + return "/"; + case 192: + return "`"; + case 219: + return "["; + case 220: + return "\\"; + case 221: + return "]"; + case 222: + return "'"; + } + + return (48 <= code && code <= 57) || (65 <= code && code <= 90) + ? String.fromCharCode(code) + : "[" + code + "]"; +} diff --git a/src/js/core/loader.js b/src/js/core/loader.js index 29afa123..d9f4bbe0 100644 --- a/src/js/core/loader.js +++ b/src/js/core/loader.js @@ -1,208 +1,207 @@ -import { makeOffscreenBuffer } from "./buffer_utils"; -import { AtlasSprite, BaseSprite, RegularSprite, SpriteAtlasLink } from "./sprites"; -import { cachebust } from "./cachebust"; -import { createLogger } from "./logging"; - -/** - * @typedef {import("../application").Application} Application - * @typedef {import("./atlas_definitions").AtlasDefinition} AtlasDefinition; - */ - -const logger = createLogger("loader"); - -const missingSpriteIds = {}; - -class LoaderImpl { - constructor() { - this.app = null; - - /** @type {Map} */ - this.sprites = new Map(); - - this.rawImages = []; - } - - /** - * @param {Application} app - */ - linkAppAfterBoot(app) { - this.app = app; - this.makeSpriteNotFoundCanvas(); - } - - /** - * Fetches a given sprite from the cache - * @param {string} key - * @returns {BaseSprite} - */ - getSpriteInternal(key) { - const sprite = this.sprites.get(key); - if (!sprite) { - if (!missingSpriteIds[key]) { - // Only show error once - missingSpriteIds[key] = true; - logger.error("Sprite '" + key + "' not found!"); - } - return this.spriteNotFoundSprite; - } - return sprite; - } - - /** - * Returns an atlas sprite from the cache - * @param {string} key - * @returns {AtlasSprite} - */ - getSprite(key) { - const sprite = this.getSpriteInternal(key); - assert(sprite instanceof AtlasSprite || sprite === this.spriteNotFoundSprite, "Not an atlas sprite"); - return /** @type {AtlasSprite} */ (sprite); - } - - /** - * Returns a regular sprite from the cache - * @param {string} key - * @returns {RegularSprite} - */ - getRegularSprite(key) { - const sprite = this.getSpriteInternal(key); - assert( - sprite instanceof RegularSprite || sprite === this.spriteNotFoundSprite, - "Not a regular sprite" - ); - return /** @type {RegularSprite} */ (sprite); - } - - /** - * - * @param {string} key - * @param {(progress: number) => void} progressHandler - * @returns {Promise} - */ - internalPreloadImage(key, progressHandler) { - return this.app.backgroundResourceLoader - .preloadWithProgress("res/" + key, progress => { - progressHandler(progress); - }) - .then(url => { - return new Promise((resolve, reject) => { - const image = new Image(); - image.addEventListener("load", () => resolve(image)); - image.addEventListener("error", err => - reject("Failed to load sprite " + key + ": " + err) - ); - image.src = url; - }); - }); - } - - /** - * Preloads a sprite - * @param {string} key - * @param {(progress: number) => void} progressHandler - * @returns {Promise} - */ - preloadCSSSprite(key, progressHandler) { - return this.internalPreloadImage(key, progressHandler).then(image => { - if (key.indexOf("game_misc") >= 0) { - // Allow access to regular sprites - this.sprites.set(key, new RegularSprite(image, image.width, image.height)); - } - this.rawImages.push(image); - }); - } - - /** - * Preloads an atlas - * @param {AtlasDefinition} atlas - * @param {(progress: number) => void} progressHandler - * @returns {Promise} - */ - preloadAtlas(atlas, progressHandler) { - return this.internalPreloadImage(atlas.getFullSourcePath(), progressHandler).then(image => { - // @ts-ignore - image.label = atlas.sourceFileName; - return this.internalParseAtlas(atlas, image); - }); - } - - /** - * - * @param {AtlasDefinition} atlas - * @param {HTMLImageElement} loadedImage - */ - internalParseAtlas({ meta: { scale }, sourceData }, loadedImage) { - this.rawImages.push(loadedImage); - - for (const spriteName in sourceData) { - const { frame, sourceSize, spriteSourceSize } = sourceData[spriteName]; - - let sprite = /** @type {AtlasSprite} */ (this.sprites.get(spriteName)); - - if (!sprite) { - sprite = new AtlasSprite(spriteName); - this.sprites.set(spriteName, sprite); - } - if (sprite.frozen) { - continue; - } - - const link = new SpriteAtlasLink({ - packedX: frame.x, - packedY: frame.y, - packedW: frame.w, - packedH: frame.h, - packOffsetX: spriteSourceSize.x, - packOffsetY: spriteSourceSize.y, - atlas: loadedImage, - w: sourceSize.w, - h: sourceSize.h, - }); - - sprite.linksByResolution[scale] = link; - } - } - - /** - * Makes the canvas which shows the question mark, shown when a sprite was not found - */ - makeSpriteNotFoundCanvas() { - const dims = 128; - - const [canvas, context] = makeOffscreenBuffer(dims, dims, { - smooth: false, - label: "not-found-sprite", - }); - context.fillStyle = "#f77"; - context.fillRect(0, 0, dims, dims); - - context.textAlign = "center"; - context.textBaseline = "middle"; - context.fillStyle = "#eee"; - context.font = "25px Arial"; - context.fillText("???", dims / 2, dims / 2); - - // TODO: Not sure why this is set here - // @ts-ignore - canvas.src = "not-found"; - - const sprite = new AtlasSprite("not-found"); - ["0.1", "0.25", "0.5", "0.75", "1"].forEach(resolution => { - sprite.linksByResolution[resolution] = new SpriteAtlasLink({ - packedX: 0, - packedY: 0, - w: dims, - h: dims, - packOffsetX: 0, - packOffsetY: 0, - packedW: dims, - packedH: dims, - atlas: canvas, - }); - }); - - this.spriteNotFoundSprite = sprite; - } -} - -export const Loader = new LoaderImpl(); +import { makeOffscreenBuffer } from "./buffer_utils"; +import { AtlasSprite, BaseSprite, RegularSprite, SpriteAtlasLink } from "./sprites"; +import { createLogger } from "./logging"; + +/** + * @typedef {import("../application").Application} Application + * @typedef {import("./atlas_definitions").AtlasDefinition} AtlasDefinition; + */ + +const logger = createLogger("loader"); + +const missingSpriteIds = {}; + +class LoaderImpl { + constructor() { + this.app = null; + + /** @type {Map} */ + this.sprites = new Map(); + + this.rawImages = []; + } + + /** + * @param {Application} app + */ + linkAppAfterBoot(app) { + this.app = app; + this.makeSpriteNotFoundCanvas(); + } + + /** + * Fetches a given sprite from the cache + * @param {string} key + * @returns {BaseSprite} + */ + getSpriteInternal(key) { + const sprite = this.sprites.get(key); + if (!sprite) { + if (!missingSpriteIds[key]) { + // Only show error once + missingSpriteIds[key] = true; + logger.error("Sprite '" + key + "' not found!"); + } + return this.spriteNotFoundSprite; + } + return sprite; + } + + /** + * Returns an atlas sprite from the cache + * @param {string} key + * @returns {AtlasSprite} + */ + getSprite(key) { + const sprite = this.getSpriteInternal(key); + assert(sprite instanceof AtlasSprite || sprite === this.spriteNotFoundSprite, "Not an atlas sprite"); + return /** @type {AtlasSprite} */ (sprite); + } + + /** + * Returns a regular sprite from the cache + * @param {string} key + * @returns {RegularSprite} + */ + getRegularSprite(key) { + const sprite = this.getSpriteInternal(key); + assert( + sprite instanceof RegularSprite || sprite === this.spriteNotFoundSprite, + "Not a regular sprite" + ); + return /** @type {RegularSprite} */ (sprite); + } + + /** + * + * @param {string} key + * @param {(progress: number) => void} progressHandler + * @returns {Promise} + */ + internalPreloadImage(key, progressHandler) { + return this.app.backgroundResourceLoader + .preloadWithProgress("res/" + key, progress => { + progressHandler(progress); + }) + .then(url => { + return new Promise((resolve, reject) => { + const image = new Image(); + image.addEventListener("load", () => resolve(image)); + image.addEventListener("error", err => + reject("Failed to load sprite " + key + ": " + err) + ); + image.src = url; + }); + }); + } + + /** + * Preloads a sprite + * @param {string} key + * @param {(progress: number) => void} progressHandler + * @returns {Promise} + */ + preloadCSSSprite(key, progressHandler) { + return this.internalPreloadImage(key, progressHandler).then(image => { + if (key.indexOf("game_misc") >= 0) { + // Allow access to regular sprites + this.sprites.set(key, new RegularSprite(image, image.width, image.height)); + } + this.rawImages.push(image); + }); + } + + /** + * Preloads an atlas + * @param {AtlasDefinition} atlas + * @param {(progress: number) => void} progressHandler + * @returns {Promise} + */ + preloadAtlas(atlas, progressHandler) { + return this.internalPreloadImage(atlas.getFullSourcePath(), progressHandler).then(image => { + // @ts-ignore + image.label = atlas.sourceFileName; + return this.internalParseAtlas(atlas, image); + }); + } + + /** + * + * @param {AtlasDefinition} atlas + * @param {HTMLImageElement} loadedImage + */ + internalParseAtlas({ meta: { scale }, sourceData }, loadedImage) { + this.rawImages.push(loadedImage); + + for (const spriteName in sourceData) { + const { frame, sourceSize, spriteSourceSize } = sourceData[spriteName]; + + let sprite = /** @type {AtlasSprite} */ (this.sprites.get(spriteName)); + + if (!sprite) { + sprite = new AtlasSprite(spriteName); + this.sprites.set(spriteName, sprite); + } + if (sprite.frozen) { + continue; + } + + const link = new SpriteAtlasLink({ + packedX: frame.x, + packedY: frame.y, + packedW: frame.w, + packedH: frame.h, + packOffsetX: spriteSourceSize.x, + packOffsetY: spriteSourceSize.y, + atlas: loadedImage, + w: sourceSize.w, + h: sourceSize.h, + }); + + sprite.linksByResolution[scale] = link; + } + } + + /** + * Makes the canvas which shows the question mark, shown when a sprite was not found + */ + makeSpriteNotFoundCanvas() { + const dims = 128; + + const [canvas, context] = makeOffscreenBuffer(dims, dims, { + smooth: false, + label: "not-found-sprite", + }); + context.fillStyle = "#f77"; + context.fillRect(0, 0, dims, dims); + + context.textAlign = "center"; + context.textBaseline = "middle"; + context.fillStyle = "#eee"; + context.font = "25px Arial"; + context.fillText("???", dims / 2, dims / 2); + + // TODO: Not sure why this is set here + // @ts-ignore + canvas.src = "not-found"; + + const sprite = new AtlasSprite("not-found"); + ["0.1", "0.25", "0.5", "0.75", "1"].forEach(resolution => { + sprite.linksByResolution[resolution] = new SpriteAtlasLink({ + packedX: 0, + packedY: 0, + w: dims, + h: dims, + packOffsetX: 0, + packOffsetY: 0, + packedW: dims, + packedH: dims, + atlas: canvas, + }); + }); + + this.spriteNotFoundSprite = sprite; + } +} + +export const Loader = new LoaderImpl(); diff --git a/src/js/core/logging.js b/src/js/core/logging.js deleted file mode 100644 index f3e938e5..00000000 --- a/src/js/core/logging.js +++ /dev/null @@ -1,247 +0,0 @@ -import { globalConfig } from "../core/config"; -const circularJson = require("circular-json"); - -/* -Logging functions -- To be extended -*/ - -/** - * Base logger class - */ -class Logger { - constructor(context) { - this.context = context; - } - - debug(...args) { - globalDebug(this.context, ...args); - } - - log(...args) { - globalLog(this.context, ...args); - } - - warn(...args) { - globalWarn(this.context, ...args); - } - - error(...args) { - globalError(this.context, ...args); - } -} - -export function createLogger(context) { - return new Logger(context); -} - -function prepareObjectForLogging(obj, maxDepth = 1) { - if (!window.Sentry) { - // Not required without sentry - return obj; - } - - if (typeof obj !== "object" && !Array.isArray(obj)) { - return obj; - } - const result = {}; - for (const key in obj) { - const val = obj[key]; - - if (typeof val === "object") { - if (maxDepth > 0) { - result[key] = prepareObjectForLogging(val, maxDepth - 1); - } else { - result[key] = "[object]"; - } - } else { - result[key] = val; - } - } - return result; -} - -/** - * Serializes an error - * @param {Error|ErrorEvent} err - */ -export function serializeError(err) { - if (!err) { - return null; - } - const result = { - type: err.constructor.name, - }; - - if (err instanceof Error) { - result.message = err.message; - result.name = err.name; - result.stack = err.stack; - result.type = "{type.Error}"; - } else if (err instanceof ErrorEvent) { - result.filename = err.filename; - result.message = err.message; - result.lineno = err.lineno; - result.colno = err.colno; - result.type = "{type.ErrorEvent}"; - - if (err.error) { - result.error = serializeError(err.error); - } else { - result.error = "{not-provided}"; - } - } else { - result.type = "{unkown-type:" + typeof err + "}"; - } - - return result; -} - -/** - * Serializes an event - * @param {Event} event - */ -function serializeEvent(event) { - let result = { - type: "{type.Event:" + typeof event + "}", - }; - result.eventType = event.type; - return result; -} - -/** - * Prepares a json payload - * @param {string} key - * @param {any} value - */ -function preparePayload(key, value) { - if (value instanceof Error || value instanceof ErrorEvent) { - return serializeError(value); - } - if (value instanceof Event) { - return serializeEvent(value); - } - if (typeof value === "undefined") { - return null; - } - return value; -} - -/** - * Stringifies an object containing circular references and errors - * @param {any} payload - */ -export function stringifyObjectContainingErrors(payload) { - return circularJson.stringify(payload, preparePayload); -} - -export function globalDebug(context, ...args) { - if (G_IS_DEV) { - logInternal(context, console.log, prepareArgsForLogging(args)); - } -} - -export function globalLog(context, ...args) { - // eslint-disable-next-line no-console - logInternal(context, console.log, prepareArgsForLogging(args)); -} - -export function globalWarn(context, ...args) { - // eslint-disable-next-line no-console - logInternal(context, console.warn, prepareArgsForLogging(args)); -} - -export function globalError(context, ...args) { - args = prepareArgsForLogging(args); - // eslint-disable-next-line no-console - logInternal(context, console.error, args); - - if (window.Sentry) { - window.Sentry.withScope(scope => { - scope.setExtra("args", args); - window.Sentry.captureMessage(internalBuildStringFromArgs(args), "error"); - }); - } -} - -function prepareArgsForLogging(args) { - let result = []; - for (let i = 0; i < args.length; ++i) { - result.push(prepareObjectForLogging(args[i])); - } - return result; -} - -/** - * @param {Array} args - */ -function internalBuildStringFromArgs(args) { - let result = []; - - for (let i = 0; i < args.length; ++i) { - let arg = args[i]; - if ( - typeof arg === "string" || - typeof arg === "number" || - typeof arg === "boolean" || - arg === null || - arg === undefined - ) { - result.push("" + arg); - } else if (arg instanceof Error) { - result.push(arg.message); - } else { - result.push("[object]"); - } - } - return result.join(" "); -} - -export function logSection(name, color) { - while (name.length <= 14) { - name = " " + name + " "; - } - name = name.padEnd(19, " "); - - const lineCss = - "letter-spacing: -3px; color: " + color + "; font-size: 6px; background: #eee; color: #eee;"; - const line = "%c----------------------------"; - console.log("\n" + line + " %c" + name + " " + line + "\n", lineCss, "color: " + color, lineCss); -} - -function extractHandleContext(handle) { - let context = handle || "unknown"; - if (handle && handle.constructor && handle.constructor.name) { - context = handle.constructor.name; - if (context === "String") { - context = handle; - } - } - - if (handle && handle.name) { - context = handle.name; - } - return context + ""; -} - -function logInternal(handle, consoleMethod, args) { - const context = extractHandleContext(handle).padEnd(20, " "); - const labelColor = handle && handle.LOG_LABEL_COLOR ? handle.LOG_LABEL_COLOR : "#aaa"; - - if (G_IS_DEV && globalConfig.debug.logTimestamps) { - const timestamp = "⏱ %c" + (Math.floor(performance.now()) + "").padEnd(6, " ") + ""; - consoleMethod.call( - console, - timestamp + " %c" + context, - "color: #7f7;", - "color: " + labelColor + ";", - ...args - ); - } else { - // if (G_IS_DEV && !globalConfig.debug.disableLoggingLogSources) { - consoleMethod.call(console, "%c" + context, "color: " + labelColor, ...args); - // } else { - // consoleMethod.call(console, ...args); - // } - } -} diff --git a/src/js/core/logging.ts b/src/js/core/logging.ts new file mode 100644 index 00000000..3e958789 --- /dev/null +++ b/src/js/core/logging.ts @@ -0,0 +1,60 @@ +export class Logger { + /** + * A simple {@link console} wrapper that retains the location of log calls. + * @param context Label to be displayed in each log message + * @param color Optional label color override + * @param debug Whether to log {@link Logger.debug} messages + */ + constructor(context: string, color = "#aaa", debug = G_IS_DEV) { + const label = "%c" + context.padEnd(20, " "); + const style = `color: ${color}`; + + if (debug) { + this.debug = console.debug.bind(console, label, style); + } + + this.log = console.log.bind(console, label, style); + this.warn = console.warn.bind(console, label, style); + this.error = console.error.bind(console, label, style); + } + + // @ts-expect-error parameters are actually used + debug(...args: unknown[]) {} + // @ts-expect-error same + log(...args: unknown[]) {} + // @ts-expect-error same + warn(...args: unknown[]) {} + // @ts-expect-error same + error(...args: unknown[]) {} +} + +/** + * @deprecated Use the {@link Logger} constructor instead + * @param handle Object to be used as the logger label + * @returns A {@link Logger} instance + */ +export function createLogger(handle: unknown) { + const context = extractHandleContext(handle); + return new Logger(context); +} + +export function logSection(name, color) { + while (name.length <= 14) { + name = " " + name + " "; + } + name = name.padEnd(19, " "); + + const lineCss = + "letter-spacing: -3px; color: " + color + "; font-size: 6px; background: #eee; color: #eee;"; + const line = "%c----------------------------"; + console.log("\n" + line + " %c" + name + " " + line + "\n", lineCss, "color: " + color, lineCss); +} + +function extractHandleContext(handle: unknown) { + handle ??= "unknown"; + if (typeof handle === "string") { + return handle; + } + + return handle.constructor.name; +} diff --git a/src/js/core/lzstring.js b/src/js/core/lzstring.js deleted file mode 100644 index 79d0523c..00000000 --- a/src/js/core/lzstring.js +++ /dev/null @@ -1,493 +0,0 @@ -// Copyright (c) 2013 Pieroxy -// This work is free. You can redistribute it and/or modify it -// under the terms of the WTFPL, Version 2 -// For more information see LICENSE.txt or http://www.wtfpl.net/ -// -// For more information, the home page: -// http://pieroxy.net/blog/pages/lz-string/testing.html -// -// LZ-based compression algorithm, version 1.4.4 - -const fromCharCode = String.fromCharCode; -const hasOwnProperty = Object.prototype.hasOwnProperty; - -const keyStrUriSafe = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-$"; -const baseReverseDic = {}; - -function getBaseValue(alphabet, character) { - if (!baseReverseDic[alphabet]) { - baseReverseDic[alphabet] = {}; - for (let i = 0; i < alphabet.length; i++) { - baseReverseDic[alphabet][alphabet.charAt(i)] = i; - } - } - return baseReverseDic[alphabet][character]; -} - -//compress into uint8array (UCS-2 big endian format) -export function compressU8(uncompressed) { - let compressed = compress(uncompressed); - let buf = new Uint8Array(compressed.length * 2); // 2 bytes per character - - for (let i = 0, TotalLen = compressed.length; i < TotalLen; i++) { - let current_value = compressed.charCodeAt(i); - buf[i * 2] = current_value >>> 8; - buf[i * 2 + 1] = current_value % 256; - } - return buf; -} - -// Compreses with header -/** - * @param {string} uncompressed - * @param {number} header - */ -export function compressU8WHeader(uncompressed, header) { - let compressed = compress(uncompressed); - let buf = new Uint8Array(2 + compressed.length * 2); // 2 bytes per character - - buf[0] = header >>> 8; - buf[1] = header % 256; - for (let i = 0, TotalLen = compressed.length; i < TotalLen; i++) { - let current_value = compressed.charCodeAt(i); - buf[2 + i * 2] = current_value >>> 8; - buf[2 + i * 2 + 1] = current_value % 256; - } - return buf; -} - -//decompress from uint8array (UCS-2 big endian format) -/** - * - * @param {Uint8Array} compressed - */ -export function decompressU8WHeader(compressed) { - // let buf = new Array(compressed.length / 2); // 2 bytes per character - // for (let i = 0, TotalLen = buf.length; i < TotalLen; i++) { - // buf[i] = compressed[i * 2] * 256 + compressed[i * 2 + 1]; - // } - - // let result = []; - // buf.forEach(function (c) { - // result.push(fromCharCode(c)); - // }); - let result = []; - for (let i = 2, n = compressed.length; i < n; i += 2) { - const code = compressed[i] * 256 + compressed[i + 1]; - result.push(fromCharCode(code)); - } - return decompress(result.join("")); -} - -//compress into a string that is already URI encoded -export function compressX64(input) { - if (input == null) return ""; - return _compress(input, 6, function (a) { - return keyStrUriSafe.charAt(a); - }); -} - -//decompress from an output of compressToEncodedURIComponent -export function decompressX64(input) { - if (input == null) return ""; - if (input == "") return null; - input = input.replace(/ /g, "+"); - return _decompress(input.length, 32, function (index) { - return getBaseValue(keyStrUriSafe, input.charAt(index)); - }); -} - -function compress(uncompressed) { - return _compress(uncompressed, 16, function (a) { - return fromCharCode(a); - }); -} - -function _compress(uncompressed, bitsPerChar, getCharFromInt) { - if (uncompressed == null) return ""; - let i, - value, - context_dictionary = {}, - context_dictionaryToCreate = {}, - context_c = "", - context_wc = "", - context_w = "", - context_enlargeIn = 2, // Compensate for the first entry which should not count - context_dictSize = 3, - context_numBits = 2, - context_data = [], - context_data_val = 0, - context_data_position = 0, - ii; - - for (ii = 0; ii < uncompressed.length; ii += 1) { - context_c = uncompressed.charAt(ii); - if (!hasOwnProperty.call(context_dictionary, context_c)) { - context_dictionary[context_c] = context_dictSize++; - context_dictionaryToCreate[context_c] = true; - } - - context_wc = context_w + context_c; - if (hasOwnProperty.call(context_dictionary, context_wc)) { - context_w = context_wc; - } else { - if (hasOwnProperty.call(context_dictionaryToCreate, context_w)) { - if (context_w.charCodeAt(0) < 256) { - for (i = 0; i < context_numBits; i++) { - context_data_val = context_data_val << 1; - if (context_data_position == bitsPerChar - 1) { - context_data_position = 0; - context_data.push(getCharFromInt(context_data_val)); - context_data_val = 0; - } else { - context_data_position++; - } - } - value = context_w.charCodeAt(0); - for (i = 0; i < 8; i++) { - context_data_val = (context_data_val << 1) | (value & 1); - if (context_data_position == bitsPerChar - 1) { - context_data_position = 0; - context_data.push(getCharFromInt(context_data_val)); - context_data_val = 0; - } else { - context_data_position++; - } - value = value >> 1; - } - } else { - value = 1; - for (i = 0; i < context_numBits; i++) { - context_data_val = (context_data_val << 1) | value; - if (context_data_position == bitsPerChar - 1) { - context_data_position = 0; - context_data.push(getCharFromInt(context_data_val)); - context_data_val = 0; - } else { - context_data_position++; - } - value = 0; - } - value = context_w.charCodeAt(0); - for (i = 0; i < 16; i++) { - context_data_val = (context_data_val << 1) | (value & 1); - if (context_data_position == bitsPerChar - 1) { - context_data_position = 0; - context_data.push(getCharFromInt(context_data_val)); - context_data_val = 0; - } else { - context_data_position++; - } - value = value >> 1; - } - } - context_enlargeIn--; - if (context_enlargeIn == 0) { - context_enlargeIn = Math.pow(2, context_numBits); - context_numBits++; - } - delete context_dictionaryToCreate[context_w]; - } else { - value = context_dictionary[context_w]; - for (i = 0; i < context_numBits; i++) { - context_data_val = (context_data_val << 1) | (value & 1); - if (context_data_position == bitsPerChar - 1) { - context_data_position = 0; - context_data.push(getCharFromInt(context_data_val)); - context_data_val = 0; - } else { - context_data_position++; - } - value = value >> 1; - } - } - context_enlargeIn--; - if (context_enlargeIn == 0) { - context_enlargeIn = Math.pow(2, context_numBits); - context_numBits++; - } - // Add wc to the dictionary. - context_dictionary[context_wc] = context_dictSize++; - context_w = String(context_c); - } - } - - // Output the code for w. - if (context_w !== "") { - if (hasOwnProperty.call(context_dictionaryToCreate, context_w)) { - if (context_w.charCodeAt(0) < 256) { - for (i = 0; i < context_numBits; i++) { - context_data_val = context_data_val << 1; - if (context_data_position == bitsPerChar - 1) { - context_data_position = 0; - context_data.push(getCharFromInt(context_data_val)); - context_data_val = 0; - } else { - context_data_position++; - } - } - value = context_w.charCodeAt(0); - for (i = 0; i < 8; i++) { - context_data_val = (context_data_val << 1) | (value & 1); - if (context_data_position == bitsPerChar - 1) { - context_data_position = 0; - context_data.push(getCharFromInt(context_data_val)); - context_data_val = 0; - } else { - context_data_position++; - } - value = value >> 1; - } - } else { - value = 1; - for (i = 0; i < context_numBits; i++) { - context_data_val = (context_data_val << 1) | value; - if (context_data_position == bitsPerChar - 1) { - context_data_position = 0; - context_data.push(getCharFromInt(context_data_val)); - context_data_val = 0; - } else { - context_data_position++; - } - value = 0; - } - value = context_w.charCodeAt(0); - for (i = 0; i < 16; i++) { - context_data_val = (context_data_val << 1) | (value & 1); - if (context_data_position == bitsPerChar - 1) { - context_data_position = 0; - context_data.push(getCharFromInt(context_data_val)); - context_data_val = 0; - } else { - context_data_position++; - } - value = value >> 1; - } - } - context_enlargeIn--; - if (context_enlargeIn == 0) { - context_enlargeIn = Math.pow(2, context_numBits); - context_numBits++; - } - delete context_dictionaryToCreate[context_w]; - } else { - value = context_dictionary[context_w]; - for (i = 0; i < context_numBits; i++) { - context_data_val = (context_data_val << 1) | (value & 1); - if (context_data_position == bitsPerChar - 1) { - context_data_position = 0; - context_data.push(getCharFromInt(context_data_val)); - context_data_val = 0; - } else { - context_data_position++; - } - value = value >> 1; - } - } - context_enlargeIn--; - if (context_enlargeIn == 0) { - context_enlargeIn = Math.pow(2, context_numBits); - context_numBits++; - } - } - - // Mark the end of the stream - value = 2; - for (i = 0; i < context_numBits; i++) { - context_data_val = (context_data_val << 1) | (value & 1); - if (context_data_position == bitsPerChar - 1) { - context_data_position = 0; - context_data.push(getCharFromInt(context_data_val)); - context_data_val = 0; - } else { - context_data_position++; - } - value = value >> 1; - } - - // Flush the last char - // eslint-disable-next-line no-constant-condition - while (true) { - context_data_val = context_data_val << 1; - if (context_data_position == bitsPerChar - 1) { - context_data.push(getCharFromInt(context_data_val)); - break; - } else context_data_position++; - } - return context_data.join(""); -} - -function decompress(compressed) { - if (compressed == null) return ""; - if (compressed == "") return null; - return _decompress(compressed.length, 32768, function (index) { - return compressed.charCodeAt(index); - }); -} - -function _decompress(length, resetValue, getNextValue) { - let dictionary = [], - next, - enlargeIn = 4, - dictSize = 4, - numBits = 3, - entry = "", - result = [], - i, - w, - bits, - resb, - maxpower, - power, - c, - data = { val: getNextValue(0), position: resetValue, index: 1 }; - - for (i = 0; i < 3; i += 1) { - dictionary[i] = i; - } - - bits = 0; - maxpower = Math.pow(2, 2); - power = 1; - while (power != maxpower) { - resb = data.val & data.position; - data.position >>= 1; - if (data.position == 0) { - data.position = resetValue; - data.val = getNextValue(data.index++); - } - bits |= (resb > 0 ? 1 : 0) * power; - power <<= 1; - } - - switch ((next = bits)) { - case 0: - bits = 0; - maxpower = Math.pow(2, 8); - power = 1; - while (power != maxpower) { - resb = data.val & data.position; - data.position >>= 1; - if (data.position == 0) { - data.position = resetValue; - data.val = getNextValue(data.index++); - } - bits |= (resb > 0 ? 1 : 0) * power; - power <<= 1; - } - c = fromCharCode(bits); - break; - case 1: - bits = 0; - maxpower = Math.pow(2, 16); - power = 1; - while (power != maxpower) { - resb = data.val & data.position; - data.position >>= 1; - if (data.position == 0) { - data.position = resetValue; - data.val = getNextValue(data.index++); - } - bits |= (resb > 0 ? 1 : 0) * power; - power <<= 1; - } - c = fromCharCode(bits); - break; - case 2: - return ""; - } - dictionary[3] = c; - w = c; - result.push(c); - - // eslint-disable-next-line no-constant-condition - while (true) { - if (data.index > length) { - return ""; - } - - bits = 0; - maxpower = Math.pow(2, numBits); - power = 1; - while (power != maxpower) { - resb = data.val & data.position; - data.position >>= 1; - if (data.position == 0) { - data.position = resetValue; - data.val = getNextValue(data.index++); - } - bits |= (resb > 0 ? 1 : 0) * power; - power <<= 1; - } - - switch ((c = bits)) { - case 0: - bits = 0; - maxpower = Math.pow(2, 8); - power = 1; - while (power != maxpower) { - resb = data.val & data.position; - data.position >>= 1; - if (data.position == 0) { - data.position = resetValue; - data.val = getNextValue(data.index++); - } - bits |= (resb > 0 ? 1 : 0) * power; - power <<= 1; - } - - dictionary[dictSize++] = fromCharCode(bits); - c = dictSize - 1; - enlargeIn--; - break; - case 1: - bits = 0; - maxpower = Math.pow(2, 16); - power = 1; - while (power != maxpower) { - resb = data.val & data.position; - data.position >>= 1; - if (data.position == 0) { - data.position = resetValue; - data.val = getNextValue(data.index++); - } - bits |= (resb > 0 ? 1 : 0) * power; - power <<= 1; - } - dictionary[dictSize++] = fromCharCode(bits); - c = dictSize - 1; - enlargeIn--; - break; - case 2: - return result.join(""); - } - - if (enlargeIn == 0) { - enlargeIn = Math.pow(2, numBits); - numBits++; - } - - if (dictionary[c]) { - // @ts-ignore - entry = dictionary[c]; - } else { - if (c === dictSize) { - entry = w + w.charAt(0); - } else { - return null; - } - } - result.push(entry); - - // Add w+entry[0] to the dictionary. - dictionary[dictSize++] = w + entry.charAt(0); - enlargeIn--; - - w = entry; - - if (enlargeIn == 0) { - enlargeIn = Math.pow(2, numBits); - numBits++; - } - } -} diff --git a/src/js/core/modal_dialog_elements.js b/src/js/core/modal_dialog_elements.ts similarity index 72% rename from src/js/core/modal_dialog_elements.js rename to src/js/core/modal_dialog_elements.ts index e90f322d..90353132 100644 --- a/src/js/core/modal_dialog_elements.js +++ b/src/js/core/modal_dialog_elements.ts @@ -1,467 +1,504 @@ -/* typehints:start */ -import { Application } from "../application"; -/* typehints:end */ - -import { Signal, STOP_PROPAGATION } from "./signal"; -import { arrayDeleteValue, waitNextFrame } from "./utils"; -import { ClickDetector } from "./click_detector"; -import { SOUNDS } from "../platform/sound"; -import { InputReceiver } from "./input_receiver"; -import { FormElement } from "./modal_dialog_forms"; -import { globalConfig } from "./config"; -import { getStringForKeyCode } from "../game/key_action_mapper"; -import { createLogger } from "./logging"; -import { T } from "../translations"; - -/* - * *************************************************** - * - * LEGACY CODE WARNING - * - * This is old code from yorg3.io and needs to be refactored - * @TODO - * - * *************************************************** - */ - -const kbEnter = 13; -const kbCancel = 27; - -const logger = createLogger("dialogs"); - -/** - * Basic text based dialog - */ -export class Dialog { - /** - * - * Constructs a new dialog with the given options - * @param {object} param0 - * @param {Application} param0.app - * @param {string} param0.title Title of the dialog - * @param {string} param0.contentHTML Inner dialog html - * @param {Array} param0.buttons - * Button list, each button contains of up to 3 parts separated by ':'. - * Part 0: The id, one of the one defined in dialog_buttons.yaml - * Part 1: The style, either good, bad or misc - * Part 2 (optional): Additional parameters separated by '/', available are: - * timeout: This button is only available after some waiting time - * kb_enter: This button is triggered by the enter key - * kb_escape This button is triggered by the escape key - * @param {string=} param0.type The dialog type, either "info" or "warn" - * @param {boolean=} param0.closeButton Whether this dialog has a close button - */ - constructor({ app, title, contentHTML, buttons, type = "info", closeButton = false }) { - this.app = app; - this.title = title; - this.contentHTML = contentHTML; - this.type = type; - this.buttonIds = buttons; - this.closeButton = closeButton; - - this.closeRequested = new Signal(); - this.buttonSignals = {}; - - for (let i = 0; i < buttons.length; ++i) { - if (G_IS_DEV && globalConfig.debug.disableTimedButtons) { - this.buttonIds[i] = this.buttonIds[i].replace(":timeout", ""); - } - - const buttonId = this.buttonIds[i].split(":")[0]; - this.buttonSignals[buttonId] = new Signal(); - } - - this.valueChosen = new Signal(); - - this.timeouts = []; - this.clickDetectors = []; - - this.inputReciever = new InputReceiver("dialog-" + this.title); - - this.inputReciever.keydown.add(this.handleKeydown, this); - - this.enterHandler = null; - this.escapeHandler = null; - } - - /** - * Internal keydown handler - * @param {object} param0 - * @param {number} param0.keyCode - * @param {boolean} param0.shift - * @param {boolean} param0.alt - * @param {boolean} param0.ctrl - */ - handleKeydown({ keyCode, shift, alt, ctrl }) { - if (keyCode === kbEnter && this.enterHandler) { - this.internalButtonHandler(this.enterHandler); - return STOP_PROPAGATION; - } - - if (keyCode === kbCancel && this.escapeHandler) { - this.internalButtonHandler(this.escapeHandler); - return STOP_PROPAGATION; - } - } - - internalButtonHandler(id, ...payload) { - this.app.inputMgr.popReciever(this.inputReciever); - - if (id !== "close-button") { - this.buttonSignals[id].dispatch(...payload); - } - this.closeRequested.dispatch(); - } - - createElement() { - const elem = document.createElement("div"); - elem.classList.add("ingameDialog"); - - this.dialogElem = document.createElement("div"); - this.dialogElem.classList.add("dialogInner"); - - if (this.type) { - this.dialogElem.classList.add(this.type); - } - elem.appendChild(this.dialogElem); - - const title = document.createElement("h1"); - title.innerText = this.title; - title.classList.add("title"); - this.dialogElem.appendChild(title); - - if (this.closeButton) { - this.dialogElem.classList.add("hasCloseButton"); - - const closeBtn = document.createElement("button"); - closeBtn.classList.add("closeButton"); - - this.trackClicks(closeBtn, () => this.internalButtonHandler("close-button"), { - applyCssClass: "pressedSmallElement", - }); - - title.appendChild(closeBtn); - this.inputReciever.backButton.add(() => this.internalButtonHandler("close-button")); - } - - const content = document.createElement("div"); - content.classList.add("content"); - content.innerHTML = this.contentHTML; - this.dialogElem.appendChild(content); - - if (this.buttonIds.length > 0) { - const buttons = document.createElement("div"); - buttons.classList.add("buttons"); - - // Create buttons - for (let i = 0; i < this.buttonIds.length; ++i) { - const [buttonId, buttonStyle, rawParams] = this.buttonIds[i].split(":"); - - const button = document.createElement("button"); - button.classList.add("button"); - button.classList.add("styledButton"); - button.classList.add(buttonStyle); - button.innerText = T.dialogs.buttons[buttonId]; - - const params = (rawParams || "").split("/"); - const useTimeout = params.indexOf("timeout") >= 0; - - const isEnter = params.indexOf("enter") >= 0; - const isEscape = params.indexOf("escape") >= 0; - - if (isEscape && this.closeButton) { - logger.warn("Showing dialog with close button, and additional cancel button"); - } - - if (useTimeout) { - button.classList.add("timedButton"); - const timeout = setTimeout(() => { - button.classList.remove("timedButton"); - arrayDeleteValue(this.timeouts, timeout); - }, 1000); - this.timeouts.push(timeout); - } - if (isEnter || isEscape) { - // if (this.app.settings.getShowKeyboardShortcuts()) { - // Show keybinding - const spacer = document.createElement("code"); - spacer.classList.add("keybinding"); - spacer.innerHTML = getStringForKeyCode(isEnter ? kbEnter : kbCancel); - button.appendChild(spacer); - // } - - if (isEnter) { - this.enterHandler = buttonId; - } - if (isEscape) { - this.escapeHandler = buttonId; - } - } - - this.trackClicks(button, () => this.internalButtonHandler(buttonId)); - buttons.appendChild(button); - } - - this.dialogElem.appendChild(buttons); - } else { - this.dialogElem.classList.add("buttonless"); - } - - this.element = elem; - this.app.inputMgr.pushReciever(this.inputReciever); - - return this.element; - } - - setIndex(index) { - this.element.style.zIndex = index; - } - - destroy() { - if (!this.element) { - assert(false, "Tried to destroy dialog twice"); - return; - } - // We need to do this here, because if the backbutton event gets - // dispatched to the modal dialogs, it will not call the internalButtonHandler, - // and thus our receiver stays attached the whole time - this.app.inputMgr.destroyReceiver(this.inputReciever); - - for (let i = 0; i < this.clickDetectors.length; ++i) { - this.clickDetectors[i].cleanup(); - } - this.clickDetectors = []; - - this.element.remove(); - this.element = null; - - for (let i = 0; i < this.timeouts.length; ++i) { - clearTimeout(this.timeouts[i]); - } - this.timeouts = []; - } - - hide() { - this.element.classList.remove("visible"); - } - - show() { - this.element.classList.add("visible"); - } - - /** - * Helper method to track clicks on an element - * @param {Element} elem - * @param {function():void} handler - * @param {import("./click_detector").ClickDetectorConstructorArgs=} args - * @returns {ClickDetector} - */ - trackClicks(elem, handler, args = {}) { - const detector = new ClickDetector(elem, args); - detector.click.add(handler, this); - this.clickDetectors.push(detector); - return detector; - } -} - -/** - * Dialog which simply shows a loading spinner - */ -export class DialogLoading extends Dialog { - constructor(app, text = "") { - super({ - app, - title: "", - contentHTML: "", - buttons: [], - type: "loading", - }); - - // Loading dialog can not get closed with back button - this.inputReciever.backButton.removeAll(); - this.inputReciever.context = "dialog-loading"; - - this.text = text; - } - - createElement() { - const elem = document.createElement("div"); - elem.classList.add("ingameDialog"); - elem.classList.add("loadingDialog"); - this.element = elem; - - if (this.text) { - const text = document.createElement("div"); - text.classList.add("text"); - text.innerText = this.text; - elem.appendChild(text); - } - - const loader = document.createElement("div"); - loader.classList.add("prefab_LoadingTextWithAnim"); - loader.classList.add("loadingIndicator"); - elem.appendChild(loader); - - this.app.inputMgr.pushReciever(this.inputReciever); - - return elem; - } -} - -export class DialogOptionChooser extends Dialog { - constructor({ app, title, options }) { - let html = "
"; - - options.options.forEach(({ value, text, desc = null, iconPrefix = null }) => { - const descHtml = desc ? `${desc}` : ""; - let iconHtml = iconPrefix ? `` : ""; - html += ` -
- ${iconHtml} - ${text} - ${descHtml} -
- `; - }); - - html += "
"; - super({ - app, - title, - contentHTML: html, - buttons: [], - type: "info", - closeButton: true, - }); - - this.options = options; - this.initialOption = options.active; - - this.buttonSignals.optionSelected = new Signal(); - } - - createElement() { - const div = super.createElement(); - this.dialogElem.classList.add("optionChooserDialog"); - - div.querySelectorAll("[data-optionvalue]").forEach(handle => { - const value = handle.getAttribute("data-optionvalue"); - if (!handle) { - logger.error("Failed to bind option value in dialog:", value); - return; - } - // Need click detector here to forward elements, otherwise scrolling does not work - const detector = new ClickDetector(handle, { - consumeEvents: false, - preventDefault: false, - clickSound: null, - applyCssClass: "pressedOption", - targetOnly: true, - }); - this.clickDetectors.push(detector); - - if (value !== this.initialOption) { - detector.click.add(() => { - const selected = div.querySelector(".option.active"); - if (selected) { - selected.classList.remove("active"); - } else { - logger.warn("No selected option"); - } - handle.classList.add("active"); - this.app.sound.playUiSound(SOUNDS.uiClick); - this.internalButtonHandler("optionSelected", value); - }); - } - }); - return div; - } -} - -export class DialogWithForm extends Dialog { - /** - * - * @param {object} param0 - * @param {Application} param0.app - * @param {string} param0.title - * @param {string} param0.desc - * @param {array=} param0.buttons - * @param {string=} param0.confirmButtonId - * @param {string=} param0.extraButton - * @param {boolean=} param0.closeButton - * @param {Array} param0.formElements - */ - constructor({ - app, - title, - desc, - formElements, - buttons = ["cancel", "ok:good"], - confirmButtonId = "ok", - closeButton = true, - }) { - let html = ""; - html += desc + "
"; - for (let i = 0; i < formElements.length; ++i) { - html += formElements[i].getHtml(); - } - - super({ - app, - title: title, - contentHTML: html, - buttons: buttons, - type: "info", - closeButton, - }); - this.confirmButtonId = confirmButtonId; - this.formElements = formElements; - - this.enterHandler = confirmButtonId; - } - - internalButtonHandler(id, ...payload) { - if (id === this.confirmButtonId) { - if (this.hasAnyInvalid()) { - this.dialogElem.classList.remove("errorShake"); - waitNextFrame().then(() => { - if (this.dialogElem) { - this.dialogElem.classList.add("errorShake"); - } - }); - this.app.sound.playUiSound(SOUNDS.uiError); - return; - } - } - - super.internalButtonHandler(id, payload); - } - - hasAnyInvalid() { - for (let i = 0; i < this.formElements.length; ++i) { - if (!this.formElements[i].isValid()) { - return true; - } - } - return false; - } - - createElement() { - const div = super.createElement(); - - for (let i = 0; i < this.formElements.length; ++i) { - const elem = this.formElements[i]; - elem.bindEvents(div, this.clickDetectors); - // elem.valueChosen.add(this.closeRequested.dispatch, this.closeRequested); - elem.valueChosen.add(this.valueChosen.dispatch, this.valueChosen); - } - - waitNextFrame().then(() => { - this.formElements[this.formElements.length - 1].focus(); - }); - - return div; - } -} +import type { Application } from "../application"; + +import { SOUNDS } from "../platform/sound"; +import { T } from "../translations"; +import { ClickDetector, ClickDetectorConstructorArgs } from "./click_detector"; +import { globalConfig } from "./config"; +import { InputReceiver, KeydownEvent } from "./input_receiver"; +import { getStringForKeyCode } from "./keycodes"; +import { createLogger } from "./logging"; +import { FormElement } from "./modal_dialog_forms"; +import { Signal, STOP_PROPAGATION } from "./signal"; +import { arrayDeleteValue, waitNextFrame } from "./utils"; + +/* + * *************************************************** + * + * LEGACY CODE WARNING + * + * This is old code from yorg3.io and needs to be refactored + * @TODO + * + * *************************************************** + */ + +const kbEnter = 13; +const kbCancel = 27; + +const logger = createLogger("dialogs"); + +export type DialogButtonStr = `${T}:${string}` | T; +export type DialogButtonType = "info" | "loading" | "warning"; + +/** + * Basic text based dialog + */ +export class Dialog { + public title: string; + public app: Application; + public contentHTML: string; + public type: string; + public buttonIds: string[]; + public closeButton: boolean; + public dialogElem: HTMLDivElement; + public element: HTMLDivElement; + + public closeRequested = new Signal(); + public buttonSignals = {} as Record>; + + public valueChosen = new Signal<[unknown]>(); + + public timeouts: number[] = []; + public clickDetectors: ClickDetector[] = []; + + public inputReceiver: InputReceiver; + public enterHandler: T = null; + public escapeHandler: T = null; + + /** + * + * Constructs a new dialog with the given options + * @param param0 + * @param param0.title Title of the dialog + * @param param0.contentHTML Inner dialog html + * @param param0.buttons + * Button list, each button contains of up to 3 parts separated by ':'. + * Part 0: The id, one of the one defined in dialog_buttons.yaml + * Part 1: The style, either good, bad or misc + * Part 2 (optional): Additional parameters separated by '/', available are: + * timeout: This button is only available after some waiting time + * kb_enter: This button is triggered by the enter key + * kb_escape This button is triggered by the escape key + * @param param0.type The dialog type, either "info", "warning", or "loading" + * @param param0.closeButton Whether this dialog has a close button + */ + constructor({ + app, + title, + contentHTML, + buttons, + type = "info", + closeButton = false, + }: { + app: Application; + title: string; + contentHTML: string; + buttons?: DialogButtonStr[]; + type?: DialogButtonType; + closeButton?: boolean; + }) { + this.app = app; + this.title = title; + this.contentHTML = contentHTML; + this.type = type; + this.buttonIds = buttons; + this.closeButton = closeButton; + + for (let i = 0; i < buttons.length; ++i) { + if (G_IS_DEV && globalConfig.debug.disableTimedButtons) { + this.buttonIds[i] = this.buttonIds[i].replace(":timeout", ""); + } + + const buttonId = this.buttonIds[i].split(":")[0]; + this.buttonSignals[buttonId] = new Signal(); + } + + this.inputReceiver = new InputReceiver("dialog-" + this.title); + + this.inputReceiver.keydown.add(this.handleKeydown, this); + } + + /** + * Internal keydown handler + */ + handleKeydown({ keyCode, shift, alt, ctrl }: KeydownEvent): void | STOP_PROPAGATION { + if (keyCode === kbEnter && this.enterHandler) { + this.internalButtonHandler(this.enterHandler); + return STOP_PROPAGATION; + } + + if (keyCode === kbCancel && this.escapeHandler) { + this.internalButtonHandler(this.escapeHandler); + return STOP_PROPAGATION; + } + } + + internalButtonHandler(id: T | "close-button", ...payload: U | []) { + this.app.inputMgr.popReceiver(this.inputReceiver); + + if (id !== "close-button") { + this.buttonSignals[id].dispatch(...payload); + } + this.closeRequested.dispatch(); + } + + createElement() { + const elem = document.createElement("div"); + elem.classList.add("ingameDialog"); + + this.dialogElem = document.createElement("div"); + this.dialogElem.classList.add("dialogInner"); + + if (this.type) { + this.dialogElem.classList.add(this.type); // @TODO: `this.type` seems unused + } + elem.appendChild(this.dialogElem); + + const title = document.createElement("h1"); + title.innerText = this.title; + title.classList.add("title"); + this.dialogElem.appendChild(title); + + if (this.closeButton) { + this.dialogElem.classList.add("hasCloseButton"); + + const closeBtn = document.createElement("button"); + closeBtn.classList.add("closeButton"); + + this.trackClicks(closeBtn, () => this.internalButtonHandler("close-button"), { + applyCssClass: "pressedSmallElement", + }); + + title.appendChild(closeBtn); + this.inputReceiver.backButton.add(() => this.internalButtonHandler("close-button")); + } + + const content = document.createElement("div"); + content.classList.add("content"); + content.innerHTML = this.contentHTML; + this.dialogElem.appendChild(content); + + if (this.buttonIds.length > 0) { + const buttons = document.createElement("div"); + buttons.classList.add("buttons"); + + // Create buttons + for (let i = 0; i < this.buttonIds.length; ++i) { + const [buttonId, buttonStyle, rawParams] = this.buttonIds[i].split(":") as [ + T, + string, + string?, + ]; // @TODO: some button strings omit `buttonStyle` + + const button = document.createElement("button"); + button.classList.add("button"); + button.classList.add("styledButton"); + button.classList.add(buttonStyle); + button.innerText = T.dialogs.buttons[buttonId as string]; + + const params = (rawParams || "").split("/"); + const useTimeout = params.indexOf("timeout") >= 0; + + const isEnter = params.indexOf("enter") >= 0; + const isEscape = params.indexOf("escape") >= 0; + + if (isEscape && this.closeButton) { + logger.warn("Showing dialog with close button, and additional cancel button"); + } + + if (useTimeout) { + button.classList.add("timedButton"); + const timeout = setTimeout(() => { + button.classList.remove("timedButton"); + arrayDeleteValue(this.timeouts, timeout); + }, 1000) as unknown as number; // @TODO: @types/node should not be affecting this + this.timeouts.push(timeout); + } + if (isEnter || isEscape) { + // if (this.app.settings.getShowKeyboardShortcuts()) { + // Show keybinding + const spacer = document.createElement("kbd"); + spacer.innerHTML = getStringForKeyCode(isEnter ? kbEnter : kbCancel); + button.appendChild(spacer); + // } + + if (isEnter) { + this.enterHandler = buttonId; + } + if (isEscape) { + this.escapeHandler = buttonId; + } + } + + this.trackClicks(button, () => this.internalButtonHandler(buttonId)); + buttons.appendChild(button); + } + + this.dialogElem.appendChild(buttons); + } else { + this.dialogElem.classList.add("buttonless"); + } + + this.element = elem; + this.app.inputMgr.pushReceiver(this.inputReceiver); + + return this.element; + } + + setIndex(index: string) { + this.element.style.zIndex = index; + } + + destroy() { + if (!this.element) { + assert(false, "Tried to destroy dialog twice"); + return; + } + // We need to do this here, because if the backbutton event gets + // dispatched to the modal dialogs, it will not call the internalButtonHandler, + // and thus our receiver stays attached the whole time + this.app.inputMgr.destroyReceiver(this.inputReceiver); + + for (let i = 0; i < this.clickDetectors.length; ++i) { + this.clickDetectors[i].cleanup(); + } + this.clickDetectors = []; + + this.element.remove(); + this.element = null; + + for (let i = 0; i < this.timeouts.length; ++i) { + clearTimeout(this.timeouts[i]); + } + this.timeouts = []; + } + + hide() { + this.element.classList.remove("visible"); + } + + show() { + this.element.classList.add("visible"); + } + + /** + * Helper method to track clicks on an element + */ + trackClicks(elem: Element, handler: () => void, args: ClickDetectorConstructorArgs = {}) { + const detector = new ClickDetector(elem, args); + detector.click.add(handler, this); + this.clickDetectors.push(detector); + return detector; + } +} + +/** + * Dialog which simply shows a loading spinner + */ +export class DialogLoading extends Dialog { + constructor( + app: Application, + public text = "" + ) { + super({ + app, + title: "", + contentHTML: "", + buttons: [], + type: "loading", + }); + + // Loading dialog can not get closed with back button + this.inputReceiver.backButton.removeAll(); + this.inputReceiver.context = "dialog-loading"; + } + + createElement() { + const elem = document.createElement("div"); + elem.classList.add("ingameDialog"); + elem.classList.add("loadingDialog"); + this.element = elem; + + if (this.text) { + const text = document.createElement("div"); + text.classList.add("text"); + text.innerText = this.text; + elem.appendChild(text); + } + + const loader = document.createElement("div"); + loader.classList.add("prefab_LoadingTextWithAnim"); + loader.classList.add("loadingIndicator"); + elem.appendChild(loader); + + this.app.inputMgr.pushReceiver(this.inputReceiver); + + return elem; + } +} + +type DialogOptionChooserOption = { value: string; text: string; desc?: string; iconPrefix?: string }; +export class DialogOptionChooser extends Dialog<"optionSelected", [string]> { + public options: { + options: DialogOptionChooserOption[]; + active: string; + }; + public initialOption: string; + + constructor({ + app, + title, + options, + }: { + app: Application; + title: string; + options: { + options: DialogOptionChooserOption[]; + active: string; + }; + }) { + let html = "
"; + + options.options.forEach(({ value, text, desc = null, iconPrefix = null }) => { + const descHtml = desc ? `${desc}` : ""; + const iconHtml = iconPrefix ? `` : ""; + html += ` +
+ ${iconHtml} + ${text} + ${descHtml} +
+ `; + }); + + html += "
"; + super({ + app, + title, + contentHTML: html, + buttons: [], + type: "info", + closeButton: true, + }); + + this.options = options; + this.initialOption = options.active; + + this.buttonSignals.optionSelected = new Signal(); + } + + createElement() { + const div = super.createElement(); + this.dialogElem.classList.add("optionChooserDialog"); + + div.querySelectorAll("[data-optionvalue]").forEach(handle => { + const value = handle.getAttribute("data-optionvalue"); + if (!handle) { + logger.error("Failed to bind option value in dialog:", value); + return; + } + // Need click detector here to forward elements, otherwise scrolling does not work + const detector = new ClickDetector(handle, { + consumeEvents: false, + preventDefault: false, + clickSound: null, + applyCssClass: "pressedOption", + targetOnly: true, + }); + this.clickDetectors.push(detector); + + if (value !== this.initialOption) { + detector.click.add(() => { + const selected = div.querySelector(".option.active"); + if (selected) { + selected.classList.remove("active"); + } else { + logger.warn("No selected option"); + } + handle.classList.add("active"); + this.app.sound.playUiSound(SOUNDS.uiClick); + this.internalButtonHandler("optionSelected", value); + }); + } + }); + return div; + } +} + +export class DialogWithForm extends Dialog { + public confirmButtonId: string; + // `FormElement` is invariant so `unknown` and `never` don't work + public formElements: FormElement[]; + + constructor({ + app, + title, + desc, + formElements, + buttons = ["cancel", "ok:good"] as any, + confirmButtonId = "ok" as any, + closeButton = true, + }: { + app: Application; + title: string; + desc: string; + formElements: FormElement[]; + buttons?: DialogButtonStr[]; + confirmButtonId?: T; + closeButton?: boolean; + }) { + let html = ""; + html += desc + "
"; + for (let i = 0; i < formElements.length; ++i) { + html += formElements[i].getHtml(); + } + + super({ + app, + title: title, + contentHTML: html, + buttons: buttons, + type: "info", + closeButton, + }); + this.confirmButtonId = confirmButtonId; + this.formElements = formElements; + + this.enterHandler = confirmButtonId; + } + + internalButtonHandler(id: T | "close-button", ...payload: []) { + if (id === this.confirmButtonId) { + if (this.hasAnyInvalid()) { + this.dialogElem.classList.remove("errorShake"); + waitNextFrame().then(() => { + if (this.dialogElem) { + this.dialogElem.classList.add("errorShake"); + } + }); + this.app.sound.playUiSound(SOUNDS.uiError); + return; + } + } + + super.internalButtonHandler(id, ...payload); + } + + hasAnyInvalid() { + for (let i = 0; i < this.formElements.length; ++i) { + if (!this.formElements[i].isValid()) { + return true; + } + } + return false; + } + + createElement() { + const div = super.createElement(); + + for (let i = 0; i < this.formElements.length; ++i) { + const elem = this.formElements[i]; + elem.bindEvents(div, this.clickDetectors); + // elem.valueChosen.add(this.closeRequested.dispatch, this.closeRequested); + elem.valueChosen.add(this.valueChosen.dispatch, this.valueChosen); + } + + waitNextFrame().then(() => { + this.formElements[this.formElements.length - 1].focus(); + }); + + return div; + } +} diff --git a/src/js/core/modal_dialog_forms.js b/src/js/core/modal_dialog_forms.ts similarity index 69% rename from src/js/core/modal_dialog_forms.js rename to src/js/core/modal_dialog_forms.ts index a4e617f8..437264c9 100644 --- a/src/js/core/modal_dialog_forms.js +++ b/src/js/core/modal_dialog_forms.ts @@ -1,249 +1,241 @@ -import { BaseItem } from "../game/base_item"; -import { ClickDetector } from "./click_detector"; -import { Signal } from "./signal"; - -/* - * *************************************************** - * - * LEGACY CODE WARNING - * - * This is old code from yorg3.io and needs to be refactored - * @TODO - * - * *************************************************** - */ - -export class FormElement { - constructor(id, label) { - this.id = id; - this.label = label; - - this.valueChosen = new Signal(); - } - - getHtml() { - abstract; - return ""; - } - - getFormElement(parent) { - return parent.querySelector("[data-formId='" + this.id + "']"); - } - - bindEvents(parent, clickTrackers) { - abstract; - } - - focus() {} - - isValid() { - return true; - } - - /** @returns {any} */ - getValue() { - abstract; - } -} - -export class FormElementInput extends FormElement { - constructor({ id, label = null, placeholder, defaultValue = "", inputType = "text", validator = null }) { - super(id, label); - this.placeholder = placeholder; - this.defaultValue = defaultValue; - this.inputType = inputType; - this.validator = validator; - - this.element = null; - } - - getHtml() { - let classes = []; - let inputType = "text"; - let maxlength = 256; - switch (this.inputType) { - case "text": { - classes.push("input-text"); - break; - } - - case "email": { - classes.push("input-email"); - inputType = "email"; - break; - } - - case "token": { - classes.push("input-token"); - inputType = "text"; - maxlength = 4; - break; - } - } - - return ` -
- ${this.label ? `` : ""} - -
- `; - } - - bindEvents(parent, clickTrackers) { - this.element = this.getFormElement(parent); - this.element.addEventListener("input", event => this.updateErrorState()); - this.updateErrorState(); - } - - updateErrorState() { - this.element.classList.toggle("errored", !this.isValid()); - - // profanity filter - if (G_WEGAME_VERSION) { - const value = String(this.element.value); - - ipcRenderer.invoke("profanity-check", value).then(newValue => { - if (value !== newValue && this.element) { - this.element.value = newValue; - } - }); - } - } - - isValid() { - return !this.validator || this.validator(this.element.value); - } - - getValue() { - return this.element.value; - } - - setValue(value) { - this.element.value = value; - this.updateErrorState(); - } - - focus() { - this.element.focus(); - this.element.select(); - } -} - -export class FormElementCheckbox extends FormElement { - constructor({ id, label, defaultValue = true }) { - super(id, label); - this.defaultValue = defaultValue; - this.value = this.defaultValue; - - this.element = null; - } - - getHtml() { - return ` -
- ${this.label ? `` : ""} -
- -
-
- `; - } - - bindEvents(parent, clickTrackers) { - this.element = this.getFormElement(parent); - const detector = new ClickDetector(this.element, { - consumeEvents: false, - preventDefault: false, - }); - clickTrackers.push(detector); - detector.click.add(this.toggle, this); - } - - getValue() { - return this.value; - } - - toggle() { - this.value = !this.value; - this.element.classList.toggle("checked", this.value); - } - - focus(parent) {} -} - -export class FormElementItemChooser extends FormElement { - /** - * - * @param {object} param0 - * @param {string} param0.id - * @param {string=} param0.label - * @param {Array} param0.items - */ - constructor({ id, label, items = [] }) { - super(id, label); - this.items = items; - this.element = null; - - /** - * @type {BaseItem} - */ - this.chosenItem = null; - } - - getHtml() { - let classes = []; - - return ` -
- ${this.label ? `` : ""} -
-
- `; - } - - /** - * @param {HTMLElement} parent - * @param {Array} clickTrackers - */ - bindEvents(parent, clickTrackers) { - this.element = this.getFormElement(parent); - - for (let i = 0; i < this.items.length; ++i) { - const item = this.items[i]; - - const canvas = document.createElement("canvas"); - canvas.width = 128; - canvas.height = 128; - const context = canvas.getContext("2d"); - item.drawFullSizeOnCanvas(context, 128); - this.element.appendChild(canvas); - - const detector = new ClickDetector(canvas, {}); - clickTrackers.push(detector); - detector.click.add(() => { - this.chosenItem = item; - this.valueChosen.dispatch(item); - }); - } - } - - isValid() { - return true; - } - - getValue() { - return null; - } - - focus() {} -} +import { BaseItem } from "../game/base_item"; +import { ClickDetector } from "./click_detector"; +import { Signal } from "./signal"; + +/* + * *************************************************** + * + * LEGACY CODE WARNING + * + * This is old code from yorg3.io and needs to be refactored + * @TODO + * + * *************************************************** + */ + +export abstract class FormElement { + public valueChosen = new Signal<[T]>(); + + constructor( + public id: string, + public label: string + ) {} + + abstract getHtml(): string; + + getFormElement(parent: HTMLElement): HTMLElement { + return parent.querySelector("[data-formId='" + this.id + "']"); + } + + abstract bindEvents(parent: HTMLDivElement, clickTrackers: ClickDetector[]): void; + + focus() {} + + isValid() { + return true; + } + + abstract getValue(): T; +} + +export class FormElementInput extends FormElement { + public placeholder: string; + public defaultValue: string; + public inputType: "text" | "email" | "token"; + public validator: (value: string) => boolean; + + public element: HTMLInputElement = null; + + constructor({ + id, + label = null, + placeholder, + defaultValue = "", + inputType = "text", + validator = null, + }: { + id: string; + label?: string; + placeholder: string; + defaultValue?: string; + inputType?: "text" | "email" | "token"; + validator?: (value: string) => boolean; + }) { + super(id, label); + this.placeholder = placeholder; + this.defaultValue = defaultValue; + this.inputType = inputType; + this.validator = validator; + } + + getHtml() { + const classes = []; + let inputType = "text"; + let maxlength = 256; + // @TODO: `inputType` and these classes are unused + switch (this.inputType) { + case "text": { + classes.push("input-text"); + break; + } + + case "email": { + classes.push("input-email"); + inputType = "email"; + break; + } + + case "token": { + classes.push("input-token"); + inputType = "text"; + maxlength = 4; + break; + } + } + + return ` +
+ ${this.label ? `` : ""} + +
+ `; + } + + bindEvents(parent: HTMLDivElement, clickTrackers: ClickDetector[]) { + this.element = this.getFormElement(parent) as HTMLInputElement; + this.element.addEventListener("input", event => this.updateErrorState()); + this.updateErrorState(); + } + + updateErrorState() { + this.element.classList.toggle("errored", !this.isValid()); + } + + isValid() { + return !this.validator || this.validator(this.element.value); + } + + getValue() { + return this.element.value; + } + + setValue(value: string) { + this.element.value = value; + this.updateErrorState(); + } + + focus() { + this.element.focus(); + this.element.select(); + } +} + +export class FormElementCheckbox extends FormElement { + public defaultValue: boolean; + public value: boolean; + public element: HTMLDivElement; + + constructor({ id, label, defaultValue = true }) { + super(id, label); + this.defaultValue = defaultValue; + this.value = this.defaultValue; + + this.element = null; + } + + getHtml() { + return ` +
+ ${this.label ? `` : ""} +
+ +
+
+ `; + } + + bindEvents(parent: HTMLDivElement, clickTrackers: ClickDetector[]) { + this.element = this.getFormElement(parent) as HTMLDivElement; + const detector = new ClickDetector(this.element, { + consumeEvents: false, + preventDefault: false, + }); + clickTrackers.push(detector); + detector.click.add(this.toggle, this); + } + + getValue() { + return this.value; + } + + toggle() { + this.value = !this.value; + this.element.classList.toggle("checked", this.value); + } + + focus() {} +} + +export class FormElementItemChooser extends FormElement { + public items: BaseItem[]; + public element: HTMLDivElement = null; + public chosenItem: BaseItem = null; + + constructor({ id, label, items = [] }: { id: string; label: string; items: BaseItem[] }) { + super(id, label); + this.items = items; + } + + getHtml() { + const classes = []; + + return ` +
+ ${this.label ? `` : ""} +
+
+ `; + } + + bindEvents(parent: HTMLElement, clickTrackers: ClickDetector[]) { + this.element = this.getFormElement(parent) as HTMLDivElement; + + for (let i = 0; i < this.items.length; ++i) { + const item = this.items[i]; + + const canvas = document.createElement("canvas"); + canvas.width = 128; + canvas.height = 128; + const context = canvas.getContext("2d"); + item.drawFullSizeOnCanvas(context, 128); + this.element.appendChild(canvas); + + const detector = new ClickDetector(canvas, {}); + clickTrackers.push(detector); + detector.click.add(() => { + this.chosenItem = item; + this.valueChosen.dispatch(item); + }); + } + } + + isValid() { + return true; + } + + getValue() { + return null; + } + + focus() {} +} diff --git a/src/js/core/polyfills.js b/src/js/core/polyfills.js deleted file mode 100644 index 52c17911..00000000 --- a/src/js/core/polyfills.js +++ /dev/null @@ -1,123 +0,0 @@ -function mathPolyfills() { - // Converts from degrees to radians. - Math.radians = function (degrees) { - return (degrees * Math.PI) / 180.0; - }; - - // Converts from radians to degrees. - Math.degrees = function (radians) { - return (radians * 180.0) / Math.PI; - }; -} - -function stringPolyfills() { - // https://github.com/uxitten/polyfill/blob/master/string.polyfill.js - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padStart - if (!String.prototype.padStart) { - String.prototype.padStart = function padStart(targetLength, padString) { - targetLength = targetLength >> 0; //truncate if number, or convert non-number to 0; - padString = String(typeof padString !== "undefined" ? padString : " "); - if (this.length >= targetLength) { - return String(this); - } else { - targetLength = targetLength - this.length; - if (targetLength > padString.length) { - padString += padString.repeat(targetLength / padString.length); //append to original to ensure we are longer than needed - } - return padString.slice(0, targetLength) + String(this); - } - }; - } - - // https://github.com/uxitten/polyfill/blob/master/string.polyfill.js - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padEnd - if (!String.prototype.padEnd) { - String.prototype.padEnd = function padEnd(targetLength, padString) { - targetLength = targetLength >> 0; //floor if number or convert non-number to 0; - padString = String(typeof padString !== "undefined" ? padString : " "); - if (this.length > targetLength) { - return String(this); - } else { - targetLength = targetLength - this.length; - if (targetLength > padString.length) { - padString += padString.repeat(targetLength / padString.length); //append to original to ensure we are longer than needed - } - return String(this) + padString.slice(0, targetLength); - } - }; - } -} - -function objectPolyfills() { - // https://github.com/tc39/proposal-object-values-entries/blob/master/polyfill.js - - // @ts-ignore - const reduce = Function.bind.call(Function.call, Array.prototype.reduce); - // @ts-ignore - const isEnumerable = Function.bind.call(Function.call, Object.prototype.propertyIsEnumerable); - // @ts-ignore - const concat = Function.bind.call(Function.call, Array.prototype.concat); - const keys = Reflect.ownKeys; - - // @ts-ignore - if (!Object.values) { - // @ts-ignore - Object.values = function values(O) { - return reduce( - keys(O), - (v, k) => concat(v, typeof k === "string" && isEnumerable(O, k) ? [O[k]] : []), - [] - ); - }; - } - - if (!Object.entries) { - // @ts-ignore - Object.entries = function entries(O) { - return reduce( - keys(O), - (e, k) => concat(e, typeof k === "string" && isEnumerable(O, k) ? [[k, O[k]]] : []), - [] - ); - }; - } -} - -function domPolyfills() { - // from:https://github.com/jserz/js_piece/blob/master/DOM/ChildNode/remove()/remove().md - (function (arr) { - arr.forEach(function (item) { - if (item.hasOwnProperty("remove")) { - return; - } - Object.defineProperty(item, "remove", { - configurable: true, - enumerable: true, - writable: true, - value: function remove() { - this.parentNode.removeChild(this); - }, - }); - }); - })([Element.prototype, CharacterData.prototype, DocumentType.prototype]); -} - -function initPolyfills() { - mathPolyfills(); - stringPolyfills(); - objectPolyfills(); - domPolyfills(); -} - -function initExtensions() { - String.prototype.replaceAll = function (search, replacement) { - var target = this; - return target.split(search).join(replacement); - }; -} - -// Fetch polyfill -import "whatwg-fetch"; -// Other polyfills -initPolyfills(); -initExtensions(); diff --git a/src/js/core/polyfills.ts b/src/js/core/polyfills.ts new file mode 100644 index 00000000..e9a2bd28 --- /dev/null +++ b/src/js/core/polyfills.ts @@ -0,0 +1,21 @@ +// Converts from degrees to radians. +Math.radians = function (degrees: number): number { + return (degrees * Math.PI) / 180.0; +}; + +// Converts from radians to degrees. +Math.degrees = function (radians: number): number { + return (radians * 180.0) / Math.PI; +}; + +// Begins a path and draws a circle. +CanvasRenderingContext2D.prototype.beginCircle = function (x: number, y: number, r: number): void { + this.beginPath(); + + if (r < 0.05) { + this.rect(x, y, 1, 1); + return; + } + + this.arc(x, y, r, 0, 2.0 * Math.PI); +}; diff --git a/src/js/core/query_parameters.js b/src/js/core/query_parameters.js deleted file mode 100644 index 5d145644..00000000 --- a/src/js/core/query_parameters.js +++ /dev/null @@ -1,29 +0,0 @@ -const queryString = require("query-string"); -const options = queryString.parse(location.search); - -export let queryParamOptions = { - embedProvider: null, - abtVariant: null, - campaign: null, - fbclid: null, - gclid: null, -}; - -if (options.embed) { - queryParamOptions.embedProvider = options.embed; -} - -if (options.abtVariant) { - queryParamOptions.abtVariant = options.abtVariant; -} - -if (options.fbclid) { - queryParamOptions.fbclid = options.fbclid; -} - -if (options.gclid) { - queryParamOptions.gclid = options.gclid; -} -if (options.utm_campaign) { - queryParamOptions.campaign = options.utm_campaign; -} diff --git a/src/js/core/read_write_proxy.js b/src/js/core/read_write_proxy.js index 7c96149b..0ce9f987 100644 --- a/src/js/core/read_write_proxy.js +++ b/src/js/core/read_write_proxy.js @@ -1,343 +1,244 @@ -/* typehints:start */ -import { Application } from "../application"; -/* typehints:end */ - -import { sha1, CRC_PREFIX, computeCrc } from "./sensitive_utils.encrypt"; -import { createLogger } from "./logging"; -import { FILE_NOT_FOUND } from "../platform/storage"; -import { accessNestedPropertyReverse } from "./utils"; -import { IS_DEBUG, globalConfig } from "./config"; -import { ExplainedResult } from "./explained_result"; -import { decompressX64, compressX64 } from "./lzstring"; -import { asyncCompressor, compressionPrefix } from "./async_compression"; -import { compressObject, decompressObject } from "../savegame/savegame_compressor"; - -const debounce = require("debounce-promise"); - -const logger = createLogger("read_write_proxy"); - -const salt = accessNestedPropertyReverse(globalConfig, ["file", "info"]); - -// Helper which only writes / reads if verify() works. Also performs migration -export class ReadWriteProxy { - constructor(app, filename) { - /** @type {Application} */ - this.app = app; - - this.filename = filename; - - /** @type {object} */ - this.currentData = null; - - // TODO: EXTREMELY HACKY! To verify we need to do this a step later - if (G_IS_DEV && IS_DEBUG) { - setTimeout(() => { - assert( - this.verify(this.getDefaultData()).result, - "Verify() failed for default data: " + this.verify(this.getDefaultData()).reason - ); - }); - } - - /** - * Store a debounced handler to prevent double writes - */ - this.debouncedWrite = debounce(this.doWriteAsync.bind(this), 50); - } - - // -- Methods to override - - /** @returns {ExplainedResult} */ - verify(data) { - abstract; - return ExplainedResult.bad(); - } - - // Should return the default data - getDefaultData() { - abstract; - return {}; - } - - // Should return the current version as an integer - getCurrentVersion() { - abstract; - return 0; - } - - // Should migrate the data (Modify in place) - /** @returns {ExplainedResult} */ - migrate(data) { - abstract; - return ExplainedResult.bad(); - } - - // -- / Methods - - // Resets whole data, returns promise - resetEverythingAsync() { - logger.warn("Reset data to default"); - this.currentData = this.getDefaultData(); - return this.writeAsync(); - } - - /** - * - * @param {object} obj - */ - static serializeObject(obj) { - const jsonString = JSON.stringify(compressObject(obj)); - const checksum = computeCrc(jsonString + salt); - return compressionPrefix + compressX64(checksum + jsonString); - } - - /** - * - * @param {object} text - */ - static deserializeObject(text) { - const decompressed = decompressX64(text.substr(compressionPrefix.length)); - if (!decompressed) { - // LZ string decompression failure - throw new Error("bad-content / decompression-failed"); - } - if (decompressed.length < 40) { - // String too short - throw new Error("bad-content / payload-too-small"); - } - - // Compare stored checksum with actual checksum - const checksum = decompressed.substring(0, 40); - const jsonString = decompressed.substr(40); - - const desiredChecksum = checksum.startsWith(CRC_PREFIX) - ? computeCrc(jsonString + salt) - : sha1(jsonString + salt); - - if (desiredChecksum !== checksum) { - // Checksum mismatch - throw new Error("bad-content / checksum-mismatch"); - } - - const parsed = JSON.parse(jsonString); - const decoded = decompressObject(parsed); - return decoded; - } - - /** - * Writes the data asychronously, fails if verify() fails. - * Debounces the operation by up to 50ms - * @returns {Promise} - */ - writeAsync() { - const verifyResult = this.internalVerifyEntry(this.currentData); - - if (!verifyResult.result) { - logger.error("Tried to write invalid data to", this.filename, "reason:", verifyResult.reason); - return Promise.reject(verifyResult.reason); - } - - return this.debouncedWrite(); - } - - /** - * Actually writes the data asychronously - * @returns {Promise} - */ - doWriteAsync() { - return asyncCompressor - .compressObjectAsync(this.currentData) - .then(compressed => { - return this.app.storage.writeFileAsync(this.filename, compressed); - }) - .then(() => { - logger.log("📄 Wrote", this.filename); - }) - .catch(err => { - logger.error("Failed to write", this.filename, ":", err); - throw err; - }); - } - - // Reads the data asynchronously, fails if verify() fails - readAsync() { - // Start read request - return ( - this.app.storage - .readFileAsync(this.filename) - - // Check for errors during read - .catch(err => { - if (err === FILE_NOT_FOUND) { - logger.log("File not found, using default data"); - - // File not found or unreadable, assume default file - return Promise.resolve(null); - } - - return Promise.reject("file-error: " + err); - }) - - // Decrypt data (if its encrypted) - // @ts-ignore - .then(rawData => { - if (rawData == null) { - // So, the file has not been found, use default data - return JSON.stringify(compressObject(this.getDefaultData())); - } - - if (rawData.startsWith(compressionPrefix)) { - const decompressed = decompressX64(rawData.substr(compressionPrefix.length)); - if (!decompressed) { - // LZ string decompression failure - return Promise.reject("bad-content / decompression-failed"); - } - if (decompressed.length < 40) { - // String too short - return Promise.reject("bad-content / payload-too-small"); - } - - // Compare stored checksum with actual checksum - const checksum = decompressed.substring(0, 40); - const jsonString = decompressed.substr(40); - - const desiredChecksum = checksum.startsWith(CRC_PREFIX) - ? computeCrc(jsonString + salt) - : sha1(jsonString + salt); - - if (desiredChecksum !== checksum) { - // Checksum mismatch - return Promise.reject( - "bad-content / checksum-mismatch: " + desiredChecksum + " vs " + checksum - ); - } - return jsonString; - } else { - if (!G_IS_DEV) { - return Promise.reject("bad-content / missing-compression"); - } - } - return rawData; - }) - - // Parse JSON, this could throw but that's fine - .then(res => { - try { - return JSON.parse(res); - } catch (ex) { - logger.error( - "Failed to parse file content of", - this.filename, - ":", - ex, - "(content was:", - res, - ")" - ); - throw new Error("invalid-serialized-data"); - } - }) - - // Decompress - .then(compressed => decompressObject(compressed)) - - // Verify basic structure - .then(contents => { - const result = this.internalVerifyBasicStructure(contents); - if (!result.isGood()) { - return Promise.reject("verify-failed: " + result.reason); - } - return contents; - }) - - // Check version and migrate if required - .then(contents => { - if (contents.version > this.getCurrentVersion()) { - return Promise.reject("stored-data-is-newer"); - } - - if (contents.version < this.getCurrentVersion()) { - logger.log( - "Trying to migrate data object from version", - contents.version, - "to", - this.getCurrentVersion() - ); - const migrationResult = this.migrate(contents); // modify in place - if (migrationResult.isBad()) { - return Promise.reject("migration-failed: " + migrationResult.reason); - } - } - return contents; - }) - - // Verify - .then(contents => { - const verifyResult = this.internalVerifyEntry(contents); - if (!verifyResult.result) { - logger.error( - "Read invalid data from", - this.filename, - "reason:", - verifyResult.reason, - "contents:", - contents - ); - return Promise.reject("invalid-data: " + verifyResult.reason); - } - return contents; - }) - - // Store - .then(contents => { - this.currentData = contents; - logger.log("📄 Read data with version", this.currentData.version, "from", this.filename); - return contents; - }) - - // Catchall - .catch(err => { - return Promise.reject("Failed to read " + this.filename + ": " + err); - }) - ); - } - - /** - * Deletes the file - * @returns {Promise} - */ - deleteAsync() { - return this.app.storage.deleteFileAsync(this.filename); - } - - // Internal - - /** @returns {ExplainedResult} */ - internalVerifyBasicStructure(data) { - if (!data) { - return ExplainedResult.bad("Data is empty"); - } - if (!Number.isInteger(data.version) || data.version < 0) { - return ExplainedResult.bad( - `Data has invalid version: ${data.version} (expected ${this.getCurrentVersion()})` - ); - } - - return ExplainedResult.good(); - } - - /** @returns {ExplainedResult} */ - internalVerifyEntry(data) { - if (data.version !== this.getCurrentVersion()) { - return ExplainedResult.bad( - "Version mismatch, got " + data.version + " and expected " + this.getCurrentVersion() - ); - } - - const verifyStructureError = this.internalVerifyBasicStructure(data); - if (!verifyStructureError.isGood()) { - return verifyStructureError; - } - return this.verify(data); - } -} +/* typehints:start */ +import { Storage } from "@/platform/storage"; +/* typehints:end */ + +import { FsError } from "@/platform/fs_error"; +import { ExplainedResult } from "./explained_result"; +import { createLogger } from "./logging"; + +import debounce from "debounce-promise"; + +const logger = createLogger("read_write_proxy"); + +// Helper which only writes / reads if verify() works. Also performs migration +export class ReadWriteProxy { + constructor(storage, filename) { + /** @type {Storage} */ + this.storage = storage; + + this.filename = filename; + + /** @type {object} */ + this.currentData = null; + + // TODO: EXTREMELY HACKY! To verify we need to do this a step later + if (G_IS_DEV) { + setTimeout(() => { + assert( + this.verify(this.getDefaultData()).result, + "Verify() failed for default data: " + this.verify(this.getDefaultData()).reason + ); + }); + } + + /** + * Store a debounced handler to prevent double writes + */ + this.debouncedWrite = debounce(this.doWriteAsync.bind(this), 50); + } + + // -- Methods to override + + /** @returns {ExplainedResult} */ + verify(data) { + abstract; + return ExplainedResult.bad(); + } + + // Should return the default data + getDefaultData() { + abstract; + return {}; + } + + // Should return the current version as an integer + getCurrentVersion() { + abstract; + return 0; + } + + // Should migrate the data (Modify in place) + /** @returns {ExplainedResult} */ + migrate(data) { + abstract; + return ExplainedResult.bad(); + } + + // -- / Methods + + // Resets whole data, returns promise + resetEverythingAsync() { + logger.warn("Reset data to default"); + this.currentData = this.getDefaultData(); + return this.writeAsync(); + } + + /** + * + * @param {object} obj + */ + static serializeObject(obj) { + // TODO: Remove redundant method + return obj; + } + + /** + * + * @param {object} text + */ + static deserializeObject(text) { + // TODO: Remove redundant method + return text; + } + + /** + * Writes the data asychronously, fails if verify() fails. + * Debounces the operation by up to 50ms + * @returns {Promise} + */ + writeAsync() { + const verifyResult = this.internalVerifyEntry(this.currentData); + + if (!verifyResult.result) { + logger.error("Tried to write invalid data to", this.filename, "reason:", verifyResult.reason); + return Promise.reject(verifyResult.reason); + } + + return this.debouncedWrite(); + } + + /** + * Actually writes the data asychronously + * @returns {Promise} + */ + doWriteAsync() { + return this.storage + .writeFileAsync(this.filename, this.currentData) + .then(() => { + logger.log("📄 Wrote", this.filename); + }) + .catch(err => { + logger.error("Failed to write", this.filename, ":", err); + throw err; + }); + } + + // Reads the data asynchronously, fails if verify() fails + readAsync() { + // Start read request + return ( + this.storage + .readFileAsync(this.filename) + + // Check for errors during read + .catch(err => { + if (err instanceof FsError && err.isFileNotFound()) { + logger.log("File not found, using default data"); + + // File not found or unreadable, assume default file + return Promise.resolve(this.getDefaultData()); + } + + return Promise.reject("file-error: " + err); + }) + + // Verify basic structure + .then(contents => { + const result = this.internalVerifyBasicStructure(contents); + if (!result.isGood()) { + return Promise.reject("verify-failed: " + result.reason); + } + return contents; + }) + + // Check version and migrate if required + .then(contents => { + if (contents.version > this.getCurrentVersion()) { + return Promise.reject("stored-data-is-newer"); + } + + if (contents.version < this.getCurrentVersion()) { + logger.log( + "Trying to migrate data object from version", + contents.version, + "to", + this.getCurrentVersion() + ); + const migrationResult = this.migrate(contents); // modify in place + if (migrationResult.isBad()) { + return Promise.reject("migration-failed: " + migrationResult.reason); + } + } + return contents; + }) + + // Verify + .then(contents => { + const verifyResult = this.internalVerifyEntry(contents); + if (!verifyResult.result) { + logger.error( + "Read invalid data from", + this.filename, + "reason:", + verifyResult.reason, + "contents:", + contents + ); + return Promise.reject("invalid-data: " + verifyResult.reason); + } + return contents; + }) + + // Store + .then(contents => { + this.currentData = contents; + logger.log("📄 Read data with version", this.currentData.version, "from", this.filename); + return contents; + }) + + // Catchall + .catch(err => { + return Promise.reject("Failed to read " + this.filename + ": " + err); + }) + ); + } + + /** + * Deletes the file + * @returns {Promise} + */ + deleteAsync() { + return this.storage.deleteFileAsync(this.filename); + } + + // Internal + + /** @returns {ExplainedResult} */ + internalVerifyBasicStructure(data) { + if (!data) { + return ExplainedResult.bad("Data is empty"); + } + if (!Number.isInteger(data.version) || data.version < 0) { + return ExplainedResult.bad( + `Data has invalid version: ${data.version} (expected ${this.getCurrentVersion()})` + ); + } + + return ExplainedResult.good(); + } + + /** @returns {ExplainedResult} */ + internalVerifyEntry(data) { + if (data.version !== this.getCurrentVersion()) { + return ExplainedResult.bad( + "Version mismatch, got " + data.version + " and expected " + this.getCurrentVersion() + ); + } + + const verifyStructureError = this.internalVerifyBasicStructure(data); + if (!verifyStructureError.isGood()) { + return verifyStructureError; + } + return this.verify(data); + } +} diff --git a/src/js/core/restriction_manager.js b/src/js/core/restriction_manager.js deleted file mode 100644 index 33783b58..00000000 --- a/src/js/core/restriction_manager.js +++ /dev/null @@ -1,134 +0,0 @@ -/* typehints:start */ -import { Application } from "../application"; -/* typehints:end */ -import { ExplainedResult } from "./explained_result"; -import { ReadWriteProxy } from "./read_write_proxy"; -import { WEB_STEAM_SSO_AUTHENTICATED } from "./steam_sso"; - -export class RestrictionManager extends ReadWriteProxy { - /** - * @param {Application} app - */ - constructor(app) { - super(app, "restriction-flags.bin"); - - this.currentData = this.getDefaultData(); - } - - // -- RW Proxy Impl - - /** - * @param {any} data - */ - verify(data) { - return ExplainedResult.good(); - } - - /** - */ - getDefaultData() { - return { - version: this.getCurrentVersion(), - }; - } - - /** - */ - getCurrentVersion() { - return 1; - } - - /** - * @param {any} data - */ - migrate(data) { - return ExplainedResult.good(); - } - - initialize() { - return this.readAsync(); - } - - // -- End RW Proxy Impl - - /** - * Returns if the app is currently running as the limited version - * @returns {boolean} - */ - isLimitedVersion() { - if (G_IS_STEAM_DEMO) { - return true; - } - - if (G_IS_STANDALONE) { - // Standalone is never limited - return false; - } - - if (WEB_STEAM_SSO_AUTHENTICATED) { - return false; - } - - if (G_IS_DEV) { - return typeof window !== "undefined" && window.location.search.indexOf("demo") >= 0; - } - - return true; - } - - /** - * Returns if the app markets the standalone version on steam - * @returns {boolean} - */ - getIsStandaloneMarketingActive() { - return this.isLimitedVersion(); - } - - /** - * Returns if exporting the base as a screenshot is possible - * @returns {boolean} - */ - getIsExportingScreenshotsPossible() { - return !this.isLimitedVersion(); - } - - /** - * Returns the maximum number of supported waypoints - * @returns {number} - */ - getMaximumWaypoints() { - return this.isLimitedVersion() ? 2 : 1e20; - } - - /** - * Returns if the user has unlimited savegames - * @returns {boolean} - */ - getHasUnlimitedSavegames() { - return !this.isLimitedVersion(); - } - - /** - * Returns if the app has all settings available - * @returns {boolean} - */ - getHasExtendedSettings() { - return !this.isLimitedVersion(); - } - - /** - * Returns if all upgrades are available - * @returns {boolean} - */ - getHasExtendedUpgrades() { - return !this.isLimitedVersion(); - } - - /** - * Returns if all levels & freeplay is available - * @returns {boolean} - */ - getHasExtendedLevelsAndFreeplay() { - return !this.isLimitedVersion(); - } -} diff --git a/src/js/core/rng.js b/src/js/core/rng.js deleted file mode 100644 index 7a6766cd..00000000 --- a/src/js/core/rng.js +++ /dev/null @@ -1,129 +0,0 @@ -// ALEA RNG - -function Mash() { - var n = 0xefc8249d; - return function (data) { - data = data.toString(); - for (var i = 0; i < data.length; i++) { - n += data.charCodeAt(i); - var h = 0.02519603282416938 * n; - n = h >>> 0; - h -= n; - h *= n; - n = h >>> 0; - h -= n; - n += h * 0x100000000; // 2^32 - } - return (n >>> 0) * 2.3283064365386963e-10; // 2^-32 - }; -} - -/** - * @param {number|string} seed - */ -function makeNewRng(seed) { - // Johannes Baagøe , 2010 - var c = 1; - var mash = Mash(); - let s0 = mash(" "); - let s1 = mash(" "); - let s2 = mash(" "); - - s0 -= mash(seed); - if (s0 < 0) { - s0 += 1; - } - s1 -= mash(seed); - if (s1 < 0) { - s1 += 1; - } - s2 -= mash(seed); - if (s2 < 0) { - s2 += 1; - } - mash = null; - - var random = function () { - var t = 2091639 * s0 + c * 2.3283064365386963e-10; // 2^-32 - s0 = s1; - s1 = s2; - return (s2 = t - (c = t | 0)); - }; - - random.exportState = function () { - return [s0, s1, s2, c]; - }; - - random.importState = function (i) { - s0 = +i[0] || 0; - s1 = +i[1] || 0; - s2 = +i[2] || 0; - c = +i[3] || 0; - }; - - return random; -} - -export class RandomNumberGenerator { - /** - * - * @param {number|string=} seed - */ - constructor(seed) { - this.internalRng = makeNewRng(seed || Math.random()); - } - - /** - * Re-seeds the generator - * @param {number|string} seed - */ - reseed(seed) { - this.internalRng = makeNewRng(seed || Math.random()); - } - - /** - * @returns {number} between 0 and 1 - */ - next() { - return this.internalRng(); - } - - /** - * Random choice of an array - * @param {array} array - */ - choice(array) { - const index = this.nextIntRange(0, array.length); - return array[index]; - } - - /** - * @param {number} min - * @param {number} max - * @returns {number} Integer in range [min, max[ - */ - nextIntRange(min, max) { - assert(Number.isFinite(min), "Minimum is no integer"); - assert(Number.isFinite(max), "Maximum is no integer"); - assert(max > min, "rng: max <= min"); - return Math.floor(this.next() * (max - min) + min); - } - - /** - * @param {number} min - * @param {number} max - * @returns {number} Number in range [min, max[ - */ - nextRange(min, max) { - assert(max > min, "rng: max <= min"); - return this.next() * (max - min) + min; - } - - /** - * Updates the seed - * @param {number} seed - */ - setSeed(seed) { - this.internalRng = makeNewRng(seed); - } -} diff --git a/src/js/core/rng.ts b/src/js/core/rng.ts new file mode 100644 index 00000000..50bed98d --- /dev/null +++ b/src/js/core/rng.ts @@ -0,0 +1,81 @@ +class Alea { + private n = 0xefc8249d; + private c = 1; + + private s0: number; + private s1: number; + private s2: number; + + constructor(seed: string) { + // Johannes Baagøe , 2010 + this.s0 = this.mash(" "); + this.s1 = this.mash(" "); + this.s2 = this.mash(" "); + + this.s0 -= this.mash(seed); + if (this.s0 < 0) { + this.s0 += 1; + } + this.s1 -= this.mash(seed); + if (this.s1 < 0) { + this.s1 += 1; + } + this.s2 -= this.mash(seed); + if (this.s2 < 0) { + this.s2 += 1; + } + } + + protected next(): number { + const t = 2091639 * this.s0 + this.c * 2.3283064365386963e-10; // 2^-32 + this.s0 = this.s1; + this.s1 = this.s2; + return (this.s2 = t - (this.c = t | 0)); + } + + private mash(data: string): number { + for (let i = 0; i < data.length; i++) { + this.n += data.charCodeAt(i); + let h = 0.02519603282416938 * this.n; + this.n = h >>> 0; + h -= this.n; + h *= this.n; + this.n = h >>> 0; + h -= this.n; + this.n += h * 0x100000000; // 2^32 + } + return (this.n >>> 0) * 2.3283064365386963e-10; // 2^-32 + } +} + +export class RandomNumberGenerator extends Alea { + constructor(seed: string | number = Math.random()) { + super(seed.toString()); + } + + /** + * Random choice of an array + */ + choice(array: T[]): T { + const index = this.nextIntRange(0, array.length); + return array[index]; + } + + /** + * @returns Integer in range [min, max] + */ + nextIntRange(min: number, max: number): number { + assert(Number.isFinite(min), "Minimum is no integer"); + assert(Number.isFinite(max), "Maximum is no integer"); + assert(max > min, "rng: max <= min"); + return Math.floor(this.next() * (max - min) + min); + } + + /** + * @returns Number in range [min, max] + */ + nextRange(min: number, max: number): number { + assert(max > min, "rng: max <= min"); + return this.next() * (max - min) + min; + } +} diff --git a/src/js/core/sensitive_utils.encrypt.js b/src/js/core/sensitive_utils.encrypt.js deleted file mode 100644 index 5a83bf76..00000000 --- a/src/js/core/sensitive_utils.encrypt.js +++ /dev/null @@ -1,23 +0,0 @@ -import { createHash } from "rusha"; -import crc32 from "crc/crc32"; -import { decompressX64 } from "./lzstring"; - -export function sha1(str) { - return createHash().update(str).digest("hex"); -} - -// Window.location.host -export function getNameOfProvider() { - return window[decompressX64("DYewxghgLgliB2Q")][decompressX64("BYewzgLgdghgtgUyA")]; -} - -// Distinguish legacy crc prefixes -export const CRC_PREFIX = "crc32".padEnd(32, "-"); - -/** - * Computes the crc for a given string - * @param {string} str - */ -export function computeCrc(str) { - return CRC_PREFIX + crc32(str).toString(16).padStart(8, "0"); -} diff --git a/src/js/core/signal.js b/src/js/core/signal.ts similarity index 67% rename from src/js/core/signal.js rename to src/js/core/signal.ts index 2dbc9f93..52b27387 100644 --- a/src/js/core/signal.js +++ b/src/js/core/signal.ts @@ -1,17 +1,16 @@ -export const STOP_PROPAGATION = "stop_propagation"; +export const STOP_PROPAGATION = "stop_propagation" as const; +export type STOP_PROPAGATION = typeof STOP_PROPAGATION; -export class Signal { - constructor() { - this.receivers = []; - this.modifyCount = 0; - } +export type SignalReceiver = (...args: T) => STOP_PROPAGATION | void; + +export class Signal { + public receivers: { receiver: SignalReceiver; scope: object }[] = []; + public modifyCount: number = 0; /** * Adds a new signal listener - * @param {function} receiver - * @param {object} scope */ - add(receiver, scope = null) { + add(receiver: SignalReceiver, scope: object = null) { assert(receiver, "receiver is null"); this.receivers.push({ receiver, scope }); ++this.modifyCount; @@ -19,10 +18,8 @@ export class Signal { /** * Adds a new signal listener - * @param {function} receiver - * @param {object} scope */ - addToTop(receiver, scope = null) { + addToTop(receiver: SignalReceiver, scope: object = null) { assert(receiver, "receiver is null"); this.receivers.unshift({ receiver, scope }); ++this.modifyCount; @@ -30,15 +27,14 @@ export class Signal { /** * Dispatches the signal - * @param {...any} payload */ - dispatch() { + dispatch(...payload: T): void | STOP_PROPAGATION { const modifyState = this.modifyCount; const n = this.receivers.length; for (let i = 0; i < n; ++i) { const { receiver, scope } = this.receivers[i]; - if (receiver.apply(scope, arguments) === STOP_PROPAGATION) { + if (receiver.apply(scope, payload) === STOP_PROPAGATION) { return STOP_PROPAGATION; } @@ -51,9 +47,8 @@ export class Signal { /** * Removes a receiver - * @param {function} receiver */ - remove(receiver) { + remove(receiver: SignalReceiver) { let index = null; const n = this.receivers.length; for (let i = 0; i < n; ++i) { diff --git a/src/js/core/singleton_factory.js b/src/js/core/singleton_factory.ts similarity index 73% rename from src/js/core/singleton_factory.js rename to src/js/core/singleton_factory.ts index 7fa38bd3..141611cb 100644 --- a/src/js/core/singleton_factory.js +++ b/src/js/core/singleton_factory.ts @@ -3,20 +3,18 @@ import { createLogger } from "./logging"; const logger = createLogger("singleton_factory"); // simple factory pattern -export class SingletonFactory { - constructor(id) { - this.id = id; +export class SingletonFactory { + // Store array as well as dictionary, to speed up lookups + public entries: T[] = []; + public idToEntry: Record = {}; - // Store array as well as dictionary, to speed up lookups - this.entries = []; - this.idToEntry = {}; - } + constructor(public id: string) {} getId() { return this.id; } - register(classHandle) { + register(classHandle: Class) { // First, construct instance const instance = new classHandle(); @@ -34,19 +32,15 @@ export class SingletonFactory { /** * Checks if a given id is registered - * @param {string} id - * @returns {boolean} */ - hasId(id) { + hasId(id: string): boolean { return !!this.idToEntry[id]; } /** * Finds an instance by a given id - * @param {string} id - * @returns {object} */ - findById(id) { + findById(id: string): T { const entry = this.idToEntry[id]; if (!entry) { logger.error("Object with id", id, "is not registered!"); @@ -58,10 +52,8 @@ export class SingletonFactory { /** * Finds an instance by its constructor (The class handle) - * @param {object} classHandle - * @returns {object} */ - findByClass(classHandle) { + findByClass(classHandle: Class): T { for (let i = 0; i < this.entries.length; ++i) { if (this.entries[i] instanceof classHandle) { return this.entries[i]; @@ -73,25 +65,22 @@ export class SingletonFactory { /** * Returns all entries - * @returns {Array} */ - getEntries() { + getEntries(): T[] { return this.entries; } /** * Returns all registered ids - * @returns {Array} */ - getAllIds() { + getAllIds(): string[] { return Object.keys(this.idToEntry); } /** * Returns amount of stored entries - * @returns {number} */ - getNumEntries() { + getNumEntries(): number { return this.entries.length; } } diff --git a/src/js/core/sprites.js b/src/js/core/sprites.js index 4caa599c..e47b380b 100644 --- a/src/js/core/sprites.js +++ b/src/js/core/sprites.js @@ -1,428 +1,425 @@ -import { DrawParameters } from "./draw_parameters"; -import { Rectangle } from "./rectangle"; -import { round3Digits } from "./utils"; - -export const ORIGINAL_SPRITE_SCALE = "0.75"; -export const FULL_CLIP_RECT = new Rectangle(0, 0, 1, 1); - -const EXTRUDE = 0.1; - -export class BaseSprite { - /** - * Returns the raw handle - * @returns {HTMLImageElement|HTMLCanvasElement} - * @abstract - */ - getRawTexture() { - abstract; - return null; - } - - /** - * Draws the sprite - * @param {CanvasRenderingContext2D} context - * @param {number} x - * @param {number} y - * @param {number} w - * @param {number} h - */ - draw(context, x, y, w, h) { - // eslint-disable-line no-unused-vars - abstract; - } -} - -/** - * Position of a sprite within an atlas - */ -export class SpriteAtlasLink { - /** - * - * @param {object} param0 - * @param {number} param0.packedX - * @param {number} param0.packedY - * @param {number} param0.packOffsetX - * @param {number} param0.packOffsetY - * @param {number} param0.packedW - * @param {number} param0.packedH - * @param {number} param0.w - * @param {number} param0.h - * @param {HTMLImageElement|HTMLCanvasElement} param0.atlas - */ - constructor({ w, h, packedX, packedY, packOffsetX, packOffsetY, packedW, packedH, atlas }) { - this.packedX = packedX; - this.packedY = packedY; - this.packedW = packedW; - this.packedH = packedH; - this.packOffsetX = packOffsetX; - this.packOffsetY = packOffsetY; - this.atlas = atlas; - this.w = w; - this.h = h; - } -} - -export class AtlasSprite extends BaseSprite { - /** - * - * @param {string} spriteName - */ - constructor(spriteName = "sprite") { - super(); - /** @type {Object.} */ - this.linksByResolution = {}; - this.spriteName = spriteName; - - this.frozen = false; - } - - getRawTexture() { - return this.linksByResolution[ORIGINAL_SPRITE_SCALE].atlas; - } - - /** - * Draws the sprite onto a regular context using no contexts - * @see {BaseSprite.draw} - */ - draw(context, x, y, w, h) { - if (G_IS_DEV) { - assert(context instanceof CanvasRenderingContext2D, "Not a valid context"); - } - - const link = this.linksByResolution[ORIGINAL_SPRITE_SCALE]; - - if (!link) { - throw new Error( - "draw: Link for " + - this.spriteName + - " not known: " + - ORIGINAL_SPRITE_SCALE + - " (having " + - Object.keys(this.linksByResolution) + - ")" - ); - } - - const width = w || link.w; - const height = h || link.h; - - const scaleW = width / link.w; - const scaleH = height / link.h; - - context.drawImage( - link.atlas, - - link.packedX, - link.packedY, - link.packedW, - link.packedH, - - x + link.packOffsetX * scaleW, - y + link.packOffsetY * scaleH, - link.packedW * scaleW, - link.packedH * scaleH - ); - } - - /** - * - * @param {DrawParameters} parameters - * @param {number} x - * @param {number} y - * @param {number} size - * @param {boolean=} clipping - */ - drawCachedCentered(parameters, x, y, size, clipping = true) { - this.drawCached(parameters, x - size / 2, y - size / 2, size, size, clipping); - } - - /** - * - * @param {CanvasRenderingContext2D} context - * @param {number} x - * @param {number} y - * @param {number} size - */ - drawCentered(context, x, y, size) { - this.draw(context, x - size / 2, y - size / 2, size, size); - } - - /** - * Draws the sprite - * @param {DrawParameters} parameters - * @param {number} x - * @param {number} y - * @param {number} w - * @param {number} h - * @param {boolean=} clipping Whether to perform culling - */ - drawCached(parameters, x, y, w = null, h = null, clipping = true) { - if (G_IS_DEV) { - assert(parameters instanceof DrawParameters, "Not a valid context"); - assert(!!w && w > 0, "Not a valid width:" + w); - assert(!!h && h > 0, "Not a valid height:" + h); - } - - const visibleRect = parameters.visibleRect; - - const scale = parameters.desiredAtlasScale; - const link = this.linksByResolution[scale]; - - if (!link) { - throw new Error( - "drawCached: Link for " + - this.spriteName + - " at scale " + - scale + - " not known (having " + - Object.keys(this.linksByResolution) + - ")" - ); - } - - const scaleW = w / link.w; - const scaleH = h / link.h; - - let destX = x + link.packOffsetX * scaleW; - let destY = y + link.packOffsetY * scaleH; - let destW = link.packedW * scaleW; - let destH = link.packedH * scaleH; - - let srcX = link.packedX; - let srcY = link.packedY; - let srcW = link.packedW; - let srcH = link.packedH; - - let intersection = null; - - if (clipping) { - const rect = new Rectangle(destX, destY, destW, destH); - intersection = rect.getIntersection(visibleRect); - if (!intersection) { - return; - } - - srcX += (intersection.x - destX) / scaleW; - srcY += (intersection.y - destY) / scaleH; - - srcW *= intersection.w / destW; - srcH *= intersection.h / destH; - - destX = intersection.x; - destY = intersection.y; - - destW = intersection.w; - destH = intersection.h; - } - - parameters.context.drawImage( - link.atlas, - - // atlas src pos - srcX, - srcY, - - // atlas src size - srcW, - srcH, - - // dest pos and size - destX - EXTRUDE, - destY - EXTRUDE, - destW + 2 * EXTRUDE, - destH + 2 * EXTRUDE - ); - } - - /** - * Draws a subset of the sprite. Does NO culling - * @param {DrawParameters} parameters - * @param {number} x - * @param {number} y - * @param {number} w - * @param {number} h - * @param {Rectangle=} clipRect The rectangle in local space (0 ... 1) to draw of the image - */ - drawCachedWithClipRect(parameters, x, y, w = null, h = null, clipRect = FULL_CLIP_RECT) { - if (G_IS_DEV) { - assert(parameters instanceof DrawParameters, "Not a valid context"); - assert(!!w && w > 0, "Not a valid width:" + w); - assert(!!h && h > 0, "Not a valid height:" + h); - assert(clipRect, "No clip rect given!"); - } - - const scale = parameters.desiredAtlasScale; - const link = this.linksByResolution[scale]; - - if (!link) { - throw new Error( - "drawCachedWithClipRect: Link for " + - this.spriteName + - " at scale " + - scale + - " not known (having " + - Object.keys(this.linksByResolution) + - ")" - ); - } - - const scaleW = w / link.w; - const scaleH = h / link.h; - - let destX = x + link.packOffsetX * scaleW + clipRect.x * w; - let destY = y + link.packOffsetY * scaleH + clipRect.y * h; - let destW = link.packedW * scaleW * clipRect.w; - let destH = link.packedH * scaleH * clipRect.h; - - let srcX = link.packedX + clipRect.x * link.packedW; - let srcY = link.packedY + clipRect.y * link.packedH; - let srcW = link.packedW * clipRect.w; - let srcH = link.packedH * clipRect.h; - - parameters.context.drawImage( - link.atlas, - - // atlas src pos - srcX, - srcY, - - // atlas src siize - srcW, - srcH, - - // dest pos and size - destX - EXTRUDE, - destY - EXTRUDE, - destW + 2 * EXTRUDE, - destH + 2 * EXTRUDE - ); - } - - /** - * Renders into an html element - * @param {HTMLElement} element - * @param {number} w - * @param {number} h - */ - renderToHTMLElement(element, w = 1, h = 1) { - element.style.position = "relative"; - element.innerHTML = this.getAsHTML(w, h); - } - - /** - * Returns the html to render as icon - * @param {number} w - * @param {number} h - */ - getAsHTML(w, h) { - const link = this.linksByResolution["0.5"]; - - if (!link) { - throw new Error( - "getAsHTML: Link for " + - this.spriteName + - " at scale 0.5" + - " not known (having " + - Object.keys(this.linksByResolution) + - ")" - ); - } - - // Find out how much we have to scale it so that it fits - const scaleX = w / link.w; - const scaleY = h / link.h; - - // Find out how big the scaled atlas is - const atlasW = link.atlas.width * scaleX; - const atlasH = link.atlas.height * scaleY; - - // @ts-ignore - const srcSafe = link.atlas.src.replaceAll("\\", "/"); - - // Find out how big we render the sprite - const widthAbsolute = scaleX * link.packedW; - const heightAbsolute = scaleY * link.packedH; - - // Compute the position in the relative container - const leftRelative = (link.packOffsetX * scaleX) / w; - const topRelative = (link.packOffsetY * scaleY) / h; - const widthRelative = widthAbsolute / w; - const heightRelative = heightAbsolute / h; - - // Scale the atlas relative to the width and height of the element - const bgW = atlasW / widthAbsolute; - const bgH = atlasH / heightAbsolute; - - // Figure out what the position of the atlas is - const bgX = link.packedX * scaleX; - const bgY = link.packedY * scaleY; - - // Fuck you, whoever thought its a good idea to make background-position work like it does now - const bgXRelative = -bgX / (widthAbsolute - atlasW); - const bgYRelative = -bgY / (heightAbsolute - atlasH); - - return ` - - `; - } -} - -export class RegularSprite extends BaseSprite { - constructor(sprite, w, h) { - super(); - this.w = w; - this.h = h; - this.sprite = sprite; - } - - getRawTexture() { - return this.sprite; - } - - /** - * Draws the sprite, do *not* use this for sprites which are rendered! Only for drawing - * images into buffers - * @param {CanvasRenderingContext2D} context - * @param {number} x - * @param {number} y - * @param {number} w - * @param {number} h - */ - draw(context, x, y, w, h) { - assert(context, "No context given"); - assert(x !== undefined, "No x given"); - assert(y !== undefined, "No y given"); - assert(w !== undefined, "No width given"); - assert(h !== undefined, "No height given"); - context.drawImage(this.sprite, x, y, w, h); - } - - /** - * Draws the sprite, do *not* use this for sprites which are rendered! Only for drawing - * images into buffers - * @param {CanvasRenderingContext2D} context - * @param {number} x - * @param {number} y - * @param {number} w - * @param {number} h - */ - drawCentered(context, x, y, w, h) { - assert(context, "No context given"); - assert(x !== undefined, "No x given"); - assert(y !== undefined, "No y given"); - assert(w !== undefined, "No width given"); - assert(h !== undefined, "No height given"); - context.drawImage(this.sprite, x - w / 2, y - h / 2, w, h); - } -} +import { DrawParameters } from "./draw_parameters"; +import { Rectangle } from "./rectangle"; +import { round3Digits } from "./utils"; + +export const ORIGINAL_SPRITE_SCALE = "0.75"; +export const FULL_CLIP_RECT = new Rectangle(0, 0, 1, 1); + +export class BaseSprite { + /** + * Returns the raw handle + * @returns {HTMLImageElement|HTMLCanvasElement} + * @abstract + */ + getRawTexture() { + abstract; + return null; + } + + /** + * Draws the sprite + * @param {CanvasRenderingContext2D} context + * @param {number} x + * @param {number} y + * @param {number} w + * @param {number} h + */ + draw(context, x, y, w, h) { + abstract; + } +} + +/** + * Position of a sprite within an atlas + */ +export class SpriteAtlasLink { + /** + * + * @param {object} param0 + * @param {number} param0.packedX + * @param {number} param0.packedY + * @param {number} param0.packOffsetX + * @param {number} param0.packOffsetY + * @param {number} param0.packedW + * @param {number} param0.packedH + * @param {number} param0.w + * @param {number} param0.h + * @param {HTMLImageElement|HTMLCanvasElement} param0.atlas + */ + constructor({ w, h, packedX, packedY, packOffsetX, packOffsetY, packedW, packedH, atlas }) { + this.packedX = packedX; + this.packedY = packedY; + this.packedW = packedW; + this.packedH = packedH; + this.packOffsetX = packOffsetX; + this.packOffsetY = packOffsetY; + this.atlas = atlas; + this.w = w; + this.h = h; + } +} + +export class AtlasSprite extends BaseSprite { + /** + * + * @param {string} spriteName + */ + constructor(spriteName = "sprite") { + super(); + /** @type {Object.} */ + this.linksByResolution = {}; + this.spriteName = spriteName; + + this.frozen = false; + } + + getRawTexture() { + return this.linksByResolution[ORIGINAL_SPRITE_SCALE].atlas; + } + + /** + * Draws the sprite onto a regular context using no contexts + * @see {BaseSprite.draw} + */ + draw(context, x, y, w, h) { + if (G_IS_DEV) { + assert(context instanceof CanvasRenderingContext2D, "Not a valid context"); + } + + const link = this.linksByResolution[ORIGINAL_SPRITE_SCALE]; + + if (!link) { + throw new Error( + "draw: Link for " + + this.spriteName + + " not known: " + + ORIGINAL_SPRITE_SCALE + + " (having " + + Object.keys(this.linksByResolution) + + ")" + ); + } + + const width = w || link.w; + const height = h || link.h; + + const scaleW = width / link.w; + const scaleH = height / link.h; + + context.drawImage( + link.atlas, + + link.packedX, + link.packedY, + link.packedW, + link.packedH, + + x + link.packOffsetX * scaleW, + y + link.packOffsetY * scaleH, + link.packedW * scaleW, + link.packedH * scaleH + ); + } + + /** + * + * @param {DrawParameters} parameters + * @param {number} x + * @param {number} y + * @param {number} size + * @param {boolean=} clipping + */ + drawCachedCentered(parameters, x, y, size, clipping = true) { + this.drawCached(parameters, x - size / 2, y - size / 2, size, size, clipping); + } + + /** + * + * @param {CanvasRenderingContext2D} context + * @param {number} x + * @param {number} y + * @param {number} size + */ + drawCentered(context, x, y, size) { + this.draw(context, x - size / 2, y - size / 2, size, size); + } + + /** + * Draws the sprite + * @param {DrawParameters} parameters + * @param {number} x + * @param {number} y + * @param {number} w + * @param {number} h + * @param {boolean=} clipping Whether to perform culling + */ + drawCached(parameters, x, y, w = null, h = null, clipping = true) { + if (G_IS_DEV) { + assert(parameters instanceof DrawParameters, "Not a valid context"); + assert(!!w && w > 0, "Not a valid width:" + w); + assert(!!h && h > 0, "Not a valid height:" + h); + } + + const visibleRect = parameters.visibleRect; + + const scale = parameters.desiredAtlasScale; + const link = this.linksByResolution[scale]; + + if (!link) { + throw new Error( + "drawCached: Link for " + + this.spriteName + + " at scale " + + scale + + " not known (having " + + Object.keys(this.linksByResolution) + + ")" + ); + } + + const scaleW = w / link.w; + const scaleH = h / link.h; + + let destX = x + link.packOffsetX * scaleW; + let destY = y + link.packOffsetY * scaleH; + let destW = link.packedW * scaleW; + let destH = link.packedH * scaleH; + + let srcX = link.packedX; + let srcY = link.packedY; + let srcW = link.packedW; + let srcH = link.packedH; + + let intersection = null; + + if (clipping) { + const rect = new Rectangle(destX, destY, destW, destH); + intersection = rect.getIntersection(visibleRect); + if (!intersection) { + return; + } + + srcX += (intersection.x - destX) / scaleW; + srcY += (intersection.y - destY) / scaleH; + + srcW *= intersection.w / destW; + srcH *= intersection.h / destH; + + destX = intersection.x; + destY = intersection.y; + + destW = intersection.w; + destH = intersection.h; + } + + parameters.context.drawImage( + link.atlas, + + // atlas src pos + srcX, + srcY, + + // atlas src size + srcW, + srcH, + + // dest pos and size + destX, + destY, + destW, + destH + ); + } + + /** + * Draws a subset of the sprite. Does NO culling + * @param {DrawParameters} parameters + * @param {number} x + * @param {number} y + * @param {number} w + * @param {number} h + * @param {Rectangle=} clipRect The rectangle in local space (0 ... 1) to draw of the image + */ + drawCachedWithClipRect(parameters, x, y, w = null, h = null, clipRect = FULL_CLIP_RECT) { + if (G_IS_DEV) { + assert(parameters instanceof DrawParameters, "Not a valid context"); + assert(!!w && w > 0, "Not a valid width:" + w); + assert(!!h && h > 0, "Not a valid height:" + h); + assert(clipRect, "No clip rect given!"); + } + + const scale = parameters.desiredAtlasScale; + const link = this.linksByResolution[scale]; + + if (!link) { + throw new Error( + "drawCachedWithClipRect: Link for " + + this.spriteName + + " at scale " + + scale + + " not known (having " + + Object.keys(this.linksByResolution) + + ")" + ); + } + + const scaleW = w / link.w; + const scaleH = h / link.h; + + let destX = x + link.packOffsetX * scaleW + clipRect.x * w; + let destY = y + link.packOffsetY * scaleH + clipRect.y * h; + let destW = link.packedW * scaleW * clipRect.w; + let destH = link.packedH * scaleH * clipRect.h; + + let srcX = link.packedX + clipRect.x * link.packedW; + let srcY = link.packedY + clipRect.y * link.packedH; + let srcW = link.packedW * clipRect.w; + let srcH = link.packedH * clipRect.h; + + parameters.context.drawImage( + link.atlas, + + // atlas src pos + srcX, + srcY, + + // atlas src siize + srcW, + srcH, + + // dest pos and size + destX, + destY, + destW, + destH + ); + } + + /** + * Renders into an html element + * @param {HTMLElement} element + * @param {number} w + * @param {number} h + */ + renderToHTMLElement(element, w = 1, h = 1) { + element.style.position = "relative"; + element.innerHTML = this.getAsHTML(w, h); + } + + /** + * Returns the html to render as icon + * @param {number} w + * @param {number} h + */ + getAsHTML(w, h) { + const link = this.linksByResolution["0.5"]; + + if (!link) { + throw new Error( + "getAsHTML: Link for " + + this.spriteName + + " at scale 0.5" + + " not known (having " + + Object.keys(this.linksByResolution) + + ")" + ); + } + + // Find out how much we have to scale it so that it fits + const scaleX = w / link.w; + const scaleY = h / link.h; + + // Find out how big the scaled atlas is + const atlasW = link.atlas.width * scaleX; + const atlasH = link.atlas.height * scaleY; + + // @ts-ignore + const srcSafe = link.atlas.src.replaceAll("\\", "/"); + + // Find out how big we render the sprite + const widthAbsolute = scaleX * link.packedW; + const heightAbsolute = scaleY * link.packedH; + + // Compute the position in the relative container + const leftRelative = (link.packOffsetX * scaleX) / w; + const topRelative = (link.packOffsetY * scaleY) / h; + const widthRelative = widthAbsolute / w; + const heightRelative = heightAbsolute / h; + + // Scale the atlas relative to the width and height of the element + const bgW = atlasW / widthAbsolute; + const bgH = atlasH / heightAbsolute; + + // Figure out what the position of the atlas is + const bgX = link.packedX * scaleX; + const bgY = link.packedY * scaleY; + + // Fuck you, whoever thought its a good idea to make background-position work like it does now + const bgXRelative = -bgX / (widthAbsolute - atlasW); + const bgYRelative = -bgY / (heightAbsolute - atlasH); + + return ` + + `; + } +} + +export class RegularSprite extends BaseSprite { + constructor(sprite, w, h) { + super(); + this.w = w; + this.h = h; + this.sprite = sprite; + } + + getRawTexture() { + return this.sprite; + } + + /** + * Draws the sprite, do *not* use this for sprites which are rendered! Only for drawing + * images into buffers + * @param {CanvasRenderingContext2D} context + * @param {number} x + * @param {number} y + * @param {number} w + * @param {number} h + */ + draw(context, x, y, w, h) { + assert(context, "No context given"); + assert(x !== undefined, "No x given"); + assert(y !== undefined, "No y given"); + assert(w !== undefined, "No width given"); + assert(h !== undefined, "No height given"); + context.drawImage(this.sprite, x, y, w, h); + } + + /** + * Draws the sprite, do *not* use this for sprites which are rendered! Only for drawing + * images into buffers + * @param {CanvasRenderingContext2D} context + * @param {number} x + * @param {number} y + * @param {number} w + * @param {number} h + */ + drawCentered(context, x, y, w, h) { + assert(context, "No context given"); + assert(x !== undefined, "No x given"); + assert(y !== undefined, "No y given"); + assert(w !== undefined, "No width given"); + assert(h !== undefined, "No height given"); + context.drawImage(this.sprite, x - w / 2, y - h / 2, w, h); + } +} diff --git a/src/js/core/stale_area_detector.js b/src/js/core/stale_area_detector.js index 631d0327..23247235 100644 --- a/src/js/core/stale_area_detector.js +++ b/src/js/core/stale_area_detector.js @@ -1,90 +1,91 @@ -import { Component } from "../game/component"; -import { Entity } from "../game/entity"; -import { globalConfig } from "./config"; -import { createLogger } from "./logging"; -import { Rectangle } from "./rectangle"; - -const logger = createLogger("stale_areas"); - -export class StaleAreaDetector { - /** - * - * @param {object} param0 - * @param {import("../game/root").GameRoot} param0.root - * @param {string} param0.name The name for reference - * @param {(Rectangle) => void} param0.recomputeMethod Method which recomputes the given area - */ - constructor({ root, name, recomputeMethod }) { - this.root = root; - this.name = name; - this.recomputeMethod = recomputeMethod; - - /** @type {Rectangle} */ - this.staleArea = null; - } - - /** - * Invalidates the given area - * @param {Rectangle} area - */ - invalidate(area) { - // logger.log(this.name, "invalidated", area.toString()); - if (this.staleArea) { - this.staleArea = this.staleArea.getUnion(area); - } else { - this.staleArea = area.clone(); - } - } - - /** - * Makes this detector recompute the area of an entity whenever - * it changes in any way - * @param {Array} components - * @param {number} tilesAround How many tiles arround to expand the area - */ - recomputeOnComponentsChanged(components, tilesAround) { - const componentIds = components.map(component => component.getId()); - - /** - * Internal checker method - * @param {Entity} entity - */ - const checker = entity => { - if (!this.root.gameInitialized) { - return; - } - - // Check for all components - for (let i = 0; i < componentIds.length; ++i) { - if (entity.components[componentIds[i]]) { - // Entity is relevant, compute affected area - const area = entity.components.StaticMapEntity.getTileSpaceBounds().expandedInAllDirections( - tilesAround - ); - this.invalidate(area); - return; - } - } - }; - - this.root.signals.entityAdded.add(checker); - this.root.signals.entityChanged.add(checker); - this.root.signals.entityComponentRemoved.add(checker); - this.root.signals.entityGotNewComponent.add(checker); - this.root.signals.entityDestroyed.add(checker); - } - - /** - * Updates the stale area - */ - update() { - if (this.staleArea) { - if (G_IS_DEV && globalConfig.debug.renderChanges) { - logger.log(this.name, "is recomputing", this.staleArea.toString()); - this.root.hud.parts.changesDebugger.renderChange(this.name, this.staleArea, "#fd145b"); - } - this.recomputeMethod(this.staleArea); - this.staleArea = null; - } - } -} +import { Component } from "../game/component"; +import { Entity } from "../game/entity"; +import { globalConfig } from "./config"; +import { createLogger } from "./logging"; +import { Rectangle } from "./rectangle"; + +const logger = createLogger("stale_areas"); + +export class StaleAreaDetector { + /** + * + * @param {object} param0 + * @param {import("../game/root").GameRoot} param0.root + * @param {string} param0.name The name for reference + * @param {(Rectangle) => void} param0.recomputeMethod Method which recomputes the given area + */ + constructor({ root, name, recomputeMethod }) { + this.root = root; + this.name = name; + this.recomputeMethod = recomputeMethod; + + /** @type {Rectangle} */ + this.staleArea = null; + } + + /** + * Invalidates the given area + * @param {Rectangle} area + */ + invalidate(area) { + // logger.log(this.name, "invalidated", area.toString()); + if (this.staleArea) { + this.staleArea = this.staleArea.getUnion(area); + } else { + this.staleArea = area.clone(); + } + } + + /** + * Makes this detector recompute the area of an entity whenever + * it changes in any way + * @param {Array} components + * @param {number} tilesAround How many tiles arround to expand the area + */ + recomputeOnComponentsChanged(components, tilesAround) { + const componentIds = components.map(component => component.getId()); + + /** + * Internal checker method + * @param {Entity} entity + */ + const checker = entity => { + if (!this.root.gameInitialized) { + return; + } + + // Check for all components + for (let i = 0; i < componentIds.length; ++i) { + if (entity.components[componentIds[i]]) { + // Entity is relevant, compute affected area + const area = + entity.components.StaticMapEntity.getTileSpaceBounds().expandedInAllDirections( + tilesAround + ); + this.invalidate(area); + return; + } + } + }; + + this.root.signals.entityAdded.add(checker); + this.root.signals.entityChanged.add(checker); + this.root.signals.entityComponentRemoved.add(checker); + this.root.signals.entityGotNewComponent.add(checker); + this.root.signals.entityDestroyed.add(checker); + } + + /** + * Updates the stale area + */ + update() { + if (this.staleArea) { + if (G_IS_DEV && globalConfig.debug.renderChanges) { + logger.log(this.name, "is recomputing", this.staleArea.toString()); + this.root.hud.parts.changesDebugger.renderChange(this.name, this.staleArea, "#fd145b"); + } + this.recomputeMethod(this.staleArea); + this.staleArea = null; + } + } +} diff --git a/src/js/core/state_manager.js b/src/js/core/state_manager.js index 70b4b7f4..52815d27 100644 --- a/src/js/core/state_manager.js +++ b/src/js/core/state_manager.js @@ -2,10 +2,10 @@ import { Application } from "../application"; /* typehints:end*/ +import { MOD_SIGNALS } from "../mods/mod_signals"; import { GameState } from "./game_state"; import { createLogger } from "./logging"; -import { waitNextFrame, removeAllChildren } from "./utils"; -import { MOD_SIGNALS } from "../mods/mod_signals"; +import { removeAllChildren, waitNextFrame } from "./utils"; const logger = createLogger("state_manager"); @@ -34,7 +34,7 @@ export class StateManager { // Create a dummy to retrieve the key const dummy = new stateClass(); assert(dummy instanceof GameState, "Not a state!"); - const key = dummy.getKey(); + const key = dummy.key; assert(!this.stateClasses[key], `State '${key}' is already registered!`); this.stateClasses[key] = stateClass; } @@ -61,7 +61,7 @@ export class StateManager { } if (this.currentState) { - if (key === this.currentState.getKey()) { + if (key === this.currentState.key) { logger.error(`State '${key}' is already active!`); return false; } @@ -69,7 +69,7 @@ export class StateManager { // Remove all references for (const stateKey in this.currentState) { - if (this.currentState.hasOwnProperty(stateKey)) { + if (Object.hasOwn(this.currentState, stateKey)) { delete this.currentState[stateKey]; } } @@ -88,25 +88,20 @@ export class StateManager { document.body.id = "state_" + key; if (this.currentState.getRemovePreviousContent()) { - document.body.innerHTML = this.currentState.internalGetFullHtml(); + const content = this.currentState.internalGetWrappedContent(); + document.body.append(content); } const dialogParent = document.createElement("div"); dialogParent.classList.add("modalDialogParent"); document.body.appendChild(dialogParent); - try { - this.currentState.internalEnterCallback(payload); - } catch (ex) { - console.error(ex); - throw ex; - } + + this.currentState.internalEnterCallback(payload); this.app.sound.playThemeMusic(this.currentState.getThemeMusic()); this.currentState.onResized(this.app.screenWidth, this.app.screenHeight); - this.app.analytics.trackStateEnter(key); - window.history.pushState( { key, diff --git a/src/js/core/steam_sso.js b/src/js/core/steam_sso.js deleted file mode 100644 index 67c6d582..00000000 --- a/src/js/core/steam_sso.js +++ /dev/null @@ -1,89 +0,0 @@ -import { T } from "../translations"; -import { openStandaloneLink } from "./config"; - -export let WEB_STEAM_SSO_AUTHENTICATED = false; - -export async function authorizeViaSSOToken(app, dialogs) { - if (G_IS_STANDALONE) { - return; - } - - if (window.location.search.includes("sso_logout_silent")) { - window.localStorage.setItem("steam_sso_auth_token", ""); - window.location.replace("/"); - return new Promise(() => null); - } - - if (window.location.search.includes("sso_logout")) { - const { ok } = dialogs.showWarning(T.dialogs.steamSsoError.title, T.dialogs.steamSsoError.desc); - window.localStorage.setItem("steam_sso_auth_token", ""); - ok.add(() => window.location.replace("/")); - return new Promise(() => null); - } - - if (window.location.search.includes("steam_sso_no_ownership")) { - const { ok, getStandalone } = dialogs.showWarning( - T.dialogs.steamSsoNoOwnership.title, - T.dialogs.steamSsoNoOwnership.desc, - ["ok", "getStandalone:good"] - ); - window.localStorage.setItem("steam_sso_auth_token", ""); - getStandalone.add(() => { - openStandaloneLink(app, "sso_ownership"); - window.location.replace("/"); - }); - ok.add(() => window.location.replace("/")); - return new Promise(() => null); - } - - const token = window.localStorage.getItem("steam_sso_auth_token"); - if (!token) { - return Promise.resolve(); - } - - const apiUrl = app.clientApi.getEndpoint(); - console.warn("Authorizing via token:", token); - - const verify = async () => { - const token = window.localStorage.getItem("steam_sso_auth_token"); - if (!token) { - window.location.replace("?sso_logout"); - return; - } - - try { - const response = await Promise.race([ - fetch(apiUrl + "/v1/sso/refresh", { - method: "POST", - body: token, - headers: { - "x-api-key": "d5c54aaa491f200709afff082c153ef2", - }, - }), - new Promise((resolve, reject) => { - setTimeout(() => reject("timeout exceeded"), 20000); - }), - ]); - - const responseText = await response.json(); - if (!responseText.token) { - console.warn("Failed to register"); - window.localStorage.setItem("steam_sso_auth_token", ""); - window.location.replace("?sso_logout"); - return; - } - - window.localStorage.setItem("steam_sso_auth_token", responseText.token); - app.clientApi.token = responseText.token; - WEB_STEAM_SSO_AUTHENTICATED = true; - } catch (ex) { - console.warn("Auth failure", ex); - window.localStorage.setItem("steam_sso_auth_token", ""); - window.location.replace("/"); - return new Promise(() => null); - } - }; - - await verify(); - setInterval(verify, 120000); -} diff --git a/src/js/core/textual_game_state.js b/src/js/core/textual_game_state.tsx similarity index 69% rename from src/js/core/textual_game_state.js rename to src/js/core/textual_game_state.tsx index 52a1f946..8cc2a0d7 100644 --- a/src/js/core/textual_game_state.js +++ b/src/js/core/textual_game_state.tsx @@ -1,20 +1,24 @@ import { HUDModalDialogs } from "../game/hud/parts/modal_dialogs"; import { GameState } from "./game_state"; -import { T } from "../translations"; /** * Baseclass for all game states which are structured similary: A header with back button + some * scrollable content. */ -export class TextualGameState extends GameState { - ///// INTERFACE //// +export abstract class TextualGameState extends GameState { + private backToStateId: string | null = null; + private backToStatePayload: {} | null = null; + + protected headerElement: HTMLElement; + protected containerElement: HTMLElement; + protected dialogs: HUDModalDialogs; /** * Should return the states inner html. If not overriden, will create a scrollable container * with the content of getMainContentHTML() - * @returns {string} + * @deprecated */ - getInnerHTML() { + getInnerHTML(): string { return `
${this.getMainContentHTML()} @@ -24,17 +28,43 @@ export class TextualGameState extends GameState { /** * Should return the states HTML content. + * @deprecated */ - getMainContentHTML() { + getMainContentHTML(): string { return ""; } + /** + * Should return the element(s) to be displayed in the state. + * If not overridden, a default layout consisting of a back button, + * title, and content returned by {@link getInitialContent}. + */ + protected override getContentLayout(): Node { + const initialContent = this.getInitialContent(); + const content = initialContent !== null &&
{initialContent}
; + + return ( + <> +
+

+ + {this.getStateHeaderTitle() ?? ""} +

+
+
{content || super.getContentLayout()}
+ + ); + } + + protected getInitialContent(): Node { + return null; + } + /** * Should return the title of the game state. If null, no title and back button will * get created - * @returns {string|null} */ - getStateHeaderTitle() { + protected getStateHeaderTitle(): string | null { return null; } @@ -44,7 +74,7 @@ export class TextualGameState extends GameState { * Back button handler, can be overridden. Per default it goes back to the main menu, * or if coming from the game it moves back to the game again. */ - onBackButton() { + override onBackButton() { if (this.backToStateId) { this.moveToState(this.backToStateId, this.backToStatePayload); } else { @@ -61,9 +91,9 @@ export class TextualGameState extends GameState { /** * Goes to a new state, telling him to go back to this state later - * @param {string} stateId + * @param stateId */ - moveToStateAddGoBack(stateId) { + moveToStateAddGoBack(stateId: string) { this.moveToState(stateId, { backToStateId: this.key, backToStatePayload: { @@ -89,43 +119,20 @@ export class TextualGameState extends GameState { } } - /** - * Overrides the GameState implementation to provide our own html - */ - internalGetFullHtml() { - let headerHtml = ""; - if (this.getStateHeaderTitle()) { - headerHtml = ` -
- -

${this.getStateHeaderTitle()}

-
`; - } - - return ` - ${headerHtml} -
- ${this.getInnerHTML()} - -
- `; - } - //// INTERNALS ///// /** * Overrides the GameState leave callback to cleanup stuff */ - internalLeaveCallback() { + override internalLeaveCallback() { super.internalLeaveCallback(); this.dialogs.cleanup(); } /** * Overrides the GameState enter callback to setup required stuff - * @param {any} payload */ - internalEnterCallback(payload) { + override internalEnterCallback(payload: any) { super.internalEnterCallback(payload, false); if (payload.backToStateId) { this.backToStateId = payload.backToStateId; diff --git a/src/js/core/tracked_state.js b/src/js/core/tracked_state.ts similarity index 69% rename from src/js/core/tracked_state.js rename to src/js/core/tracked_state.ts index 1a538bc6..f5da2ab4 100644 --- a/src/js/core/tracked_state.js +++ b/src/js/core/tracked_state.ts @@ -1,7 +1,10 @@ -export class TrackedState { - constructor(callbackMethod = null, callbackScope = null) { - this.lastSeenValue = null; +export type TrackedStateCallback = (value: T) => void; +export class TrackedState { + public lastSeenValue: T = null; + public callback: TrackedStateCallback; + + constructor(callbackMethod: TrackedStateCallback = null, callbackScope: unknown = null) { if (callbackMethod) { this.callback = callbackMethod; if (callbackScope) { @@ -10,7 +13,7 @@ export class TrackedState { } } - set(value, changeHandler = null, changeScope = null) { + set(value: T, changeHandler: TrackedStateCallback = null, changeScope: unknown = null) { if (value !== this.lastSeenValue) { // Copy value since the changeHandler call could actually modify our lastSeenValue const valueCopy = value; @@ -29,11 +32,11 @@ export class TrackedState { } } - setSilent(value) { + setSilent(value: T) { this.lastSeenValue = value; } - get() { + get(): T { return this.lastSeenValue; } } diff --git a/src/js/core/utils.js b/src/js/core/utils.js index 9e2126b1..87ec41ff 100644 --- a/src/js/core/utils.js +++ b/src/js/core/utils.js @@ -1,790 +1,666 @@ -import { T } from "../translations"; -import { rando } from "@nastyox/rando.js"; -import { WEB_STEAM_SSO_AUTHENTICATED } from "./steam_sso"; - -const bigNumberSuffixTranslationKeys = ["thousands", "millions", "billions", "trillions"]; - -/** - * Returns a platform name - * @returns {"android" | "browser" | "ios" | "standalone" | "unknown"} - */ -export function getPlatformName() { - if (G_IS_STANDALONE) { - return "standalone"; - } else if (G_IS_BROWSER) { - return "browser"; - } - return "unknown"; -} - -/** - * Makes a new 2D array with undefined contents - * @param {number} w - * @param {number} h - * @returns {Array>} - */ -export function make2DUndefinedArray(w, h) { - const result = new Array(w); - for (let x = 0; x < w; ++x) { - result[x] = new Array(h); - } - return result; -} - -/** - * Creates a new map (an empty object without any props) - */ -export function newEmptyMap() { - return Object.create(null); -} - -/** - * Returns a random integer in the range [start,end] - * @param {number} start - * @param {number} end - */ -export function randomInt(start, end) { - return rando(start, end); -} - -/** - * Access an object in a very annoying way, used for obsfuscation. - * @param {any} obj - * @param {Array} keys - */ -export function accessNestedPropertyReverse(obj, keys) { - let result = obj; - for (let i = keys.length - 1; i >= 0; --i) { - result = result[keys[i]]; - } - return result; -} - -/** - * Chooses a random entry of an array - * @template T - * @param {T[]} arr - * @returns {T} - */ -export function randomChoice(arr) { - return arr[Math.floor(Math.random() * arr.length)]; -} - -/** - * Deletes from an array by swapping with the last element - * @param {Array} array - * @param {number} index - */ -export function fastArrayDelete(array, index) { - if (index < 0 || index >= array.length) { - throw new Error("Out of bounds"); - } - // When the element is not the last element - if (index !== array.length - 1) { - // Get the last element, and swap it with the one we want to delete - const last = array[array.length - 1]; - array[index] = last; - } - - // Finally remove the last element - array.length -= 1; -} - -/** - * Deletes from an array by swapping with the last element. Searches - * for the value in the array first - * @param {Array} array - * @param {any} value - */ -export function fastArrayDeleteValue(array, value) { - if (array == null) { - throw new Error("Tried to delete from non array!"); - } - const index = array.indexOf(value); - if (index < 0) { - console.error("Value", value, "not contained in array:", array, "!"); - return value; - } - return fastArrayDelete(array, index); -} - -/** - * @see fastArrayDeleteValue - * @param {Array} array - * @param {any} value - */ -export function fastArrayDeleteValueIfContained(array, value) { - if (array == null) { - throw new Error("Tried to delete from non array!"); - } - const index = array.indexOf(value); - if (index < 0) { - return value; - } - return fastArrayDelete(array, index); -} - -/** - * Deletes from an array at the given index - * @param {Array} array - * @param {number} index - */ -export function arrayDelete(array, index) { - if (index < 0 || index >= array.length) { - throw new Error("Out of bounds"); - } - array.splice(index, 1); -} - -/** - * Deletes the given value from an array - * @param {Array} array - * @param {any} value - */ -export function arrayDeleteValue(array, value) { - if (array == null) { - throw new Error("Tried to delete from non array!"); - } - const index = array.indexOf(value); - if (index < 0) { - console.error("Value", value, "not contained in array:", array, "!"); - return value; - } - return arrayDelete(array, index); -} - -/** - * Compare two floats for epsilon equality - * @param {number} a - * @param {number} b - * @returns {boolean} - */ -export function epsilonCompare(a, b, epsilon = 1e-5) { - return Math.abs(a - b) < epsilon; -} - -/** - * Interpolates two numbers - * @param {number} a - * @param {number} b - * @param {number} x Mix factor, 0 means 100% a, 1 means 100%b, rest is interpolated - */ -export function lerp(a, b, x) { - return a * (1 - x) + b * x; -} - -/** - * Finds a value which is nice to display, e.g. 15669 -> 15000. Also handles fractional stuff - * @param {number} num - */ -export function findNiceValue(num) { - if (num > 1e8) { - return num; - } - if (num < 0.00001) { - return 0; - } - - let roundAmount = 1; - if (num > 50000) { - roundAmount = 10000; - } else if (num > 20000) { - roundAmount = 5000; - } else if (num > 5000) { - roundAmount = 1000; - } else if (num > 2000) { - roundAmount = 500; - } else if (num > 1000) { - roundAmount = 100; - } else if (num > 100) { - roundAmount = 20; - } else if (num > 20) { - roundAmount = 5; - } - - const niceValue = Math.floor(num / roundAmount) * roundAmount; - if (num >= 10) { - return Math.round(niceValue); - } - if (num >= 1) { - return Math.round(niceValue * 10) / 10; - } - - return Math.round(niceValue * 100) / 100; -} - -/** - * Finds a nice integer value - * @see findNiceValue - * @param {number} num - */ -export function findNiceIntegerValue(num) { - return Math.ceil(findNiceValue(num)); -} - -/** - * Formats a big number - * @param {number} num - * @param {string=} separator The decimal separator for numbers like 50.1 (separator='.') - * @returns {string} - */ -export function formatBigNumber(num, separator = T.global.decimalSeparator) { - const sign = num < 0 ? "-" : ""; - num = Math.abs(num); - - if (num > 1e54) { - return sign + T.global.infinite; - } - - if (num < 10 && !Number.isInteger(num)) { - return sign + num.toFixed(2); - } - if (num < 50 && !Number.isInteger(num)) { - return sign + num.toFixed(1); - } - num = Math.floor(num); - - if (num < 1000) { - return sign + "" + num; - } else { - let leadingDigits = num; - let suffix = ""; - for (let suffixIndex = 0; suffixIndex < bigNumberSuffixTranslationKeys.length; ++suffixIndex) { - leadingDigits = leadingDigits / 1000; - suffix = T.global.suffix[bigNumberSuffixTranslationKeys[suffixIndex]]; - if (leadingDigits < 1000) { - break; - } - } - const leadingDigitsRounded = round1Digit(leadingDigits); - const leadingDigitsNoTrailingDecimal = leadingDigitsRounded - .toString() - .replace(".0", "") - .replace(".", separator); - return sign + leadingDigitsNoTrailingDecimal + suffix; - } -} - -/** - * Formats a big number, but does not add any suffix and instead uses its full representation - * @param {number} num - * @param {string=} divider The divider for numbers like 50,000 (divider=',') - * @returns {string} - */ -export function formatBigNumberFull(num, divider = T.global.thousandsDivider) { - if (num < 1000) { - return num + ""; - } - if (num > 1e54) { - return T.global.infinite; - } - let rest = num; - let out = ""; - while (rest >= 1000) { - out = (rest % 1000).toString().padStart(3, "0") + divider + out; - rest = Math.floor(rest / 1000); - } - out = rest + divider + out; - - return out.substring(0, out.length - 1); -} - -/** - * Waits two frames so the ui is updated - * @returns {Promise} - */ -export function waitNextFrame() { - return new Promise(function (resolve) { - window.requestAnimationFrame(function () { - window.requestAnimationFrame(function () { - resolve(); - }); - }); - }); -} - -/** - * Rounds 1 digit - * @param {number} n - * @returns {number} - */ -export function round1Digit(n) { - return Math.floor(n * 10.0) / 10.0; -} - -/** - * Rounds 2 digits - * @param {number} n - * @returns {number} - */ -export function round2Digits(n) { - return Math.floor(n * 100.0) / 100.0; -} - -/** - * Rounds 3 digits - * @param {number} n - * @returns {number} - */ -export function round3Digits(n) { - return Math.floor(n * 1000.0) / 1000.0; -} - -/** - * Rounds 4 digits - * @param {number} n - * @returns {number} - */ -export function round4Digits(n) { - return Math.floor(n * 10000.0) / 10000.0; -} - -/** - * Clamps a value between [min, max] - * @param {number} v - * @param {number=} minimum Default 0 - * @param {number=} maximum Default 1 - */ -export function clamp(v, minimum = 0, maximum = 1) { - return Math.max(minimum, Math.min(maximum, v)); -} - -/** - * Helper method to create a new div element - * @param {string=} id - * @param {Array=} classes - * @param {string=} innerHTML - */ -export function makeDivElement(id = null, classes = [], innerHTML = "") { - const div = document.createElement("div"); - if (id) { - div.id = id; - } - for (let i = 0; i < classes.length; ++i) { - div.classList.add(classes[i]); - } - div.innerHTML = innerHTML; - return div; -} - -/** - * Helper method to create a new div - * @param {Element} parent - * @param {string=} id - * @param {Array=} classes - * @param {string=} innerHTML - */ -export function makeDiv(parent, id = null, classes = [], innerHTML = "") { - const div = makeDivElement(id, classes, innerHTML); - parent.appendChild(div); - return div; -} - -/** - * Helper method to create a new button element - * @param {Array=} classes - * @param {string=} innerHTML - */ -export function makeButtonElement(classes = [], innerHTML = "") { - const element = document.createElement("button"); - for (let i = 0; i < classes.length; ++i) { - element.classList.add(classes[i]); - } - element.classList.add("styledButton"); - element.innerHTML = innerHTML; - return element; -} - -/** - * Helper method to create a new button - * @param {Element} parent - * @param {Array=} classes - * @param {string=} innerHTML - */ -export function makeButton(parent, classes = [], innerHTML = "") { - const element = makeButtonElement(classes, innerHTML); - parent.appendChild(element); - return element; -} - -/** - * Removes all children of the given element - * @param {Element} elem - */ -export function removeAllChildren(elem) { - if (elem) { - var range = document.createRange(); - range.selectNodeContents(elem); - range.deleteContents(); - } -} - -/** - * Returns if the game supports this browser - */ -export function isSupportedBrowser() { - // please note, - // that IE11 now returns undefined again for window.chrome - // and new Opera 30 outputs true for window.chrome - // but needs to check if window.opr is not undefined - // and new IE Edge outputs to true now for window.chrome - // and if not iOS Chrome check - // so use the below updated condition - - if (G_IS_STANDALONE) { - return true; - } - - // @ts-ignore - var isChromium = window.chrome; - var winNav = window.navigator; - var vendorName = winNav.vendor; - // @ts-ignore - var isIEedge = winNav.userAgent.indexOf("Edge") > -1; - var isIOSChrome = winNav.userAgent.match("CriOS"); - - if (isIOSChrome) { - // is Google Chrome on IOS - return false; - } else if ( - isChromium !== null && - typeof isChromium !== "undefined" && - vendorName === "Google Inc." && - isIEedge === false - ) { - // is Google Chrome - return true; - } else { - // not Google Chrome - return false; - } -} - -/** - * Formats an amount of seconds into something like "5s ago" - * @param {number} secs Seconds - * @returns {string} - */ -export function formatSecondsToTimeAgo(secs) { - const seconds = Math.floor(secs); - const minutes = Math.floor(seconds / 60); - const hours = Math.floor(minutes / 60); - const days = Math.floor(hours / 24); - - if (seconds < 60) { - if (seconds === 1) { - return T.global.time.oneSecondAgo; - } - return T.global.time.xSecondsAgo.replace("", "" + seconds); - } else if (minutes < 60) { - if (minutes === 1) { - return T.global.time.oneMinuteAgo; - } - return T.global.time.xMinutesAgo.replace("", "" + minutes); - } else if (hours < 24) { - if (hours === 1) { - return T.global.time.oneHourAgo; - } - return T.global.time.xHoursAgo.replace("", "" + hours); - } else { - if (days === 1) { - return T.global.time.oneDayAgo; - } - return T.global.time.xDaysAgo.replace("", "" + days); - } -} - -/** - * Formats seconds into a readable string like "5h 23m" - * @param {number} secs Seconds - * @returns {string} - */ -export function formatSeconds(secs) { - const trans = T.global.time; - secs = Math.ceil(secs); - if (secs < 60) { - return trans.secondsShort.replace("", "" + secs); - } else if (secs < 60 * 60) { - const minutes = Math.floor(secs / 60); - const seconds = secs % 60; - return trans.minutesAndSecondsShort - .replace("", "" + seconds) - .replace("", "" + minutes); - } else { - const hours = Math.floor(secs / 3600); - const minutes = Math.floor(secs / 60) % 60; - return trans.hoursAndMinutesShort.replace("", "" + minutes).replace("", "" + hours); - } -} - -/** - * Formats a number like 2.51 to "2.5" - * @param {number} speed - * @param {string=} separator The decimal separator for numbers like 50.1 (separator='.') - */ -export function round1DigitLocalized(speed, separator = T.global.decimalSeparator) { - return round1Digit(speed).toString().replace(".", separator); -} - -/** - * Formats a number like 2.51 to "2.51 items / s" - * @param {number} speed - * @param {boolean=} double - * @param {string=} separator The decimal separator for numbers like 50.1 (separator='.') - */ -export function formatItemsPerSecond(speed, double = false, separator = T.global.decimalSeparator) { - return ( - (speed === 1.0 - ? T.ingame.buildingPlacement.infoTexts.oneItemPerSecond - : T.ingame.buildingPlacement.infoTexts.itemsPerSecond.replace( - "", - round2Digits(speed).toString().replace(".", separator) - )) + (double ? " " + T.ingame.buildingPlacement.infoTexts.itemsPerSecondDouble : "") - ); -} - -/** - * Rotates a flat 3x3 matrix clockwise - * Entries: - * 0 lo - * 1 mo - * 2 ro - * 3 lm - * 4 mm - * 5 rm - * 6 lu - * 7 mu - * 8 ru - * @param {Array} flatMatrix - */ - -export function rotateFlatMatrix3x3(flatMatrix) { - return [ - flatMatrix[6], - flatMatrix[3], - flatMatrix[0], - flatMatrix[7], - flatMatrix[4], - flatMatrix[1], - flatMatrix[8], - flatMatrix[5], - flatMatrix[2], - ]; -} - -/** - * Generates rotated variants of the matrix - * @param {Array} originalMatrix - * @returns {Object>} - */ -export function generateMatrixRotations(originalMatrix) { - const result = { - 0: originalMatrix, - }; - - originalMatrix = rotateFlatMatrix3x3(originalMatrix); - result[90] = originalMatrix; - - originalMatrix = rotateFlatMatrix3x3(originalMatrix); - result[180] = originalMatrix; - - originalMatrix = rotateFlatMatrix3x3(originalMatrix); - result[270] = originalMatrix; - - return result; -} - -/** - * - * @typedef {{ - * top: any, - * right: any, - * bottom: any, - * left: any - * }} DirectionalObject - */ - -/** - * Rotates a directional object - * @param {DirectionalObject} obj - * @returns {DirectionalObject} - */ -export function rotateDirectionalObject(obj, rotation) { - const queue = [obj.top, obj.right, obj.bottom, obj.left]; - while (rotation !== 0) { - rotation -= 90; - queue.push(queue.shift()); - } - - return { - top: queue[0], - right: queue[1], - bottom: queue[2], - left: queue[3], - }; -} - -/** - * Modulo which works for negative numbers - * @param {number} n - * @param {number} m - */ -export function safeModulo(n, m) { - return ((n % m) + m) % m; -} - -/** - * Returns a smooth pulse between 0 and 1 - * @param {number} time time in seconds - * @returns {number} - */ -export function smoothPulse(time) { - return Math.sin(time * 4) * 0.5 + 0.5; -} - -/** - * Fills in a tag - * @param {string} translation - * @param {string} link - */ -export function fillInLinkIntoTranslation(translation, link) { - return translation - .replace("", "") - .replace("", ""); -} - -/** - * Generates a file download - * @param {string} filename - * @param {string} text - */ -export function generateFileDownload(filename, text) { - var element = document.createElement("a"); - element.setAttribute("href", "data:text/plain;charset=utf-8," + encodeURIComponent(text)); - element.setAttribute("download", filename); - - element.style.display = "none"; - document.body.appendChild(element); - - element.click(); - document.body.removeChild(element); -} - -/** - * Starts a file chooser - * @param {string} acceptedType - */ -export function startFileChoose(acceptedType = ".bin") { - var input = document.createElement("input"); - input.type = "file"; - input.accept = acceptedType; - - return new Promise(resolve => { - input.onchange = _ => resolve(input.files[0]); - input.click(); - }); -} - -const MAX_ROMAN_NUMBER = 49; -const romanLiteralsCache = ["0"]; - -/** - * - * @param {number} number - * @returns {string} - */ -export function getRomanNumber(number) { - if (G_WEGAME_VERSION) { - return String(number); - } - - number = Math.max(0, Math.round(number)); - if (romanLiteralsCache[number]) { - return romanLiteralsCache[number]; - } - - if (number > MAX_ROMAN_NUMBER) { - return String(number); - } - - function formatDigit(digit, unit, quintuple, decuple) { - switch (digit) { - case 0: - return ""; - case 1: // I - return unit; - case 2: // II - return unit + unit; - case 3: // III - return unit + unit + unit; - case 4: // IV - return unit + quintuple; - case 9: // IX - return unit + decuple; - default: - // V, VI, VII, VIII - return quintuple + formatDigit(digit - 5, unit, quintuple, decuple); - } - } - - let thousands = Math.floor(number / 1000); - let thousandsPart = ""; - while (thousands > 0) { - thousandsPart += "M"; - thousands -= 1; - } - - const hundreds = Math.floor((number % 1000) / 100); - const hundredsPart = formatDigit(hundreds, "C", "D", "M"); - - const tens = Math.floor((number % 100) / 10); - const tensPart = formatDigit(tens, "X", "L", "C"); - - const units = number % 10; - const unitsPart = formatDigit(units, "I", "V", "X"); - - const formatted = thousandsPart + hundredsPart + tensPart + unitsPart; - - romanLiteralsCache[number] = formatted; - return formatted; -} - -/** - * Returns the appropriate logo sprite path - */ -export function getLogoSprite() { - if (G_WEGAME_VERSION) { - return "logo_wegame.png"; - } - - if (G_IS_STEAM_DEMO) { - return "logo_demo.png"; - } - - if (G_CHINA_VERSION) { - return "logo_cn.png"; - } - - if (G_IS_STANDALONE || WEB_STEAM_SSO_AUTHENTICATED) { - return "logo.png"; - } - - if (G_IS_BROWSER) { - return "logo_demo.png"; - } - - return "logo.png"; -} - -/** - * Rejects a promise after X ms - * @param {Promise} promise - */ -export function timeoutPromise(promise, timeout = 30000) { - return Promise.race([ - new Promise((resolve, reject) => { - setTimeout(() => reject("timeout of " + timeout + " ms exceeded"), timeout); - }), - promise, - ]); -} +import { T } from "../translations"; + +const bigNumberSuffixTranslationKeys = ["thousands", "millions", "billions", "trillions"]; + +/** + * Returns a platform name + * @returns {"standalone"} + */ +export function getPlatformName() { + return "standalone"; +} + +/** + * Makes a new 2D array with undefined contents + * @param {number} w + * @param {number} h + * @returns {Array>} + */ +export function make2DUndefinedArray(w, h) { + const result = new Array(w); + for (let x = 0; x < w; ++x) { + result[x] = new Array(h); + } + return result; +} + +/** + * Creates a new map (an empty object without any props) + */ +export function newEmptyMap() { + return Object.create(null); +} + +/** + * Returns a random integer in the range [start,end] + * @param {number} start + * @param {number} end + */ +export function randomInt(start, end) { + return Math.floor(Math.random() * (end - start + 1) + start); +} + +/** + * Chooses a random entry of an array + * @template T + * @param {T[]} arr + * @returns {T} + */ +export function randomChoice(arr) { + return arr[Math.floor(Math.random() * arr.length)]; +} + +/** + * Deletes from an array by swapping with the last element + * @param {Array} array + * @param {number} index + */ +export function fastArrayDelete(array, index) { + if (index < 0 || index >= array.length) { + throw new Error("Out of bounds"); + } + // When the element is not the last element + if (index !== array.length - 1) { + // Get the last element, and swap it with the one we want to delete + const last = array[array.length - 1]; + array[index] = last; + } + + // Finally remove the last element + array.length -= 1; +} + +/** + * Deletes from an array by swapping with the last element. Searches + * for the value in the array first + * @param {Array} array + * @param {any} value + */ +export function fastArrayDeleteValue(array, value) { + if (array == null) { + throw new Error("Tried to delete from non array!"); + } + const index = array.indexOf(value); + if (index < 0) { + console.error("Value", value, "not contained in array:", array, "!"); + return value; + } + return fastArrayDelete(array, index); +} + +/** + * @see fastArrayDeleteValue + * @param {Array} array + * @param {any} value + */ +export function fastArrayDeleteValueIfContained(array, value) { + if (array == null) { + throw new Error("Tried to delete from non array!"); + } + const index = array.indexOf(value); + if (index < 0) { + return value; + } + return fastArrayDelete(array, index); +} + +/** + * Deletes from an array at the given index + * @param {Array} array + * @param {number} index + */ +export function arrayDelete(array, index) { + if (index < 0 || index >= array.length) { + throw new Error("Out of bounds"); + } + array.splice(index, 1); +} + +/** + * Deletes the given value from an array + * @param {Array} array + * @param {any} value + */ +export function arrayDeleteValue(array, value) { + if (array == null) { + throw new Error("Tried to delete from non array!"); + } + const index = array.indexOf(value); + if (index < 0) { + console.error("Value", value, "not contained in array:", array, "!"); + return value; + } + return arrayDelete(array, index); +} + +/** + * Compare two floats for epsilon equality + * @param {number} a + * @param {number} b + * @returns {boolean} + */ +export function epsilonCompare(a, b, epsilon = 1e-5) { + return Math.abs(a - b) < epsilon; +} + +/** + * Interpolates two numbers + * @param {number} a + * @param {number} b + * @param {number} x Mix factor, 0 means 100% a, 1 means 100%b, rest is interpolated + */ +export function lerp(a, b, x) { + return a * (1 - x) + b * x; +} + +/** + * Finds a value which is nice to display, e.g. 15669 -> 15000. Also handles fractional stuff + * @param {number} num + */ +export function findNiceValue(num) { + if (num > 1e8) { + return num; + } + if (num < 0.00001) { + return 0; + } + + let roundAmount = 1; + if (num > 50000) { + roundAmount = 10000; + } else if (num > 20000) { + roundAmount = 5000; + } else if (num > 5000) { + roundAmount = 1000; + } else if (num > 2000) { + roundAmount = 500; + } else if (num > 1000) { + roundAmount = 100; + } else if (num > 100) { + roundAmount = 20; + } else if (num > 20) { + roundAmount = 5; + } + + const niceValue = Math.floor(num / roundAmount) * roundAmount; + if (num >= 10) { + return Math.round(niceValue); + } + if (num >= 1) { + return Math.round(niceValue * 10) / 10; + } + + return Math.round(niceValue * 100) / 100; +} + +/** + * Finds a nice integer value + * @see findNiceValue + * @param {number} num + */ +export function findNiceIntegerValue(num) { + return Math.ceil(findNiceValue(num)); +} + +/** + * Formats a big number + * @param {number} num + * @param {string=} separator The decimal separator for numbers like 50.1 (separator='.') + * @returns {string} + */ +export function formatBigNumber(num, separator = T.global.decimalSeparator) { + const sign = num < 0 ? "-" : ""; + num = Math.abs(num); + + if (num > 1e54) { + return sign + T.global.infinite; + } + + if (num < 10 && !Number.isInteger(num)) { + return sign + num.toFixed(2); + } + if (num < 50 && !Number.isInteger(num)) { + return sign + num.toFixed(1); + } + num = Math.floor(num); + + if (num < 1000) { + return sign + "" + num; + } else { + let leadingDigits = num; + let suffix = ""; + for (let suffixIndex = 0; suffixIndex < bigNumberSuffixTranslationKeys.length; ++suffixIndex) { + leadingDigits = leadingDigits / 1000; + suffix = T.global.suffix[bigNumberSuffixTranslationKeys[suffixIndex]]; + if (leadingDigits < 1000) { + break; + } + } + const leadingDigitsRounded = round1Digit(leadingDigits); + const leadingDigitsNoTrailingDecimal = leadingDigitsRounded + .toString() + .replace(".0", "") + .replace(".", separator); + return sign + leadingDigitsNoTrailingDecimal + suffix; + } +} + +/** + * Formats a big number, but does not add any suffix and instead uses its full representation + * @param {number} num + * @param {string=} divider The divider for numbers like 50,000 (divider=',') + * @returns {string} + */ +export function formatBigNumberFull(num, divider = T.global.thousandsDivider) { + if (num < 1000) { + return num + ""; + } + if (num > 1e54) { + return T.global.infinite; + } + let rest = num; + let out = ""; + while (rest >= 1000) { + out = (rest % 1000).toString().padStart(3, "0") + divider + out; + rest = Math.floor(rest / 1000); + } + out = rest + divider + out; + + return out.substring(0, out.length - 1); +} + +/** + * Waits two frames so the ui is updated + * @returns {Promise} + */ +export function waitNextFrame() { + return new Promise(function (resolve) { + window.requestAnimationFrame(function () { + window.requestAnimationFrame(function () { + resolve(); + }); + }); + }); +} + +/** + * Rounds 1 digit + * @param {number} n + * @returns {number} + */ +export function round1Digit(n) { + return Math.floor(n * 10.0) / 10.0; +} + +/** + * Rounds 2 digits + * @param {number} n + * @returns {number} + */ +export function round2Digits(n) { + return Math.floor(n * 100.0) / 100.0; +} + +/** + * Rounds 3 digits + * @param {number} n + * @returns {number} + */ +export function round3Digits(n) { + return Math.floor(n * 1000.0) / 1000.0; +} + +/** + * Rounds 4 digits + * @param {number} n + * @returns {number} + */ +export function round4Digits(n) { + return Math.floor(n * 10000.0) / 10000.0; +} + +/** + * Clamps a value between [min, max] + * @param {number} v + * @param {number=} minimum Default 0 + * @param {number=} maximum Default 1 + */ +export function clamp(v, minimum = 0, maximum = 1) { + return Math.max(minimum, Math.min(maximum, v)); +} + +/** + * Helper method to create a new div element + * @param {string=} id + * @param {Array=} classes + * @param {string=} innerHTML + */ +export function makeDivElement(id = null, classes = [], innerHTML = "") { + const div = document.createElement("div"); + if (id) { + div.id = id; + } + for (let i = 0; i < classes.length; ++i) { + div.classList.add(classes[i]); + } + div.innerHTML = innerHTML; + return div; +} + +/** + * Helper method to create a new div + * @param {Element} parent + * @param {string=} id + * @param {Array=} classes + * @param {string=} innerHTML + */ +export function makeDiv(parent, id = null, classes = [], innerHTML = "") { + const div = makeDivElement(id, classes, innerHTML); + parent.appendChild(div); + return div; +} + +/** + * Helper method to create a new button element + * @param {Array=} classes + * @param {string=} innerHTML + */ +export function makeButtonElement(classes = [], innerHTML = "") { + const element = document.createElement("button"); + for (let i = 0; i < classes.length; ++i) { + element.classList.add(classes[i]); + } + element.classList.add("styledButton"); + element.innerHTML = innerHTML; + return element; +} + +/** + * Helper method to create a new button + * @param {Element} parent + * @param {Array=} classes + * @param {string=} innerHTML + */ +export function makeButton(parent, classes = [], innerHTML = "") { + const element = makeButtonElement(classes, innerHTML); + parent.appendChild(element); + return element; +} + +/** + * Removes all children of the given element + * @param {Element} elem + */ +export function removeAllChildren(elem) { + if (elem) { + const range = document.createRange(); + range.selectNodeContents(elem); + range.deleteContents(); + } +} + +/** + * Formats an amount of seconds into something like "5s ago" + * @param {number} secs Seconds + * @returns {string} + */ +export function formatSecondsToTimeAgo(secs) { + const seconds = Math.floor(secs); + const minutes = Math.floor(seconds / 60); + const hours = Math.floor(minutes / 60); + const days = Math.floor(hours / 24); + + if (seconds < 60) { + if (seconds === 1) { + return T.global.time.oneSecondAgo; + } + return T.global.time.xSecondsAgo.replace("", "" + seconds); + } else if (minutes < 60) { + if (minutes === 1) { + return T.global.time.oneMinuteAgo; + } + return T.global.time.xMinutesAgo.replace("", "" + minutes); + } else if (hours < 24) { + if (hours === 1) { + return T.global.time.oneHourAgo; + } + return T.global.time.xHoursAgo.replace("", "" + hours); + } else { + if (days === 1) { + return T.global.time.oneDayAgo; + } + return T.global.time.xDaysAgo.replace("", "" + days); + } +} + +/** + * Formats seconds into a readable string like "5h 23m" + * @param {number} secs Seconds + * @returns {string} + */ +export function formatSeconds(secs) { + const trans = T.global.time; + secs = Math.ceil(secs); + if (secs < 60) { + return trans.secondsShort.replace("", "" + secs); + } else if (secs < 60 * 60) { + const minutes = Math.floor(secs / 60); + const seconds = secs % 60; + return trans.minutesAndSecondsShort + .replace("", "" + seconds) + .replace("", "" + minutes); + } else { + const hours = Math.floor(secs / 3600); + const minutes = Math.floor(secs / 60) % 60; + return trans.hoursAndMinutesShort.replace("", "" + minutes).replace("", "" + hours); + } +} + +/** + * Formats a number like 2.51 to "2.5" + * @param {number} speed + * @param {string=} separator The decimal separator for numbers like 50.1 (separator='.') + */ +export function round1DigitLocalized(speed, separator = T.global.decimalSeparator) { + return round1Digit(speed).toString().replace(".", separator); +} + +/** + * Formats a number like 2.51 to "2.51 items / s" + * @param {number} speed + * @param {boolean=} double + * @param {string=} separator The decimal separator for numbers like 50.1 (separator='.') + */ +export function formatItemsPerSecond(speed, double = false, separator = T.global.decimalSeparator) { + return ( + (speed === 1.0 + ? T.ingame.buildingPlacement.infoTexts.oneItemPerSecond + : T.ingame.buildingPlacement.infoTexts.itemsPerSecond.replace( + "", + round2Digits(speed).toString().replace(".", separator) + )) + (double ? " " + T.ingame.buildingPlacement.infoTexts.itemsPerSecondDouble : "") + ); +} + +/** + * Rotates a flat 3x3 matrix clockwise + * Entries: + * 0 lo + * 1 mo + * 2 ro + * 3 lm + * 4 mm + * 5 rm + * 6 lu + * 7 mu + * 8 ru + * @param {Array} flatMatrix + */ + +export function rotateFlatMatrix3x3(flatMatrix) { + return [ + flatMatrix[6], + flatMatrix[3], + flatMatrix[0], + flatMatrix[7], + flatMatrix[4], + flatMatrix[1], + flatMatrix[8], + flatMatrix[5], + flatMatrix[2], + ]; +} + +/** + * Generates rotated variants of the matrix + * @param {Array} originalMatrix + * @returns {Object>} + */ +export function generateMatrixRotations(originalMatrix) { + const result = { + 0: originalMatrix, + }; + + originalMatrix = rotateFlatMatrix3x3(originalMatrix); + result[90] = originalMatrix; + + originalMatrix = rotateFlatMatrix3x3(originalMatrix); + result[180] = originalMatrix; + + originalMatrix = rotateFlatMatrix3x3(originalMatrix); + result[270] = originalMatrix; + + return result; +} + +/** + * + * @typedef {{ + * top: any, + * right: any, + * bottom: any, + * left: any + * }} DirectionalObject + */ + +/** + * Rotates a directional object + * @param {DirectionalObject} obj + * @returns {DirectionalObject} + */ +export function rotateDirectionalObject(obj, rotation) { + const queue = [obj.top, obj.right, obj.bottom, obj.left]; + while (rotation !== 0) { + rotation -= 90; + queue.push(queue.shift()); + } + + return { + top: queue[0], + right: queue[1], + bottom: queue[2], + left: queue[3], + }; +} + +/** + * Modulo which works for negative numbers + * @param {number} n + * @param {number} m + */ +export function safeModulo(n, m) { + return ((n % m) + m) % m; +} + +/** + * Returns a smooth pulse between 0 and 1 + * @param {number} time time in seconds + * @returns {number} + */ +export function smoothPulse(time) { + return Math.sin(time * 4) * 0.5 + 0.5; +} + +/** + * Fills in a tag + * @param {string} translation + * @param {string} link + */ +export function fillInLinkIntoTranslation(translation, link) { + return translation + .replace("", "") + .replace("", ""); +} + +const MAX_ROMAN_NUMBER = 49; +const romanLiteralsCache = ["0"]; + +/** + * + * @param {number} number + * @returns {string} + */ +export function getRomanNumber(number) { + number = Math.max(0, Math.round(number)); + if (romanLiteralsCache[number]) { + return romanLiteralsCache[number]; + } + + if (number > MAX_ROMAN_NUMBER) { + return String(number); + } + + function formatDigit(digit, unit, quintuple, decuple) { + switch (digit) { + case 0: + return ""; + case 1: // I + return unit; + case 2: // II + return unit + unit; + case 3: // III + return unit + unit + unit; + case 4: // IV + return unit + quintuple; + case 9: // IX + return unit + decuple; + default: + // V, VI, VII, VIII + return quintuple + formatDigit(digit - 5, unit, quintuple, decuple); + } + } + + let thousands = Math.floor(number / 1000); + let thousandsPart = ""; + while (thousands > 0) { + thousandsPart += "M"; + thousands -= 1; + } + + const hundreds = Math.floor((number % 1000) / 100); + const hundredsPart = formatDigit(hundreds, "C", "D", "M"); + + const tens = Math.floor((number % 100) / 10); + const tensPart = formatDigit(tens, "X", "L", "C"); + + const units = number % 10; + const unitsPart = formatDigit(units, "I", "V", "X"); + + const formatted = thousandsPart + hundredsPart + tensPart + unitsPart; + + romanLiteralsCache[number] = formatted; + return formatted; +} + +/** + * Rejects a promise after X ms + * @param {Promise} promise + */ +export function timeoutPromise(promise, timeout = 30000) { + return Promise.race([ + new Promise((resolve, reject) => { + setTimeout(() => reject("timeout of " + timeout + " ms exceeded"), timeout); + }), + promise, + ]); +} diff --git a/src/js/core/vector.js b/src/js/core/vector.js index 50fa9433..9525f5b3 100644 --- a/src/js/core/vector.js +++ b/src/js/core/vector.js @@ -489,7 +489,6 @@ export class Vector { } default: { assertAlways(false, "Invalid fast inplace rotation: " + angle); - return this; } } // return new Vector(this.x * cos - this.y * sin, this.x * sin + this.y * cos); @@ -519,7 +518,6 @@ export class Vector { } default: { assertAlways(false, "Invalid fast inplace rotation: " + angle); - return new Vector(); } } } @@ -593,7 +591,6 @@ export class Vector { } default: assertAlways(false, "Invalid angle: " + angle); - return; } } @@ -603,7 +600,7 @@ export class Vector { * @returns {Boolean} */ equalsEpsilon(v, epsilon = 1e-5) { - return Math.abs(this.x - v.x) < 1e-5 && Math.abs(this.y - v.y) < epsilon; + return Math.abs(this.x - v.x) < epsilon && Math.abs(this.y - v.y) < epsilon; } /** diff --git a/src/js/game/achievement_proxy.js b/src/js/game/achievement_proxy.js deleted file mode 100644 index 9077b283..00000000 --- a/src/js/game/achievement_proxy.js +++ /dev/null @@ -1,154 +0,0 @@ -/* typehints:start */ -import { Entity } from "./entity"; -import { GameRoot } from "./root"; -/* typehints:end */ - -import { globalConfig } from "../core/config"; -import { createLogger } from "../core/logging"; -import { ACHIEVEMENTS } from "../platform/achievement_provider"; -import { getBuildingDataFromCode } from "./building_codes"; - -const logger = createLogger("achievement_proxy"); - -const ROTATER = "rotater"; -const DEFAULT = "default"; - -export class AchievementProxy { - /** @param {GameRoot} root */ - constructor(root) { - this.root = root; - this.provider = this.root.app.achievementProvider; - this.disabled = true; - - if (G_IS_DEV && globalConfig.debug.testAchievements) { - // still enable the proxy - } else if (!this.provider.hasAchievements()) { - return; - } - - this.sliceTime = 0; - - this.root.signals.postLoadHook.add(this.onLoad, this); - } - - onLoad() { - if (!this.root.gameMode.hasAchievements()) { - logger.log("Disabling achievements because game mode does not have achievements"); - this.disabled = true; - return; - } - - this.provider - .onLoad(this.root) - .then(() => { - this.disabled = false; - logger.log("Recieving achievement signals"); - this.initialize(); - }) - .catch(err => { - this.disabled = true; - logger.error("Ignoring achievement signals", err); - }); - } - - initialize() { - this.root.signals.achievementCheck.dispatch(ACHIEVEMENTS.darkMode, null); - - if (this.has(ACHIEVEMENTS.mam)) { - this.root.signals.entityAdded.add(this.onMamFailure, this); - this.root.signals.entityDestroyed.add(this.onMamFailure, this); - this.root.signals.storyGoalCompleted.add(this.onStoryGoalCompleted, this); - } - - if (this.has(ACHIEVEMENTS.noInverseRotater)) { - this.root.signals.entityAdded.add(this.onEntityAdded, this); - } - - this.startSlice(); - } - - startSlice() { - this.sliceTime = this.root.time.now(); - - this.root.signals.bulkAchievementCheck.dispatch( - ACHIEVEMENTS.storeShape, - this.sliceTime, - ACHIEVEMENTS.throughputBp25, - this.sliceTime, - ACHIEVEMENTS.throughputBp50, - this.sliceTime, - ACHIEVEMENTS.throughputLogo25, - this.sliceTime, - ACHIEVEMENTS.throughputLogo50, - this.sliceTime, - ACHIEVEMENTS.throughputRocket10, - this.sliceTime, - ACHIEVEMENTS.throughputRocket20, - this.sliceTime, - ACHIEVEMENTS.play1h, - this.sliceTime, - ACHIEVEMENTS.play10h, - this.sliceTime, - ACHIEVEMENTS.play20h, - this.sliceTime - ); - } - - update() { - if (this.disabled) { - return; - } - - if (this.root.time.now() - this.sliceTime > globalConfig.achievementSliceDuration) { - this.startSlice(); - } - } - - /** - * @param {string} key - * @returns {boolean} - */ - has(key) { - if (!this.provider.collection) { - return false; - } - return this.provider.collection.map.has(key); - } - - /** @param {Entity} entity */ - onEntityAdded(entity) { - if (!entity.components.StaticMapEntity) { - return; - } - - const building = getBuildingDataFromCode(entity.components.StaticMapEntity.code); - - if (building.metaInstance.id !== ROTATER) { - return; - } - - if (building.variant === DEFAULT) { - return; - } - - this.root.savegame.currentData.stats.usedInverseRotater = true; - this.root.signals.entityAdded.remove(this.onEntityAdded); - } - - /** @param {number} level */ - onStoryGoalCompleted(level) { - if (level > 26) { - this.root.signals.entityAdded.add(this.onMamFailure, this); - this.root.signals.entityDestroyed.add(this.onMamFailure, this); - } - - this.root.signals.achievementCheck.dispatch(ACHIEVEMENTS.mam, null); - - // reset on every level - this.root.savegame.currentData.stats.failedMam = false; - } - - onMamFailure() { - this.root.savegame.currentData.stats.failedMam = true; - } -} diff --git a/src/js/game/automatic_save.js b/src/js/game/automatic_save.js index 9d841966..eeccfe78 100644 --- a/src/js/game/automatic_save.js +++ b/src/js/game/automatic_save.js @@ -1,79 +1,79 @@ -import { globalConfig } from "../core/config"; -import { createLogger } from "../core/logging"; -import { GameRoot } from "./root"; - -// How important it is that a savegame is created -/** - * @enum {number} - */ -export const enumSavePriority = { - regular: 2, - asap: 100, -}; - -const logger = createLogger("autosave"); - -export class AutomaticSave { - constructor(root) { - /** @type {GameRoot} */ - this.root = root; - - // Store the current maximum save importance - this.saveImportance = enumSavePriority.regular; - - this.lastSaveAttempt = -1000; - } - - setSaveImportance(importance) { - this.saveImportance = Math.max(this.saveImportance, importance); - } - - doSave() { - if (G_IS_DEV && globalConfig.debug.disableSavegameWrite) { - return; - } - - this.root.gameState.doSave(); - this.saveImportance = enumSavePriority.regular; - } - - update() { - if (!this.root.gameInitialized) { - // Bad idea - return; - } - - const saveInterval = this.root.app.settings.getAutosaveIntervalSeconds(); - if (!saveInterval) { - // Disabled - return; - } - - // Check when the last save was, but make sure that if it fails, we don't spam - const lastSaveTime = Math.max(this.lastSaveAttempt, this.root.savegame.getRealLastUpdate()); - - const secondsSinceLastSave = (Date.now() - lastSaveTime) / 1000.0; - let shouldSave = false; - - switch (this.saveImportance) { - case enumSavePriority.asap: - // High always should save - shouldSave = true; - break; - - case enumSavePriority.regular: - // Could determine if there is a good / bad point here - shouldSave = secondsSinceLastSave > saveInterval; - break; - - default: - assert(false, "Unknown save prio: " + this.saveImportance); - break; - } - if (shouldSave) { - logger.log("Saving automatically"); - this.lastSaveAttempt = Date.now(); - this.doSave(); - } - } -} +import { globalConfig } from "../core/config"; +import { createLogger } from "../core/logging"; +import { GameRoot } from "./root"; + +// How important it is that a savegame is created +/** + * @enum {number} + */ +export const enumSavePriority = { + regular: 2, + asap: 100, +}; + +const logger = createLogger("autosave"); + +export class AutomaticSave { + constructor(root) { + /** @type {GameRoot} */ + this.root = root; + + // Store the current maximum save importance + this.saveImportance = enumSavePriority.regular; + + this.lastSaveAttempt = -1000; + } + + setSaveImportance(importance) { + this.saveImportance = Math.max(this.saveImportance, importance); + } + + doSave() { + if (G_IS_DEV && globalConfig.debug.disableSavegameWrite) { + return; + } + + this.root.gameState.doSave(); + this.saveImportance = enumSavePriority.regular; + } + + update() { + if (!this.root.gameInitialized) { + // Bad idea + return; + } + + const saveInterval = this.root.app.settings.getAutosaveIntervalSeconds(); + if (!saveInterval) { + // Disabled + return; + } + + // Check when the last save was, but make sure that if it fails, we don't spam + const lastSaveTime = Math.max(this.lastSaveAttempt, this.root.savegame.getRealLastUpdate()); + + const secondsSinceLastSave = (Date.now() - lastSaveTime) / 1000.0; + let shouldSave = false; + + switch (this.saveImportance) { + case enumSavePriority.asap: + // High always should save + shouldSave = true; + break; + + case enumSavePriority.regular: + // Could determine if there is a good / bad point here + shouldSave = secondsSinceLastSave > saveInterval; + break; + + default: + assert(false, "Unknown save prio: " + this.saveImportance); + break; + } + if (shouldSave) { + logger.log("Saving automatically"); + this.lastSaveAttempt = Date.now(); + this.doSave(); + } + } +} diff --git a/src/js/game/base_item.js b/src/js/game/base_item.js index a6b38a08..d5861415 100644 --- a/src/js/game/base_item.js +++ b/src/js/game/base_item.js @@ -1,101 +1,101 @@ -import { globalConfig } from "../core/config"; -import { DrawParameters } from "../core/draw_parameters"; -import { BasicSerializableObject } from "../savegame/serialization"; - -/** - * Class for items on belts etc. Not an entity for performance reasons - */ -export class BaseItem extends BasicSerializableObject { - constructor() { - super(); - this._type = this.getItemType(); - } - - static getId() { - return "base_item"; - } - - /** @returns {import("../savegame/serialization").Schema} */ - static getSchema() { - return {}; - } - - /** @returns {ItemType} **/ - getItemType() { - abstract; - return "shape"; - } - - /** - * Returns a string id of the item - * @returns {string} - * @abstract - */ - getAsCopyableKey() { - abstract; - return ""; - } - - /** - * Returns if the item equals the other itme - * @param {BaseItem} other - * @returns {boolean} - */ - equals(other) { - if (this.getItemType() !== other.getItemType()) { - return false; - } - return this.equalsImpl(other); - } - - /** - * Override for custom comparison - * @param {BaseItem} other - * @returns {boolean} - * @abstract - */ - equalsImpl(other) { - abstract; - return false; - } - - /** - * Draws the item to a canvas - * @param {CanvasRenderingContext2D} context - * @param {number} size - * @abstract - */ - drawFullSizeOnCanvas(context, size) { - abstract; - } - - /** - * Draws the item at the given position - * @param {number} x - * @param {number} y - * @param {DrawParameters} parameters - * @param {number=} diameter - */ - drawItemCenteredClipped(x, y, parameters, diameter = globalConfig.defaultItemDiameter) { - if (parameters.visibleRect.containsCircle(x, y, diameter / 2)) { - this.drawItemCenteredImpl(x, y, parameters, diameter); - } - } - - /** - * INTERNAL - * @param {number} x - * @param {number} y - * @param {DrawParameters} parameters - * @param {number=} diameter - * @abstract - */ - drawItemCenteredImpl(x, y, parameters, diameter = globalConfig.defaultItemDiameter) { - abstract; - } - - getBackgroundColorAsResource() { - abstract; - return ""; - } -} +import { globalConfig } from "../core/config"; +import { DrawParameters } from "../core/draw_parameters"; +import { BasicSerializableObject } from "../savegame/serialization"; + +/** + * Class for items on belts etc. Not an entity for performance reasons + */ +export class BaseItem extends BasicSerializableObject { + constructor() { + super(); + this._type = this.getItemType(); + } + + static getId() { + return "base_item"; + } + + /** @returns {import("../savegame/serialization").Schema} */ + static getSchema() { + return {}; + } + + /** @returns {ItemType} **/ + getItemType() { + abstract; + return "shape"; + } + + /** + * Returns a string id of the item + * @returns {string} + * @abstract + */ + getAsCopyableKey() { + abstract; + return ""; + } + + /** + * Returns if the item equals the other itme + * @param {BaseItem} other + * @returns {boolean} + */ + equals(other) { + if (this.getItemType() !== other.getItemType()) { + return false; + } + return this.equalsImpl(other); + } + + /** + * Override for custom comparison + * @param {BaseItem} other + * @returns {boolean} + * @abstract + */ + equalsImpl(other) { + abstract; + return false; + } + + /** + * Draws the item to a canvas + * @param {CanvasRenderingContext2D} context + * @param {number} size + * @abstract + */ + drawFullSizeOnCanvas(context, size) { + abstract; + } + + /** + * Draws the item at the given position + * @param {number} x + * @param {number} y + * @param {DrawParameters} parameters + * @param {number=} diameter + */ + drawItemCenteredClipped(x, y, parameters, diameter = globalConfig.defaultItemDiameter) { + if (parameters.visibleRect.containsCircle(x, y, diameter / 2)) { + this.drawItemCenteredImpl(x, y, parameters, diameter); + } + } + + /** + * INTERNAL + * @param {number} x + * @param {number} y + * @param {DrawParameters} parameters + * @param {number=} diameter + * @abstract + */ + drawItemCenteredImpl(x, y, parameters, diameter = globalConfig.defaultItemDiameter) { + abstract; + } + + getBackgroundColorAsResource() { + abstract; + return ""; + } +} diff --git a/src/js/game/belt_path.js b/src/js/game/belt_path.js index 9ef4a3f3..dbb298a1 100644 --- a/src/js/game/belt_path.js +++ b/src/js/game/belt_path.js @@ -1,1661 +1,1662 @@ -import { globalConfig } from "../core/config"; -import { smoothenDpi } from "../core/dpi_manager"; -import { DrawParameters } from "../core/draw_parameters"; -import { createLogger } from "../core/logging"; -import { Rectangle } from "../core/rectangle"; -import { ORIGINAL_SPRITE_SCALE } from "../core/sprites"; -import { clamp, epsilonCompare, round4Digits } from "../core/utils"; -import { enumDirection, enumDirectionToVector, enumInvertedDirections, Vector } from "../core/vector"; -import { BasicSerializableObject, types } from "../savegame/serialization"; -import { BaseItem } from "./base_item"; -import { Entity } from "./entity"; -import { typeItemSingleton } from "./item_resolver"; -import { GameRoot } from "./root"; - -const logger = createLogger("belt_path"); - -// Helpers for more semantic access into interleaved arrays - -const DEBUG = G_IS_DEV && false; - -/** - * Stores a path of belts, used for optimizing performance - */ -export class BeltPath extends BasicSerializableObject { - static getId() { - return "BeltPath"; - } - - static getSchema() { - return { - entityPath: types.array(types.entity), - items: types.array(types.pair(types.ufloat, typeItemSingleton)), - spacingToFirstItem: types.ufloat, - }; - } - - /** - * Creates a path from a serialized object - * @param {GameRoot} root - * @param {Object} data - * @returns {BeltPath|string} - */ - static fromSerialized(root, data) { - // Create fake object which looks like a belt path but skips the constructor - const fakeObject = /** @type {BeltPath} */ (Object.create(BeltPath.prototype)); - fakeObject.root = root; - - // Deserialize the data - const errorCodeDeserialize = fakeObject.deserialize(data); - if (errorCodeDeserialize) { - return errorCodeDeserialize; - } - - // Compute other properties - fakeObject.init(false); - - return fakeObject; - } - - /** - * @param {GameRoot} root - * @param {Array} entityPath - */ - constructor(root, entityPath) { - super(); - this.root = root; - - assert(entityPath.length > 0, "invalid entity path"); - this.entityPath = entityPath; - - /** - * Stores the items sorted, and their distance to the previous item (or start) - * Layout: [distanceToNext, item] - * @type {Array<[number, BaseItem]>} - */ - this.items = []; - - /** - * Stores the spacing to the first item - */ - - this.init(); - - if (G_IS_DEV && globalConfig.debug.checkBeltPaths) { - this.debug_checkIntegrity("constructor"); - } - } - /** - * Initializes the path by computing the properties which are not saved - * @param {boolean} computeSpacing Whether to also compute the spacing - */ - init(computeSpacing = true) { - this.onPathChanged(); - - this.totalLength = this.computeTotalLength(); - - if (computeSpacing) { - this.spacingToFirstItem = this.totalLength; - } - - /** - * Current bounds of this path - * @type {Rectangle} - */ - this.worldBounds = this.computeBounds(); - - // Connect the belts - for (let i = 0; i < this.entityPath.length; ++i) { - this.entityPath[i].components.Belt.assignedPath = this; - } - } - - /** - * Clears all items - */ - clearAllItems() { - this.items = []; - this.spacingToFirstItem = this.totalLength; - this.numCompressedItemsAfterFirstItem = 0; - } - - /** - * Returns whether this path can accept a new item - * @returns {boolean} - */ - canAcceptItem() { - return this.spacingToFirstItem >= globalConfig.itemSpacingOnBelts; - } - - /** - * Tries to accept the item - * @param {BaseItem} item - */ - tryAcceptItem(item) { - if (this.spacingToFirstItem >= globalConfig.itemSpacingOnBelts) { - // So, since we already need one tick to accept this item we will add this directly. - const beltProgressPerTick = - this.root.hubGoals.getBeltBaseSpeed() * - this.root.dynamicTickrate.deltaSeconds * - globalConfig.itemSpacingOnBelts; - - // First, compute how much progress we can make *at max* - const maxProgress = Math.max(0, this.spacingToFirstItem - globalConfig.itemSpacingOnBelts); - const initialProgress = Math.min(maxProgress, beltProgressPerTick); - - this.items.unshift([this.spacingToFirstItem - initialProgress, item]); - this.spacingToFirstItem = initialProgress; - - if (G_IS_DEV && globalConfig.debug.checkBeltPaths) { - this.debug_checkIntegrity("accept-item"); - } - - return true; - } - return false; - } - - /** - * SLOW / Tries to find the item closest to the given tile - * @param {Vector} tile - * @returns {BaseItem|null} - */ - findItemAtTile(tile) { - // @TODO: This breaks color blind mode otherwise - return null; - } - - /** - * Computes the tile bounds of the path - * @returns {Rectangle} - */ - computeBounds() { - let bounds = this.entityPath[0].components.StaticMapEntity.getTileSpaceBounds(); - for (let i = 1; i < this.entityPath.length; ++i) { - const staticComp = this.entityPath[i].components.StaticMapEntity; - const otherBounds = staticComp.getTileSpaceBounds(); - bounds = bounds.getUnion(otherBounds); - } - return bounds.allScaled(globalConfig.tileSize); - } - - /** - * Recomputes cache variables once the path was changed - */ - onPathChanged() { - this.boundAcceptor = this.computeAcceptingEntityAndSlot().acceptor; - - /** - * How many items past the first item are compressed - */ - this.numCompressedItemsAfterFirstItem = 0; - } - - /** - * Called by the belt system when the surroundings changed - */ - onSurroundingsChanged() { - this.onPathChanged(); - } - - /** - * Finds the entity which accepts our items - * @param {boolean=} debug_Silent Whether debug output should be silent - * @return { { acceptor?: (BaseItem, number?) => boolean, entity?: Entity } } - */ - computeAcceptingEntityAndSlot(debug_Silent = false) { - DEBUG && !debug_Silent && logger.log("Recomputing acceptor target"); - - const lastEntity = this.entityPath[this.entityPath.length - 1]; - const lastStatic = lastEntity.components.StaticMapEntity; - const lastBeltComp = lastEntity.components.Belt; - - // Figure out where and into which direction we eject items - const ejectSlotWsTile = lastStatic.localTileToWorld(new Vector(0, 0)); - const ejectSlotWsDirection = lastStatic.localDirectionToWorld(lastBeltComp.direction); - const ejectSlotWsDirectionVector = enumDirectionToVector[ejectSlotWsDirection]; - const ejectSlotTargetWsTile = ejectSlotWsTile.add(ejectSlotWsDirectionVector); - - // Try to find the given acceptor component to take the item - const targetEntity = this.root.map.getLayerContentXY( - ejectSlotTargetWsTile.x, - ejectSlotTargetWsTile.y, - "regular" - ); - - if (!targetEntity) { - return {}; - } - - const noSimplifiedBelts = !this.root.app.settings.getAllSettings().simplifiedBelts; - - DEBUG && !debug_Silent && logger.log(" Found target entity", targetEntity.uid); - const targetStaticComp = targetEntity.components.StaticMapEntity; - const targetBeltComp = targetEntity.components.Belt; - - // Check for belts (special case) - if (targetBeltComp) { - const beltAcceptingDirection = targetStaticComp.localDirectionToWorld(enumDirection.top); - DEBUG && - !debug_Silent && - logger.log( - " Entity is accepting items from", - ejectSlotWsDirection, - "vs", - beltAcceptingDirection, - "Rotation:", - targetStaticComp.rotation - ); - if (ejectSlotWsDirection === beltAcceptingDirection) { - return { - entity: targetEntity, - acceptor: item => { - const path = targetBeltComp.assignedPath; - assert(path, "belt has no path"); - return path.tryAcceptItem(item); - }, - }; - } - } - - // Check for item acceptors - const targetAcceptorComp = targetEntity.components.ItemAcceptor; - if (!targetAcceptorComp) { - // Entity doesn't accept items - return {}; - } - - const ejectingDirection = targetStaticComp.worldDirectionToLocal(ejectSlotWsDirection); - const matchingSlot = targetAcceptorComp.findMatchingSlot( - targetStaticComp.worldToLocalTile(ejectSlotTargetWsTile), - ejectingDirection - ); - - if (!matchingSlot) { - // No matching slot found - return {}; - } - - const matchingSlotIndex = matchingSlot.index; - const passOver = this.computePassOverFunctionWithoutBelts(targetEntity, matchingSlotIndex); - if (!passOver) { - return {}; - } - - const matchingDirection = enumInvertedDirections[ejectingDirection]; - const filter = matchingSlot.slot.filter; - - return { - entity: targetEntity, - acceptor: function (item, remainingProgress = 0.0) { - // Check if the acceptor has a filter - if (filter && item._type !== filter) { - return false; - } - - // Try to pass over - if (passOver(item, matchingSlotIndex)) { - // Trigger animation on the acceptor comp - if (noSimplifiedBelts) { - targetAcceptorComp.onItemAccepted( - matchingSlotIndex, - matchingDirection, - item, - remainingProgress - ); - } - return true; - } - return false; - }, - }; - } - - /** - * Computes a method to pass over the item to the entity - * @param {Entity} entity - * @param {number} matchingSlotIndex - * @returns {(item: BaseItem, slotIndex: number) => boolean | void} - */ - computePassOverFunctionWithoutBelts(entity, matchingSlotIndex) { - const systems = this.root.systemMgr.systems; - const hubGoals = this.root.hubGoals; - - // NOTICE: THIS IS COPIED FROM THE ITEM EJECTOR SYSTEM FOR PEROFMANCE REASONS - - const itemProcessorComp = entity.components.ItemProcessor; - if (itemProcessorComp) { - // Its an item processor .. - return function (item) { - // Check for potential filters - if (!systems.itemProcessor.checkRequirements(entity, item, matchingSlotIndex)) { - return; - } - return itemProcessorComp.tryTakeItem(item, matchingSlotIndex); - }; - } - - const undergroundBeltComp = entity.components.UndergroundBelt; - if (undergroundBeltComp) { - // Its an underground belt. yay. - return function (item) { - return undergroundBeltComp.tryAcceptExternalItem( - item, - hubGoals.getUndergroundBeltBaseSpeed() - ); - }; - } - - const storageComp = entity.components.Storage; - if (storageComp) { - // It's a storage - return function (item) { - if (storageComp.canAcceptItem(item)) { - storageComp.takeItem(item); - return true; - } - }; - } - - const filterComp = entity.components.Filter; - if (filterComp) { - // It's a filter! Unfortunately the filter has to know a lot about it's - // surrounding state and components, so it can't be within the component itself. - return function (item) { - if (systems.filter.tryAcceptItem(entity, matchingSlotIndex, item)) { - return true; - } - }; - } - } - - // Following code will be compiled out outside of dev versions - /* dev:start */ - - /** - * Helper to throw an error on mismatch - * @param {string} change - * @param {Array} reason - */ - debug_failIntegrity(change, ...reason) { - throw new Error("belt path invalid (" + change + "): " + reason.map(i => "" + i).join(" ")); - } - - /** - * Checks if this path is valid - */ - debug_checkIntegrity(currentChange = "change") { - const fail = (...args) => this.debug_failIntegrity(currentChange, ...args); - - // Check for empty path - if (this.entityPath.length === 0) { - return fail("Belt path is empty"); - } - - // Check for mismatching length - const totalLength = this.computeTotalLength(); - if (!epsilonCompare(this.totalLength, totalLength, 0.01)) { - return this.debug_failIntegrity( - currentChange, - "Total length mismatch, stored =", - this.totalLength, - "but correct is", - totalLength - ); - } - - // Check for misconnected entities - for (let i = 0; i < this.entityPath.length - 1; ++i) { - const entity = this.entityPath[i]; - if (entity.destroyed) { - return fail("Reference to destroyed entity " + entity.uid); - } - - const followUp = this.root.systemMgr.systems.belt.findFollowUpEntity(entity); - if (!followUp) { - return fail( - "Follow up entity for the", - i, - "-th entity (total length", - this.entityPath.length, - ") was null!" - ); - } - if (followUp !== this.entityPath[i + 1]) { - return fail( - "Follow up entity mismatch, stored is", - this.entityPath[i + 1].uid, - "but real one is", - followUp.uid - ); - } - if (entity.components.Belt.assignedPath !== this) { - return fail( - "Entity with uid", - entity.uid, - "doesn't have this path assigned, but this path contains the entity." - ); - } - } - - // Check spacing - if (this.spacingToFirstItem > this.totalLength + 0.005) { - return fail( - currentChange, - "spacing to first item (", - this.spacingToFirstItem, - ") is greater than total length (", - this.totalLength, - ")" - ); - } - - // Check distance if empty - if (this.items.length === 0 && !epsilonCompare(this.spacingToFirstItem, this.totalLength, 0.01)) { - return fail( - currentChange, - "Path is empty but spacing to first item (", - this.spacingToFirstItem, - ") does not equal total length (", - this.totalLength, - ")" - ); - } - - // Check items etc - let currentPos = this.spacingToFirstItem; - for (let i = 0; i < this.items.length; ++i) { - const item = this.items[i]; - - if (item[0 /* nextDistance */] < 0 || item[0 /* nextDistance */] > this.totalLength + 0.02) { - return fail( - "Item has invalid offset to next item: ", - item[0 /* nextDistance */], - "(total length:", - this.totalLength, - ")" - ); - } - - currentPos += item[0 /* nextDistance */]; - } - - // Check the total sum matches - if (!epsilonCompare(currentPos, this.totalLength, 0.01)) { - return fail( - "total sum (", - currentPos, - ") of first item spacing (", - this.spacingToFirstItem, - ") and items does not match total length (", - this.totalLength, - ") -> items: " + this.items.map(i => i[0 /* nextDistance */]).join("|") - ); - } - - // Check bounds - const actualBounds = this.computeBounds(); - if (!actualBounds.equalsEpsilon(this.worldBounds, 0.01)) { - return fail("Bounds are stale"); - } - - // Check acceptor - const acceptor = this.computeAcceptingEntityAndSlot(true).acceptor; - if (!!acceptor !== !!this.boundAcceptor) { - return fail("Acceptor target mismatch, acceptor", !!acceptor, "vs stored", !!this.boundAcceptor); - } - - // Check first nonzero offset - let firstNonzero = 0; - for (let i = this.items.length - 2; i >= 0; --i) { - if (this.items[i][0 /* nextDistance */] < globalConfig.itemSpacingOnBelts + 1e-5) { - ++firstNonzero; - } else { - break; - } - } - - // Should warn, but this check isn't actually accurate - // if (firstNonzero !== this.numCompressedItemsAfterFirstItem) { - // console.warn( - // "First nonzero index is " + - // firstNonzero + - // " but stored is " + - // this.numCompressedItemsAfterFirstItem - // ); - // } - } - - /* dev:end */ - - /** - * Extends the belt path by the given belt - * @param {Entity} entity - */ - extendOnEnd(entity) { - DEBUG && logger.log("Extending belt path by entity at", entity.components.StaticMapEntity.origin); - - const beltComp = entity.components.Belt; - - // Append the entity - this.entityPath.push(entity); - this.onPathChanged(); - - // Extend the path length - const additionalLength = beltComp.getEffectiveLengthTiles(); - this.totalLength += additionalLength; - DEBUG && logger.log(" Extended total length by", additionalLength, "to", this.totalLength); - - // If we have no item, just update the distance to the first item - if (this.items.length === 0) { - this.spacingToFirstItem = this.totalLength; - DEBUG && logger.log(" Extended spacing to first to", this.totalLength, "(= total length)"); - } else { - // Otherwise, update the next-distance of the last item - const lastItem = this.items[this.items.length - 1]; - DEBUG && - logger.log( - " Extended spacing of last item from", - lastItem[0 /* nextDistance */], - "to", - lastItem[0 /* nextDistance */] + additionalLength - ); - lastItem[0 /* nextDistance */] += additionalLength; - } - - // Assign reference - beltComp.assignedPath = this; - - // Update bounds - this.worldBounds = this.computeBounds(); - - if (G_IS_DEV && globalConfig.debug.checkBeltPaths) { - this.debug_checkIntegrity("extend-on-end"); - } - } - - /** - * Extends the path with the given entity on the beginning - * @param {Entity} entity - */ - extendOnBeginning(entity) { - const beltComp = entity.components.Belt; - - DEBUG && logger.log("Extending the path on the beginning"); - - // All items on that belt are simply lost (for now) - - const length = beltComp.getEffectiveLengthTiles(); - - // Extend the length of this path - this.totalLength += length; - - // Simply adjust the first item spacing cuz we have no items contained - this.spacingToFirstItem += length; - - // Set handles and append entity - beltComp.assignedPath = this; - this.entityPath.unshift(entity); - this.onPathChanged(); - - // Update bounds - this.worldBounds = this.computeBounds(); - - if (G_IS_DEV && globalConfig.debug.checkBeltPaths) { - this.debug_checkIntegrity("extend-on-begin"); - } - } - - /** - * Returns if the given entity is the end entity of the path - * @param {Entity} entity - * @returns {boolean} - */ - isEndEntity(entity) { - return this.entityPath[this.entityPath.length - 1] === entity; - } - - /** - * Returns if the given entity is the start entity of the path - * @param {Entity} entity - * @returns {boolean} - */ - isStartEntity(entity) { - return this.entityPath[0] === entity; - } - - /** - * Splits this path at the given entity by removing it, and - * returning the new secondary paht - * @param {Entity} entity - * @returns {BeltPath} - */ - deleteEntityOnPathSplitIntoTwo(entity) { - DEBUG && logger.log("Splitting path at entity", entity.components.StaticMapEntity.origin); - - // First, find where the current path ends - const beltComp = entity.components.Belt; - beltComp.assignedPath = null; - - const entityLength = beltComp.getEffectiveLengthTiles(); - assert(this.entityPath.indexOf(entity) >= 0, "Entity not contained for split"); - assert(this.entityPath.indexOf(entity) !== 0, "Entity is first"); - assert(this.entityPath.indexOf(entity) !== this.entityPath.length - 1, "Entity is last"); - - let firstPathEntityCount = 0; - let firstPathLength = 0; - let firstPathEndEntity = null; - - for (let i = 0; i < this.entityPath.length; ++i) { - const otherEntity = this.entityPath[i]; - if (otherEntity === entity) { - DEBUG && logger.log("Found entity at", i, "of length", firstPathLength); - break; - } - - ++firstPathEntityCount; - firstPathEndEntity = otherEntity; - firstPathLength += otherEntity.components.Belt.getEffectiveLengthTiles(); - } - - DEBUG && - logger.log( - "First path ends at", - firstPathLength, - "and entity", - firstPathEndEntity.components.StaticMapEntity.origin, - "and has", - firstPathEntityCount, - "entities" - ); - - // Compute length of second path - const secondPathLength = this.totalLength - firstPathLength - entityLength; - const secondPathStart = firstPathLength + entityLength; - const secondEntities = this.entityPath.splice(firstPathEntityCount + 1); - DEBUG && - logger.log( - "Second path starts at", - secondPathStart, - "and has a length of ", - secondPathLength, - "with", - secondEntities.length, - "entities" - ); - - // Remove the last item - this.entityPath.pop(); - - DEBUG && logger.log("Splitting", this.items.length, "items"); - DEBUG && - logger.log( - "Old items are", - this.items.map(i => i[0 /* nextDistance */]) - ); - - // Create second path - const secondPath = new BeltPath(this.root, secondEntities); - - // Remove all items which are no longer relevant and transfer them to the second path - let itemPos = this.spacingToFirstItem; - for (let i = 0; i < this.items.length; ++i) { - const item = this.items[i]; - const distanceToNext = item[0 /* nextDistance */]; - - DEBUG && logger.log(" Checking item at", itemPos, "with distance of", distanceToNext, "to next"); - - // Check if this item is past the first path - if (itemPos >= firstPathLength) { - // Remove it from the first path - this.items.splice(i, 1); - i -= 1; - DEBUG && - logger.log(" Removed item from first path since its no longer contained @", itemPos); - - // Check if its on the second path (otherwise its on the removed belt and simply lost) - if (itemPos >= secondPathStart) { - // Put item on second path - secondPath.items.push([distanceToNext, item[1 /* item */]]); - DEBUG && - logger.log( - " Put item to second path @", - itemPos, - "with distance to next =", - distanceToNext - ); - - // If it was the first item, adjust the distance to the first item - if (secondPath.items.length === 1) { - DEBUG && logger.log(" Sinc it was the first, set sapcing of first to", itemPos); - secondPath.spacingToFirstItem = itemPos - secondPathStart; - } - } else { - DEBUG && logger.log(" Item was on the removed belt, so its gone - forever!"); - } - } else { - // Seems this item is on the first path (so all good), so just make sure it doesn't - // have a nextDistance which is bigger than the total path length - const clampedDistanceToNext = Math.min(itemPos + distanceToNext, firstPathLength) - itemPos; - if (clampedDistanceToNext < distanceToNext) { - DEBUG && - logger.log( - "Correcting next distance (first path) from", - distanceToNext, - "to", - clampedDistanceToNext - ); - item[0 /* nextDistance */] = clampedDistanceToNext; - } - } - - // Advance items - itemPos += distanceToNext; - } - - DEBUG && - logger.log( - "New items are", - this.items.map(i => i[0 /* nextDistance */]) - ); - - DEBUG && - logger.log( - "And second path items are", - secondPath.items.map(i => i[0 /* nextDistance */]) - ); - - // Adjust our total length - this.totalLength = firstPathLength; - - // Make sure that if we are empty, we set our first distance properly - if (this.items.length === 0) { - this.spacingToFirstItem = this.totalLength; - } - - this.onPathChanged(); - secondPath.onPathChanged(); - - // Update bounds - this.worldBounds = this.computeBounds(); - - if (G_IS_DEV && globalConfig.debug.checkBeltPaths) { - this.debug_checkIntegrity("split-two-first"); - secondPath.debug_checkIntegrity("split-two-second"); - } - - return secondPath; - } - - /** - * Deletes the last entity - * @param {Entity} entity - */ - deleteEntityOnEnd(entity) { - assert( - this.entityPath[this.entityPath.length - 1] === entity, - "Not actually the last entity (instead " + this.entityPath.indexOf(entity) + ")" - ); - - // Ok, first remove the entity - const beltComp = entity.components.Belt; - const beltLength = beltComp.getEffectiveLengthTiles(); - - DEBUG && - logger.log( - "Deleting last entity on path with length", - this.entityPath.length, - "(reducing", - this.totalLength, - " by", - beltLength, - ")" - ); - this.totalLength -= beltLength; - this.entityPath.pop(); - this.onPathChanged(); - - DEBUG && - logger.log( - " New path has length of", - this.totalLength, - "with", - this.entityPath.length, - "entities" - ); - - // This is just for sanity - beltComp.assignedPath = null; - - // Clean up items - if (this.items.length === 0) { - // Simple case with no items, just update the first item spacing - this.spacingToFirstItem = this.totalLength; - } else { - // Ok, make sure we simply drop all items which are no longer contained - let itemOffset = this.spacingToFirstItem; - let lastItemOffset = itemOffset; - - DEBUG && logger.log(" Adjusting", this.items.length, "items"); - - for (let i = 0; i < this.items.length; ++i) { - const item = this.items[i]; - - // Get rid of items past this path - if (itemOffset >= this.totalLength) { - DEBUG && logger.log("Dropping item (current index=", i, ")"); - this.items.splice(i, 1); - i -= 1; - continue; - } - - DEBUG && - logger.log( - "Item", - i, - "is at", - itemOffset, - "with next offset", - item[0 /* nextDistance */] - ); - lastItemOffset = itemOffset; - itemOffset += item[0 /* nextDistance */]; - } - - // If we still have an item, make sure the last item matches - if (this.items.length > 0) { - // We can easily compute the next distance since we know where the last item is now - const lastDistance = this.totalLength - lastItemOffset; - assert( - lastDistance >= 0.0, - "Last item distance mismatch: " + - lastDistance + - " -> Total length was " + - this.totalLength + - " and lastItemOffset was " + - lastItemOffset - ); - - DEBUG && - logger.log( - "Adjusted distance of last item: it is at", - lastItemOffset, - "so it has a distance of", - lastDistance, - "to the end (", - this.totalLength, - ")" - ); - this.items[this.items.length - 1][0 /* nextDistance */] = lastDistance; - } else { - DEBUG && logger.log(" Removed all items so we'll update spacing to total length"); - - // We removed all items so update our spacing - this.spacingToFirstItem = this.totalLength; - } - } - - // Update bounds - this.worldBounds = this.computeBounds(); - - if (G_IS_DEV && globalConfig.debug.checkBeltPaths) { - this.debug_checkIntegrity("delete-on-end"); - } - } - - /** - * Deletes the entity of the start of the path - * @see deleteEntityOnEnd - * @param {Entity} entity - */ - deleteEntityOnStart(entity) { - assert( - entity === this.entityPath[0], - "Not actually the start entity (instead " + this.entityPath.indexOf(entity) + ")" - ); - - // Ok, first remove the entity - const beltComp = entity.components.Belt; - const beltLength = beltComp.getEffectiveLengthTiles(); - - DEBUG && - logger.log( - "Deleting first entity on path with length", - this.entityPath.length, - "(reducing", - this.totalLength, - " by", - beltLength, - ")" - ); - this.totalLength -= beltLength; - this.entityPath.shift(); - this.onPathChanged(); - - DEBUG && - logger.log( - " New path has length of", - this.totalLength, - "with", - this.entityPath.length, - "entities" - ); - - // This is just for sanity - beltComp.assignedPath = null; - - // Clean up items - if (this.items.length === 0) { - // Simple case with no items, just update the first item spacing - this.spacingToFirstItem = this.totalLength; - } else { - // Simple case, we had no item on the beginning -> all good - if (this.spacingToFirstItem >= beltLength) { - DEBUG && - logger.log( - " No item on the first place, so we can just adjust the spacing (spacing=", - this.spacingToFirstItem, - ") removed =", - beltLength - ); - this.spacingToFirstItem -= beltLength; - } else { - // Welp, okay we need to drop all items which are < beltLength and adjust - // the other item offsets as well - - DEBUG && - logger.log( - " We have at least one item in the beginning, drop those and adjust spacing (first item @", - this.spacingToFirstItem, - ") since we removed", - beltLength, - "length from path" - ); - DEBUG && - logger.log( - " Items:", - this.items.map(i => i[0 /* nextDistance */]) - ); - - // Find offset to first item - let itemOffset = this.spacingToFirstItem; - for (let i = 0; i < this.items.length; ++i) { - const item = this.items[i]; - if (itemOffset <= beltLength) { - DEBUG && - logger.log( - " -> Dropping item with index", - i, - "at", - itemOffset, - "since it was on the removed belt" - ); - // This item must be dropped - this.items.splice(i, 1); - i -= 1; - itemOffset += item[0 /* nextDistance */]; - continue; - } else { - // This item can be kept, thus its the first we know - break; - } - } - - if (this.items.length > 0) { - DEBUG && - logger.log( - " Offset of first non-dropped item was at:", - itemOffset, - "-> setting spacing to it (total length=", - this.totalLength, - ")" - ); - - this.spacingToFirstItem = itemOffset - beltLength; - assert( - this.spacingToFirstItem >= 0.0, - "Invalid spacing after delete on start: " + this.spacingToFirstItem - ); - } else { - DEBUG && logger.log(" We dropped all items, simply set spacing to total length"); - // We dropped all items, simple one - this.spacingToFirstItem = this.totalLength; - } - } - } - - // Update bounds - this.worldBounds = this.computeBounds(); - - if (G_IS_DEV && globalConfig.debug.checkBeltPaths) { - this.debug_checkIntegrity("delete-on-start"); - } - } - - /** - * Extends the path by the given other path - * @param {BeltPath} otherPath - */ - extendByPath(otherPath) { - assert(otherPath !== this, "Circular path dependency"); - - const entities = otherPath.entityPath; - DEBUG && logger.log("Extending path by other path, starting to add entities"); - - const oldLength = this.totalLength; - - DEBUG && logger.log(" Adding", entities.length, "new entities, current length =", this.totalLength); - - // First, append entities - for (let i = 0; i < entities.length; ++i) { - const entity = entities[i]; - const beltComp = entity.components.Belt; - - // Add to path and update references - this.entityPath.push(entity); - beltComp.assignedPath = this; - - // Update our length - const additionalLength = beltComp.getEffectiveLengthTiles(); - this.totalLength += additionalLength; - } - - DEBUG && - logger.log( - " Path is now", - this.entityPath.length, - "entities and has a length of", - this.totalLength - ); - - // Now, update the distance of our last item - if (this.items.length !== 0) { - const lastItem = this.items[this.items.length - 1]; - lastItem[0 /* nextDistance */] += otherPath.spacingToFirstItem; - DEBUG && - logger.log( - " Add distance to last item, effectively being", - lastItem[0 /* nextDistance */], - "now" - ); - } else { - // Seems we have no items, update our first item distance - this.spacingToFirstItem = oldLength + otherPath.spacingToFirstItem; - DEBUG && - logger.log( - " We had no items, so our new spacing to first is old length (", - oldLength, - ") plus others spacing to first (", - otherPath.spacingToFirstItem, - ") =", - this.spacingToFirstItem - ); - } - - DEBUG && logger.log(" Pushing", otherPath.items.length, "items from other path"); - - // Aaand push the other paths items - for (let i = 0; i < otherPath.items.length; ++i) { - const item = otherPath.items[i]; - this.items.push([item[0 /* nextDistance */], item[1 /* item */]]); - } - - // Update bounds - this.worldBounds = this.computeBounds(); - - this.onPathChanged(); - - if (G_IS_DEV && globalConfig.debug.checkBeltPaths) { - this.debug_checkIntegrity("extend-by-path"); - } - } - - /** - * Computes the total length of the path - * @returns {number} - */ - computeTotalLength() { - let length = 0; - for (let i = 0; i < this.entityPath.length; ++i) { - const entity = this.entityPath[i]; - length += entity.components.Belt.getEffectiveLengthTiles(); - } - return length; - } - - /** - * Performs one tick - */ - update() { - if (G_IS_DEV && globalConfig.debug.checkBeltPaths) { - this.debug_checkIntegrity("pre-update"); - } - - // Skip empty belts - if (this.items.length === 0) { - return; - } - - // Divide by item spacing on belts since we use throughput and not speed - let beltSpeed = - this.root.hubGoals.getBeltBaseSpeed() * - this.root.dynamicTickrate.deltaSeconds * - globalConfig.itemSpacingOnBelts; - - if (G_IS_DEV && globalConfig.debug.instantBelts) { - beltSpeed *= 100; - } - - // Store whether this is the first item we processed, so premature - // item ejection is available - let isFirstItemProcessed = true; - - // Store how much velocity (strictly its distance, not velocity) we have to distribute over all items - let remainingVelocity = beltSpeed; - - // Store the last item we processed, so we can skip clashed ones - let lastItemProcessed; - - for (lastItemProcessed = this.items.length - 1; lastItemProcessed >= 0; --lastItemProcessed) { - const nextDistanceAndItem = this.items[lastItemProcessed]; - - // Compute how much spacing we need at least - const minimumSpacing = - lastItemProcessed === this.items.length - 1 ? 0 : globalConfig.itemSpacingOnBelts; - - // Compute how much we can advance - let clampedProgress = nextDistanceAndItem[0 /* nextDistance */] - minimumSpacing; - - // Make sure we don't advance more than the remaining velocity has stored - if (remainingVelocity < clampedProgress) { - clampedProgress = remainingVelocity; - } - - // Make sure we don't advance back - if (clampedProgress < 0) { - clampedProgress = 0; - } - - // Reduce our velocity by the amount we consumed - remainingVelocity -= clampedProgress; - - // Reduce the spacing - nextDistanceAndItem[0 /* nextDistance */] -= clampedProgress; - - // Advance all items behind by the progress we made - this.spacingToFirstItem += clampedProgress; - - // If the last item can be ejected, eject it and reduce the spacing, because otherwise - // we lose velocity - if (isFirstItemProcessed && nextDistanceAndItem[0 /* nextDistance */] < 1e-7) { - // Store how much velocity we "lost" because we bumped the item to the end of the - // belt but couldn't move it any farther. We need this to tell the item acceptor - // animation to start a tad later, so everything matches up. Yes I'm a perfectionist. - const excessVelocity = beltSpeed - clampedProgress; - - // Try to directly get rid of the item - if ( - this.boundAcceptor && - this.boundAcceptor(nextDistanceAndItem[1 /* item */], excessVelocity) - ) { - this.items.pop(); - - const itemBehind = this.items[lastItemProcessed - 1]; - if (itemBehind && this.numCompressedItemsAfterFirstItem > 0) { - // So, with the next tick we will skip this item, but it actually has the potential - // to process farther -> If we don't advance here, we loose a tiny bit of progress - // every tick which causes the belt to be slower than it actually is. - // Also see #999 - const fixupProgress = Math.max( - 0, - Math.min(remainingVelocity, itemBehind[0 /* nextDistance */]) - ); - - // See above - itemBehind[0 /* nextDistance */] -= fixupProgress; - remainingVelocity -= fixupProgress; - this.spacingToFirstItem += fixupProgress; - } - - // Reduce the number of compressed items since the first item no longer exists - this.numCompressedItemsAfterFirstItem = Math.max( - 0, - this.numCompressedItemsAfterFirstItem - 1 - ); - } - } - - if (isFirstItemProcessed) { - // Skip N null items after first items - lastItemProcessed -= this.numCompressedItemsAfterFirstItem; - } - - isFirstItemProcessed = false; - if (remainingVelocity < 1e-7) { - break; - } - } - - // Compute compressed item count - this.numCompressedItemsAfterFirstItem = Math.max( - 0, - this.numCompressedItemsAfterFirstItem, - this.items.length - 2 - lastItemProcessed - ); - - // Check if we have an item which is ready to be emitted - const lastItem = this.items[this.items.length - 1]; - if (lastItem && lastItem[0 /* nextDistance */] === 0) { - if (this.boundAcceptor && this.boundAcceptor(lastItem[1 /* item */])) { - this.items.pop(); - this.numCompressedItemsAfterFirstItem = Math.max( - 0, - this.numCompressedItemsAfterFirstItem - 1 - ); - } - } - - if (G_IS_DEV && globalConfig.debug.checkBeltPaths) { - this.debug_checkIntegrity("post-update"); - } - } - - /** - * Computes a world space position from the given progress - * @param {number} progress - * @returns {Vector} - */ - computePositionFromProgress(progress) { - let currentLength = 0; - - // floating point issues .. - assert(progress <= this.totalLength + 0.02, "Progress too big: " + progress); - - for (let i = 0; i < this.entityPath.length; ++i) { - const beltComp = this.entityPath[i].components.Belt; - const localLength = beltComp.getEffectiveLengthTiles(); - - if (currentLength + localLength >= progress || i === this.entityPath.length - 1) { - // Min required here due to floating point issues - const localProgress = Math.min(1.0, progress - currentLength); - - assert(localProgress >= 0.0, "Invalid local progress: " + localProgress); - const localSpace = beltComp.transformBeltToLocalSpace(localProgress); - return this.entityPath[i].components.StaticMapEntity.localTileToWorld(localSpace); - } - currentLength += localLength; - } - - assert(false, "invalid progress: " + progress + " (max: " + this.totalLength + ")"); - } - - /** - * - * @param {DrawParameters} parameters - */ - drawDebug(parameters) { - if (!parameters.visibleRect.containsRect(this.worldBounds)) { - return; - } - - parameters.context.fillStyle = "#d79a25"; - parameters.context.strokeStyle = "#d79a25"; - parameters.context.beginPath(); - - for (let i = 0; i < this.entityPath.length; ++i) { - const entity = this.entityPath[i]; - const pos = entity.components.StaticMapEntity; - const worldPos = pos.origin.toWorldSpaceCenterOfTile(); - - if (i === 0) { - parameters.context.moveTo(worldPos.x, worldPos.y); - } else { - parameters.context.lineTo(worldPos.x, worldPos.y); - } - } - parameters.context.stroke(); - - // Items - let progress = this.spacingToFirstItem; - for (let i = 0; i < this.items.length; ++i) { - const nextDistanceAndItem = this.items[i]; - const worldPos = this.computePositionFromProgress(progress).toWorldSpaceCenterOfTile(); - parameters.context.fillStyle = "#268e4d"; - parameters.context.beginRoundedRect(worldPos.x - 5, worldPos.y - 5, 10, 10, 3); - parameters.context.fill(); - parameters.context.font = "6px GameFont"; - parameters.context.fillStyle = "#111"; - parameters.context.fillText( - "" + round4Digits(nextDistanceAndItem[0 /* nextDistance */]), - worldPos.x + 5, - worldPos.y + 2 - ); - progress += nextDistanceAndItem[0 /* nextDistance */]; - - if (this.items.length - 1 - this.numCompressedItemsAfterFirstItem === i) { - parameters.context.fillStyle = "red"; - parameters.context.fillRect(worldPos.x + 5, worldPos.y, 20, 3); - } - } - - for (let i = 0; i < this.entityPath.length; ++i) { - const entity = this.entityPath[i]; - parameters.context.fillStyle = "#d79a25"; - const pos = entity.components.StaticMapEntity; - const worldPos = pos.origin.toWorldSpaceCenterOfTile(); - parameters.context.beginCircle(worldPos.x, worldPos.y, i === 0 ? 5 : 3); - parameters.context.fill(); - } - - for (let progress = 0; progress <= this.totalLength + 0.01; progress += 0.2) { - const worldPos = this.computePositionFromProgress(progress).toWorldSpaceCenterOfTile(); - parameters.context.fillStyle = "red"; - parameters.context.beginCircle(worldPos.x, worldPos.y, 1); - parameters.context.fill(); - } - - const firstItemIndicator = this.computePositionFromProgress( - this.spacingToFirstItem - ).toWorldSpaceCenterOfTile(); - parameters.context.fillStyle = "purple"; - parameters.context.fillRect(firstItemIndicator.x - 3, firstItemIndicator.y - 1, 6, 2); - } - - /** - * Checks if this belt path should render simplified - */ - checkIsPotatoMode() { - // POTATO Mode: Only show items when belt is hovered - if (!this.root.app.settings.getAllSettings().simplifiedBelts) { - return false; - } - - if (this.root.currentLayer !== "regular") { - // Not in regular layer - return true; - } - - const mousePos = this.root.app.mousePosition; - if (!mousePos) { - // Mouse not registered - return true; - } - - const tile = this.root.camera.screenToWorld(mousePos).toTileSpace(); - const contents = this.root.map.getLayerContentXY(tile.x, tile.y, "regular"); - if (!contents || !contents.components.Belt) { - // Nothing below - return true; - } - - if (contents.components.Belt.assignedPath !== this) { - // Not this path - return true; - } - return false; - } - - /** - * Draws the path - * @param {DrawParameters} parameters - */ - draw(parameters) { - if (!parameters.visibleRect.containsRect(this.worldBounds)) { - return; - } - - if (this.items.length === 0) { - // Early out - return; - } - - if (this.checkIsPotatoMode()) { - const firstItem = this.items[0]; - if (this.entityPath.length > 1 && firstItem) { - const medianBeltIndex = clamp( - Math.round(this.entityPath.length / 2 - 1), - 0, - this.entityPath.length - 1 - ); - const medianBelt = this.entityPath[medianBeltIndex]; - const beltComp = medianBelt.components.Belt; - const staticComp = medianBelt.components.StaticMapEntity; - const centerPosLocal = beltComp.transformBeltToLocalSpace( - this.entityPath.length % 2 === 0 ? beltComp.getEffectiveLengthTiles() : 0.5 - ); - const centerPos = staticComp.localTileToWorld(centerPosLocal).toWorldSpaceCenterOfTile(); - - parameters.context.globalAlpha = 0.5; - firstItem[1 /* item */].drawItemCenteredClipped(centerPos.x, centerPos.y, parameters); - parameters.context.globalAlpha = 1; - } - - return; - } - - let currentItemPos = this.spacingToFirstItem; - let currentItemIndex = 0; - - let trackPos = 0.0; - - /** - * @type {Array<[Vector, BaseItem]>} - */ - let drawStack = []; - let drawStackProp = ""; - - // Iterate whole track and check items - for (let i = 0; i < this.entityPath.length; ++i) { - const entity = this.entityPath[i]; - const beltComp = entity.components.Belt; - const beltLength = beltComp.getEffectiveLengthTiles(); - - // Check if the current items are on the belt - while (trackPos + beltLength >= currentItemPos - 1e-5) { - // It's on the belt, render it now - const staticComp = entity.components.StaticMapEntity; - assert( - currentItemPos - trackPos >= 0, - "invalid track pos: " + currentItemPos + " vs " + trackPos + " (l =" + beltLength + ")" - ); - - const localPos = beltComp.transformBeltToLocalSpace(currentItemPos - trackPos); - const worldPos = staticComp.localTileToWorld(localPos).toWorldSpaceCenterOfTile(); - - const distanceAndItem = this.items[currentItemIndex]; - const item = distanceAndItem[1 /* item */]; - const nextItemDistance = distanceAndItem[0 /* nextDistance */]; - - if ( - !parameters.visibleRect.containsCircle( - worldPos.x, - worldPos.y, - globalConfig.defaultItemDiameter - ) - ) { - // this one isn't visible, do not append it - // Start a new stack - this.drawDrawStack(drawStack, parameters, drawStackProp); - drawStack = []; - drawStackProp = ""; - } else { - if (drawStack.length > 1) { - // Check if we can append to the stack, since its already a stack of two same items - const referenceItem = drawStack[0]; - - if ( - referenceItem[1].equals(item) && - Math.abs(referenceItem[0][drawStackProp] - worldPos[drawStackProp]) < 0.001 - ) { - // Will continue stack - } else { - // Start a new stack, since item doesn't follow in row - this.drawDrawStack(drawStack, parameters, drawStackProp); - drawStack = []; - drawStackProp = ""; - } - } else if (drawStack.length === 1) { - const firstItem = drawStack[0]; - - // Check if we can make it a stack - if (firstItem[1 /* item */].equals(item)) { - // Same item, check if it is either horizontal or vertical - const startPos = firstItem[0 /* pos */]; - - if (Math.abs(startPos.x - worldPos.x) < 0.001) { - drawStackProp = "x"; - } else if (Math.abs(startPos.y - worldPos.y) < 0.001) { - drawStackProp = "y"; - } else { - // Start a new stack - this.drawDrawStack(drawStack, parameters, drawStackProp); - drawStack = []; - drawStackProp = ""; - } - } else { - // Start a new stack, since item doesn't equal - this.drawDrawStack(drawStack, parameters, drawStackProp); - drawStack = []; - drawStackProp = ""; - } - } else { - // First item of stack, do nothing - } - - drawStack.push([worldPos, item]); - } - - // Check for the next item - currentItemPos += nextItemDistance; - ++currentItemIndex; - - if ( - nextItemDistance > globalConfig.itemSpacingOnBelts + 0.001 || - drawStack.length > globalConfig.maxBeltShapeBundleSize - ) { - // If next item is not directly following, abort drawing - this.drawDrawStack(drawStack, parameters, drawStackProp); - drawStack = []; - drawStackProp = ""; - } - - if (currentItemIndex >= this.items.length) { - // We rendered all items - - this.drawDrawStack(drawStack, parameters, drawStackProp); - return; - } - } - - trackPos += beltLength; - } - - this.drawDrawStack(drawStack, parameters, drawStackProp); - } - - /** - * - * @param {HTMLCanvasElement} canvas - * @param {CanvasRenderingContext2D} context - * @param {number} w - * @param {number} h - * @param {number} dpi - * @param {object} param0 - * @param {string} param0.direction - * @param {Array<[Vector, BaseItem]>} param0.stack - * @param {GameRoot} param0.root - * @param {number} param0.zoomLevel - */ - drawShapesInARow(canvas, context, w, h, dpi, { direction, stack, root, zoomLevel }) { - context.scale(dpi, dpi); - - if (G_IS_DEV && globalConfig.debug.showShapeGrouping) { - context.fillStyle = "rgba(0, 0, 255, 0.5)"; - context.fillRect(0, 0, w, h); - } - - const parameters = new DrawParameters({ - context, - desiredAtlasScale: ORIGINAL_SPRITE_SCALE, - root, - visibleRect: new Rectangle(-1000, -1000, 2000, 2000), - zoomLevel, - }); - - const itemSize = globalConfig.itemSpacingOnBelts * globalConfig.tileSize; - const item = stack[0]; - const pos = new Vector(itemSize / 2, itemSize / 2); - - for (let i = 0; i < stack.length; i++) { - item[1].drawItemCenteredClipped(pos.x, pos.y, parameters, globalConfig.defaultItemDiameter); - pos[direction] += globalConfig.itemSpacingOnBelts * globalConfig.tileSize; - } - } - - /** - * @param {Array<[Vector, BaseItem]>} stack - * @param {DrawParameters} parameters - */ - drawDrawStack(stack, parameters, directionProp) { - if (stack.length === 0) { - return; - } - - const firstItem = stack[0]; - const firstItemPos = firstItem[0]; - if (stack.length === 1) { - firstItem[1].drawItemCenteredClipped( - firstItemPos.x, - firstItemPos.y, - parameters, - globalConfig.defaultItemDiameter - ); - return; - } - - const itemSize = globalConfig.itemSpacingOnBelts * globalConfig.tileSize; - const inverseDirection = directionProp === "x" ? "y" : "x"; - - const dimensions = new Vector(itemSize, itemSize); - dimensions[inverseDirection] *= stack.length; - - const directionVector = firstItemPos.copy().sub(stack[1][0]); - - const dpi = smoothenDpi(globalConfig.shapesSharpness * parameters.zoomLevel); - - const sprite = this.root.buffers.getForKey({ - key: "beltpaths", - subKey: - "stack-" + - directionProp + - "-" + - dpi + - "#" + - stack.length + - "#" + - firstItem[1].getItemType() + - "#" + - firstItem[1].serialize(), - dpi, - w: dimensions.x, - h: dimensions.y, - redrawMethod: this.drawShapesInARow.bind(this), - additionalParams: { - direction: inverseDirection, - stack, - root: this.root, - zoomLevel: parameters.zoomLevel, - }, - }); - - const anchor = directionVector[inverseDirection] < 0 ? firstItem : stack[stack.length - 1]; - - parameters.context.drawImage( - sprite, - anchor[0].x - itemSize / 2, - anchor[0].y - itemSize / 2, - dimensions.x, - dimensions.y - ); - } -} +import { globalConfig } from "../core/config"; +import { smoothenDpi } from "../core/dpi_manager"; +import { DrawParameters } from "../core/draw_parameters"; +import { createLogger } from "../core/logging"; +import { Rectangle } from "../core/rectangle"; +import { ORIGINAL_SPRITE_SCALE } from "../core/sprites"; +import { clamp, epsilonCompare, round4Digits } from "../core/utils"; +import { enumDirection, enumDirectionToVector, enumInvertedDirections, Vector } from "../core/vector"; +import { BasicSerializableObject, types } from "../savegame/serialization"; +import { BaseItem } from "./base_item"; +import { Entity } from "./entity"; +import { typeItemSingleton } from "./item_resolver"; +import { GameRoot } from "./root"; + +const logger = createLogger("belt_path"); + +// Helpers for more semantic access into interleaved arrays + +const DEBUG = G_IS_DEV && false; + +/** + * Stores a path of belts, used for optimizing performance + */ +export class BeltPath extends BasicSerializableObject { + static getId() { + return "BeltPath"; + } + + static getSchema() { + return { + entityPath: types.array(types.entity), + items: types.array(types.pair(types.ufloat, typeItemSingleton)), + spacingToFirstItem: types.ufloat, + }; + } + + /** + * Creates a path from a serialized object + * @param {GameRoot} root + * @param {Object} data + * @returns {BeltPath|string} + */ + static fromSerialized(root, data) { + // Create fake object which looks like a belt path but skips the constructor + const fakeObject = /** @type {BeltPath} */ (Object.create(BeltPath.prototype)); + fakeObject.root = root; + + // Deserialize the data + const errorCodeDeserialize = fakeObject.deserialize(data); + if (errorCodeDeserialize) { + return errorCodeDeserialize; + } + + // Compute other properties + fakeObject.init(false); + + return fakeObject; + } + + /** + * @param {GameRoot} root + * @param {Array} entityPath + */ + constructor(root, entityPath) { + super(); + this.root = root; + + assert(entityPath.length > 0, "invalid entity path"); + this.entityPath = entityPath; + + /** + * Stores the items sorted, and their distance to the previous item (or start) + * Layout: [distanceToNext, item] + * @type {Array<[number, BaseItem]>} + */ + this.items = []; + + /** + * Stores the spacing to the first item + */ + + this.init(); + + if (G_IS_DEV && globalConfig.debug.checkBeltPaths) { + this.debug_checkIntegrity("constructor"); + } + } + /** + * Initializes the path by computing the properties which are not saved + * @param {boolean} computeSpacing Whether to also compute the spacing + */ + init(computeSpacing = true) { + this.onPathChanged(); + + this.totalLength = this.computeTotalLength(); + + if (computeSpacing) { + this.spacingToFirstItem = this.totalLength; + } + + /** + * Current bounds of this path + * @type {Rectangle} + */ + this.worldBounds = this.computeBounds(); + + // Connect the belts + for (let i = 0; i < this.entityPath.length; ++i) { + this.entityPath[i].components.Belt.assignedPath = this; + } + } + + /** + * Clears all items + */ + clearAllItems() { + this.items = []; + this.spacingToFirstItem = this.totalLength; + this.numCompressedItemsAfterFirstItem = 0; + } + + /** + * Returns whether this path can accept a new item + * @returns {boolean} + */ + canAcceptItem() { + return this.spacingToFirstItem >= globalConfig.itemSpacingOnBelts; + } + + /** + * Tries to accept the item + * @param {BaseItem} item + */ + tryAcceptItem(item) { + if (this.spacingToFirstItem >= globalConfig.itemSpacingOnBelts) { + // So, since we already need one tick to accept this item we will add this directly. + const beltProgressPerTick = + this.root.hubGoals.getBeltBaseSpeed() * + this.root.dynamicTickrate.deltaSeconds * + globalConfig.itemSpacingOnBelts; + + // First, compute how much progress we can make *at max* + const maxProgress = Math.max(0, this.spacingToFirstItem - globalConfig.itemSpacingOnBelts); + const initialProgress = Math.min(maxProgress, beltProgressPerTick); + + this.items.unshift([this.spacingToFirstItem - initialProgress, item]); + this.spacingToFirstItem = initialProgress; + + if (G_IS_DEV && globalConfig.debug.checkBeltPaths) { + this.debug_checkIntegrity("accept-item"); + } + + return true; + } + return false; + } + + /** + * SLOW / Tries to find the item closest to the given tile + * @param {Vector} tile + * @returns {BaseItem|null} + */ + findItemAtTile(tile) { + // @TODO: This breaks color blind mode otherwise + return null; + } + + /** + * Computes the tile bounds of the path + * @returns {Rectangle} + */ + computeBounds() { + let bounds = this.entityPath[0].components.StaticMapEntity.getTileSpaceBounds(); + for (let i = 1; i < this.entityPath.length; ++i) { + const staticComp = this.entityPath[i].components.StaticMapEntity; + const otherBounds = staticComp.getTileSpaceBounds(); + bounds = bounds.getUnion(otherBounds); + } + return bounds.allScaled(globalConfig.tileSize); + } + + /** + * Recomputes cache variables once the path was changed + */ + onPathChanged() { + this.boundAcceptor = this.computeAcceptingEntityAndSlot().acceptor; + + /** + * How many items past the first item are compressed + */ + this.numCompressedItemsAfterFirstItem = 0; + } + + /** + * Called by the belt system when the surroundings changed + */ + onSurroundingsChanged() { + this.onPathChanged(); + } + + /** + * Finds the entity which accepts our items + * @param {boolean=} debug_Silent Whether debug output should be silent + * @return { { acceptor?: (BaseItem, number?) => boolean, entity?: Entity } } + */ + computeAcceptingEntityAndSlot(debug_Silent = false) { + DEBUG && !debug_Silent && logger.log("Recomputing acceptor target"); + + const lastEntity = this.entityPath[this.entityPath.length - 1]; + const lastStatic = lastEntity.components.StaticMapEntity; + const lastBeltComp = lastEntity.components.Belt; + + // Figure out where and into which direction we eject items + const ejectSlotWsTile = lastStatic.localTileToWorld(new Vector(0, 0)); + const ejectSlotWsDirection = lastStatic.localDirectionToWorld(lastBeltComp.direction); + const ejectSlotWsDirectionVector = enumDirectionToVector[ejectSlotWsDirection]; + const ejectSlotTargetWsTile = ejectSlotWsTile.add(ejectSlotWsDirectionVector); + + // Try to find the given acceptor component to take the item + const targetEntity = this.root.map.getLayerContentXY( + ejectSlotTargetWsTile.x, + ejectSlotTargetWsTile.y, + "regular" + ); + + if (!targetEntity) { + return {}; + } + + const noSimplifiedBelts = !this.root.app.settings.getAllSettings().simplifiedBelts; + + DEBUG && !debug_Silent && logger.log(" Found target entity", targetEntity.uid); + const targetStaticComp = targetEntity.components.StaticMapEntity; + const targetBeltComp = targetEntity.components.Belt; + + // Check for belts (special case) + if (targetBeltComp) { + const beltAcceptingDirection = targetStaticComp.localDirectionToWorld(enumDirection.top); + DEBUG && + !debug_Silent && + logger.log( + " Entity is accepting items from", + ejectSlotWsDirection, + "vs", + beltAcceptingDirection, + "Rotation:", + targetStaticComp.rotation + ); + if (ejectSlotWsDirection === beltAcceptingDirection) { + return { + entity: targetEntity, + acceptor: item => { + const path = targetBeltComp.assignedPath; + assert(path, "belt has no path"); + return path.tryAcceptItem(item); + }, + }; + } + } + + // Check for item acceptors + const targetAcceptorComp = targetEntity.components.ItemAcceptor; + if (!targetAcceptorComp) { + // Entity doesn't accept items + return {}; + } + + const ejectingDirection = targetStaticComp.worldDirectionToLocal(ejectSlotWsDirection); + const matchingSlot = targetAcceptorComp.findMatchingSlot( + targetStaticComp.worldToLocalTile(ejectSlotTargetWsTile), + ejectingDirection + ); + + if (!matchingSlot) { + // No matching slot found + return {}; + } + + const matchingSlotIndex = matchingSlot.index; + const passOver = this.computePassOverFunctionWithoutBelts(targetEntity, matchingSlotIndex); + if (!passOver) { + return {}; + } + + const matchingDirection = enumInvertedDirections[ejectingDirection]; + const filter = matchingSlot.slot.filter; + + return { + entity: targetEntity, + acceptor: function (item, remainingProgress = 0.0) { + // Check if the acceptor has a filter + if (filter && item._type !== filter) { + return false; + } + + // Try to pass over + if (passOver(item, matchingSlotIndex)) { + // Trigger animation on the acceptor comp + if (noSimplifiedBelts) { + targetAcceptorComp.onItemAccepted( + matchingSlotIndex, + matchingDirection, + item, + remainingProgress + ); + } + return true; + } + return false; + }, + }; + } + + /** + * Computes a method to pass over the item to the entity + * @param {Entity} entity + * @param {number} matchingSlotIndex + * @returns {(item: BaseItem, slotIndex: number) => boolean | void} + */ + computePassOverFunctionWithoutBelts(entity, matchingSlotIndex) { + const systems = this.root.systemMgr.systems; + const hubGoals = this.root.hubGoals; + + // NOTICE: THIS IS COPIED FROM THE ITEM EJECTOR SYSTEM FOR PEROFMANCE REASONS + + const itemProcessorComp = entity.components.ItemProcessor; + if (itemProcessorComp) { + // Its an item processor .. + return function (item) { + // Check for potential filters + if (!systems.itemProcessor.checkRequirements(entity, item, matchingSlotIndex)) { + return; + } + return itemProcessorComp.tryTakeItem(item, matchingSlotIndex); + }; + } + + const undergroundBeltComp = entity.components.UndergroundBelt; + if (undergroundBeltComp) { + // Its an underground belt. yay. + return function (item) { + return undergroundBeltComp.tryAcceptExternalItem( + item, + hubGoals.getUndergroundBeltBaseSpeed() + ); + }; + } + + const storageComp = entity.components.Storage; + if (storageComp) { + // It's a storage + return function (item) { + if (storageComp.canAcceptItem(item)) { + storageComp.takeItem(item); + return true; + } + }; + } + + const filterComp = entity.components.Filter; + if (filterComp) { + // It's a filter! Unfortunately the filter has to know a lot about it's + // surrounding state and components, so it can't be within the component itself. + return function (item) { + if (systems.filter.tryAcceptItem(entity, matchingSlotIndex, item)) { + return true; + } + }; + } + } + + // Following code will be compiled out outside of dev versions + /* dev:start */ + + /** + * Helper to throw an error on mismatch + * @param {string} change + * @param {Array} reason + */ + debug_failIntegrity(change, ...reason) { + throw new Error("belt path invalid (" + change + "): " + reason.map(i => "" + i).join(" ")); + } + + /** + * Checks if this path is valid + */ + debug_checkIntegrity(currentChange = "change") { + const fail = (...args) => this.debug_failIntegrity(currentChange, ...args); + + // Check for empty path + if (this.entityPath.length === 0) { + return fail("Belt path is empty"); + } + + // Check for mismatching length + const totalLength = this.computeTotalLength(); + if (!epsilonCompare(this.totalLength, totalLength, 0.01)) { + return this.debug_failIntegrity( + currentChange, + "Total length mismatch, stored =", + this.totalLength, + "but correct is", + totalLength + ); + } + + // Check for misconnected entities + for (let i = 0; i < this.entityPath.length - 1; ++i) { + const entity = this.entityPath[i]; + if (entity.destroyed) { + return fail("Reference to destroyed entity " + entity.uid); + } + + const followUp = this.root.systemMgr.systems.belt.findFollowUpEntity(entity); + if (!followUp) { + return fail( + "Follow up entity for the", + i, + "-th entity (total length", + this.entityPath.length, + ") was null!" + ); + } + if (followUp !== this.entityPath[i + 1]) { + return fail( + "Follow up entity mismatch, stored is", + this.entityPath[i + 1].uid, + "but real one is", + followUp.uid + ); + } + if (entity.components.Belt.assignedPath !== this) { + return fail( + "Entity with uid", + entity.uid, + "doesn't have this path assigned, but this path contains the entity." + ); + } + } + + // Check spacing + if (this.spacingToFirstItem > this.totalLength + 0.005) { + return fail( + currentChange, + "spacing to first item (", + this.spacingToFirstItem, + ") is greater than total length (", + this.totalLength, + ")" + ); + } + + // Check distance if empty + if (this.items.length === 0 && !epsilonCompare(this.spacingToFirstItem, this.totalLength, 0.01)) { + return fail( + currentChange, + "Path is empty but spacing to first item (", + this.spacingToFirstItem, + ") does not equal total length (", + this.totalLength, + ")" + ); + } + + // Check items etc + let currentPos = this.spacingToFirstItem; + for (let i = 0; i < this.items.length; ++i) { + const item = this.items[i]; + + if (item[0 /* nextDistance */] < 0 || item[0 /* nextDistance */] > this.totalLength + 0.02) { + return fail( + "Item has invalid offset to next item: ", + item[0 /* nextDistance */], + "(total length:", + this.totalLength, + ")" + ); + } + + currentPos += item[0 /* nextDistance */]; + } + + // Check the total sum matches + if (!epsilonCompare(currentPos, this.totalLength, 0.01)) { + return fail( + "total sum (", + currentPos, + ") of first item spacing (", + this.spacingToFirstItem, + ") and items does not match total length (", + this.totalLength, + ") -> items: " + this.items.map(i => i[0 /* nextDistance */]).join("|") + ); + } + + // Check bounds + const actualBounds = this.computeBounds(); + if (!actualBounds.equalsEpsilon(this.worldBounds, 0.01)) { + return fail("Bounds are stale"); + } + + // Check acceptor + const acceptor = this.computeAcceptingEntityAndSlot(true).acceptor; + if (!!acceptor !== !!this.boundAcceptor) { + return fail("Acceptor target mismatch, acceptor", !!acceptor, "vs stored", !!this.boundAcceptor); + } + + // Check first nonzero offset + let firstNonzero = 0; + for (let i = this.items.length - 2; i >= 0; --i) { + if (this.items[i][0 /* nextDistance */] < globalConfig.itemSpacingOnBelts + 1e-5) { + ++firstNonzero; + } else { + break; + } + } + + // Should warn, but this check isn't actually accurate + // if (firstNonzero !== this.numCompressedItemsAfterFirstItem) { + // console.warn( + // "First nonzero index is " + + // firstNonzero + + // " but stored is " + + // this.numCompressedItemsAfterFirstItem + // ); + // } + } + + /* dev:end */ + + /** + * Extends the belt path by the given belt + * @param {Entity} entity + */ + extendOnEnd(entity) { + DEBUG && logger.log("Extending belt path by entity at", entity.components.StaticMapEntity.origin); + + const beltComp = entity.components.Belt; + + // Append the entity + this.entityPath.push(entity); + this.onPathChanged(); + + // Extend the path length + const additionalLength = beltComp.getEffectiveLengthTiles(); + this.totalLength += additionalLength; + DEBUG && logger.log(" Extended total length by", additionalLength, "to", this.totalLength); + + // If we have no item, just update the distance to the first item + if (this.items.length === 0) { + this.spacingToFirstItem = this.totalLength; + DEBUG && logger.log(" Extended spacing to first to", this.totalLength, "(= total length)"); + } else { + // Otherwise, update the next-distance of the last item + const lastItem = this.items[this.items.length - 1]; + DEBUG && + logger.log( + " Extended spacing of last item from", + lastItem[0 /* nextDistance */], + "to", + lastItem[0 /* nextDistance */] + additionalLength + ); + lastItem[0 /* nextDistance */] += additionalLength; + } + + // Assign reference + beltComp.assignedPath = this; + + // Update bounds + this.worldBounds = this.computeBounds(); + + if (G_IS_DEV && globalConfig.debug.checkBeltPaths) { + this.debug_checkIntegrity("extend-on-end"); + } + } + + /** + * Extends the path with the given entity on the beginning + * @param {Entity} entity + */ + extendOnBeginning(entity) { + const beltComp = entity.components.Belt; + + DEBUG && logger.log("Extending the path on the beginning"); + + // All items on that belt are simply lost (for now) + + const length = beltComp.getEffectiveLengthTiles(); + + // Extend the length of this path + this.totalLength += length; + + // Simply adjust the first item spacing cuz we have no items contained + this.spacingToFirstItem += length; + + // Set handles and append entity + beltComp.assignedPath = this; + this.entityPath.unshift(entity); + this.onPathChanged(); + + // Update bounds + this.worldBounds = this.computeBounds(); + + if (G_IS_DEV && globalConfig.debug.checkBeltPaths) { + this.debug_checkIntegrity("extend-on-begin"); + } + } + + /** + * Returns if the given entity is the end entity of the path + * @param {Entity} entity + * @returns {boolean} + */ + isEndEntity(entity) { + return this.entityPath[this.entityPath.length - 1] === entity; + } + + /** + * Returns if the given entity is the start entity of the path + * @param {Entity} entity + * @returns {boolean} + */ + isStartEntity(entity) { + return this.entityPath[0] === entity; + } + + /** + * Splits this path at the given entity by removing it, and + * returning the new secondary paht + * @param {Entity} entity + * @returns {BeltPath} + */ + deleteEntityOnPathSplitIntoTwo(entity) { + DEBUG && logger.log("Splitting path at entity", entity.components.StaticMapEntity.origin); + + // First, find where the current path ends + const beltComp = entity.components.Belt; + beltComp.assignedPath = null; + + const entityLength = beltComp.getEffectiveLengthTiles(); + assert(this.entityPath.indexOf(entity) >= 0, "Entity not contained for split"); + assert(this.entityPath.indexOf(entity) !== 0, "Entity is first"); + assert(this.entityPath.indexOf(entity) !== this.entityPath.length - 1, "Entity is last"); + + let firstPathEntityCount = 0; + let firstPathLength = 0; + let firstPathEndEntity = null; + + for (let i = 0; i < this.entityPath.length; ++i) { + const otherEntity = this.entityPath[i]; + if (otherEntity === entity) { + DEBUG && logger.log("Found entity at", i, "of length", firstPathLength); + break; + } + + ++firstPathEntityCount; + firstPathEndEntity = otherEntity; + firstPathLength += otherEntity.components.Belt.getEffectiveLengthTiles(); + } + + DEBUG && + logger.log( + "First path ends at", + firstPathLength, + "and entity", + firstPathEndEntity.components.StaticMapEntity.origin, + "and has", + firstPathEntityCount, + "entities" + ); + + // Compute length of second path + const secondPathLength = this.totalLength - firstPathLength - entityLength; + const secondPathStart = firstPathLength + entityLength; + const secondEntities = this.entityPath.splice(firstPathEntityCount + 1); + DEBUG && + logger.log( + "Second path starts at", + secondPathStart, + "and has a length of ", + secondPathLength, + "with", + secondEntities.length, + "entities" + ); + + // Remove the last item + this.entityPath.pop(); + + DEBUG && logger.log("Splitting", this.items.length, "items"); + DEBUG && + logger.log( + "Old items are", + this.items.map(i => i[0 /* nextDistance */]) + ); + + // Create second path + const secondPath = new BeltPath(this.root, secondEntities); + + // Remove all items which are no longer relevant and transfer them to the second path + let itemPos = this.spacingToFirstItem; + for (let i = 0; i < this.items.length; ++i) { + const item = this.items[i]; + const distanceToNext = item[0 /* nextDistance */]; + + DEBUG && logger.log(" Checking item at", itemPos, "with distance of", distanceToNext, "to next"); + + // Check if this item is past the first path + if (itemPos >= firstPathLength) { + // Remove it from the first path + this.items.splice(i, 1); + i -= 1; + DEBUG && + logger.log(" Removed item from first path since its no longer contained @", itemPos); + + // Check if its on the second path (otherwise its on the removed belt and simply lost) + if (itemPos >= secondPathStart) { + // Put item on second path + secondPath.items.push([distanceToNext, item[1 /* item */]]); + DEBUG && + logger.log( + " Put item to second path @", + itemPos, + "with distance to next =", + distanceToNext + ); + + // If it was the first item, adjust the distance to the first item + if (secondPath.items.length === 1) { + DEBUG && logger.log(" Sinc it was the first, set sapcing of first to", itemPos); + secondPath.spacingToFirstItem = itemPos - secondPathStart; + } + } else { + DEBUG && logger.log(" Item was on the removed belt, so its gone - forever!"); + } + } else { + // Seems this item is on the first path (so all good), so just make sure it doesn't + // have a nextDistance which is bigger than the total path length + const clampedDistanceToNext = Math.min(itemPos + distanceToNext, firstPathLength) - itemPos; + if (clampedDistanceToNext < distanceToNext) { + DEBUG && + logger.log( + "Correcting next distance (first path) from", + distanceToNext, + "to", + clampedDistanceToNext + ); + item[0 /* nextDistance */] = clampedDistanceToNext; + } + } + + // Advance items + itemPos += distanceToNext; + } + + DEBUG && + logger.log( + "New items are", + this.items.map(i => i[0 /* nextDistance */]) + ); + + DEBUG && + logger.log( + "And second path items are", + secondPath.items.map(i => i[0 /* nextDistance */]) + ); + + // Adjust our total length + this.totalLength = firstPathLength; + + // Make sure that if we are empty, we set our first distance properly + if (this.items.length === 0) { + this.spacingToFirstItem = this.totalLength; + } + + this.onPathChanged(); + secondPath.onPathChanged(); + + // Update bounds + this.worldBounds = this.computeBounds(); + + if (G_IS_DEV && globalConfig.debug.checkBeltPaths) { + this.debug_checkIntegrity("split-two-first"); + secondPath.debug_checkIntegrity("split-two-second"); + } + + return secondPath; + } + + /** + * Deletes the last entity + * @param {Entity} entity + */ + deleteEntityOnEnd(entity) { + assert( + this.entityPath[this.entityPath.length - 1] === entity, + "Not actually the last entity (instead " + this.entityPath.indexOf(entity) + ")" + ); + + // Ok, first remove the entity + const beltComp = entity.components.Belt; + const beltLength = beltComp.getEffectiveLengthTiles(); + + DEBUG && + logger.log( + "Deleting last entity on path with length", + this.entityPath.length, + "(reducing", + this.totalLength, + " by", + beltLength, + ")" + ); + this.totalLength -= beltLength; + this.entityPath.pop(); + this.onPathChanged(); + + DEBUG && + logger.log( + " New path has length of", + this.totalLength, + "with", + this.entityPath.length, + "entities" + ); + + // This is just for sanity + beltComp.assignedPath = null; + + // Clean up items + if (this.items.length === 0) { + // Simple case with no items, just update the first item spacing + this.spacingToFirstItem = this.totalLength; + } else { + // Ok, make sure we simply drop all items which are no longer contained + let itemOffset = this.spacingToFirstItem; + let lastItemOffset = itemOffset; + + DEBUG && logger.log(" Adjusting", this.items.length, "items"); + + for (let i = 0; i < this.items.length; ++i) { + const item = this.items[i]; + + // Get rid of items past this path + if (itemOffset >= this.totalLength) { + DEBUG && logger.log("Dropping item (current index=", i, ")"); + this.items.splice(i, 1); + i -= 1; + continue; + } + + DEBUG && + logger.log( + "Item", + i, + "is at", + itemOffset, + "with next offset", + item[0 /* nextDistance */] + ); + lastItemOffset = itemOffset; + itemOffset += item[0 /* nextDistance */]; + } + + // If we still have an item, make sure the last item matches + if (this.items.length > 0) { + // We can easily compute the next distance since we know where the last item is now + const lastDistance = this.totalLength - lastItemOffset; + assert( + lastDistance >= 0.0, + "Last item distance mismatch: " + + lastDistance + + " -> Total length was " + + this.totalLength + + " and lastItemOffset was " + + lastItemOffset + ); + + DEBUG && + logger.log( + "Adjusted distance of last item: it is at", + lastItemOffset, + "so it has a distance of", + lastDistance, + "to the end (", + this.totalLength, + ")" + ); + this.items[this.items.length - 1][0 /* nextDistance */] = lastDistance; + } else { + DEBUG && logger.log(" Removed all items so we'll update spacing to total length"); + + // We removed all items so update our spacing + this.spacingToFirstItem = this.totalLength; + } + } + + // Update bounds + this.worldBounds = this.computeBounds(); + + if (G_IS_DEV && globalConfig.debug.checkBeltPaths) { + this.debug_checkIntegrity("delete-on-end"); + } + } + + /** + * Deletes the entity of the start of the path + * @see deleteEntityOnEnd + * @param {Entity} entity + */ + deleteEntityOnStart(entity) { + assert( + entity === this.entityPath[0], + "Not actually the start entity (instead " + this.entityPath.indexOf(entity) + ")" + ); + + // Ok, first remove the entity + const beltComp = entity.components.Belt; + const beltLength = beltComp.getEffectiveLengthTiles(); + + DEBUG && + logger.log( + "Deleting first entity on path with length", + this.entityPath.length, + "(reducing", + this.totalLength, + " by", + beltLength, + ")" + ); + this.totalLength -= beltLength; + this.entityPath.shift(); + this.onPathChanged(); + + DEBUG && + logger.log( + " New path has length of", + this.totalLength, + "with", + this.entityPath.length, + "entities" + ); + + // This is just for sanity + beltComp.assignedPath = null; + + // Clean up items + if (this.items.length === 0) { + // Simple case with no items, just update the first item spacing + this.spacingToFirstItem = this.totalLength; + } else { + // Simple case, we had no item on the beginning -> all good + if (this.spacingToFirstItem >= beltLength) { + DEBUG && + logger.log( + " No item on the first place, so we can just adjust the spacing (spacing=", + this.spacingToFirstItem, + ") removed =", + beltLength + ); + this.spacingToFirstItem -= beltLength; + } else { + // Welp, okay we need to drop all items which are < beltLength and adjust + // the other item offsets as well + + DEBUG && + logger.log( + " We have at least one item in the beginning, drop those and adjust spacing (first item @", + this.spacingToFirstItem, + ") since we removed", + beltLength, + "length from path" + ); + DEBUG && + logger.log( + " Items:", + this.items.map(i => i[0 /* nextDistance */]) + ); + + // Find offset to first item + let itemOffset = this.spacingToFirstItem; + for (let i = 0; i < this.items.length; ++i) { + const item = this.items[i]; + if (itemOffset <= beltLength) { + DEBUG && + logger.log( + " -> Dropping item with index", + i, + "at", + itemOffset, + "since it was on the removed belt" + ); + // This item must be dropped + this.items.splice(i, 1); + i -= 1; + itemOffset += item[0 /* nextDistance */]; + continue; + } else { + // This item can be kept, thus its the first we know + break; + } + } + + if (this.items.length > 0) { + DEBUG && + logger.log( + " Offset of first non-dropped item was at:", + itemOffset, + "-> setting spacing to it (total length=", + this.totalLength, + ")" + ); + + this.spacingToFirstItem = itemOffset - beltLength; + assert( + this.spacingToFirstItem >= 0.0, + "Invalid spacing after delete on start: " + this.spacingToFirstItem + ); + } else { + DEBUG && logger.log(" We dropped all items, simply set spacing to total length"); + // We dropped all items, simple one + this.spacingToFirstItem = this.totalLength; + } + } + } + + // Update bounds + this.worldBounds = this.computeBounds(); + + if (G_IS_DEV && globalConfig.debug.checkBeltPaths) { + this.debug_checkIntegrity("delete-on-start"); + } + } + + /** + * Extends the path by the given other path + * @param {BeltPath} otherPath + */ + extendByPath(otherPath) { + assert(otherPath !== this, "Circular path dependency"); + + const entities = otherPath.entityPath; + DEBUG && logger.log("Extending path by other path, starting to add entities"); + + const oldLength = this.totalLength; + + DEBUG && logger.log(" Adding", entities.length, "new entities, current length =", this.totalLength); + + // First, append entities + for (let i = 0; i < entities.length; ++i) { + const entity = entities[i]; + const beltComp = entity.components.Belt; + + // Add to path and update references + this.entityPath.push(entity); + beltComp.assignedPath = this; + + // Update our length + const additionalLength = beltComp.getEffectiveLengthTiles(); + this.totalLength += additionalLength; + } + + DEBUG && + logger.log( + " Path is now", + this.entityPath.length, + "entities and has a length of", + this.totalLength + ); + + // Now, update the distance of our last item + if (this.items.length !== 0) { + const lastItem = this.items[this.items.length - 1]; + lastItem[0 /* nextDistance */] += otherPath.spacingToFirstItem; + DEBUG && + logger.log( + " Add distance to last item, effectively being", + lastItem[0 /* nextDistance */], + "now" + ); + } else { + // Seems we have no items, update our first item distance + this.spacingToFirstItem = oldLength + otherPath.spacingToFirstItem; + DEBUG && + logger.log( + " We had no items, so our new spacing to first is old length (", + oldLength, + ") plus others spacing to first (", + otherPath.spacingToFirstItem, + ") =", + this.spacingToFirstItem + ); + } + + DEBUG && logger.log(" Pushing", otherPath.items.length, "items from other path"); + + // Aaand push the other paths items + for (let i = 0; i < otherPath.items.length; ++i) { + const item = otherPath.items[i]; + this.items.push([item[0 /* nextDistance */], item[1 /* item */]]); + } + + // Update bounds + this.worldBounds = this.computeBounds(); + + this.onPathChanged(); + + if (G_IS_DEV && globalConfig.debug.checkBeltPaths) { + this.debug_checkIntegrity("extend-by-path"); + } + } + + /** + * Computes the total length of the path + * @returns {number} + */ + computeTotalLength() { + let length = 0; + for (let i = 0; i < this.entityPath.length; ++i) { + const entity = this.entityPath[i]; + length += entity.components.Belt.getEffectiveLengthTiles(); + } + return length; + } + + /** + * Performs one tick + */ + update() { + if (G_IS_DEV && globalConfig.debug.checkBeltPaths) { + this.debug_checkIntegrity("pre-update"); + } + + // Skip empty belts + if (this.items.length === 0) { + return; + } + + // Divide by item spacing on belts since we use throughput and not speed + let beltSpeed = + this.root.hubGoals.getBeltBaseSpeed() * + this.root.dynamicTickrate.deltaSeconds * + globalConfig.itemSpacingOnBelts; + + if (G_IS_DEV && globalConfig.debug.instantBelts) { + beltSpeed *= 100; + } + + // Store whether this is the first item we processed, so premature + // item ejection is available + let isFirstItemProcessed = true; + + // Store how much velocity (strictly its distance, not velocity) we have to distribute over all items + let remainingVelocity = beltSpeed; + + // Store the last item we processed, so we can skip clashed ones + let lastItemProcessed; + + for (lastItemProcessed = this.items.length - 1; lastItemProcessed >= 0; --lastItemProcessed) { + const nextDistanceAndItem = this.items[lastItemProcessed]; + + // Compute how much spacing we need at least + const minimumSpacing = + lastItemProcessed === this.items.length - 1 ? 0 : globalConfig.itemSpacingOnBelts; + + // Compute how much we can advance + let clampedProgress = nextDistanceAndItem[0 /* nextDistance */] - minimumSpacing; + + // Make sure we don't advance more than the remaining velocity has stored + if (remainingVelocity < clampedProgress) { + clampedProgress = remainingVelocity; + } + + // Make sure we don't advance back + if (clampedProgress < 0) { + clampedProgress = 0; + } + + // Reduce our velocity by the amount we consumed + remainingVelocity -= clampedProgress; + + // Reduce the spacing + nextDistanceAndItem[0 /* nextDistance */] -= clampedProgress; + + // Advance all items behind by the progress we made + this.spacingToFirstItem += clampedProgress; + + // If the last item can be ejected, eject it and reduce the spacing, because otherwise + // we lose velocity + if (isFirstItemProcessed && nextDistanceAndItem[0 /* nextDistance */] < 1e-7) { + // Store how much velocity we "lost" because we bumped the item to the end of the + // belt but couldn't move it any farther. We need this to tell the item acceptor + // animation to start a tad later, so everything matches up. Yes I'm a perfectionist. + const excessVelocity = beltSpeed - clampedProgress; + + // Try to directly get rid of the item + if ( + this.boundAcceptor && + this.boundAcceptor(nextDistanceAndItem[1 /* item */], excessVelocity) + ) { + this.items.pop(); + + const itemBehind = this.items[lastItemProcessed - 1]; + if (itemBehind && this.numCompressedItemsAfterFirstItem > 0) { + // So, with the next tick we will skip this item, but it actually has the potential + // to process farther -> If we don't advance here, we loose a tiny bit of progress + // every tick which causes the belt to be slower than it actually is. + // Also see #999 + const fixupProgress = Math.max( + 0, + Math.min(remainingVelocity, itemBehind[0 /* nextDistance */]) + ); + + // See above + itemBehind[0 /* nextDistance */] -= fixupProgress; + remainingVelocity -= fixupProgress; + this.spacingToFirstItem += fixupProgress; + } + + // Reduce the number of compressed items since the first item no longer exists + this.numCompressedItemsAfterFirstItem = Math.max( + 0, + this.numCompressedItemsAfterFirstItem - 1 + ); + } + } + + if (isFirstItemProcessed) { + // Skip N null items after first items + lastItemProcessed -= this.numCompressedItemsAfterFirstItem; + } + + isFirstItemProcessed = false; + if (remainingVelocity < 1e-7) { + break; + } + } + + // Compute compressed item count + this.numCompressedItemsAfterFirstItem = Math.max( + 0, + this.numCompressedItemsAfterFirstItem, + this.items.length - 2 - lastItemProcessed + ); + + // Check if we have an item which is ready to be emitted + const lastItem = this.items[this.items.length - 1]; + if (lastItem && lastItem[0 /* nextDistance */] === 0) { + if (this.boundAcceptor && this.boundAcceptor(lastItem[1 /* item */])) { + this.items.pop(); + this.numCompressedItemsAfterFirstItem = Math.max( + 0, + this.numCompressedItemsAfterFirstItem - 1 + ); + } + } + + if (G_IS_DEV && globalConfig.debug.checkBeltPaths) { + this.debug_checkIntegrity("post-update"); + } + } + + /** + * Computes a world space position from the given progress + * @param {number} progress + * @returns {Vector} + */ + computePositionFromProgress(progress) { + let currentLength = 0; + + // floating point issues .. + assert(progress <= this.totalLength + 0.02, "Progress too big: " + progress); + + for (let i = 0; i < this.entityPath.length; ++i) { + const beltComp = this.entityPath[i].components.Belt; + const localLength = beltComp.getEffectiveLengthTiles(); + + if (currentLength + localLength >= progress || i === this.entityPath.length - 1) { + // Min required here due to floating point issues + const localProgress = Math.min(1.0, progress - currentLength); + + assert(localProgress >= 0.0, "Invalid local progress: " + localProgress); + const localSpace = beltComp.transformBeltToLocalSpace(localProgress); + return this.entityPath[i].components.StaticMapEntity.localTileToWorld(localSpace); + } + currentLength += localLength; + } + + assert(false, "invalid progress: " + progress + " (max: " + this.totalLength + ")"); + } + + /** + * + * @param {DrawParameters} parameters + */ + drawDebug(parameters) { + if (!parameters.visibleRect.containsRect(this.worldBounds)) { + return; + } + + parameters.context.fillStyle = "#d79a25"; + parameters.context.strokeStyle = "#d79a25"; + parameters.context.beginPath(); + + for (let i = 0; i < this.entityPath.length; ++i) { + const entity = this.entityPath[i]; + const pos = entity.components.StaticMapEntity; + const worldPos = pos.origin.toWorldSpaceCenterOfTile(); + + if (i === 0) { + parameters.context.moveTo(worldPos.x, worldPos.y); + } else { + parameters.context.lineTo(worldPos.x, worldPos.y); + } + } + parameters.context.stroke(); + + // Items + let progress = this.spacingToFirstItem; + for (let i = 0; i < this.items.length; ++i) { + const nextDistanceAndItem = this.items[i]; + const worldPos = this.computePositionFromProgress(progress).toWorldSpaceCenterOfTile(); + parameters.context.fillStyle = "#268e4d"; + parameters.context.beginPath(); + parameters.context.roundRect(worldPos.x - 5, worldPos.y - 5, 10, 10, 3); + parameters.context.fill(); + parameters.context.font = "6px GameFont"; + parameters.context.fillStyle = "#111"; + parameters.context.fillText( + "" + round4Digits(nextDistanceAndItem[0 /* nextDistance */]), + worldPos.x + 5, + worldPos.y + 2 + ); + progress += nextDistanceAndItem[0 /* nextDistance */]; + + if (this.items.length - 1 - this.numCompressedItemsAfterFirstItem === i) { + parameters.context.fillStyle = "red"; + parameters.context.fillRect(worldPos.x + 5, worldPos.y, 20, 3); + } + } + + for (let i = 0; i < this.entityPath.length; ++i) { + const entity = this.entityPath[i]; + parameters.context.fillStyle = "#d79a25"; + const pos = entity.components.StaticMapEntity; + const worldPos = pos.origin.toWorldSpaceCenterOfTile(); + parameters.context.beginCircle(worldPos.x, worldPos.y, i === 0 ? 5 : 3); + parameters.context.fill(); + } + + for (let progress = 0; progress <= this.totalLength + 0.01; progress += 0.2) { + const worldPos = this.computePositionFromProgress(progress).toWorldSpaceCenterOfTile(); + parameters.context.fillStyle = "red"; + parameters.context.beginCircle(worldPos.x, worldPos.y, 1); + parameters.context.fill(); + } + + const firstItemIndicator = this.computePositionFromProgress( + this.spacingToFirstItem + ).toWorldSpaceCenterOfTile(); + parameters.context.fillStyle = "purple"; + parameters.context.fillRect(firstItemIndicator.x - 3, firstItemIndicator.y - 1, 6, 2); + } + + /** + * Checks if this belt path should render simplified + */ + checkIsPotatoMode() { + // POTATO Mode: Only show items when belt is hovered + if (!this.root.app.settings.getAllSettings().simplifiedBelts) { + return false; + } + + if (this.root.currentLayer !== "regular") { + // Not in regular layer + return true; + } + + const mousePos = this.root.app.mousePosition; + if (!mousePos) { + // Mouse not registered + return true; + } + + const tile = this.root.camera.screenToWorld(mousePos).toTileSpace(); + const contents = this.root.map.getLayerContentXY(tile.x, tile.y, "regular"); + if (!contents || !contents.components.Belt) { + // Nothing below + return true; + } + + if (contents.components.Belt.assignedPath !== this) { + // Not this path + return true; + } + return false; + } + + /** + * Draws the path + * @param {DrawParameters} parameters + */ + draw(parameters) { + if (!parameters.visibleRect.containsRect(this.worldBounds)) { + return; + } + + if (this.items.length === 0) { + // Early out + return; + } + + if (this.checkIsPotatoMode()) { + const firstItem = this.items[0]; + if (this.entityPath.length > 1 && firstItem) { + const medianBeltIndex = clamp( + Math.round(this.entityPath.length / 2 - 1), + 0, + this.entityPath.length - 1 + ); + const medianBelt = this.entityPath[medianBeltIndex]; + const beltComp = medianBelt.components.Belt; + const staticComp = medianBelt.components.StaticMapEntity; + const centerPosLocal = beltComp.transformBeltToLocalSpace( + this.entityPath.length % 2 === 0 ? beltComp.getEffectiveLengthTiles() : 0.5 + ); + const centerPos = staticComp.localTileToWorld(centerPosLocal).toWorldSpaceCenterOfTile(); + + parameters.context.globalAlpha = 0.5; + firstItem[1 /* item */].drawItemCenteredClipped(centerPos.x, centerPos.y, parameters); + parameters.context.globalAlpha = 1; + } + + return; + } + + let currentItemPos = this.spacingToFirstItem; + let currentItemIndex = 0; + + let trackPos = 0.0; + + /** + * @type {Array<[Vector, BaseItem]>} + */ + let drawStack = []; + let drawStackProp = ""; + + // Iterate whole track and check items + for (let i = 0; i < this.entityPath.length; ++i) { + const entity = this.entityPath[i]; + const beltComp = entity.components.Belt; + const beltLength = beltComp.getEffectiveLengthTiles(); + + // Check if the current items are on the belt + while (trackPos + beltLength >= currentItemPos - 1e-5) { + // It's on the belt, render it now + const staticComp = entity.components.StaticMapEntity; + assert( + currentItemPos - trackPos >= 0, + "invalid track pos: " + currentItemPos + " vs " + trackPos + " (l =" + beltLength + ")" + ); + + const localPos = beltComp.transformBeltToLocalSpace(currentItemPos - trackPos); + const worldPos = staticComp.localTileToWorld(localPos).toWorldSpaceCenterOfTile(); + + const distanceAndItem = this.items[currentItemIndex]; + const item = distanceAndItem[1 /* item */]; + const nextItemDistance = distanceAndItem[0 /* nextDistance */]; + + if ( + !parameters.visibleRect.containsCircle( + worldPos.x, + worldPos.y, + globalConfig.defaultItemDiameter + ) + ) { + // this one isn't visible, do not append it + // Start a new stack + this.drawDrawStack(drawStack, parameters, drawStackProp); + drawStack = []; + drawStackProp = ""; + } else { + if (drawStack.length > 1) { + // Check if we can append to the stack, since its already a stack of two same items + const referenceItem = drawStack[0]; + + if ( + referenceItem[1].equals(item) && + Math.abs(referenceItem[0][drawStackProp] - worldPos[drawStackProp]) < 0.001 + ) { + // Will continue stack + } else { + // Start a new stack, since item doesn't follow in row + this.drawDrawStack(drawStack, parameters, drawStackProp); + drawStack = []; + drawStackProp = ""; + } + } else if (drawStack.length === 1) { + const firstItem = drawStack[0]; + + // Check if we can make it a stack + if (firstItem[1 /* item */].equals(item)) { + // Same item, check if it is either horizontal or vertical + const startPos = firstItem[0 /* pos */]; + + if (Math.abs(startPos.x - worldPos.x) < 0.001) { + drawStackProp = "x"; + } else if (Math.abs(startPos.y - worldPos.y) < 0.001) { + drawStackProp = "y"; + } else { + // Start a new stack + this.drawDrawStack(drawStack, parameters, drawStackProp); + drawStack = []; + drawStackProp = ""; + } + } else { + // Start a new stack, since item doesn't equal + this.drawDrawStack(drawStack, parameters, drawStackProp); + drawStack = []; + drawStackProp = ""; + } + } else { + // First item of stack, do nothing + } + + drawStack.push([worldPos, item]); + } + + // Check for the next item + currentItemPos += nextItemDistance; + ++currentItemIndex; + + if ( + nextItemDistance > globalConfig.itemSpacingOnBelts + 0.001 || + drawStack.length > globalConfig.maxBeltShapeBundleSize + ) { + // If next item is not directly following, abort drawing + this.drawDrawStack(drawStack, parameters, drawStackProp); + drawStack = []; + drawStackProp = ""; + } + + if (currentItemIndex >= this.items.length) { + // We rendered all items + + this.drawDrawStack(drawStack, parameters, drawStackProp); + return; + } + } + + trackPos += beltLength; + } + + this.drawDrawStack(drawStack, parameters, drawStackProp); + } + + /** + * + * @param {HTMLCanvasElement} canvas + * @param {CanvasRenderingContext2D} context + * @param {number} w + * @param {number} h + * @param {number} dpi + * @param {object} param0 + * @param {string} param0.direction + * @param {Array<[Vector, BaseItem]>} param0.stack + * @param {GameRoot} param0.root + * @param {number} param0.zoomLevel + */ + drawShapesInARow(canvas, context, w, h, dpi, { direction, stack, root, zoomLevel }) { + context.scale(dpi, dpi); + + if (G_IS_DEV && globalConfig.debug.showShapeGrouping) { + context.fillStyle = "rgba(0, 0, 255, 0.5)"; + context.fillRect(0, 0, w, h); + } + + const parameters = new DrawParameters({ + context, + desiredAtlasScale: ORIGINAL_SPRITE_SCALE, + root, + visibleRect: new Rectangle(-1000, -1000, 2000, 2000), + zoomLevel, + }); + + const itemSize = globalConfig.itemSpacingOnBelts * globalConfig.tileSize; + const item = stack[0]; + const pos = new Vector(itemSize / 2, itemSize / 2); + + for (let i = 0; i < stack.length; i++) { + item[1].drawItemCenteredClipped(pos.x, pos.y, parameters, globalConfig.defaultItemDiameter); + pos[direction] += globalConfig.itemSpacingOnBelts * globalConfig.tileSize; + } + } + + /** + * @param {Array<[Vector, BaseItem]>} stack + * @param {DrawParameters} parameters + */ + drawDrawStack(stack, parameters, directionProp) { + if (stack.length === 0) { + return; + } + + const firstItem = stack[0]; + const firstItemPos = firstItem[0]; + if (stack.length === 1) { + firstItem[1].drawItemCenteredClipped( + firstItemPos.x, + firstItemPos.y, + parameters, + globalConfig.defaultItemDiameter + ); + return; + } + + const itemSize = globalConfig.itemSpacingOnBelts * globalConfig.tileSize; + const inverseDirection = directionProp === "x" ? "y" : "x"; + + const dimensions = new Vector(itemSize, itemSize); + dimensions[inverseDirection] *= stack.length; + + const directionVector = firstItemPos.copy().sub(stack[1][0]); + + const dpi = smoothenDpi(globalConfig.shapesSharpness * parameters.zoomLevel); + + const sprite = this.root.buffers.getForKey({ + key: "beltpaths", + subKey: + "stack-" + + directionProp + + "-" + + dpi + + "#" + + stack.length + + "#" + + firstItem[1].getItemType() + + "#" + + firstItem[1].serialize(), + dpi, + w: dimensions.x, + h: dimensions.y, + redrawMethod: this.drawShapesInARow.bind(this), + additionalParams: { + direction: inverseDirection, + stack, + root: this.root, + zoomLevel: parameters.zoomLevel, + }, + }); + + const anchor = directionVector[inverseDirection] < 0 ? firstItem : stack[stack.length - 1]; + + parameters.context.drawImage( + sprite, + anchor[0].x - itemSize / 2, + anchor[0].y - itemSize / 2, + dimensions.x, + dimensions.y + ); + } +} diff --git a/src/js/game/blueprint.js b/src/js/game/blueprint.js index 14848485..34e4ed23 100644 --- a/src/js/game/blueprint.js +++ b/src/js/game/blueprint.js @@ -3,7 +3,6 @@ import { DrawParameters } from "../core/draw_parameters"; import { findNiceIntegerValue } from "../core/utils"; import { Vector } from "../core/vector"; import { Entity } from "./entity"; -import { ACHIEVEMENTS } from "../platform/achievement_provider"; import { GameRoot } from "./root"; export class Blueprint { @@ -88,7 +87,13 @@ export class Blueprint { parameters.context.globalAlpha = 1; } - staticComp.drawSpriteOnBoundsClipped(parameters, staticComp.getBlueprintSprite(), 0, newPos); + staticComp.drawSpriteOnBoundsClipped( + parameters, + staticComp.getBlueprintSprite(), + 0, + newPos, + true + ); } parameters.context.globalAlpha = 1; } @@ -172,13 +177,6 @@ export class Blueprint { count++; } - root.signals.bulkAchievementCheck.dispatch( - ACHIEVEMENTS.placeBlueprint, - count, - ACHIEVEMENTS.placeBp1000, - count - ); - return count !== 0; }); }); diff --git a/src/js/game/building_codes.js b/src/js/game/building_codes.js index 1d178daf..7dd853ba 100644 --- a/src/js/game/building_codes.js +++ b/src/js/game/building_codes.js @@ -1,109 +1,109 @@ -/* typehints:start */ -import { MetaBuilding } from "./meta_building"; -import { AtlasSprite } from "../core/sprites"; -import { Vector } from "../core/vector"; -/* typehints:end */ - -import { gMetaBuildingRegistry } from "../core/global_registries"; - -/** - * @typedef {{ - * metaClass: typeof MetaBuilding, - * metaInstance?: MetaBuilding, - * variant?: string, - * rotationVariant?: number, - * tileSize?: Vector, - * sprite?: AtlasSprite, - * blueprintSprite?: AtlasSprite, - * silhouetteColor?: string - * }} BuildingVariantIdentifier - */ - -/** - * Stores a lookup table for all building variants (for better performance) - * @type {Object} - */ -export const gBuildingVariants = { - // Set later -}; - -/** - * Mapping from 'metaBuildingId/variant/rotationVariant' to building code - * @type {Map} - */ -const variantsCache = new Map(); - -/** - * Registers a new variant - * @param {number|string} code - * @param {typeof MetaBuilding} meta - * @param {string} variant - * @param {number} rotationVariant - */ -export function registerBuildingVariant( - code, - meta, - variant = "default" /* @TODO: Circular dependency, actually its defaultBuildingVariant */, - rotationVariant = 0 -) { - assert(!gBuildingVariants[code], "Duplicate id: " + code); - gBuildingVariants[code] = { - metaClass: meta, - metaInstance: gMetaBuildingRegistry.findByClass(meta), - variant, - rotationVariant, - // @ts-ignore - tileSize: new meta().getDimensions(variant), - }; -} - -/** - * Hashes the combination of buildng, variant and rotation variant - * @param {string} buildingId - * @param {string} variant - * @param {number} rotationVariant - * @returns - */ -function generateBuildingHash(buildingId, variant, rotationVariant) { - return buildingId + "/" + variant + "/" + rotationVariant; -} - -/** - * - * @param {string|number} code - * @returns {BuildingVariantIdentifier} - */ -export function getBuildingDataFromCode(code) { - assert(gBuildingVariants[code], "Invalid building code: " + code); - return gBuildingVariants[code]; -} - -/** - * Builds the cache for the codes - */ -export function buildBuildingCodeCache() { - for (const code in gBuildingVariants) { - const data = gBuildingVariants[code]; - const hash = generateBuildingHash(data.metaInstance.getId(), data.variant, data.rotationVariant); - variantsCache.set(hash, isNaN(+code) ? code : +code); - } -} - -/** - * Finds the code for a given variant - * @param {MetaBuilding} metaBuilding - * @param {string} variant - * @param {number} rotationVariant - * @returns {number|string} - */ -export function getCodeFromBuildingData(metaBuilding, variant, rotationVariant) { - const hash = generateBuildingHash(metaBuilding.getId(), variant, rotationVariant); - const result = variantsCache.get(hash); - if (G_IS_DEV) { - if (!result) { - console.warn("Known hashes:", Array.from(variantsCache.keys())); - assertAlways(false, "Building not found by data: " + hash); - } - } - return result; -} +/* typehints:start */ +import { MetaBuilding } from "./meta_building"; +import { AtlasSprite } from "../core/sprites"; +import { Vector } from "../core/vector"; +/* typehints:end */ + +import { gMetaBuildingRegistry } from "../core/global_registries"; + +/** + * @typedef {{ + * metaClass: typeof MetaBuilding, + * metaInstance?: MetaBuilding, + * variant?: string, + * rotationVariant?: number, + * tileSize?: Vector, + * sprite?: AtlasSprite, + * blueprintSprite?: AtlasSprite, + * silhouetteColor?: string + * }} BuildingVariantIdentifier + */ + +/** + * Stores a lookup table for all building variants (for better performance) + * @type {Object} + */ +export const gBuildingVariants = { + // Set later +}; + +/** + * Mapping from 'metaBuildingId/variant/rotationVariant' to building code + * @type {Map} + */ +const variantsCache = new Map(); + +/** + * Registers a new variant + * @param {number|string} code + * @param {typeof MetaBuilding} meta + * @param {string} variant + * @param {number} rotationVariant + */ +export function registerBuildingVariant( + code, + meta, + variant = "default" /* @TODO: Circular dependency, actually its defaultBuildingVariant */, + rotationVariant = 0 +) { + assert(!gBuildingVariants[code], "Duplicate id: " + code); + gBuildingVariants[code] = { + metaClass: meta, + metaInstance: gMetaBuildingRegistry.findByClass(meta), + variant, + rotationVariant, + // @ts-ignore + tileSize: new meta().getDimensions(variant), + }; +} + +/** + * Hashes the combination of buildng, variant and rotation variant + * @param {string} buildingId + * @param {string} variant + * @param {number} rotationVariant + * @returns + */ +function generateBuildingHash(buildingId, variant, rotationVariant) { + return buildingId + "/" + variant + "/" + rotationVariant; +} + +/** + * + * @param {string|number} code + * @returns {BuildingVariantIdentifier} + */ +export function getBuildingDataFromCode(code) { + assert(gBuildingVariants[code], "Invalid building code: " + code); + return gBuildingVariants[code]; +} + +/** + * Builds the cache for the codes + */ +export function buildBuildingCodeCache() { + for (const code in gBuildingVariants) { + const data = gBuildingVariants[code]; + const hash = generateBuildingHash(data.metaInstance.getId(), data.variant, data.rotationVariant); + variantsCache.set(hash, isNaN(+code) ? code : +code); + } +} + +/** + * Finds the code for a given variant + * @param {MetaBuilding} metaBuilding + * @param {string} variant + * @param {number} rotationVariant + * @returns {number|string} + */ +export function getCodeFromBuildingData(metaBuilding, variant, rotationVariant) { + const hash = generateBuildingHash(metaBuilding.getId(), variant, rotationVariant); + const result = variantsCache.get(hash); + if (G_IS_DEV) { + if (!result) { + console.warn("Known hashes:", Array.from(variantsCache.keys())); + assertAlways(false, "Building not found by data: " + hash); + } + } + return result; +} diff --git a/src/js/game/buildings/analyzer.js b/src/js/game/buildings/analyzer.js index 13597d7b..68cfbfa2 100644 --- a/src/js/game/buildings/analyzer.js +++ b/src/js/game/buildings/analyzer.js @@ -1,88 +1,88 @@ -import { generateMatrixRotations } from "../../core/utils"; -import { enumDirection, Vector } from "../../core/vector"; -import { enumLogicGateType, LogicGateComponent } from "../components/logic_gate"; -import { enumPinSlotType, WiredPinsComponent } from "../components/wired_pins"; -import { Entity } from "../entity"; -import { defaultBuildingVariant, MetaBuilding } from "../meta_building"; -import { GameRoot } from "../root"; -import { enumHubGoalRewards } from "../tutorial_goals"; - -const overlayMatrix = generateMatrixRotations([1, 1, 0, 1, 1, 1, 0, 1, 0]); - -export class MetaAnalyzerBuilding extends MetaBuilding { - constructor() { - super("analyzer"); - } - - static getAllVariantCombinations() { - return [ - { - internalId: 43, - variant: defaultBuildingVariant, - }, - ]; - } - - getSilhouetteColor() { - return "#3a52bc"; - } - - /** - * @param {GameRoot} root - */ - getIsUnlocked(root) { - return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_virtual_processing); - } - - /** @returns {"wires"} **/ - getLayer() { - return "wires"; - } - - getDimensions() { - return new Vector(1, 1); - } - - getRenderPins() { - // We already have it included - return false; - } - - getSpecialOverlayRenderMatrix(rotation, rotationVariant, variant) { - return overlayMatrix[rotation]; - } - - /** - * Creates the entity at the given location - * @param {Entity} entity - */ - setupEntityComponents(entity) { - entity.addComponent( - new WiredPinsComponent({ - slots: [ - { - pos: new Vector(0, 0), - direction: enumDirection.left, - type: enumPinSlotType.logicalEjector, - }, - { - pos: new Vector(0, 0), - direction: enumDirection.right, - type: enumPinSlotType.logicalEjector, - }, - { - pos: new Vector(0, 0), - direction: enumDirection.bottom, - type: enumPinSlotType.logicalAcceptor, - }, - ], - }) - ); - - entity.addComponent( - new LogicGateComponent({ - type: enumLogicGateType.analyzer, - }) - ); - } -} +import { generateMatrixRotations } from "../../core/utils"; +import { enumDirection, Vector } from "../../core/vector"; +import { enumLogicGateType, LogicGateComponent } from "../components/logic_gate"; +import { enumPinSlotType, WiredPinsComponent } from "../components/wired_pins"; +import { Entity } from "../entity"; +import { defaultBuildingVariant, MetaBuilding } from "../meta_building"; +import { GameRoot } from "../root"; +import { enumHubGoalRewards } from "../tutorial_goals"; + +const overlayMatrix = generateMatrixRotations([1, 1, 0, 1, 1, 1, 0, 1, 0]); + +export class MetaAnalyzerBuilding extends MetaBuilding { + constructor() { + super("analyzer"); + } + + static getAllVariantCombinations() { + return [ + { + internalId: 43, + variant: defaultBuildingVariant, + }, + ]; + } + + getSilhouetteColor() { + return "#3a52bc"; + } + + /** + * @param {GameRoot} root + */ + getIsUnlocked(root) { + return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_virtual_processing); + } + + /** @returns {"wires"} **/ + getLayer() { + return "wires"; + } + + getDimensions() { + return new Vector(1, 1); + } + + getRenderPins() { + // We already have it included + return false; + } + + getSpecialOverlayRenderMatrix(rotation, rotationVariant, variant) { + return overlayMatrix[rotation]; + } + + /** + * Creates the entity at the given location + * @param {Entity} entity + */ + setupEntityComponents(entity) { + entity.addComponent( + new WiredPinsComponent({ + slots: [ + { + pos: new Vector(0, 0), + direction: enumDirection.left, + type: enumPinSlotType.logicalEjector, + }, + { + pos: new Vector(0, 0), + direction: enumDirection.right, + type: enumPinSlotType.logicalEjector, + }, + { + pos: new Vector(0, 0), + direction: enumDirection.bottom, + type: enumPinSlotType.logicalAcceptor, + }, + ], + }) + ); + + entity.addComponent( + new LogicGateComponent({ + type: enumLogicGateType.analyzer, + }) + ); + } +} diff --git a/src/js/game/buildings/balancer.js b/src/js/game/buildings/balancer.js index ce685a9a..181bcb87 100644 --- a/src/js/game/buildings/balancer.js +++ b/src/js/game/buildings/balancer.js @@ -1,261 +1,261 @@ -import { enumDirection, Vector } from "../../core/vector"; -import { ItemAcceptorComponent } from "../components/item_acceptor"; -import { ItemEjectorComponent } from "../components/item_ejector"; -import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/item_processor"; -import { Entity } from "../entity"; -import { MetaBuilding, defaultBuildingVariant } from "../meta_building"; -import { GameRoot } from "../root"; -import { enumHubGoalRewards } from "../tutorial_goals"; -import { T } from "../../translations"; -import { formatItemsPerSecond, generateMatrixRotations } from "../../core/utils"; -import { BeltUnderlaysComponent } from "../components/belt_underlays"; - -/** @enum {string} */ -export const enumBalancerVariants = { - merger: "merger", - mergerInverse: "merger-inverse", - splitter: "splitter", - splitterInverse: "splitter-inverse", -}; - -const overlayMatrices = { - [defaultBuildingVariant]: null, - [enumBalancerVariants.merger]: generateMatrixRotations([0, 1, 0, 0, 1, 1, 0, 1, 0]), - [enumBalancerVariants.mergerInverse]: generateMatrixRotations([0, 1, 0, 1, 1, 0, 0, 1, 0]), - [enumBalancerVariants.splitter]: generateMatrixRotations([0, 1, 0, 0, 1, 1, 0, 1, 0]), - [enumBalancerVariants.splitterInverse]: generateMatrixRotations([0, 1, 0, 1, 1, 0, 0, 1, 0]), -}; - -export class MetaBalancerBuilding extends MetaBuilding { - constructor() { - super("balancer"); - } - - static getAllVariantCombinations() { - return [ - { - internalId: 4, - variant: defaultBuildingVariant, - }, - { - internalId: 5, - variant: enumBalancerVariants.merger, - }, - { - internalId: 6, - variant: enumBalancerVariants.mergerInverse, - }, - { - internalId: 47, - variant: enumBalancerVariants.splitter, - }, - { - internalId: 48, - variant: enumBalancerVariants.splitterInverse, - }, - ]; - } - - getDimensions(variant) { - switch (variant) { - case defaultBuildingVariant: - return new Vector(2, 1); - case enumBalancerVariants.merger: - case enumBalancerVariants.mergerInverse: - case enumBalancerVariants.splitter: - case enumBalancerVariants.splitterInverse: - return new Vector(1, 1); - default: - assertAlways(false, "Unknown balancer variant: " + variant); - } - } - - /** - * @param {number} rotation - * @param {number} rotationVariant - * @param {string} variant - * @param {Entity} entity - * @returns {Array|null} - */ - getSpecialOverlayRenderMatrix(rotation, rotationVariant, variant, entity) { - const matrix = overlayMatrices[variant]; - if (matrix) { - return matrix[rotation]; - } - return null; - } - - /** - * @param {GameRoot} root - * @param {string} variant - * @returns {Array<[string, string]>} - */ - getAdditionalStatistics(root, variant) { - if (root.gameMode.throughputDoesNotMatter()) { - return []; - } - - let speedMultiplier = 2; - switch (variant) { - case enumBalancerVariants.merger: - case enumBalancerVariants.mergerInverse: - case enumBalancerVariants.splitter: - case enumBalancerVariants.splitterInverse: - speedMultiplier = 1; - } - - const speed = - (root.hubGoals.getProcessorBaseSpeed(enumItemProcessorTypes.balancer) / 2) * speedMultiplier; - return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(speed)]]; - } - - getSilhouetteColor() { - return "#555759"; - } - - /** - * @param {GameRoot} root - */ - getAvailableVariants(root) { - const deterministic = root.gameMode.getIsDeterministic(); - - let available = deterministic ? [] : [defaultBuildingVariant]; - - if (!deterministic && root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_merger)) { - available.push(enumBalancerVariants.merger, enumBalancerVariants.mergerInverse); - } - - if (root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_splitter)) { - available.push(enumBalancerVariants.splitter, enumBalancerVariants.splitterInverse); - } - - return available; - } - - /** - * @param {GameRoot} root - */ - getIsUnlocked(root) { - return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_balancer); - } - - /** - * Creates the entity at the given location - * @param {Entity} entity - */ - setupEntityComponents(entity) { - entity.addComponent( - new ItemAcceptorComponent({ - slots: [], // set later - }) - ); - - entity.addComponent( - new ItemProcessorComponent({ - inputsPerCharge: 1, - processorType: enumItemProcessorTypes.balancer, - }) - ); - - entity.addComponent( - new ItemEjectorComponent({ - slots: [], // set later - renderFloatingItems: false, - }) - ); - - entity.addComponent(new BeltUnderlaysComponent({ underlays: [] })); - } - - /** - * - * @param {Entity} entity - * @param {number} rotationVariant - * @param {string} variant - */ - updateVariants(entity, rotationVariant, variant) { - switch (variant) { - case defaultBuildingVariant: { - entity.components.ItemAcceptor.setSlots([ - { - pos: new Vector(0, 0), - direction: enumDirection.bottom, - }, - { - pos: new Vector(1, 0), - direction: enumDirection.bottom, - }, - ]); - - entity.components.ItemEjector.setSlots([ - { pos: new Vector(0, 0), direction: enumDirection.top }, - { pos: new Vector(1, 0), direction: enumDirection.top }, - ]); - - entity.components.BeltUnderlays.underlays = [ - { pos: new Vector(0, 0), direction: enumDirection.top }, - { pos: new Vector(1, 0), direction: enumDirection.top }, - ]; - - break; - } - case enumBalancerVariants.merger: - case enumBalancerVariants.mergerInverse: { - entity.components.ItemAcceptor.setSlots([ - { - pos: new Vector(0, 0), - direction: enumDirection.bottom, - }, - { - pos: new Vector(0, 0), - direction: - variant === enumBalancerVariants.mergerInverse - ? enumDirection.left - : enumDirection.right, - }, - ]); - - entity.components.ItemEjector.setSlots([ - { pos: new Vector(0, 0), direction: enumDirection.top }, - ]); - - entity.components.BeltUnderlays.underlays = [ - { pos: new Vector(0, 0), direction: enumDirection.top }, - ]; - - break; - } - case enumBalancerVariants.splitter: - case enumBalancerVariants.splitterInverse: { - entity.components.ItemAcceptor.setSlots([ - { - pos: new Vector(0, 0), - direction: enumDirection.bottom, - }, - ]); - - entity.components.ItemEjector.setSlots([ - { - pos: new Vector(0, 0), - direction: enumDirection.top, - }, - { - pos: new Vector(0, 0), - direction: - variant === enumBalancerVariants.splitterInverse - ? enumDirection.left - : enumDirection.right, - }, - ]); - - entity.components.BeltUnderlays.underlays = [ - { pos: new Vector(0, 0), direction: enumDirection.top }, - ]; - - break; - } - default: - assertAlways(false, "Unknown balancer variant: " + variant); - } - } -} +import { enumDirection, Vector } from "../../core/vector"; +import { ItemAcceptorComponent } from "../components/item_acceptor"; +import { ItemEjectorComponent } from "../components/item_ejector"; +import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/item_processor"; +import { Entity } from "../entity"; +import { MetaBuilding, defaultBuildingVariant } from "../meta_building"; +import { GameRoot } from "../root"; +import { enumHubGoalRewards } from "../tutorial_goals"; +import { T } from "../../translations"; +import { formatItemsPerSecond, generateMatrixRotations } from "../../core/utils"; +import { BeltUnderlaysComponent } from "../components/belt_underlays"; + +/** @enum {string} */ +export const enumBalancerVariants = { + merger: "merger", + mergerInverse: "merger-inverse", + splitter: "splitter", + splitterInverse: "splitter-inverse", +}; + +const overlayMatrices = { + [defaultBuildingVariant]: null, + [enumBalancerVariants.merger]: generateMatrixRotations([0, 1, 0, 0, 1, 1, 0, 1, 0]), + [enumBalancerVariants.mergerInverse]: generateMatrixRotations([0, 1, 0, 1, 1, 0, 0, 1, 0]), + [enumBalancerVariants.splitter]: generateMatrixRotations([0, 1, 0, 0, 1, 1, 0, 1, 0]), + [enumBalancerVariants.splitterInverse]: generateMatrixRotations([0, 1, 0, 1, 1, 0, 0, 1, 0]), +}; + +export class MetaBalancerBuilding extends MetaBuilding { + constructor() { + super("balancer"); + } + + static getAllVariantCombinations() { + return [ + { + internalId: 4, + variant: defaultBuildingVariant, + }, + { + internalId: 5, + variant: enumBalancerVariants.merger, + }, + { + internalId: 6, + variant: enumBalancerVariants.mergerInverse, + }, + { + internalId: 47, + variant: enumBalancerVariants.splitter, + }, + { + internalId: 48, + variant: enumBalancerVariants.splitterInverse, + }, + ]; + } + + getDimensions(variant) { + switch (variant) { + case defaultBuildingVariant: + return new Vector(2, 1); + case enumBalancerVariants.merger: + case enumBalancerVariants.mergerInverse: + case enumBalancerVariants.splitter: + case enumBalancerVariants.splitterInverse: + return new Vector(1, 1); + default: + assertAlways(false, "Unknown balancer variant: " + variant); + } + } + + /** + * @param {number} rotation + * @param {number} rotationVariant + * @param {string} variant + * @param {Entity} entity + * @returns {Array|null} + */ + getSpecialOverlayRenderMatrix(rotation, rotationVariant, variant, entity) { + const matrix = overlayMatrices[variant]; + if (matrix) { + return matrix[rotation]; + } + return null; + } + + /** + * @param {GameRoot} root + * @param {string} variant + * @returns {Array<[string, string]>} + */ + getAdditionalStatistics(root, variant) { + if (root.gameMode.throughputDoesNotMatter()) { + return []; + } + + let speedMultiplier = 2; + switch (variant) { + case enumBalancerVariants.merger: + case enumBalancerVariants.mergerInverse: + case enumBalancerVariants.splitter: + case enumBalancerVariants.splitterInverse: + speedMultiplier = 1; + } + + const speed = + (root.hubGoals.getProcessorBaseSpeed(enumItemProcessorTypes.balancer) / 2) * speedMultiplier; + return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(speed)]]; + } + + getSilhouetteColor() { + return "#555759"; + } + + /** + * @param {GameRoot} root + */ + getAvailableVariants(root) { + const deterministic = root.gameMode.getIsDeterministic(); + + let available = deterministic ? [] : [defaultBuildingVariant]; + + if (!deterministic && root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_merger)) { + available.push(enumBalancerVariants.merger, enumBalancerVariants.mergerInverse); + } + + if (root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_splitter)) { + available.push(enumBalancerVariants.splitter, enumBalancerVariants.splitterInverse); + } + + return available; + } + + /** + * @param {GameRoot} root + */ + getIsUnlocked(root) { + return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_balancer); + } + + /** + * Creates the entity at the given location + * @param {Entity} entity + */ + setupEntityComponents(entity) { + entity.addComponent( + new ItemAcceptorComponent({ + slots: [], // set later + }) + ); + + entity.addComponent( + new ItemProcessorComponent({ + inputsPerCharge: 1, + processorType: enumItemProcessorTypes.balancer, + }) + ); + + entity.addComponent( + new ItemEjectorComponent({ + slots: [], // set later + renderFloatingItems: false, + }) + ); + + entity.addComponent(new BeltUnderlaysComponent({ underlays: [] })); + } + + /** + * + * @param {Entity} entity + * @param {number} rotationVariant + * @param {string} variant + */ + updateVariants(entity, rotationVariant, variant) { + switch (variant) { + case defaultBuildingVariant: { + entity.components.ItemAcceptor.setSlots([ + { + pos: new Vector(0, 0), + direction: enumDirection.bottom, + }, + { + pos: new Vector(1, 0), + direction: enumDirection.bottom, + }, + ]); + + entity.components.ItemEjector.setSlots([ + { pos: new Vector(0, 0), direction: enumDirection.top }, + { pos: new Vector(1, 0), direction: enumDirection.top }, + ]); + + entity.components.BeltUnderlays.underlays = [ + { pos: new Vector(0, 0), direction: enumDirection.top }, + { pos: new Vector(1, 0), direction: enumDirection.top }, + ]; + + break; + } + case enumBalancerVariants.merger: + case enumBalancerVariants.mergerInverse: { + entity.components.ItemAcceptor.setSlots([ + { + pos: new Vector(0, 0), + direction: enumDirection.bottom, + }, + { + pos: new Vector(0, 0), + direction: + variant === enumBalancerVariants.mergerInverse + ? enumDirection.left + : enumDirection.right, + }, + ]); + + entity.components.ItemEjector.setSlots([ + { pos: new Vector(0, 0), direction: enumDirection.top }, + ]); + + entity.components.BeltUnderlays.underlays = [ + { pos: new Vector(0, 0), direction: enumDirection.top }, + ]; + + break; + } + case enumBalancerVariants.splitter: + case enumBalancerVariants.splitterInverse: { + entity.components.ItemAcceptor.setSlots([ + { + pos: new Vector(0, 0), + direction: enumDirection.bottom, + }, + ]); + + entity.components.ItemEjector.setSlots([ + { + pos: new Vector(0, 0), + direction: enumDirection.top, + }, + { + pos: new Vector(0, 0), + direction: + variant === enumBalancerVariants.splitterInverse + ? enumDirection.left + : enumDirection.right, + }, + ]); + + entity.components.BeltUnderlays.underlays = [ + { pos: new Vector(0, 0), direction: enumDirection.top }, + ]; + + break; + } + default: + assertAlways(false, "Unknown balancer variant: " + variant); + } + } +} diff --git a/src/js/game/buildings/belt.js b/src/js/game/buildings/belt.js index 11a53cdf..14d629db 100644 --- a/src/js/game/buildings/belt.js +++ b/src/js/game/buildings/belt.js @@ -1,252 +1,252 @@ -import { Loader } from "../../core/loader"; -import { formatItemsPerSecond, generateMatrixRotations } from "../../core/utils"; -import { enumAngleToDirection, enumDirection, Vector } from "../../core/vector"; -import { SOUNDS } from "../../platform/sound"; -import { T } from "../../translations"; -import { BeltComponent } from "../components/belt"; -import { Entity } from "../entity"; -import { defaultBuildingVariant, MetaBuilding } from "../meta_building"; -import { GameRoot } from "../root"; -import { THEME } from "../theme"; - -export const arrayBeltVariantToRotation = [enumDirection.top, enumDirection.left, enumDirection.right]; - -export const beltOverlayMatrices = { - [enumDirection.top]: generateMatrixRotations([0, 1, 0, 0, 1, 0, 0, 1, 0]), - [enumDirection.left]: generateMatrixRotations([0, 0, 0, 1, 1, 0, 0, 1, 0]), - [enumDirection.right]: generateMatrixRotations([0, 0, 0, 0, 1, 1, 0, 1, 0]), -}; - -export class MetaBeltBuilding extends MetaBuilding { - constructor() { - super("belt"); - } - - static getAllVariantCombinations() { - return [ - { - internalId: 1, - variant: defaultBuildingVariant, - rotationVariant: 0, - }, - { - internalId: 2, - variant: defaultBuildingVariant, - rotationVariant: 1, - }, - { - internalId: 3, - variant: defaultBuildingVariant, - rotationVariant: 2, - }, - ]; - } - - getSilhouetteColor() { - return THEME.map.chunkOverview.beltColor; - } - - getPlacementSound() { - return SOUNDS.placeBelt; - } - - getHasDirectionLockAvailable() { - return true; - } - getStayInPlacementMode() { - return true; - } - - getRotateAutomaticallyWhilePlacing() { - return true; - } - - getSprite() { - return null; - } - - getIsReplaceable() { - return true; - } - - /** - * @param {GameRoot} root - * @param {string} variant - * @returns {Array<[string, string]>} - */ - getAdditionalStatistics(root, variant) { - if (root.gameMode.throughputDoesNotMatter()) { - return []; - } - const beltSpeed = root.hubGoals.getBeltBaseSpeed(); - return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(beltSpeed)]]; - } - - getPreviewSprite(rotationVariant) { - switch (arrayBeltVariantToRotation[rotationVariant]) { - case enumDirection.top: { - return Loader.getSprite("sprites/buildings/belt_top.png"); - } - case enumDirection.left: { - return Loader.getSprite("sprites/buildings/belt_left.png"); - } - case enumDirection.right: { - return Loader.getSprite("sprites/buildings/belt_right.png"); - } - default: { - assertAlways(false, "Invalid belt rotation variant"); - } - } - } - - getBlueprintSprite(rotationVariant) { - switch (arrayBeltVariantToRotation[rotationVariant]) { - case enumDirection.top: { - return Loader.getSprite("sprites/blueprints/belt_top.png"); - } - case enumDirection.left: { - return Loader.getSprite("sprites/blueprints/belt_left.png"); - } - case enumDirection.right: { - return Loader.getSprite("sprites/blueprints/belt_right.png"); - } - default: { - assertAlways(false, "Invalid belt rotation variant"); - } - } - } - - /** - * - * @param {number} rotation - * @param {number} rotationVariant - * @param {string} variant - * @param {Entity} entity - */ - getSpecialOverlayRenderMatrix(rotation, rotationVariant, variant, entity) { - return beltOverlayMatrices[entity.components.Belt.direction][rotation]; - } - - /** - * Creates the entity at the given location - * @param {Entity} entity - */ - setupEntityComponents(entity) { - entity.addComponent( - new BeltComponent({ - direction: enumDirection.top, // updated later - }) - ); - } - - /** - * - * @param {Entity} entity - * @param {number} rotationVariant - */ - updateVariants(entity, rotationVariant) { - entity.components.Belt.direction = arrayBeltVariantToRotation[rotationVariant]; - } - - /** - * Should compute the optimal rotation variant on the given tile - * @param {object} param0 - * @param {GameRoot} param0.root - * @param {Vector} param0.tile - * @param {number} param0.rotation - * @param {string} param0.variant - * @param {Layer} param0.layer - * @return {{ rotation: number, rotationVariant: number, connectedEntities?: Array }} - */ - computeOptimalDirectionAndRotationVariantAtTile({ root, tile, rotation, variant, layer }) { - const topDirection = enumAngleToDirection[rotation]; - const rightDirection = enumAngleToDirection[(rotation + 90) % 360]; - const bottomDirection = enumAngleToDirection[(rotation + 180) % 360]; - const leftDirection = enumAngleToDirection[(rotation + 270) % 360]; - - const { ejectors, acceptors } = root.logic.getEjectorsAndAcceptorsAtTile(tile); - - let hasBottomEjector = false; - let hasRightEjector = false; - let hasLeftEjector = false; - - let hasTopAcceptor = false; - let hasLeftAcceptor = false; - let hasRightAcceptor = false; - - // Check all ejectors - for (let i = 0; i < ejectors.length; ++i) { - const ejector = ejectors[i]; - - if (ejector.toDirection === topDirection) { - hasBottomEjector = true; - } else if (ejector.toDirection === leftDirection) { - hasRightEjector = true; - } else if (ejector.toDirection === rightDirection) { - hasLeftEjector = true; - } - } - - // Check all acceptors - for (let i = 0; i < acceptors.length; ++i) { - const acceptor = acceptors[i]; - if (acceptor.fromDirection === bottomDirection) { - hasTopAcceptor = true; - } else if (acceptor.fromDirection === rightDirection) { - hasLeftAcceptor = true; - } else if (acceptor.fromDirection === leftDirection) { - hasRightAcceptor = true; - } - } - - // Soo .. if there is any ejector below us we always prioritize - // this ejector - if (!hasBottomEjector) { - // When something ejects to us from the left and nothing from the right, - // do a curve from the left to the top - - if (hasRightEjector && !hasLeftEjector) { - return { - rotation: (rotation + 270) % 360, - rotationVariant: 2, - }; - } - - // When something ejects to us from the right and nothing from the left, - // do a curve from the right to the top - if (hasLeftEjector && !hasRightEjector) { - return { - rotation: (rotation + 90) % 360, - rotationVariant: 1, - }; - } - } - - // When there is a top acceptor, ignore sides - // NOTICE: This makes the belt prefer side turns *way* too much! - if (!hasTopAcceptor) { - // When there is an acceptor to the right but no acceptor to the left, - // do a turn to the right - if (hasRightAcceptor && !hasLeftAcceptor) { - return { - rotation, - rotationVariant: 2, - }; - } - - // When there is an acceptor to the left but no acceptor to the right, - // do a turn to the left - if (hasLeftAcceptor && !hasRightAcceptor) { - return { - rotation, - rotationVariant: 1, - }; - } - } - - return { - rotation, - rotationVariant: 0, - }; - } -} +import { Loader } from "../../core/loader"; +import { formatItemsPerSecond, generateMatrixRotations } from "../../core/utils"; +import { enumAngleToDirection, enumDirection, Vector } from "../../core/vector"; +import { SOUNDS } from "../../platform/sound"; +import { T } from "../../translations"; +import { BeltComponent } from "../components/belt"; +import { Entity } from "../entity"; +import { defaultBuildingVariant, MetaBuilding } from "../meta_building"; +import { GameRoot } from "../root"; +import { THEME } from "../theme"; + +export const arrayBeltVariantToRotation = [enumDirection.top, enumDirection.left, enumDirection.right]; + +export const beltOverlayMatrices = { + [enumDirection.top]: generateMatrixRotations([0, 1, 0, 0, 1, 0, 0, 1, 0]), + [enumDirection.left]: generateMatrixRotations([0, 0, 0, 1, 1, 0, 0, 1, 0]), + [enumDirection.right]: generateMatrixRotations([0, 0, 0, 0, 1, 1, 0, 1, 0]), +}; + +export class MetaBeltBuilding extends MetaBuilding { + constructor() { + super("belt"); + } + + static getAllVariantCombinations() { + return [ + { + internalId: 1, + variant: defaultBuildingVariant, + rotationVariant: 0, + }, + { + internalId: 2, + variant: defaultBuildingVariant, + rotationVariant: 1, + }, + { + internalId: 3, + variant: defaultBuildingVariant, + rotationVariant: 2, + }, + ]; + } + + getSilhouetteColor() { + return THEME.map.chunkOverview.beltColor; + } + + getPlacementSound() { + return SOUNDS.placeBelt; + } + + getHasDirectionLockAvailable() { + return true; + } + getStayInPlacementMode() { + return true; + } + + getRotateAutomaticallyWhilePlacing() { + return true; + } + + getSprite() { + return null; + } + + getIsReplaceable() { + return true; + } + + /** + * @param {GameRoot} root + * @param {string} variant + * @returns {Array<[string, string]>} + */ + getAdditionalStatistics(root, variant) { + if (root.gameMode.throughputDoesNotMatter()) { + return []; + } + const beltSpeed = root.hubGoals.getBeltBaseSpeed(); + return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(beltSpeed)]]; + } + + getPreviewSprite(rotationVariant) { + switch (arrayBeltVariantToRotation[rotationVariant]) { + case enumDirection.top: { + return Loader.getSprite("sprites/buildings/belt_top.png"); + } + case enumDirection.left: { + return Loader.getSprite("sprites/buildings/belt_left.png"); + } + case enumDirection.right: { + return Loader.getSprite("sprites/buildings/belt_right.png"); + } + default: { + assertAlways(false, "Invalid belt rotation variant"); + } + } + } + + getBlueprintSprite(rotationVariant) { + switch (arrayBeltVariantToRotation[rotationVariant]) { + case enumDirection.top: { + return Loader.getSprite("sprites/blueprints/belt_top.png"); + } + case enumDirection.left: { + return Loader.getSprite("sprites/blueprints/belt_left.png"); + } + case enumDirection.right: { + return Loader.getSprite("sprites/blueprints/belt_right.png"); + } + default: { + assertAlways(false, "Invalid belt rotation variant"); + } + } + } + + /** + * + * @param {number} rotation + * @param {number} rotationVariant + * @param {string} variant + * @param {Entity} entity + */ + getSpecialOverlayRenderMatrix(rotation, rotationVariant, variant, entity) { + return beltOverlayMatrices[entity.components.Belt.direction][rotation]; + } + + /** + * Creates the entity at the given location + * @param {Entity} entity + */ + setupEntityComponents(entity) { + entity.addComponent( + new BeltComponent({ + direction: enumDirection.top, // updated later + }) + ); + } + + /** + * + * @param {Entity} entity + * @param {number} rotationVariant + */ + updateVariants(entity, rotationVariant) { + entity.components.Belt.direction = arrayBeltVariantToRotation[rotationVariant]; + } + + /** + * Should compute the optimal rotation variant on the given tile + * @param {object} param0 + * @param {GameRoot} param0.root + * @param {Vector} param0.tile + * @param {number} param0.rotation + * @param {string} param0.variant + * @param {Layer} param0.layer + * @return {{ rotation: number, rotationVariant: number, connectedEntities?: Array }} + */ + computeOptimalDirectionAndRotationVariantAtTile({ root, tile, rotation, variant, layer }) { + const topDirection = enumAngleToDirection[rotation]; + const rightDirection = enumAngleToDirection[(rotation + 90) % 360]; + const bottomDirection = enumAngleToDirection[(rotation + 180) % 360]; + const leftDirection = enumAngleToDirection[(rotation + 270) % 360]; + + const { ejectors, acceptors } = root.logic.getEjectorsAndAcceptorsAtTile(tile); + + let hasBottomEjector = false; + let hasRightEjector = false; + let hasLeftEjector = false; + + let hasTopAcceptor = false; + let hasLeftAcceptor = false; + let hasRightAcceptor = false; + + // Check all ejectors + for (let i = 0; i < ejectors.length; ++i) { + const ejector = ejectors[i]; + + if (ejector.toDirection === topDirection) { + hasBottomEjector = true; + } else if (ejector.toDirection === leftDirection) { + hasRightEjector = true; + } else if (ejector.toDirection === rightDirection) { + hasLeftEjector = true; + } + } + + // Check all acceptors + for (let i = 0; i < acceptors.length; ++i) { + const acceptor = acceptors[i]; + if (acceptor.fromDirection === bottomDirection) { + hasTopAcceptor = true; + } else if (acceptor.fromDirection === rightDirection) { + hasLeftAcceptor = true; + } else if (acceptor.fromDirection === leftDirection) { + hasRightAcceptor = true; + } + } + + // Soo .. if there is any ejector below us we always prioritize + // this ejector + if (!hasBottomEjector) { + // When something ejects to us from the left and nothing from the right, + // do a curve from the left to the top + + if (hasRightEjector && !hasLeftEjector) { + return { + rotation: (rotation + 270) % 360, + rotationVariant: 2, + }; + } + + // When something ejects to us from the right and nothing from the left, + // do a curve from the right to the top + if (hasLeftEjector && !hasRightEjector) { + return { + rotation: (rotation + 90) % 360, + rotationVariant: 1, + }; + } + } + + // When there is a top acceptor, ignore sides + // NOTICE: This makes the belt prefer side turns *way* too much! + if (!hasTopAcceptor) { + // When there is an acceptor to the right but no acceptor to the left, + // do a turn to the right + if (hasRightAcceptor && !hasLeftAcceptor) { + return { + rotation, + rotationVariant: 2, + }; + } + + // When there is an acceptor to the left but no acceptor to the right, + // do a turn to the left + if (hasLeftAcceptor && !hasRightAcceptor) { + return { + rotation, + rotationVariant: 1, + }; + } + } + + return { + rotation, + rotationVariant: 0, + }; + } +} diff --git a/src/js/game/buildings/comparator.js b/src/js/game/buildings/comparator.js index 1f45dbbb..f6dc727b 100644 --- a/src/js/game/buildings/comparator.js +++ b/src/js/game/buildings/comparator.js @@ -1,81 +1,81 @@ -import { enumDirection, Vector } from "../../core/vector"; -import { enumLogicGateType, LogicGateComponent } from "../components/logic_gate"; -import { enumPinSlotType, WiredPinsComponent } from "../components/wired_pins"; -import { Entity } from "../entity"; -import { defaultBuildingVariant, MetaBuilding } from "../meta_building"; -import { GameRoot } from "../root"; -import { enumHubGoalRewards } from "../tutorial_goals"; - -export class MetaComparatorBuilding extends MetaBuilding { - constructor() { - super("comparator"); - } - - static getAllVariantCombinations() { - return [ - { - internalId: 46, - variant: defaultBuildingVariant, - }, - ]; - } - - getSilhouetteColor() { - return "#823cab"; - } - - /** - * @param {GameRoot} root - */ - getIsUnlocked(root) { - return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_virtual_processing); - } - - /** @returns {"wires"} **/ - getLayer() { - return "wires"; - } - - getDimensions() { - return new Vector(1, 1); - } - - getRenderPins() { - // We already have it included - return false; - } - - /** - * Creates the entity at the given location - * @param {Entity} entity - */ - setupEntityComponents(entity) { - entity.addComponent( - new WiredPinsComponent({ - slots: [ - { - pos: new Vector(0, 0), - direction: enumDirection.top, - type: enumPinSlotType.logicalEjector, - }, - { - pos: new Vector(0, 0), - direction: enumDirection.left, - type: enumPinSlotType.logicalAcceptor, - }, - { - pos: new Vector(0, 0), - direction: enumDirection.right, - type: enumPinSlotType.logicalAcceptor, - }, - ], - }) - ); - - entity.addComponent( - new LogicGateComponent({ - type: enumLogicGateType.compare, - }) - ); - } -} +import { enumDirection, Vector } from "../../core/vector"; +import { enumLogicGateType, LogicGateComponent } from "../components/logic_gate"; +import { enumPinSlotType, WiredPinsComponent } from "../components/wired_pins"; +import { Entity } from "../entity"; +import { defaultBuildingVariant, MetaBuilding } from "../meta_building"; +import { GameRoot } from "../root"; +import { enumHubGoalRewards } from "../tutorial_goals"; + +export class MetaComparatorBuilding extends MetaBuilding { + constructor() { + super("comparator"); + } + + static getAllVariantCombinations() { + return [ + { + internalId: 46, + variant: defaultBuildingVariant, + }, + ]; + } + + getSilhouetteColor() { + return "#823cab"; + } + + /** + * @param {GameRoot} root + */ + getIsUnlocked(root) { + return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_virtual_processing); + } + + /** @returns {"wires"} **/ + getLayer() { + return "wires"; + } + + getDimensions() { + return new Vector(1, 1); + } + + getRenderPins() { + // We already have it included + return false; + } + + /** + * Creates the entity at the given location + * @param {Entity} entity + */ + setupEntityComponents(entity) { + entity.addComponent( + new WiredPinsComponent({ + slots: [ + { + pos: new Vector(0, 0), + direction: enumDirection.top, + type: enumPinSlotType.logicalEjector, + }, + { + pos: new Vector(0, 0), + direction: enumDirection.left, + type: enumPinSlotType.logicalAcceptor, + }, + { + pos: new Vector(0, 0), + direction: enumDirection.right, + type: enumPinSlotType.logicalAcceptor, + }, + ], + }) + ); + + entity.addComponent( + new LogicGateComponent({ + type: enumLogicGateType.compare, + }) + ); + } +} diff --git a/src/js/game/buildings/constant_signal.js b/src/js/game/buildings/constant_signal.js index 2bb72def..d4056309 100644 --- a/src/js/game/buildings/constant_signal.js +++ b/src/js/game/buildings/constant_signal.js @@ -1,72 +1,72 @@ -import { enumDirection, Vector } from "../../core/vector"; -import { enumPinSlotType, WiredPinsComponent } from "../components/wired_pins"; -import { Entity } from "../entity"; -import { defaultBuildingVariant, MetaBuilding } from "../meta_building"; -import { GameRoot } from "../root"; -import { ConstantSignalComponent } from "../components/constant_signal"; -import { generateMatrixRotations } from "../../core/utils"; -import { enumHubGoalRewards } from "../tutorial_goals"; - -const overlayMatrix = generateMatrixRotations([0, 1, 0, 1, 1, 1, 1, 1, 1]); - -export class MetaConstantSignalBuilding extends MetaBuilding { - constructor() { - super("constant_signal"); - } - - static getAllVariantCombinations() { - return [ - { - internalId: 31, - variant: defaultBuildingVariant, - }, - ]; - } - - getSilhouetteColor() { - return "#2b84fd"; - } - - /** - * @param {GameRoot} root - */ - getIsUnlocked(root) { - return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_constant_signal); - } - - /** @returns {"wires"} **/ - getLayer() { - return "wires"; - } - - getDimensions() { - return new Vector(1, 1); - } - - getRenderPins() { - return false; - } - - getSpecialOverlayRenderMatrix(rotation) { - return overlayMatrix[rotation]; - } - - /** - * Creates the entity at the given location - * @param {Entity} entity - */ - setupEntityComponents(entity) { - entity.addComponent( - new WiredPinsComponent({ - slots: [ - { - pos: new Vector(0, 0), - direction: enumDirection.top, - type: enumPinSlotType.logicalEjector, - }, - ], - }) - ); - entity.addComponent(new ConstantSignalComponent({})); - } -} +import { enumDirection, Vector } from "../../core/vector"; +import { enumPinSlotType, WiredPinsComponent } from "../components/wired_pins"; +import { Entity } from "../entity"; +import { defaultBuildingVariant, MetaBuilding } from "../meta_building"; +import { GameRoot } from "../root"; +import { ConstantSignalComponent } from "../components/constant_signal"; +import { generateMatrixRotations } from "../../core/utils"; +import { enumHubGoalRewards } from "../tutorial_goals"; + +const overlayMatrix = generateMatrixRotations([0, 1, 0, 1, 1, 1, 1, 1, 1]); + +export class MetaConstantSignalBuilding extends MetaBuilding { + constructor() { + super("constant_signal"); + } + + static getAllVariantCombinations() { + return [ + { + internalId: 31, + variant: defaultBuildingVariant, + }, + ]; + } + + getSilhouetteColor() { + return "#2b84fd"; + } + + /** + * @param {GameRoot} root + */ + getIsUnlocked(root) { + return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_constant_signal); + } + + /** @returns {"wires"} **/ + getLayer() { + return "wires"; + } + + getDimensions() { + return new Vector(1, 1); + } + + getRenderPins() { + return false; + } + + getSpecialOverlayRenderMatrix(rotation) { + return overlayMatrix[rotation]; + } + + /** + * Creates the entity at the given location + * @param {Entity} entity + */ + setupEntityComponents(entity) { + entity.addComponent( + new WiredPinsComponent({ + slots: [ + { + pos: new Vector(0, 0), + direction: enumDirection.top, + type: enumPinSlotType.logicalEjector, + }, + ], + }) + ); + entity.addComponent(new ConstantSignalComponent({})); + } +} diff --git a/src/js/game/buildings/cutter.js b/src/js/game/buildings/cutter.js index 21acdaa0..dd71b527 100644 --- a/src/js/game/buildings/cutter.js +++ b/src/js/game/buildings/cutter.js @@ -1,138 +1,138 @@ -import { formatItemsPerSecond } from "../../core/utils"; -import { enumDirection, Vector } from "../../core/vector"; -import { T } from "../../translations"; -import { ItemAcceptorComponent } from "../components/item_acceptor"; -import { ItemEjectorComponent } from "../components/item_ejector"; -import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/item_processor"; -import { Entity } from "../entity"; -import { defaultBuildingVariant, MetaBuilding } from "../meta_building"; -import { GameRoot } from "../root"; -import { enumHubGoalRewards } from "../tutorial_goals"; - -/** @enum {string} */ -export const enumCutterVariants = { quad: "quad" }; - -export class MetaCutterBuilding extends MetaBuilding { - constructor() { - super("cutter"); - } - - static getAllVariantCombinations() { - return [ - { - internalId: 9, - variant: defaultBuildingVariant, - }, - { - internalId: 10, - variant: enumCutterVariants.quad, - }, - ]; - } - - getSilhouetteColor() { - return "#7dcda2"; - } - - getDimensions(variant) { - switch (variant) { - case defaultBuildingVariant: - return new Vector(2, 1); - case enumCutterVariants.quad: - return new Vector(4, 1); - default: - assertAlways(false, "Unknown cutter variant: " + variant); - } - } - - /** - * @param {GameRoot} root - * @param {string} variant - * @returns {Array<[string, string]>} - */ - getAdditionalStatistics(root, variant) { - if (root.gameMode.throughputDoesNotMatter()) { - return []; - } - const speed = root.hubGoals.getProcessorBaseSpeed( - variant === enumCutterVariants.quad - ? enumItemProcessorTypes.cutterQuad - : enumItemProcessorTypes.cutter - ); - return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(speed)]]; - } - - /** - * @param {GameRoot} root - */ - getAvailableVariants(root) { - if (root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_cutter_quad)) { - return [defaultBuildingVariant, enumCutterVariants.quad]; - } - return super.getAvailableVariants(root); - } - - /** - * @param {GameRoot} root - */ - getIsUnlocked(root) { - return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_cutter_and_trash); - } - - /** - * Creates the entity at the given location - * @param {Entity} entity - */ - setupEntityComponents(entity) { - entity.addComponent( - new ItemProcessorComponent({ - inputsPerCharge: 1, - processorType: enumItemProcessorTypes.cutter, - }) - ); - entity.addComponent(new ItemEjectorComponent({})); - entity.addComponent( - new ItemAcceptorComponent({ - slots: [ - { - pos: new Vector(0, 0), - direction: enumDirection.bottom, - filter: "shape", - }, - ], - }) - ); - } - - /** - * - * @param {Entity} entity - * @param {number} rotationVariant - * @param {string} variant - */ - updateVariants(entity, rotationVariant, variant) { - switch (variant) { - case defaultBuildingVariant: { - entity.components.ItemEjector.setSlots([ - { pos: new Vector(0, 0), direction: enumDirection.top }, - { pos: new Vector(1, 0), direction: enumDirection.top }, - ]); - entity.components.ItemProcessor.type = enumItemProcessorTypes.cutter; - break; - } - case enumCutterVariants.quad: { - entity.components.ItemEjector.setSlots([ - { pos: new Vector(0, 0), direction: enumDirection.top }, - { pos: new Vector(1, 0), direction: enumDirection.top }, - { pos: new Vector(2, 0), direction: enumDirection.top }, - { pos: new Vector(3, 0), direction: enumDirection.top }, - ]); - entity.components.ItemProcessor.type = enumItemProcessorTypes.cutterQuad; - break; - } - - default: - assertAlways(false, "Unknown painter variant: " + variant); - } - } -} +import { formatItemsPerSecond } from "../../core/utils"; +import { enumDirection, Vector } from "../../core/vector"; +import { T } from "../../translations"; +import { ItemAcceptorComponent } from "../components/item_acceptor"; +import { ItemEjectorComponent } from "../components/item_ejector"; +import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/item_processor"; +import { Entity } from "../entity"; +import { defaultBuildingVariant, MetaBuilding } from "../meta_building"; +import { GameRoot } from "../root"; +import { enumHubGoalRewards } from "../tutorial_goals"; + +/** @enum {string} */ +export const enumCutterVariants = { quad: "quad" }; + +export class MetaCutterBuilding extends MetaBuilding { + constructor() { + super("cutter"); + } + + static getAllVariantCombinations() { + return [ + { + internalId: 9, + variant: defaultBuildingVariant, + }, + { + internalId: 10, + variant: enumCutterVariants.quad, + }, + ]; + } + + getSilhouetteColor() { + return "#7dcda2"; + } + + getDimensions(variant) { + switch (variant) { + case defaultBuildingVariant: + return new Vector(2, 1); + case enumCutterVariants.quad: + return new Vector(4, 1); + default: + assertAlways(false, "Unknown cutter variant: " + variant); + } + } + + /** + * @param {GameRoot} root + * @param {string} variant + * @returns {Array<[string, string]>} + */ + getAdditionalStatistics(root, variant) { + if (root.gameMode.throughputDoesNotMatter()) { + return []; + } + const speed = root.hubGoals.getProcessorBaseSpeed( + variant === enumCutterVariants.quad + ? enumItemProcessorTypes.cutterQuad + : enumItemProcessorTypes.cutter + ); + return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(speed)]]; + } + + /** + * @param {GameRoot} root + */ + getAvailableVariants(root) { + if (root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_cutter_quad)) { + return [defaultBuildingVariant, enumCutterVariants.quad]; + } + return super.getAvailableVariants(root); + } + + /** + * @param {GameRoot} root + */ + getIsUnlocked(root) { + return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_cutter_and_trash); + } + + /** + * Creates the entity at the given location + * @param {Entity} entity + */ + setupEntityComponents(entity) { + entity.addComponent( + new ItemProcessorComponent({ + inputsPerCharge: 1, + processorType: enumItemProcessorTypes.cutter, + }) + ); + entity.addComponent(new ItemEjectorComponent({})); + entity.addComponent( + new ItemAcceptorComponent({ + slots: [ + { + pos: new Vector(0, 0), + direction: enumDirection.bottom, + filter: "shape", + }, + ], + }) + ); + } + + /** + * + * @param {Entity} entity + * @param {number} rotationVariant + * @param {string} variant + */ + updateVariants(entity, rotationVariant, variant) { + switch (variant) { + case defaultBuildingVariant: { + entity.components.ItemEjector.setSlots([ + { pos: new Vector(0, 0), direction: enumDirection.top }, + { pos: new Vector(1, 0), direction: enumDirection.top }, + ]); + entity.components.ItemProcessor.type = enumItemProcessorTypes.cutter; + break; + } + case enumCutterVariants.quad: { + entity.components.ItemEjector.setSlots([ + { pos: new Vector(0, 0), direction: enumDirection.top }, + { pos: new Vector(1, 0), direction: enumDirection.top }, + { pos: new Vector(2, 0), direction: enumDirection.top }, + { pos: new Vector(3, 0), direction: enumDirection.top }, + ]); + entity.components.ItemProcessor.type = enumItemProcessorTypes.cutterQuad; + break; + } + + default: + assertAlways(false, "Unknown painter variant: " + variant); + } + } +} diff --git a/src/js/game/buildings/display.js b/src/js/game/buildings/display.js index 7c1277c2..a3819ec2 100644 --- a/src/js/game/buildings/display.js +++ b/src/js/game/buildings/display.js @@ -1,60 +1,60 @@ -import { enumDirection, Vector } from "../../core/vector"; -import { enumPinSlotType, WiredPinsComponent } from "../components/wired_pins"; -import { Entity } from "../entity"; -import { defaultBuildingVariant, MetaBuilding } from "../meta_building"; -import { GameRoot } from "../root"; -import { DisplayComponent } from "../components/display"; -import { enumHubGoalRewards } from "../tutorial_goals"; - -export class MetaDisplayBuilding extends MetaBuilding { - constructor() { - super("display"); - } - - static getAllVariantCombinations() { - return [ - { - internalId: 40, - variant: defaultBuildingVariant, - }, - ]; - } - - getSilhouetteColor() { - return "#aaaaaa"; - } - - /** - * @param {GameRoot} root - */ - getIsUnlocked(root) { - return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_display); - } - - getDimensions() { - return new Vector(1, 1); - } - - getShowWiresLayerPreview() { - return true; - } - - /** - * Creates the entity at the given location - * @param {Entity} entity - */ - setupEntityComponents(entity) { - entity.addComponent( - new WiredPinsComponent({ - slots: [ - { - pos: new Vector(0, 0), - direction: enumDirection.bottom, - type: enumPinSlotType.logicalAcceptor, - }, - ], - }) - ); - entity.addComponent(new DisplayComponent()); - } -} +import { enumDirection, Vector } from "../../core/vector"; +import { enumPinSlotType, WiredPinsComponent } from "../components/wired_pins"; +import { Entity } from "../entity"; +import { defaultBuildingVariant, MetaBuilding } from "../meta_building"; +import { GameRoot } from "../root"; +import { DisplayComponent } from "../components/display"; +import { enumHubGoalRewards } from "../tutorial_goals"; + +export class MetaDisplayBuilding extends MetaBuilding { + constructor() { + super("display"); + } + + static getAllVariantCombinations() { + return [ + { + internalId: 40, + variant: defaultBuildingVariant, + }, + ]; + } + + getSilhouetteColor() { + return "#aaaaaa"; + } + + /** + * @param {GameRoot} root + */ + getIsUnlocked(root) { + return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_display); + } + + getDimensions() { + return new Vector(1, 1); + } + + getShowWiresLayerPreview() { + return true; + } + + /** + * Creates the entity at the given location + * @param {Entity} entity + */ + setupEntityComponents(entity) { + entity.addComponent( + new WiredPinsComponent({ + slots: [ + { + pos: new Vector(0, 0), + direction: enumDirection.bottom, + type: enumPinSlotType.logicalAcceptor, + }, + ], + }) + ); + entity.addComponent(new DisplayComponent()); + } +} diff --git a/src/js/game/buildings/filter.js b/src/js/game/buildings/filter.js index f8c29b1f..9276159f 100644 --- a/src/js/game/buildings/filter.js +++ b/src/js/game/buildings/filter.js @@ -1,104 +1,104 @@ -import { formatItemsPerSecond } from "../../core/utils"; -import { enumDirection, Vector } from "../../core/vector"; -import { T } from "../../translations"; -import { FilterComponent } from "../components/filter"; -import { ItemAcceptorComponent } from "../components/item_acceptor"; -import { ItemEjectorComponent } from "../components/item_ejector"; -import { enumPinSlotType, WiredPinsComponent } from "../components/wired_pins"; -import { Entity } from "../entity"; -import { defaultBuildingVariant, MetaBuilding } from "../meta_building"; -import { GameRoot } from "../root"; -import { enumHubGoalRewards } from "../tutorial_goals"; - -export class MetaFilterBuilding extends MetaBuilding { - constructor() { - super("filter"); - } - - static getAllVariantCombinations() { - return [ - { - internalId: 37, - variant: defaultBuildingVariant, - }, - ]; - } - - getSilhouetteColor() { - return "#c45c2e"; - } - - /** - * @param {GameRoot} root - */ - getIsUnlocked(root) { - return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_filter); - } - - getDimensions() { - return new Vector(2, 1); - } - - getShowWiresLayerPreview() { - return true; - } - - /** - * @param {GameRoot} root - * @param {string} variant - * @returns {Array<[string, string]>} - */ - getAdditionalStatistics(root, variant) { - if (root.gameMode.throughputDoesNotMatter()) { - return []; - } - const beltSpeed = root.hubGoals.getBeltBaseSpeed(); - return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(beltSpeed)]]; - } - - /** - * Creates the entity at the given location - * @param {Entity} entity - */ - setupEntityComponents(entity) { - entity.addComponent( - new WiredPinsComponent({ - slots: [ - { - pos: new Vector(0, 0), - direction: enumDirection.left, - type: enumPinSlotType.logicalAcceptor, - }, - ], - }) - ); - - entity.addComponent( - new ItemAcceptorComponent({ - slots: [ - { - pos: new Vector(0, 0), - direction: enumDirection.bottom, - }, - ], - }) - ); - - entity.addComponent( - new ItemEjectorComponent({ - slots: [ - { - pos: new Vector(0, 0), - direction: enumDirection.top, - }, - { - pos: new Vector(1, 0), - direction: enumDirection.right, - }, - ], - }) - ); - - entity.addComponent(new FilterComponent()); - } -} +import { formatItemsPerSecond } from "../../core/utils"; +import { enumDirection, Vector } from "../../core/vector"; +import { T } from "../../translations"; +import { FilterComponent } from "../components/filter"; +import { ItemAcceptorComponent } from "../components/item_acceptor"; +import { ItemEjectorComponent } from "../components/item_ejector"; +import { enumPinSlotType, WiredPinsComponent } from "../components/wired_pins"; +import { Entity } from "../entity"; +import { defaultBuildingVariant, MetaBuilding } from "../meta_building"; +import { GameRoot } from "../root"; +import { enumHubGoalRewards } from "../tutorial_goals"; + +export class MetaFilterBuilding extends MetaBuilding { + constructor() { + super("filter"); + } + + static getAllVariantCombinations() { + return [ + { + internalId: 37, + variant: defaultBuildingVariant, + }, + ]; + } + + getSilhouetteColor() { + return "#c45c2e"; + } + + /** + * @param {GameRoot} root + */ + getIsUnlocked(root) { + return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_filter); + } + + getDimensions() { + return new Vector(2, 1); + } + + getShowWiresLayerPreview() { + return true; + } + + /** + * @param {GameRoot} root + * @param {string} variant + * @returns {Array<[string, string]>} + */ + getAdditionalStatistics(root, variant) { + if (root.gameMode.throughputDoesNotMatter()) { + return []; + } + const beltSpeed = root.hubGoals.getBeltBaseSpeed(); + return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(beltSpeed)]]; + } + + /** + * Creates the entity at the given location + * @param {Entity} entity + */ + setupEntityComponents(entity) { + entity.addComponent( + new WiredPinsComponent({ + slots: [ + { + pos: new Vector(0, 0), + direction: enumDirection.left, + type: enumPinSlotType.logicalAcceptor, + }, + ], + }) + ); + + entity.addComponent( + new ItemAcceptorComponent({ + slots: [ + { + pos: new Vector(0, 0), + direction: enumDirection.bottom, + }, + ], + }) + ); + + entity.addComponent( + new ItemEjectorComponent({ + slots: [ + { + pos: new Vector(0, 0), + direction: enumDirection.top, + }, + { + pos: new Vector(1, 0), + direction: enumDirection.right, + }, + ], + }) + ); + + entity.addComponent(new FilterComponent()); + } +} diff --git a/src/js/game/buildings/hub.js b/src/js/game/buildings/hub.js index a0a9227e..7620ebfe 100644 --- a/src/js/game/buildings/hub.js +++ b/src/js/game/buildings/hub.js @@ -36,11 +36,6 @@ export class MetaHubBuilding extends MetaBuilding { return null; } - getSprite() { - // We render it ourself - return null; - } - getIsRemovable() { return false; } diff --git a/src/js/game/buildings/item_producer.js b/src/js/game/buildings/item_producer.js index a367beae..f6378931 100644 --- a/src/js/game/buildings/item_producer.js +++ b/src/js/game/buildings/item_producer.js @@ -1,53 +1,53 @@ -import { enumDirection, Vector } from "../../core/vector"; -import { ItemEjectorComponent } from "../components/item_ejector"; -import { ItemProducerComponent } from "../components/item_producer"; -import { enumPinSlotType, WiredPinsComponent } from "../components/wired_pins"; -import { Entity } from "../entity"; -import { defaultBuildingVariant, MetaBuilding } from "../meta_building"; - -export class MetaItemProducerBuilding extends MetaBuilding { - constructor() { - super("item_producer"); - } - - static getAllVariantCombinations() { - return [ - { - internalId: 61, - variant: defaultBuildingVariant, - }, - ]; - } - - getSilhouetteColor() { - return "#b37dcd"; - } - - getShowWiresLayerPreview() { - return true; - } - - /** - * Creates the entity at the given location - * @param {Entity} entity - */ - setupEntityComponents(entity) { - entity.addComponent( - new ItemEjectorComponent({ - slots: [{ pos: new Vector(0, 0), direction: enumDirection.top }], - }) - ); - entity.addComponent( - new WiredPinsComponent({ - slots: [ - { - pos: new Vector(0, 0), - type: enumPinSlotType.logicalAcceptor, - direction: enumDirection.bottom, - }, - ], - }) - ); - entity.addComponent(new ItemProducerComponent({})); - } -} +import { enumDirection, Vector } from "../../core/vector"; +import { ItemEjectorComponent } from "../components/item_ejector"; +import { ItemProducerComponent } from "../components/item_producer"; +import { enumPinSlotType, WiredPinsComponent } from "../components/wired_pins"; +import { Entity } from "../entity"; +import { defaultBuildingVariant, MetaBuilding } from "../meta_building"; + +export class MetaItemProducerBuilding extends MetaBuilding { + constructor() { + super("item_producer"); + } + + static getAllVariantCombinations() { + return [ + { + internalId: 61, + variant: defaultBuildingVariant, + }, + ]; + } + + getSilhouetteColor() { + return "#b37dcd"; + } + + getShowWiresLayerPreview() { + return true; + } + + /** + * Creates the entity at the given location + * @param {Entity} entity + */ + setupEntityComponents(entity) { + entity.addComponent( + new ItemEjectorComponent({ + slots: [{ pos: new Vector(0, 0), direction: enumDirection.top }], + }) + ); + entity.addComponent( + new WiredPinsComponent({ + slots: [ + { + pos: new Vector(0, 0), + type: enumPinSlotType.logicalAcceptor, + direction: enumDirection.bottom, + }, + ], + }) + ); + entity.addComponent(new ItemProducerComponent({})); + } +} diff --git a/src/js/game/buildings/lever.js b/src/js/game/buildings/lever.js index 970a4bc6..5f593e8b 100644 --- a/src/js/game/buildings/lever.js +++ b/src/js/game/buildings/lever.js @@ -1,66 +1,66 @@ -import { enumDirection, Vector } from "../../core/vector"; -import { enumPinSlotType, WiredPinsComponent } from "../components/wired_pins"; -import { Entity } from "../entity"; -import { defaultBuildingVariant, MetaBuilding } from "../meta_building"; -import { GameRoot } from "../root"; -import { LeverComponent } from "../components/lever"; -import { enumHubGoalRewards } from "../tutorial_goals"; - -export class MetaLeverBuilding extends MetaBuilding { - constructor() { - super("lever"); - } - - static getAllVariantCombinations() { - return [ - { - internalId: 33, - variant: defaultBuildingVariant, - }, - ]; - } - - getSilhouetteColor() { - // @todo: Render differently based on if its activated or not - return "#1a678b"; - } - - /** - * @param {GameRoot} root - */ - getIsUnlocked(root) { - return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_wires_painter_and_levers); - } - - getDimensions() { - return new Vector(1, 1); - } - - getSprite() { - return null; - } - - getShowWiresLayerPreview() { - return true; - } - - /** - * Creates the entity at the given location - * @param {Entity} entity - */ - setupEntityComponents(entity) { - entity.addComponent( - new WiredPinsComponent({ - slots: [ - { - pos: new Vector(0, 0), - direction: enumDirection.top, - type: enumPinSlotType.logicalEjector, - }, - ], - }) - ); - - entity.addComponent(new LeverComponent({})); - } -} +import { enumDirection, Vector } from "../../core/vector"; +import { enumPinSlotType, WiredPinsComponent } from "../components/wired_pins"; +import { Entity } from "../entity"; +import { defaultBuildingVariant, MetaBuilding } from "../meta_building"; +import { GameRoot } from "../root"; +import { LeverComponent } from "../components/lever"; +import { enumHubGoalRewards } from "../tutorial_goals"; + +export class MetaLeverBuilding extends MetaBuilding { + constructor() { + super("lever"); + } + + static getAllVariantCombinations() { + return [ + { + internalId: 33, + variant: defaultBuildingVariant, + }, + ]; + } + + getSilhouetteColor() { + // @todo: Render differently based on if its activated or not + return "#1a678b"; + } + + /** + * @param {GameRoot} root + */ + getIsUnlocked(root) { + return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_wires_painter_and_levers); + } + + getDimensions() { + return new Vector(1, 1); + } + + getSprite() { + return null; + } + + getShowWiresLayerPreview() { + return true; + } + + /** + * Creates the entity at the given location + * @param {Entity} entity + */ + setupEntityComponents(entity) { + entity.addComponent( + new WiredPinsComponent({ + slots: [ + { + pos: new Vector(0, 0), + direction: enumDirection.top, + type: enumPinSlotType.logicalEjector, + }, + ], + }) + ); + + entity.addComponent(new LeverComponent({})); + } +} diff --git a/src/js/game/buildings/logic_gate.js b/src/js/game/buildings/logic_gate.js index bd5e1940..0052363e 100644 --- a/src/js/game/buildings/logic_gate.js +++ b/src/js/game/buildings/logic_gate.js @@ -1,172 +1,172 @@ -import { enumDirection, Vector } from "../../core/vector"; -import { enumPinSlotType, WiredPinsComponent } from "../components/wired_pins"; -import { Entity } from "../entity"; -import { MetaBuilding, defaultBuildingVariant } from "../meta_building"; -import { GameRoot } from "../root"; -import { enumLogicGateType, LogicGateComponent } from "../components/logic_gate"; -import { generateMatrixRotations } from "../../core/utils"; -import { enumHubGoalRewards } from "../tutorial_goals"; - -/** @enum {string} */ -export const enumLogicGateVariants = { - not: "not", - xor: "xor", - or: "or", -}; - -/** @enum {string} */ -const enumVariantToGate = { - [defaultBuildingVariant]: enumLogicGateType.and, - [enumLogicGateVariants.not]: enumLogicGateType.not, - [enumLogicGateVariants.xor]: enumLogicGateType.xor, - [enumLogicGateVariants.or]: enumLogicGateType.or, -}; - -const overlayMatrices = { - [defaultBuildingVariant]: generateMatrixRotations([0, 1, 0, 1, 1, 1, 0, 1, 1]), - [enumLogicGateVariants.xor]: generateMatrixRotations([0, 1, 0, 1, 1, 1, 0, 1, 1]), - [enumLogicGateVariants.or]: generateMatrixRotations([0, 1, 0, 1, 1, 1, 0, 1, 1]), - [enumLogicGateVariants.not]: generateMatrixRotations([0, 1, 0, 0, 1, 0, 0, 1, 0]), -}; - -const colors = { - [defaultBuildingVariant]: "#f48d41", - [enumLogicGateVariants.xor]: "#f4a241", - [enumLogicGateVariants.or]: "#f4d041", - [enumLogicGateVariants.not]: "#f44184", -}; - -export class MetaLogicGateBuilding extends MetaBuilding { - constructor() { - super("logic_gate"); - } - - static getAllVariantCombinations() { - return [ - { - internalId: 32, - variant: defaultBuildingVariant, - }, - { - internalId: 34, - variant: enumLogicGateVariants.not, - }, - { - internalId: 35, - variant: enumLogicGateVariants.xor, - }, - { - internalId: 36, - variant: enumLogicGateVariants.or, - }, - ]; - } - - getSilhouetteColor(variant) { - return colors[variant]; - } - - /** - * @param {GameRoot} root - */ - getIsUnlocked(root) { - return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_logic_gates); - } - - /** @returns {"wires"} **/ - getLayer() { - return "wires"; - } - - getDimensions() { - return new Vector(1, 1); - } - - getSpecialOverlayRenderMatrix(rotation, rotationVariant, variant) { - return overlayMatrices[variant][rotation]; - } - - getAvailableVariants() { - return [ - defaultBuildingVariant, - enumLogicGateVariants.or, - enumLogicGateVariants.not, - enumLogicGateVariants.xor, - ]; - } - - getRenderPins() { - // We already have it included - return false; - } - - /** - * - * @param {Entity} entity - * @param {number} rotationVariant - */ - updateVariants(entity, rotationVariant, variant) { - const gateType = enumVariantToGate[variant]; - entity.components.LogicGate.type = gateType; - - const pinComp = entity.components.WiredPins; - - switch (gateType) { - case enumLogicGateType.and: - case enumLogicGateType.xor: - case enumLogicGateType.or: { - pinComp.setSlots([ - { - pos: new Vector(0, 0), - direction: enumDirection.top, - type: enumPinSlotType.logicalEjector, - }, - { - pos: new Vector(0, 0), - direction: enumDirection.left, - type: enumPinSlotType.logicalAcceptor, - }, - { - pos: new Vector(0, 0), - direction: enumDirection.right, - type: enumPinSlotType.logicalAcceptor, - }, - ]); - break; - } - - case enumLogicGateType.not: { - pinComp.setSlots([ - { - pos: new Vector(0, 0), - direction: enumDirection.top, - type: enumPinSlotType.logicalEjector, - }, - { - pos: new Vector(0, 0), - direction: enumDirection.bottom, - type: enumPinSlotType.logicalAcceptor, - }, - ]); - break; - } - - default: - assertAlways("unknown logic gate type: " + gateType); - } - } - - /** - * Creates the entity at the given location - * @param {Entity} entity - */ - setupEntityComponents(entity) { - entity.addComponent( - new WiredPinsComponent({ - slots: [], - }) - ); - - entity.addComponent(new LogicGateComponent({})); - } -} +import { enumDirection, Vector } from "../../core/vector"; +import { enumPinSlotType, WiredPinsComponent } from "../components/wired_pins"; +import { Entity } from "../entity"; +import { MetaBuilding, defaultBuildingVariant } from "../meta_building"; +import { GameRoot } from "../root"; +import { enumLogicGateType, LogicGateComponent } from "../components/logic_gate"; +import { generateMatrixRotations } from "../../core/utils"; +import { enumHubGoalRewards } from "../tutorial_goals"; + +/** @enum {string} */ +export const enumLogicGateVariants = { + not: "not", + xor: "xor", + or: "or", +}; + +/** @enum {string} */ +const enumVariantToGate = { + [defaultBuildingVariant]: enumLogicGateType.and, + [enumLogicGateVariants.not]: enumLogicGateType.not, + [enumLogicGateVariants.xor]: enumLogicGateType.xor, + [enumLogicGateVariants.or]: enumLogicGateType.or, +}; + +const overlayMatrices = { + [defaultBuildingVariant]: generateMatrixRotations([0, 1, 0, 1, 1, 1, 0, 1, 1]), + [enumLogicGateVariants.xor]: generateMatrixRotations([0, 1, 0, 1, 1, 1, 0, 1, 1]), + [enumLogicGateVariants.or]: generateMatrixRotations([0, 1, 0, 1, 1, 1, 0, 1, 1]), + [enumLogicGateVariants.not]: generateMatrixRotations([0, 1, 0, 0, 1, 0, 0, 1, 0]), +}; + +const colors = { + [defaultBuildingVariant]: "#f48d41", + [enumLogicGateVariants.xor]: "#f4a241", + [enumLogicGateVariants.or]: "#f4d041", + [enumLogicGateVariants.not]: "#f44184", +}; + +export class MetaLogicGateBuilding extends MetaBuilding { + constructor() { + super("logic_gate"); + } + + static getAllVariantCombinations() { + return [ + { + internalId: 32, + variant: defaultBuildingVariant, + }, + { + internalId: 34, + variant: enumLogicGateVariants.not, + }, + { + internalId: 35, + variant: enumLogicGateVariants.xor, + }, + { + internalId: 36, + variant: enumLogicGateVariants.or, + }, + ]; + } + + getSilhouetteColor(variant) { + return colors[variant]; + } + + /** + * @param {GameRoot} root + */ + getIsUnlocked(root) { + return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_logic_gates); + } + + /** @returns {"wires"} **/ + getLayer() { + return "wires"; + } + + getDimensions() { + return new Vector(1, 1); + } + + getSpecialOverlayRenderMatrix(rotation, rotationVariant, variant) { + return overlayMatrices[variant][rotation]; + } + + getAvailableVariants() { + return [ + defaultBuildingVariant, + enumLogicGateVariants.or, + enumLogicGateVariants.not, + enumLogicGateVariants.xor, + ]; + } + + getRenderPins() { + // We already have it included + return false; + } + + /** + * + * @param {Entity} entity + * @param {number} rotationVariant + */ + updateVariants(entity, rotationVariant, variant) { + const gateType = enumVariantToGate[variant]; + entity.components.LogicGate.type = gateType; + + const pinComp = entity.components.WiredPins; + + switch (gateType) { + case enumLogicGateType.and: + case enumLogicGateType.xor: + case enumLogicGateType.or: { + pinComp.setSlots([ + { + pos: new Vector(0, 0), + direction: enumDirection.top, + type: enumPinSlotType.logicalEjector, + }, + { + pos: new Vector(0, 0), + direction: enumDirection.left, + type: enumPinSlotType.logicalAcceptor, + }, + { + pos: new Vector(0, 0), + direction: enumDirection.right, + type: enumPinSlotType.logicalAcceptor, + }, + ]); + break; + } + + case enumLogicGateType.not: { + pinComp.setSlots([ + { + pos: new Vector(0, 0), + direction: enumDirection.top, + type: enumPinSlotType.logicalEjector, + }, + { + pos: new Vector(0, 0), + direction: enumDirection.bottom, + type: enumPinSlotType.logicalAcceptor, + }, + ]); + break; + } + + default: + assertAlways("unknown logic gate type: " + gateType); + } + } + + /** + * Creates the entity at the given location + * @param {Entity} entity + */ + setupEntityComponents(entity) { + entity.addComponent( + new WiredPinsComponent({ + slots: [], + }) + ); + + entity.addComponent(new LogicGateComponent({})); + } +} diff --git a/src/js/game/buildings/miner.js b/src/js/game/buildings/miner.js index b086a562..5365b67f 100644 --- a/src/js/game/buildings/miner.js +++ b/src/js/game/buildings/miner.js @@ -1,97 +1,97 @@ -import { enumDirection, Vector } from "../../core/vector"; -import { ItemEjectorComponent } from "../components/item_ejector"; -import { MinerComponent } from "../components/miner"; -import { Entity } from "../entity"; -import { MetaBuilding, defaultBuildingVariant } from "../meta_building"; -import { GameRoot } from "../root"; -import { enumHubGoalRewards } from "../tutorial_goals"; -import { T } from "../../translations"; -import { formatItemsPerSecond, generateMatrixRotations } from "../../core/utils"; - -/** @enum {string} */ -export const enumMinerVariants = { chainable: "chainable" }; - -const overlayMatrix = { - [defaultBuildingVariant]: generateMatrixRotations([1, 1, 1, 1, 0, 1, 1, 1, 1]), - [enumMinerVariants.chainable]: generateMatrixRotations([0, 1, 0, 1, 1, 1, 1, 1, 1]), -}; - -export class MetaMinerBuilding extends MetaBuilding { - constructor() { - super("miner"); - } - - static getAllVariantCombinations() { - return [ - { - internalId: 7, - variant: defaultBuildingVariant, - }, - { - internalId: 8, - variant: enumMinerVariants.chainable, - }, - ]; - } - - getSilhouetteColor() { - return "#b37dcd"; - } - - /** - * @param {GameRoot} root - * @param {string} variant - * @returns {Array<[string, string]>} - */ - getAdditionalStatistics(root, variant) { - if (root.gameMode.throughputDoesNotMatter()) { - return []; - } - const speed = root.hubGoals.getMinerBaseSpeed(); - return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(speed)]]; - } - - /** - * - * @param {GameRoot} root - */ - getAvailableVariants(root) { - if (root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_miner_chainable)) { - return [enumMinerVariants.chainable]; - } - return super.getAvailableVariants(root); - } - - /** - * @param {number} rotation - * @param {number} rotationVariant - * @param {string} variant - * @param {Entity} entity - */ - getSpecialOverlayRenderMatrix(rotation, rotationVariant, variant, entity) { - return overlayMatrix[variant][rotation]; - } - - /** - * Creates the entity at the given location - * @param {Entity} entity - */ - setupEntityComponents(entity) { - entity.addComponent(new MinerComponent({})); - entity.addComponent( - new ItemEjectorComponent({ - slots: [{ pos: new Vector(0, 0), direction: enumDirection.top }], - }) - ); - } - - /** - * - * @param {Entity} entity - * @param {number} rotationVariant - * @param {string} variant - */ - updateVariants(entity, rotationVariant, variant) { - entity.components.Miner.chainable = variant === enumMinerVariants.chainable; - } -} +import { enumDirection, Vector } from "../../core/vector"; +import { ItemEjectorComponent } from "../components/item_ejector"; +import { MinerComponent } from "../components/miner"; +import { Entity } from "../entity"; +import { MetaBuilding, defaultBuildingVariant } from "../meta_building"; +import { GameRoot } from "../root"; +import { enumHubGoalRewards } from "../tutorial_goals"; +import { T } from "../../translations"; +import { formatItemsPerSecond, generateMatrixRotations } from "../../core/utils"; + +/** @enum {string} */ +export const enumMinerVariants = { chainable: "chainable" }; + +const overlayMatrix = { + [defaultBuildingVariant]: generateMatrixRotations([1, 1, 1, 1, 0, 1, 1, 1, 1]), + [enumMinerVariants.chainable]: generateMatrixRotations([0, 1, 0, 1, 1, 1, 1, 1, 1]), +}; + +export class MetaMinerBuilding extends MetaBuilding { + constructor() { + super("miner"); + } + + static getAllVariantCombinations() { + return [ + { + internalId: 7, + variant: defaultBuildingVariant, + }, + { + internalId: 8, + variant: enumMinerVariants.chainable, + }, + ]; + } + + getSilhouetteColor() { + return "#b37dcd"; + } + + /** + * @param {GameRoot} root + * @param {string} variant + * @returns {Array<[string, string]>} + */ + getAdditionalStatistics(root, variant) { + if (root.gameMode.throughputDoesNotMatter()) { + return []; + } + const speed = root.hubGoals.getMinerBaseSpeed(); + return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(speed)]]; + } + + /** + * + * @param {GameRoot} root + */ + getAvailableVariants(root) { + if (root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_miner_chainable)) { + return [enumMinerVariants.chainable]; + } + return super.getAvailableVariants(root); + } + + /** + * @param {number} rotation + * @param {number} rotationVariant + * @param {string} variant + * @param {Entity} entity + */ + getSpecialOverlayRenderMatrix(rotation, rotationVariant, variant, entity) { + return overlayMatrix[variant][rotation]; + } + + /** + * Creates the entity at the given location + * @param {Entity} entity + */ + setupEntityComponents(entity) { + entity.addComponent(new MinerComponent({})); + entity.addComponent( + new ItemEjectorComponent({ + slots: [{ pos: new Vector(0, 0), direction: enumDirection.top }], + }) + ); + } + + /** + * + * @param {Entity} entity + * @param {number} rotationVariant + * @param {string} variant + */ + updateVariants(entity, rotationVariant, variant) { + entity.components.Miner.chainable = variant === enumMinerVariants.chainable; + } +} diff --git a/src/js/game/buildings/reader.js b/src/js/game/buildings/reader.js index 5c9307c3..417d2fcb 100644 --- a/src/js/game/buildings/reader.js +++ b/src/js/game/buildings/reader.js @@ -1,124 +1,124 @@ -import { enumDirection, Vector } from "../../core/vector"; -import { ItemAcceptorComponent } from "../components/item_acceptor"; -import { ItemEjectorComponent } from "../components/item_ejector"; -import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/item_processor"; -import { enumPinSlotType, WiredPinsComponent } from "../components/wired_pins"; -import { Entity } from "../entity"; -import { defaultBuildingVariant, MetaBuilding } from "../meta_building"; -import { GameRoot } from "../root"; -import { BeltUnderlaysComponent } from "../components/belt_underlays"; -import { BeltReaderComponent } from "../components/belt_reader"; -import { enumHubGoalRewards } from "../tutorial_goals"; -import { generateMatrixRotations } from "../../core/utils"; - -const overlayMatrix = generateMatrixRotations([0, 1, 0, 0, 1, 0, 0, 1, 0]); - -export class MetaReaderBuilding extends MetaBuilding { - constructor() { - super("reader"); - } - - static getAllVariantCombinations() { - return [ - { - internalId: 49, - variant: defaultBuildingVariant, - }, - ]; - } - - getSilhouetteColor() { - return "#25fff2"; - } - - /** - * @param {GameRoot} root - */ - getIsUnlocked(root) { - return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_belt_reader); - } - - getDimensions() { - return new Vector(1, 1); - } - - getShowWiresLayerPreview() { - return true; - } - - /** - * @param {number} rotation - * @param {number} rotationVariant - * @param {string} variant - * @param {Entity} entity - * @returns {Array|null} - */ - getSpecialOverlayRenderMatrix(rotation, rotationVariant, variant, entity) { - return overlayMatrix[rotation]; - } - - /** - * Creates the entity at the given location - * @param {Entity} entity - */ - setupEntityComponents(entity) { - entity.addComponent( - new WiredPinsComponent({ - slots: [ - { - pos: new Vector(0, 0), - direction: enumDirection.right, - type: enumPinSlotType.logicalEjector, - }, - { - pos: new Vector(0, 0), - direction: enumDirection.left, - type: enumPinSlotType.logicalEjector, - }, - ], - }) - ); - - entity.addComponent( - new ItemAcceptorComponent({ - slots: [ - { - pos: new Vector(0, 0), - direction: enumDirection.bottom, - }, - ], - }) - ); - - entity.addComponent( - new ItemEjectorComponent({ - slots: [ - { - pos: new Vector(0, 0), - direction: enumDirection.top, - }, - ], - }) - ); - - entity.addComponent( - new ItemProcessorComponent({ - processorType: enumItemProcessorTypes.reader, - inputsPerCharge: 1, - }) - ); - - entity.addComponent( - new BeltUnderlaysComponent({ - underlays: [ - { - pos: new Vector(0, 0), - direction: enumDirection.top, - }, - ], - }) - ); - - entity.addComponent(new BeltReaderComponent()); - } -} +import { enumDirection, Vector } from "../../core/vector"; +import { ItemAcceptorComponent } from "../components/item_acceptor"; +import { ItemEjectorComponent } from "../components/item_ejector"; +import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/item_processor"; +import { enumPinSlotType, WiredPinsComponent } from "../components/wired_pins"; +import { Entity } from "../entity"; +import { defaultBuildingVariant, MetaBuilding } from "../meta_building"; +import { GameRoot } from "../root"; +import { BeltUnderlaysComponent } from "../components/belt_underlays"; +import { BeltReaderComponent } from "../components/belt_reader"; +import { enumHubGoalRewards } from "../tutorial_goals"; +import { generateMatrixRotations } from "../../core/utils"; + +const overlayMatrix = generateMatrixRotations([0, 1, 0, 0, 1, 0, 0, 1, 0]); + +export class MetaReaderBuilding extends MetaBuilding { + constructor() { + super("reader"); + } + + static getAllVariantCombinations() { + return [ + { + internalId: 49, + variant: defaultBuildingVariant, + }, + ]; + } + + getSilhouetteColor() { + return "#25fff2"; + } + + /** + * @param {GameRoot} root + */ + getIsUnlocked(root) { + return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_belt_reader); + } + + getDimensions() { + return new Vector(1, 1); + } + + getShowWiresLayerPreview() { + return true; + } + + /** + * @param {number} rotation + * @param {number} rotationVariant + * @param {string} variant + * @param {Entity} entity + * @returns {Array|null} + */ + getSpecialOverlayRenderMatrix(rotation, rotationVariant, variant, entity) { + return overlayMatrix[rotation]; + } + + /** + * Creates the entity at the given location + * @param {Entity} entity + */ + setupEntityComponents(entity) { + entity.addComponent( + new WiredPinsComponent({ + slots: [ + { + pos: new Vector(0, 0), + direction: enumDirection.right, + type: enumPinSlotType.logicalEjector, + }, + { + pos: new Vector(0, 0), + direction: enumDirection.left, + type: enumPinSlotType.logicalEjector, + }, + ], + }) + ); + + entity.addComponent( + new ItemAcceptorComponent({ + slots: [ + { + pos: new Vector(0, 0), + direction: enumDirection.bottom, + }, + ], + }) + ); + + entity.addComponent( + new ItemEjectorComponent({ + slots: [ + { + pos: new Vector(0, 0), + direction: enumDirection.top, + }, + ], + }) + ); + + entity.addComponent( + new ItemProcessorComponent({ + processorType: enumItemProcessorTypes.reader, + inputsPerCharge: 1, + }) + ); + + entity.addComponent( + new BeltUnderlaysComponent({ + underlays: [ + { + pos: new Vector(0, 0), + direction: enumDirection.top, + }, + ], + }) + ); + + entity.addComponent(new BeltReaderComponent()); + } +} diff --git a/src/js/game/buildings/rotater.js b/src/js/game/buildings/rotator.js similarity index 78% rename from src/js/game/buildings/rotater.js rename to src/js/game/buildings/rotator.js index e1080767..b0a86f67 100644 --- a/src/js/game/buildings/rotater.js +++ b/src/js/game/buildings/rotator.js @@ -1,163 +1,163 @@ -import { formatItemsPerSecond, generateMatrixRotations } from "../../core/utils"; -import { enumDirection, Vector } from "../../core/vector"; -import { T } from "../../translations"; -import { ItemAcceptorComponent } from "../components/item_acceptor"; -import { ItemEjectorComponent } from "../components/item_ejector"; -import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/item_processor"; -import { Entity } from "../entity"; -import { defaultBuildingVariant, MetaBuilding } from "../meta_building"; -import { GameRoot } from "../root"; -import { enumHubGoalRewards } from "../tutorial_goals"; - -/** @enum {string} */ -export const enumRotaterVariants = { ccw: "ccw", rotate180: "rotate180" }; - -const overlayMatrices = { - [defaultBuildingVariant]: generateMatrixRotations([0, 1, 1, 1, 1, 0, 0, 1, 1]), - [enumRotaterVariants.ccw]: generateMatrixRotations([1, 1, 0, 0, 1, 1, 1, 1, 0]), - [enumRotaterVariants.rotate180]: generateMatrixRotations([1, 1, 0, 1, 1, 1, 0, 1, 1]), -}; - -export class MetaRotaterBuilding extends MetaBuilding { - constructor() { - super("rotater"); - } - - static getAllVariantCombinations() { - return [ - { - internalId: 11, - variant: defaultBuildingVariant, - }, - { - internalId: 12, - variant: enumRotaterVariants.ccw, - }, - { - internalId: 13, - variant: enumRotaterVariants.rotate180, - }, - ]; - } - - getSilhouetteColor() { - return "#7dc6cd"; - } - - /** - * @param {number} rotation - * @param {number} rotationVariant - * @param {string} variant - * @param {Entity} entity - * @returns {Array|null} - */ - getSpecialOverlayRenderMatrix(rotation, rotationVariant, variant, entity) { - const matrix = overlayMatrices[variant]; - if (matrix) { - return matrix[rotation]; - } - return null; - } - - /** - * @param {GameRoot} root - * @param {string} variant - * @returns {Array<[string, string]>} - */ - getAdditionalStatistics(root, variant) { - if (root.gameMode.throughputDoesNotMatter()) { - return []; - } - switch (variant) { - case defaultBuildingVariant: { - const speed = root.hubGoals.getProcessorBaseSpeed(enumItemProcessorTypes.rotater); - return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(speed)]]; - } - case enumRotaterVariants.ccw: { - const speed = root.hubGoals.getProcessorBaseSpeed(enumItemProcessorTypes.rotaterCCW); - return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(speed)]]; - } - case enumRotaterVariants.rotate180: { - const speed = root.hubGoals.getProcessorBaseSpeed(enumItemProcessorTypes.rotater180); - return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(speed)]]; - } - } - } - - /** - * - * @param {GameRoot} root - */ - getAvailableVariants(root) { - let variants = [defaultBuildingVariant]; - if (root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_rotater_ccw)) { - variants.push(enumRotaterVariants.ccw); - } - if (root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_rotater_180)) { - variants.push(enumRotaterVariants.rotate180); - } - return variants; - } - - /** - * @param {GameRoot} root - */ - getIsUnlocked(root) { - return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_rotater); - } - - /** - * Creates the entity at the given location - * @param {Entity} entity - */ - setupEntityComponents(entity) { - entity.addComponent( - new ItemProcessorComponent({ - inputsPerCharge: 1, - processorType: enumItemProcessorTypes.rotater, - }) - ); - - entity.addComponent( - new ItemEjectorComponent({ - slots: [{ pos: new Vector(0, 0), direction: enumDirection.top }], - }) - ); - entity.addComponent( - new ItemAcceptorComponent({ - slots: [ - { - pos: new Vector(0, 0), - direction: enumDirection.bottom, - filter: "shape", - }, - ], - }) - ); - } - - /** - * - * @param {Entity} entity - * @param {number} rotationVariant - * @param {string} variant - */ - updateVariants(entity, rotationVariant, variant) { - switch (variant) { - case defaultBuildingVariant: { - entity.components.ItemProcessor.type = enumItemProcessorTypes.rotater; - break; - } - case enumRotaterVariants.ccw: { - entity.components.ItemProcessor.type = enumItemProcessorTypes.rotaterCCW; - break; - } - case enumRotaterVariants.rotate180: { - entity.components.ItemProcessor.type = enumItemProcessorTypes.rotater180; - break; - } - default: - assertAlways(false, "Unknown rotater variant: " + variant); - } - } -} +import { formatItemsPerSecond, generateMatrixRotations } from "../../core/utils"; +import { enumDirection, Vector } from "../../core/vector"; +import { T } from "../../translations"; +import { ItemAcceptorComponent } from "../components/item_acceptor"; +import { ItemEjectorComponent } from "../components/item_ejector"; +import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/item_processor"; +import { Entity } from "../entity"; +import { defaultBuildingVariant, MetaBuilding } from "../meta_building"; +import { GameRoot } from "../root"; +import { enumHubGoalRewards } from "../tutorial_goals"; + +/** @enum {string} */ +export const enumRotatorVariants = { ccw: "ccw", rotate180: "rotate180" }; + +const overlayMatrices = { + [defaultBuildingVariant]: generateMatrixRotations([0, 1, 1, 1, 1, 0, 0, 1, 1]), + [enumRotatorVariants.ccw]: generateMatrixRotations([1, 1, 0, 0, 1, 1, 1, 1, 0]), + [enumRotatorVariants.rotate180]: generateMatrixRotations([1, 1, 0, 1, 1, 1, 0, 1, 1]), +}; + +export class MetaRotatorBuilding extends MetaBuilding { + constructor() { + super("rotator"); + } + + static getAllVariantCombinations() { + return [ + { + internalId: 11, + variant: defaultBuildingVariant, + }, + { + internalId: 12, + variant: enumRotatorVariants.ccw, + }, + { + internalId: 13, + variant: enumRotatorVariants.rotate180, + }, + ]; + } + + getSilhouetteColor() { + return "#7dc6cd"; + } + + /** + * @param {number} rotation + * @param {number} rotationVariant + * @param {string} variant + * @param {Entity} entity + * @returns {Array|null} + */ + getSpecialOverlayRenderMatrix(rotation, rotationVariant, variant, entity) { + const matrix = overlayMatrices[variant]; + if (matrix) { + return matrix[rotation]; + } + return null; + } + + /** + * @param {GameRoot} root + * @param {string} variant + * @returns {Array<[string, string]>} + */ + getAdditionalStatistics(root, variant) { + if (root.gameMode.throughputDoesNotMatter()) { + return []; + } + switch (variant) { + case defaultBuildingVariant: { + const speed = root.hubGoals.getProcessorBaseSpeed(enumItemProcessorTypes.rotator); + return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(speed)]]; + } + case enumRotatorVariants.ccw: { + const speed = root.hubGoals.getProcessorBaseSpeed(enumItemProcessorTypes.rotatorCCW); + return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(speed)]]; + } + case enumRotatorVariants.rotate180: { + const speed = root.hubGoals.getProcessorBaseSpeed(enumItemProcessorTypes.rotator180); + return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(speed)]]; + } + } + } + + /** + * + * @param {GameRoot} root + */ + getAvailableVariants(root) { + let variants = [defaultBuildingVariant]; + if (root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_rotator_ccw)) { + variants.push(enumRotatorVariants.ccw); + } + if (root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_rotator_180)) { + variants.push(enumRotatorVariants.rotate180); + } + return variants; + } + + /** + * @param {GameRoot} root + */ + getIsUnlocked(root) { + return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_rotator); + } + + /** + * Creates the entity at the given location + * @param {Entity} entity + */ + setupEntityComponents(entity) { + entity.addComponent( + new ItemProcessorComponent({ + inputsPerCharge: 1, + processorType: enumItemProcessorTypes.rotator, + }) + ); + + entity.addComponent( + new ItemEjectorComponent({ + slots: [{ pos: new Vector(0, 0), direction: enumDirection.top }], + }) + ); + entity.addComponent( + new ItemAcceptorComponent({ + slots: [ + { + pos: new Vector(0, 0), + direction: enumDirection.bottom, + filter: "shape", + }, + ], + }) + ); + } + + /** + * + * @param {Entity} entity + * @param {number} rotationVariant + * @param {string} variant + */ + updateVariants(entity, rotationVariant, variant) { + switch (variant) { + case defaultBuildingVariant: { + entity.components.ItemProcessor.type = enumItemProcessorTypes.rotator; + break; + } + case enumRotatorVariants.ccw: { + entity.components.ItemProcessor.type = enumItemProcessorTypes.rotatorCCW; + break; + } + case enumRotatorVariants.rotate180: { + entity.components.ItemProcessor.type = enumItemProcessorTypes.rotator180; + break; + } + default: + assertAlways(false, "Unknown rotator variant: " + variant); + } + } +} diff --git a/src/js/game/buildings/storage.js b/src/js/game/buildings/storage.js index 78f398be..4a8d39c5 100644 --- a/src/js/game/buildings/storage.js +++ b/src/js/game/buildings/storage.js @@ -1,110 +1,110 @@ -import { formatBigNumber } from "../../core/utils"; -import { enumDirection, Vector } from "../../core/vector"; -import { T } from "../../translations"; -import { ItemAcceptorComponent } from "../components/item_acceptor"; -import { ItemEjectorComponent } from "../components/item_ejector"; -import { StorageComponent } from "../components/storage"; -import { enumPinSlotType, WiredPinsComponent } from "../components/wired_pins"; -import { Entity } from "../entity"; -import { defaultBuildingVariant, MetaBuilding } from "../meta_building"; -import { GameRoot } from "../root"; -import { enumHubGoalRewards } from "../tutorial_goals"; - -const storageSize = 5000; - -export class MetaStorageBuilding extends MetaBuilding { - constructor() { - super("storage"); - } - - static getAllVariantCombinations() { - return [ - { - internalId: 21, - variant: defaultBuildingVariant, - }, - ]; - } - - getSilhouetteColor() { - return "#bbdf6d"; - } - - /** - * @returns {Array<[string, string]>} - */ - getAdditionalStatistics(root, variant) { - return [[T.ingame.buildingPlacement.infoTexts.storage, formatBigNumber(storageSize)]]; - } - - getDimensions() { - return new Vector(2, 2); - } - - /** - * @param {GameRoot} root - */ - getIsUnlocked(root) { - return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_storage); - } - - /** - * Creates the entity at the given location - * @param {Entity} entity - */ - setupEntityComponents(entity) { - // Required, since the item processor needs this. - entity.addComponent( - new ItemEjectorComponent({ - slots: [ - { - pos: new Vector(0, 0), - direction: enumDirection.top, - }, - { - pos: new Vector(1, 0), - direction: enumDirection.top, - }, - ], - }) - ); - - entity.addComponent( - new ItemAcceptorComponent({ - slots: [ - { - pos: new Vector(0, 1), - direction: enumDirection.bottom, - }, - { - pos: new Vector(1, 1), - direction: enumDirection.bottom, - }, - ], - }) - ); - - entity.addComponent( - new StorageComponent({ - maximumStorage: storageSize, - }) - ); - - entity.addComponent( - new WiredPinsComponent({ - slots: [ - { - pos: new Vector(1, 1), - direction: enumDirection.right, - type: enumPinSlotType.logicalEjector, - }, - { - pos: new Vector(0, 1), - direction: enumDirection.left, - type: enumPinSlotType.logicalEjector, - }, - ], - }) - ); - } -} +import { formatBigNumber } from "../../core/utils"; +import { enumDirection, Vector } from "../../core/vector"; +import { T } from "../../translations"; +import { ItemAcceptorComponent } from "../components/item_acceptor"; +import { ItemEjectorComponent } from "../components/item_ejector"; +import { StorageComponent } from "../components/storage"; +import { enumPinSlotType, WiredPinsComponent } from "../components/wired_pins"; +import { Entity } from "../entity"; +import { defaultBuildingVariant, MetaBuilding } from "../meta_building"; +import { GameRoot } from "../root"; +import { enumHubGoalRewards } from "../tutorial_goals"; + +const storageSize = 5000; + +export class MetaStorageBuilding extends MetaBuilding { + constructor() { + super("storage"); + } + + static getAllVariantCombinations() { + return [ + { + internalId: 21, + variant: defaultBuildingVariant, + }, + ]; + } + + getSilhouetteColor() { + return "#bbdf6d"; + } + + /** + * @returns {Array<[string, string]>} + */ + getAdditionalStatistics(root, variant) { + return [[T.ingame.buildingPlacement.infoTexts.storage, formatBigNumber(storageSize)]]; + } + + getDimensions() { + return new Vector(2, 2); + } + + /** + * @param {GameRoot} root + */ + getIsUnlocked(root) { + return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_storage); + } + + /** + * Creates the entity at the given location + * @param {Entity} entity + */ + setupEntityComponents(entity) { + // Required, since the item processor needs this. + entity.addComponent( + new ItemEjectorComponent({ + slots: [ + { + pos: new Vector(0, 0), + direction: enumDirection.top, + }, + { + pos: new Vector(1, 0), + direction: enumDirection.top, + }, + ], + }) + ); + + entity.addComponent( + new ItemAcceptorComponent({ + slots: [ + { + pos: new Vector(0, 1), + direction: enumDirection.bottom, + }, + { + pos: new Vector(1, 1), + direction: enumDirection.bottom, + }, + ], + }) + ); + + entity.addComponent( + new StorageComponent({ + maximumStorage: storageSize, + }) + ); + + entity.addComponent( + new WiredPinsComponent({ + slots: [ + { + pos: new Vector(1, 1), + direction: enumDirection.right, + type: enumPinSlotType.logicalEjector, + }, + { + pos: new Vector(0, 1), + direction: enumDirection.left, + type: enumPinSlotType.logicalEjector, + }, + ], + }) + ); + } +} diff --git a/src/js/game/buildings/transistor.js b/src/js/game/buildings/transistor.js index 1505903a..8db1dbac 100644 --- a/src/js/game/buildings/transistor.js +++ b/src/js/game/buildings/transistor.js @@ -1,114 +1,114 @@ -import { generateMatrixRotations } from "../../core/utils"; -import { enumDirection, Vector } from "../../core/vector"; -import { enumLogicGateType, LogicGateComponent } from "../components/logic_gate"; -import { enumPinSlotType, WiredPinsComponent } from "../components/wired_pins"; -import { Entity } from "../entity"; -import { defaultBuildingVariant, MetaBuilding } from "../meta_building"; -import { GameRoot } from "../root"; -import { enumHubGoalRewards } from "../tutorial_goals"; - -/** @enum {string} */ -export const enumTransistorVariants = { - mirrored: "mirrored", -}; - -const overlayMatrices = { - [defaultBuildingVariant]: generateMatrixRotations([0, 1, 0, 1, 1, 0, 0, 1, 0]), - [enumTransistorVariants.mirrored]: generateMatrixRotations([0, 1, 0, 0, 1, 1, 0, 1, 0]), -}; - -export class MetaTransistorBuilding extends MetaBuilding { - constructor() { - super("transistor"); - } - - static getAllVariantCombinations() { - return [ - { - internalId: 38, - variant: defaultBuildingVariant, - }, - { - internalId: 60, - variant: enumTransistorVariants.mirrored, - }, - ]; - } - - getSilhouetteColor() { - return "#bc3a61"; - } - - /** - * @param {GameRoot} root - */ - getIsUnlocked(root) { - return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_logic_gates); - } - - /** @returns {"wires"} **/ - getLayer() { - return "wires"; - } - - getDimensions() { - return new Vector(1, 1); - } - - getAvailableVariants() { - return [defaultBuildingVariant, enumTransistorVariants.mirrored]; - } - - getSpecialOverlayRenderMatrix(rotation, rotationVariant, variant) { - return overlayMatrices[variant][rotation]; - } - - getRenderPins() { - // We already have it included - return false; - } - - /** - * - * @param {Entity} entity - * @param {number} rotationVariant - */ - updateVariants(entity, rotationVariant, variant) { - entity.components.WiredPins.slots[1].direction = - variant === enumTransistorVariants.mirrored ? enumDirection.right : enumDirection.left; - } - - /** - * Creates the entity at the given location - * @param {Entity} entity - */ - setupEntityComponents(entity) { - entity.addComponent( - new WiredPinsComponent({ - slots: [ - { - pos: new Vector(0, 0), - direction: enumDirection.top, - type: enumPinSlotType.logicalEjector, - }, - { - pos: new Vector(0, 0), - direction: enumDirection.left, - type: enumPinSlotType.logicalAcceptor, - }, - { - pos: new Vector(0, 0), - direction: enumDirection.bottom, - type: enumPinSlotType.logicalAcceptor, - }, - ], - }) - ); - - entity.addComponent( - new LogicGateComponent({ - type: enumLogicGateType.transistor, - }) - ); - } -} +import { generateMatrixRotations } from "../../core/utils"; +import { enumDirection, Vector } from "../../core/vector"; +import { enumLogicGateType, LogicGateComponent } from "../components/logic_gate"; +import { enumPinSlotType, WiredPinsComponent } from "../components/wired_pins"; +import { Entity } from "../entity"; +import { defaultBuildingVariant, MetaBuilding } from "../meta_building"; +import { GameRoot } from "../root"; +import { enumHubGoalRewards } from "../tutorial_goals"; + +/** @enum {string} */ +export const enumTransistorVariants = { + mirrored: "mirrored", +}; + +const overlayMatrices = { + [defaultBuildingVariant]: generateMatrixRotations([0, 1, 0, 1, 1, 0, 0, 1, 0]), + [enumTransistorVariants.mirrored]: generateMatrixRotations([0, 1, 0, 0, 1, 1, 0, 1, 0]), +}; + +export class MetaTransistorBuilding extends MetaBuilding { + constructor() { + super("transistor"); + } + + static getAllVariantCombinations() { + return [ + { + internalId: 38, + variant: defaultBuildingVariant, + }, + { + internalId: 60, + variant: enumTransistorVariants.mirrored, + }, + ]; + } + + getSilhouetteColor() { + return "#bc3a61"; + } + + /** + * @param {GameRoot} root + */ + getIsUnlocked(root) { + return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_logic_gates); + } + + /** @returns {"wires"} **/ + getLayer() { + return "wires"; + } + + getDimensions() { + return new Vector(1, 1); + } + + getAvailableVariants() { + return [defaultBuildingVariant, enumTransistorVariants.mirrored]; + } + + getSpecialOverlayRenderMatrix(rotation, rotationVariant, variant) { + return overlayMatrices[variant][rotation]; + } + + getRenderPins() { + // We already have it included + return false; + } + + /** + * + * @param {Entity} entity + * @param {number} rotationVariant + */ + updateVariants(entity, rotationVariant, variant) { + entity.components.WiredPins.slots[1].direction = + variant === enumTransistorVariants.mirrored ? enumDirection.right : enumDirection.left; + } + + /** + * Creates the entity at the given location + * @param {Entity} entity + */ + setupEntityComponents(entity) { + entity.addComponent( + new WiredPinsComponent({ + slots: [ + { + pos: new Vector(0, 0), + direction: enumDirection.top, + type: enumPinSlotType.logicalEjector, + }, + { + pos: new Vector(0, 0), + direction: enumDirection.left, + type: enumPinSlotType.logicalAcceptor, + }, + { + pos: new Vector(0, 0), + direction: enumDirection.bottom, + type: enumPinSlotType.logicalAcceptor, + }, + ], + }) + ); + + entity.addComponent( + new LogicGateComponent({ + type: enumLogicGateType.transistor, + }) + ); + } +} diff --git a/src/js/game/buildings/trash.js b/src/js/game/buildings/trash.js index fcf7f11f..87dbcf09 100644 --- a/src/js/game/buildings/trash.js +++ b/src/js/game/buildings/trash.js @@ -1,6 +1,5 @@ import { generateMatrixRotations } from "../../core/utils"; import { enumDirection, Vector } from "../../core/vector"; -import { ACHIEVEMENTS } from "../../platform/achievement_provider"; import { ItemAcceptorComponent } from "../components/item_acceptor"; import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/item_processor"; import { Entity } from "../entity"; @@ -47,25 +46,6 @@ export class MetaTrashBuilding extends MetaBuilding { return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_cutter_and_trash); } - addAchievementReceiver(entity) { - if (!entity.root) { - return; - } - - const itemProcessor = entity.components.ItemProcessor; - const tryTakeItem = itemProcessor.tryTakeItem.bind(itemProcessor); - - itemProcessor.tryTakeItem = () => { - const taken = tryTakeItem(...arguments); - - if (taken) { - entity.root.signals.achievementCheck.dispatch(ACHIEVEMENTS.trash1000, 1); - } - - return taken; - }; - } - /** * Creates the entity at the given location * @param {Entity} entity @@ -100,7 +80,5 @@ export class MetaTrashBuilding extends MetaBuilding { processorType: enumItemProcessorTypes.trash, }) ); - - this.addAchievementReceiver(entity); } } diff --git a/src/js/game/buildings/underground_belt.js b/src/js/game/buildings/underground_belt.js index 7009ebd7..e9d0e8e9 100644 --- a/src/js/game/buildings/underground_belt.js +++ b/src/js/game/buildings/underground_belt.js @@ -1,300 +1,300 @@ -import { Loader } from "../../core/loader"; -import { enumDirection, Vector, enumAngleToDirection, enumDirectionToVector } from "../../core/vector"; -import { ItemAcceptorComponent } from "../components/item_acceptor"; -import { ItemEjectorComponent } from "../components/item_ejector"; -import { enumUndergroundBeltMode, UndergroundBeltComponent } from "../components/underground_belt"; -import { Entity } from "../entity"; -import { MetaBuilding, defaultBuildingVariant } from "../meta_building"; -import { GameRoot } from "../root"; -import { globalConfig } from "../../core/config"; -import { enumHubGoalRewards } from "../tutorial_goals"; -import { formatItemsPerSecond, generateMatrixRotations } from "../../core/utils"; -import { T } from "../../translations"; - -/** @enum {string} */ -export const arrayUndergroundRotationVariantToMode = [ - enumUndergroundBeltMode.sender, - enumUndergroundBeltMode.receiver, -]; - -/** @enum {string} */ -export const enumUndergroundBeltVariants = { tier2: "tier2" }; - -export const enumUndergroundBeltVariantToTier = { - [defaultBuildingVariant]: 0, - [enumUndergroundBeltVariants.tier2]: 1, -}; - -const colorsByRotationVariant = ["#6d9dff", "#71ff9c"]; - -const overlayMatrices = [ - // Sender - generateMatrixRotations([1, 1, 1, 0, 1, 0, 0, 1, 0]), - - // Receiver - generateMatrixRotations([0, 1, 0, 0, 1, 0, 1, 1, 1]), -]; - -export class MetaUndergroundBeltBuilding extends MetaBuilding { - constructor() { - super("underground_belt"); - } - - static getAllVariantCombinations() { - return [ - { - internalId: 22, - variant: defaultBuildingVariant, - rotationVariant: 0, - }, - { - internalId: 23, - variant: defaultBuildingVariant, - rotationVariant: 1, - }, - { - internalId: 24, - variant: enumUndergroundBeltVariants.tier2, - rotationVariant: 0, - }, - { - internalId: 25, - variant: enumUndergroundBeltVariants.tier2, - rotationVariant: 1, - }, - ]; - } - - getSilhouetteColor(variant, rotationVariant) { - return colorsByRotationVariant[rotationVariant]; - } - - getFlipOrientationAfterPlacement() { - return true; - } - - getStayInPlacementMode() { - return true; - } - - /** - * @param {number} rotation - * @param {number} rotationVariant - * @param {string} variant - * @param {Entity} entity - */ - getSpecialOverlayRenderMatrix(rotation, rotationVariant, variant, entity) { - return overlayMatrices[rotationVariant][rotation]; - } - - /** - * @param {GameRoot} root - * @param {string} variant - * @returns {Array<[string, string]>} - */ - getAdditionalStatistics(root, variant) { - const rangeTiles = - globalConfig.undergroundBeltMaxTilesByTier[enumUndergroundBeltVariantToTier[variant]]; - - const beltSpeed = root.hubGoals.getUndergroundBeltBaseSpeed(); - - /** @type {Array<[string, string]>} */ - const stats = [ - [ - T.ingame.buildingPlacement.infoTexts.range, - T.ingame.buildingPlacement.infoTexts.tiles.replace("", "" + rangeTiles), - ], - ]; - - if (root.gameMode.throughputDoesNotMatter()) { - return stats; - } - stats.push([T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(beltSpeed)]); - - return stats; - } - - /** - * @param {GameRoot} root - */ - getAvailableVariants(root) { - if (root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_underground_belt_tier_2)) { - return [defaultBuildingVariant, enumUndergroundBeltVariants.tier2]; - } - return super.getAvailableVariants(root); - } - - /** - * @param {number} rotationVariant - * @param {string} variant - */ - getPreviewSprite(rotationVariant, variant) { - let suffix = ""; - if (variant !== defaultBuildingVariant) { - suffix = "-" + variant; - } - - switch (arrayUndergroundRotationVariantToMode[rotationVariant]) { - case enumUndergroundBeltMode.sender: - return Loader.getSprite("sprites/buildings/underground_belt_entry" + suffix + ".png"); - case enumUndergroundBeltMode.receiver: - return Loader.getSprite("sprites/buildings/underground_belt_exit" + suffix + ".png"); - default: - assertAlways(false, "Invalid rotation variant"); - } - } - - /** - * @param {number} rotationVariant - * @param {string} variant - */ - getBlueprintSprite(rotationVariant, variant) { - let suffix = ""; - if (variant !== defaultBuildingVariant) { - suffix = "-" + variant; - } - - switch (arrayUndergroundRotationVariantToMode[rotationVariant]) { - case enumUndergroundBeltMode.sender: - return Loader.getSprite("sprites/blueprints/underground_belt_entry" + suffix + ".png"); - case enumUndergroundBeltMode.receiver: - return Loader.getSprite("sprites/blueprints/underground_belt_exit" + suffix + ".png"); - default: - assertAlways(false, "Invalid rotation variant"); - } - } - - /** - * @param {number} rotationVariant - * @param {string} variant - */ - getSprite(rotationVariant, variant) { - return this.getPreviewSprite(rotationVariant, variant); - } - - /** - * @param {GameRoot} root - */ - getIsUnlocked(root) { - return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_tunnel); - } - - /** - * Creates the entity at the given location - * @param {Entity} entity - */ - setupEntityComponents(entity) { - // Required, since the item processor needs this. - entity.addComponent( - new ItemEjectorComponent({ - slots: [], - }) - ); - - entity.addComponent(new UndergroundBeltComponent({})); - entity.addComponent( - new ItemAcceptorComponent({ - slots: [], - }) - ); - } - - /** - * Should compute the optimal rotation variant on the given tile - * @param {object} param0 - * @param {GameRoot} param0.root - * @param {Vector} param0.tile - * @param {number} param0.rotation - * @param {string} param0.variant - * @param {Layer} param0.layer - * @return {{ rotation: number, rotationVariant: number, connectedEntities?: Array }} - */ - computeOptimalDirectionAndRotationVariantAtTile({ root, tile, rotation, variant, layer }) { - const searchDirection = enumAngleToDirection[rotation]; - const searchVector = enumDirectionToVector[searchDirection]; - const tier = enumUndergroundBeltVariantToTier[variant]; - - const targetRotation = (rotation + 180) % 360; - const targetSenderRotation = rotation; - - for ( - let searchOffset = 1; - searchOffset <= globalConfig.undergroundBeltMaxTilesByTier[tier]; - ++searchOffset - ) { - tile = tile.addScalars(searchVector.x, searchVector.y); - - const contents = root.map.getTileContent(tile, "regular"); - if (contents) { - const undergroundComp = contents.components.UndergroundBelt; - if (undergroundComp && undergroundComp.tier === tier) { - const staticComp = contents.components.StaticMapEntity; - if (staticComp.rotation === targetRotation) { - if (undergroundComp.mode !== enumUndergroundBeltMode.sender) { - // If we encounter an underground receiver on our way which is also faced in our direction, we don't accept that - break; - } - return { - rotation: targetRotation, - rotationVariant: 1, - connectedEntities: [contents], - }; - } else if (staticComp.rotation === targetSenderRotation) { - // Draw connections to receivers - if (undergroundComp.mode === enumUndergroundBeltMode.receiver) { - return { - rotation: rotation, - rotationVariant: 0, - connectedEntities: [contents], - }; - } else { - break; - } - } - } - } - } - - return { - rotation, - rotationVariant: 0, - }; - } - - /** - * - * @param {Entity} entity - * @param {number} rotationVariant - * @param {string} variant - */ - updateVariants(entity, rotationVariant, variant) { - entity.components.UndergroundBelt.tier = enumUndergroundBeltVariantToTier[variant]; - - switch (arrayUndergroundRotationVariantToMode[rotationVariant]) { - case enumUndergroundBeltMode.sender: { - entity.components.UndergroundBelt.mode = enumUndergroundBeltMode.sender; - entity.components.ItemEjector.setSlots([]); - entity.components.ItemAcceptor.setSlots([ - { - pos: new Vector(0, 0), - direction: enumDirection.bottom, - }, - ]); - return; - } - case enumUndergroundBeltMode.receiver: { - entity.components.UndergroundBelt.mode = enumUndergroundBeltMode.receiver; - entity.components.ItemAcceptor.setSlots([]); - entity.components.ItemEjector.setSlots([ - { - pos: new Vector(0, 0), - direction: enumDirection.top, - }, - ]); - return; - } - default: - assertAlways(false, "Invalid rotation variant"); - } - } -} +import { Loader } from "../../core/loader"; +import { enumDirection, Vector, enumAngleToDirection, enumDirectionToVector } from "../../core/vector"; +import { ItemAcceptorComponent } from "../components/item_acceptor"; +import { ItemEjectorComponent } from "../components/item_ejector"; +import { enumUndergroundBeltMode, UndergroundBeltComponent } from "../components/underground_belt"; +import { Entity } from "../entity"; +import { MetaBuilding, defaultBuildingVariant } from "../meta_building"; +import { GameRoot } from "../root"; +import { globalConfig } from "../../core/config"; +import { enumHubGoalRewards } from "../tutorial_goals"; +import { formatItemsPerSecond, generateMatrixRotations } from "../../core/utils"; +import { T } from "../../translations"; + +/** @enum {string} */ +export const arrayUndergroundRotationVariantToMode = [ + enumUndergroundBeltMode.sender, + enumUndergroundBeltMode.receiver, +]; + +/** @enum {string} */ +export const enumUndergroundBeltVariants = { tier2: "tier2" }; + +export const enumUndergroundBeltVariantToTier = { + [defaultBuildingVariant]: 0, + [enumUndergroundBeltVariants.tier2]: 1, +}; + +const colorsByRotationVariant = ["#6d9dff", "#71ff9c"]; + +const overlayMatrices = [ + // Sender + generateMatrixRotations([1, 1, 1, 0, 1, 0, 0, 1, 0]), + + // Receiver + generateMatrixRotations([0, 1, 0, 0, 1, 0, 1, 1, 1]), +]; + +export class MetaUndergroundBeltBuilding extends MetaBuilding { + constructor() { + super("underground_belt"); + } + + static getAllVariantCombinations() { + return [ + { + internalId: 22, + variant: defaultBuildingVariant, + rotationVariant: 0, + }, + { + internalId: 23, + variant: defaultBuildingVariant, + rotationVariant: 1, + }, + { + internalId: 24, + variant: enumUndergroundBeltVariants.tier2, + rotationVariant: 0, + }, + { + internalId: 25, + variant: enumUndergroundBeltVariants.tier2, + rotationVariant: 1, + }, + ]; + } + + getSilhouetteColor(variant, rotationVariant) { + return colorsByRotationVariant[rotationVariant]; + } + + getFlipOrientationAfterPlacement() { + return true; + } + + getStayInPlacementMode() { + return true; + } + + /** + * @param {number} rotation + * @param {number} rotationVariant + * @param {string} variant + * @param {Entity} entity + */ + getSpecialOverlayRenderMatrix(rotation, rotationVariant, variant, entity) { + return overlayMatrices[rotationVariant][rotation]; + } + + /** + * @param {GameRoot} root + * @param {string} variant + * @returns {Array<[string, string]>} + */ + getAdditionalStatistics(root, variant) { + const rangeTiles = + globalConfig.undergroundBeltMaxTilesByTier[enumUndergroundBeltVariantToTier[variant]]; + + const beltSpeed = root.hubGoals.getUndergroundBeltBaseSpeed(); + + /** @type {Array<[string, string]>} */ + const stats = [ + [ + T.ingame.buildingPlacement.infoTexts.range, + T.ingame.buildingPlacement.infoTexts.tiles.replace("", "" + rangeTiles), + ], + ]; + + if (root.gameMode.throughputDoesNotMatter()) { + return stats; + } + stats.push([T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(beltSpeed)]); + + return stats; + } + + /** + * @param {GameRoot} root + */ + getAvailableVariants(root) { + if (root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_underground_belt_tier_2)) { + return [defaultBuildingVariant, enumUndergroundBeltVariants.tier2]; + } + return super.getAvailableVariants(root); + } + + /** + * @param {number} rotationVariant + * @param {string} variant + */ + getPreviewSprite(rotationVariant, variant) { + let suffix = ""; + if (variant !== defaultBuildingVariant) { + suffix = "-" + variant; + } + + switch (arrayUndergroundRotationVariantToMode[rotationVariant]) { + case enumUndergroundBeltMode.sender: + return Loader.getSprite("sprites/buildings/underground_belt_entry" + suffix + ".png"); + case enumUndergroundBeltMode.receiver: + return Loader.getSprite("sprites/buildings/underground_belt_exit" + suffix + ".png"); + default: + assertAlways(false, "Invalid rotation variant"); + } + } + + /** + * @param {number} rotationVariant + * @param {string} variant + */ + getBlueprintSprite(rotationVariant, variant) { + let suffix = ""; + if (variant !== defaultBuildingVariant) { + suffix = "-" + variant; + } + + switch (arrayUndergroundRotationVariantToMode[rotationVariant]) { + case enumUndergroundBeltMode.sender: + return Loader.getSprite("sprites/blueprints/underground_belt_entry" + suffix + ".png"); + case enumUndergroundBeltMode.receiver: + return Loader.getSprite("sprites/blueprints/underground_belt_exit" + suffix + ".png"); + default: + assertAlways(false, "Invalid rotation variant"); + } + } + + /** + * @param {number} rotationVariant + * @param {string} variant + */ + getSprite(rotationVariant, variant) { + return this.getPreviewSprite(rotationVariant, variant); + } + + /** + * @param {GameRoot} root + */ + getIsUnlocked(root) { + return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_tunnel); + } + + /** + * Creates the entity at the given location + * @param {Entity} entity + */ + setupEntityComponents(entity) { + // Required, since the item processor needs this. + entity.addComponent( + new ItemEjectorComponent({ + slots: [], + }) + ); + + entity.addComponent(new UndergroundBeltComponent({})); + entity.addComponent( + new ItemAcceptorComponent({ + slots: [], + }) + ); + } + + /** + * Should compute the optimal rotation variant on the given tile + * @param {object} param0 + * @param {GameRoot} param0.root + * @param {Vector} param0.tile + * @param {number} param0.rotation + * @param {string} param0.variant + * @param {Layer} param0.layer + * @return {{ rotation: number, rotationVariant: number, connectedEntities?: Array }} + */ + computeOptimalDirectionAndRotationVariantAtTile({ root, tile, rotation, variant, layer }) { + const searchDirection = enumAngleToDirection[rotation]; + const searchVector = enumDirectionToVector[searchDirection]; + const tier = enumUndergroundBeltVariantToTier[variant]; + + const targetRotation = (rotation + 180) % 360; + const targetSenderRotation = rotation; + + for ( + let searchOffset = 1; + searchOffset <= globalConfig.undergroundBeltMaxTilesByTier[tier]; + ++searchOffset + ) { + tile = tile.addScalars(searchVector.x, searchVector.y); + + const contents = root.map.getTileContent(tile, "regular"); + if (contents) { + const undergroundComp = contents.components.UndergroundBelt; + if (undergroundComp && undergroundComp.tier === tier) { + const staticComp = contents.components.StaticMapEntity; + if (staticComp.rotation === targetRotation) { + if (undergroundComp.mode !== enumUndergroundBeltMode.sender) { + // If we encounter an underground receiver on our way which is also faced in our direction, we don't accept that + break; + } + return { + rotation: targetRotation, + rotationVariant: 1, + connectedEntities: [contents], + }; + } else if (staticComp.rotation === targetSenderRotation) { + // Draw connections to receivers + if (undergroundComp.mode === enumUndergroundBeltMode.receiver) { + return { + rotation: rotation, + rotationVariant: 0, + connectedEntities: [contents], + }; + } else { + break; + } + } + } + } + } + + return { + rotation, + rotationVariant: 0, + }; + } + + /** + * + * @param {Entity} entity + * @param {number} rotationVariant + * @param {string} variant + */ + updateVariants(entity, rotationVariant, variant) { + entity.components.UndergroundBelt.tier = enumUndergroundBeltVariantToTier[variant]; + + switch (arrayUndergroundRotationVariantToMode[rotationVariant]) { + case enumUndergroundBeltMode.sender: { + entity.components.UndergroundBelt.mode = enumUndergroundBeltMode.sender; + entity.components.ItemEjector.setSlots([]); + entity.components.ItemAcceptor.setSlots([ + { + pos: new Vector(0, 0), + direction: enumDirection.bottom, + }, + ]); + return; + } + case enumUndergroundBeltMode.receiver: { + entity.components.UndergroundBelt.mode = enumUndergroundBeltMode.receiver; + entity.components.ItemAcceptor.setSlots([]); + entity.components.ItemEjector.setSlots([ + { + pos: new Vector(0, 0), + direction: enumDirection.top, + }, + ]); + return; + } + default: + assertAlways(false, "Invalid rotation variant"); + } + } +} diff --git a/src/js/game/buildings/virtual_processor.js b/src/js/game/buildings/virtual_processor.js index b42ceb5d..477a4bd9 100644 --- a/src/js/game/buildings/virtual_processor.js +++ b/src/js/game/buildings/virtual_processor.js @@ -1,188 +1,188 @@ -import { Vector, enumDirection } from "../../core/vector"; -import { LogicGateComponent, enumLogicGateType } from "../components/logic_gate"; -import { WiredPinsComponent, enumPinSlotType } from "../components/wired_pins"; -import { Entity } from "../entity"; -import { defaultBuildingVariant, MetaBuilding } from "../meta_building"; -import { GameRoot } from "../root"; -import { enumHubGoalRewards } from "../tutorial_goals"; -import { MetaCutterBuilding } from "./cutter"; -import { MetaPainterBuilding } from "./painter"; -import { MetaRotaterBuilding } from "./rotater"; -import { MetaStackerBuilding } from "./stacker"; - -/** @enum {string} */ -export const enumVirtualProcessorVariants = { - rotater: "rotater", - unstacker: "unstacker", - stacker: "stacker", - painter: "painter", -}; - -/** @enum {string} */ -const enumVariantToGate = { - [defaultBuildingVariant]: enumLogicGateType.cutter, - [enumVirtualProcessorVariants.rotater]: enumLogicGateType.rotater, - [enumVirtualProcessorVariants.unstacker]: enumLogicGateType.unstacker, - [enumVirtualProcessorVariants.stacker]: enumLogicGateType.stacker, - [enumVirtualProcessorVariants.painter]: enumLogicGateType.painter, -}; - -const colors = { - [defaultBuildingVariant]: new MetaCutterBuilding().getSilhouetteColor(), - [enumVirtualProcessorVariants.rotater]: new MetaRotaterBuilding().getSilhouetteColor(), - [enumVirtualProcessorVariants.unstacker]: new MetaStackerBuilding().getSilhouetteColor(), - [enumVirtualProcessorVariants.stacker]: new MetaStackerBuilding().getSilhouetteColor(), - [enumVirtualProcessorVariants.painter]: new MetaPainterBuilding().getSilhouetteColor(), -}; - -export class MetaVirtualProcessorBuilding extends MetaBuilding { - constructor() { - super("virtual_processor"); - } - - static getAllVariantCombinations() { - return [ - { - internalId: 42, - variant: defaultBuildingVariant, - }, - { - internalId: 44, - variant: enumVirtualProcessorVariants.rotater, - }, - { - internalId: 45, - variant: enumVirtualProcessorVariants.unstacker, - }, - { - internalId: 50, - variant: enumVirtualProcessorVariants.stacker, - }, - { - internalId: 51, - variant: enumVirtualProcessorVariants.painter, - }, - ]; - } - - getSilhouetteColor(variant) { - return colors[variant]; - } - - /** - * @param {GameRoot} root - */ - getIsUnlocked(root) { - return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_virtual_processing); - } - - /** @returns {"wires"} **/ - getLayer() { - return "wires"; - } - - getDimensions() { - return new Vector(1, 1); - } - - getAvailableVariants() { - return [ - defaultBuildingVariant, - enumVirtualProcessorVariants.rotater, - enumVirtualProcessorVariants.stacker, - enumVirtualProcessorVariants.painter, - enumVirtualProcessorVariants.unstacker, - ]; - } - - getRenderPins() { - // We already have it included - return false; - } - - /** - * - * @param {Entity} entity - * @param {number} rotationVariant - */ - updateVariants(entity, rotationVariant, variant) { - const gateType = enumVariantToGate[variant]; - entity.components.LogicGate.type = gateType; - const pinComp = entity.components.WiredPins; - switch (gateType) { - case enumLogicGateType.cutter: - case enumLogicGateType.unstacker: { - pinComp.setSlots([ - { - pos: new Vector(0, 0), - direction: enumDirection.left, - type: enumPinSlotType.logicalEjector, - }, - { - pos: new Vector(0, 0), - direction: enumDirection.right, - type: enumPinSlotType.logicalEjector, - }, - { - pos: new Vector(0, 0), - direction: enumDirection.bottom, - type: enumPinSlotType.logicalAcceptor, - }, - ]); - break; - } - case enumLogicGateType.rotater: { - pinComp.setSlots([ - { - pos: new Vector(0, 0), - direction: enumDirection.top, - type: enumPinSlotType.logicalEjector, - }, - { - pos: new Vector(0, 0), - direction: enumDirection.bottom, - type: enumPinSlotType.logicalAcceptor, - }, - ]); - break; - } - case enumLogicGateType.stacker: - case enumLogicGateType.painter: { - pinComp.setSlots([ - { - pos: new Vector(0, 0), - direction: enumDirection.top, - type: enumPinSlotType.logicalEjector, - }, - { - pos: new Vector(0, 0), - direction: enumDirection.bottom, - type: enumPinSlotType.logicalAcceptor, - }, - { - pos: new Vector(0, 0), - direction: enumDirection.right, - type: enumPinSlotType.logicalAcceptor, - }, - ]); - break; - } - default: - assertAlways("unknown logic gate type: " + gateType); - } - } - - /** - * Creates the entity at the given location - * @param {Entity} entity - */ - setupEntityComponents(entity) { - entity.addComponent( - new WiredPinsComponent({ - slots: [], - }) - ); - - entity.addComponent(new LogicGateComponent({})); - } -} +import { Vector, enumDirection } from "../../core/vector"; +import { LogicGateComponent, enumLogicGateType } from "../components/logic_gate"; +import { WiredPinsComponent, enumPinSlotType } from "../components/wired_pins"; +import { Entity } from "../entity"; +import { MetaBuilding, defaultBuildingVariant } from "../meta_building"; +import { GameRoot } from "../root"; +import { enumHubGoalRewards } from "../tutorial_goals"; +import { MetaCutterBuilding } from "./cutter"; +import { MetaPainterBuilding } from "./painter"; +import { MetaRotatorBuilding } from "./rotator"; +import { MetaStackerBuilding } from "./stacker"; + +/** @enum {string} */ +export const enumVirtualProcessorVariants = { + rotator: "rotator", + unstacker: "unstacker", + stacker: "stacker", + painter: "painter", +}; + +/** @enum {string} */ +const enumVariantToGate = { + [defaultBuildingVariant]: enumLogicGateType.cutter, + [enumVirtualProcessorVariants.rotator]: enumLogicGateType.rotator, + [enumVirtualProcessorVariants.unstacker]: enumLogicGateType.unstacker, + [enumVirtualProcessorVariants.stacker]: enumLogicGateType.stacker, + [enumVirtualProcessorVariants.painter]: enumLogicGateType.painter, +}; + +const colors = { + [defaultBuildingVariant]: new MetaCutterBuilding().getSilhouetteColor(), + [enumVirtualProcessorVariants.rotator]: new MetaRotatorBuilding().getSilhouetteColor(), + [enumVirtualProcessorVariants.unstacker]: new MetaStackerBuilding().getSilhouetteColor(), + [enumVirtualProcessorVariants.stacker]: new MetaStackerBuilding().getSilhouetteColor(), + [enumVirtualProcessorVariants.painter]: new MetaPainterBuilding().getSilhouetteColor(), +}; + +export class MetaVirtualProcessorBuilding extends MetaBuilding { + constructor() { + super("virtual_processor"); + } + + static getAllVariantCombinations() { + return [ + { + internalId: 42, + variant: defaultBuildingVariant, + }, + { + internalId: 44, + variant: enumVirtualProcessorVariants.rotator, + }, + { + internalId: 45, + variant: enumVirtualProcessorVariants.unstacker, + }, + { + internalId: 50, + variant: enumVirtualProcessorVariants.stacker, + }, + { + internalId: 51, + variant: enumVirtualProcessorVariants.painter, + }, + ]; + } + + getSilhouetteColor(variant) { + return colors[variant]; + } + + /** + * @param {GameRoot} root + */ + getIsUnlocked(root) { + return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_virtual_processing); + } + + /** @returns {"wires"} **/ + getLayer() { + return "wires"; + } + + getDimensions() { + return new Vector(1, 1); + } + + getAvailableVariants() { + return [ + defaultBuildingVariant, + enumVirtualProcessorVariants.rotator, + enumVirtualProcessorVariants.stacker, + enumVirtualProcessorVariants.painter, + enumVirtualProcessorVariants.unstacker, + ]; + } + + getRenderPins() { + // We already have it included + return false; + } + + /** + * + * @param {Entity} entity + * @param {number} rotationVariant + */ + updateVariants(entity, rotationVariant, variant) { + const gateType = enumVariantToGate[variant]; + entity.components.LogicGate.type = gateType; + const pinComp = entity.components.WiredPins; + switch (gateType) { + case enumLogicGateType.cutter: + case enumLogicGateType.unstacker: { + pinComp.setSlots([ + { + pos: new Vector(0, 0), + direction: enumDirection.left, + type: enumPinSlotType.logicalEjector, + }, + { + pos: new Vector(0, 0), + direction: enumDirection.right, + type: enumPinSlotType.logicalEjector, + }, + { + pos: new Vector(0, 0), + direction: enumDirection.bottom, + type: enumPinSlotType.logicalAcceptor, + }, + ]); + break; + } + case enumLogicGateType.rotator: { + pinComp.setSlots([ + { + pos: new Vector(0, 0), + direction: enumDirection.top, + type: enumPinSlotType.logicalEjector, + }, + { + pos: new Vector(0, 0), + direction: enumDirection.bottom, + type: enumPinSlotType.logicalAcceptor, + }, + ]); + break; + } + case enumLogicGateType.stacker: + case enumLogicGateType.painter: { + pinComp.setSlots([ + { + pos: new Vector(0, 0), + direction: enumDirection.top, + type: enumPinSlotType.logicalEjector, + }, + { + pos: new Vector(0, 0), + direction: enumDirection.bottom, + type: enumPinSlotType.logicalAcceptor, + }, + { + pos: new Vector(0, 0), + direction: enumDirection.right, + type: enumPinSlotType.logicalAcceptor, + }, + ]); + break; + } + default: + assertAlways("unknown logic gate type: " + gateType); + } + } + + /** + * Creates the entity at the given location + * @param {Entity} entity + */ + setupEntityComponents(entity) { + entity.addComponent( + new WiredPinsComponent({ + slots: [], + }) + ); + + entity.addComponent(new LogicGateComponent({})); + } +} diff --git a/src/js/game/buildings/wire.js b/src/js/game/buildings/wire.js index c69dbfa1..1c2fced5 100644 --- a/src/js/game/buildings/wire.js +++ b/src/js/game/buildings/wire.js @@ -1,315 +1,315 @@ -import { Loader } from "../../core/loader"; -import { generateMatrixRotations } from "../../core/utils"; -import { enumDirection, Vector } from "../../core/vector"; -import { SOUNDS } from "../../platform/sound"; -import { enumWireType, enumWireVariant, WireComponent } from "../components/wire"; -import { Entity } from "../entity"; -import { defaultBuildingVariant, MetaBuilding } from "../meta_building"; -import { GameRoot } from "../root"; -import { enumHubGoalRewards } from "../tutorial_goals"; - -export const arrayWireRotationVariantToType = [ - enumWireType.forward, - enumWireType.turn, - enumWireType.split, - enumWireType.cross, -]; - -export const wireOverlayMatrices = { - [enumWireType.forward]: generateMatrixRotations([0, 1, 0, 0, 1, 0, 0, 1, 0]), - [enumWireType.split]: generateMatrixRotations([0, 0, 0, 1, 1, 1, 0, 1, 0]), - [enumWireType.turn]: generateMatrixRotations([0, 0, 0, 0, 1, 1, 0, 1, 0]), - [enumWireType.cross]: generateMatrixRotations([0, 1, 0, 1, 1, 1, 0, 1, 0]), -}; - -/** @enum {string} */ -export const wireVariants = { - second: "second", -}; - -const enumWireVariantToVariant = { - [defaultBuildingVariant]: enumWireVariant.first, - [wireVariants.second]: enumWireVariant.second, -}; - -export class MetaWireBuilding extends MetaBuilding { - constructor() { - super("wire"); - } - - static getAllVariantCombinations() { - return [ - { - internalId: 27, - variant: defaultBuildingVariant, - rotationVariant: 0, - }, - { - internalId: 28, - variant: defaultBuildingVariant, - rotationVariant: 1, - }, - { - internalId: 29, - variant: defaultBuildingVariant, - rotationVariant: 2, - }, - { - internalId: 30, - variant: defaultBuildingVariant, - rotationVariant: 3, - }, - { - internalId: 52, - variant: enumWireVariant.second, - rotationVariant: 0, - }, - { - internalId: 53, - variant: enumWireVariant.second, - rotationVariant: 1, - }, - { - internalId: 54, - variant: enumWireVariant.second, - rotationVariant: 2, - }, - { - internalId: 55, - variant: enumWireVariant.second, - rotationVariant: 3, - }, - ]; - } - - getHasDirectionLockAvailable() { - return true; - } - - getSilhouetteColor() { - return "#61ef6f"; - } - - getAvailableVariants() { - return [defaultBuildingVariant, wireVariants.second]; - } - - getDimensions() { - return new Vector(1, 1); - } - - getStayInPlacementMode() { - return true; - } - - getPlacementSound() { - return SOUNDS.placeBelt; - } - - getRotateAutomaticallyWhilePlacing() { - return true; - } - - /** @returns {"wires"} **/ - getLayer() { - return "wires"; - } - - getSprite() { - return null; - } - - getIsReplaceable() { - return true; - } - - /** - * @param {GameRoot} root - */ - getIsUnlocked(root) { - return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_wires_painter_and_levers); - } - - /** - * Creates the entity at the given location - * @param {Entity} entity - */ - setupEntityComponents(entity) { - entity.addComponent(new WireComponent({})); - } - - /** - * - * @param {Entity} entity - * @param {number} rotationVariant - * @param {string} variant - */ - updateVariants(entity, rotationVariant, variant) { - entity.components.Wire.type = arrayWireRotationVariantToType[rotationVariant]; - entity.components.Wire.variant = enumWireVariantToVariant[variant]; - } - - /** - * - * @param {number} rotation - * @param {number} rotationVariant - * @param {string} variant - * @param {Entity} entity - */ - getSpecialOverlayRenderMatrix(rotation, rotationVariant, variant, entity) { - return wireOverlayMatrices[entity.components.Wire.type][rotation]; - } - - /** - * - * @param {number} rotationVariant - * @param {string} variant - * @returns {import("../../core/draw_utils").AtlasSprite} - */ - getPreviewSprite(rotationVariant, variant) { - const wireVariant = enumWireVariantToVariant[variant]; - switch (arrayWireRotationVariantToType[rotationVariant]) { - case enumWireType.forward: { - return Loader.getSprite("sprites/wires/sets/" + wireVariant + "_forward.png"); - } - case enumWireType.turn: { - return Loader.getSprite("sprites/wires/sets/" + wireVariant + "_turn.png"); - } - case enumWireType.split: { - return Loader.getSprite("sprites/wires/sets/" + wireVariant + "_split.png"); - } - case enumWireType.cross: { - return Loader.getSprite("sprites/wires/sets/" + wireVariant + "_cross.png"); - } - default: { - assertAlways(false, "Invalid wire rotation variant"); - } - } - } - - getBlueprintSprite(rotationVariant, variant) { - return this.getPreviewSprite(rotationVariant, variant); - } - - /** - * Should compute the optimal rotation variant on the given tile - * @param {object} param0 - * @param {GameRoot} param0.root - * @param {Vector} param0.tile - * @param {number} param0.rotation - * @param {string} param0.variant - * @param {string} param0.layer - * @return {{ rotation: number, rotationVariant: number, connectedEntities?: Array }} - */ - computeOptimalDirectionAndRotationVariantAtTile({ root, tile, rotation, variant, layer }) { - const wireVariant = enumWireVariantToVariant[variant]; - const connections = { - top: root.logic.computeWireEdgeStatus({ tile, wireVariant, edge: enumDirection.top }), - right: root.logic.computeWireEdgeStatus({ tile, wireVariant, edge: enumDirection.right }), - bottom: root.logic.computeWireEdgeStatus({ tile, wireVariant, edge: enumDirection.bottom }), - left: root.logic.computeWireEdgeStatus({ tile, wireVariant, edge: enumDirection.left }), - }; - - let flag = 0; - flag |= connections.top ? 0x1000 : 0; - flag |= connections.right ? 0x100 : 0; - flag |= connections.bottom ? 0x10 : 0; - flag |= connections.left ? 0x1 : 0; - - let targetType = enumWireType.forward; - - // First, reset rotation - rotation = 0; - - switch (flag) { - case 0x0000: - // Nothing - break; - - case 0x0001: - // Left - rotation += 90; - break; - - case 0x0010: - // Bottom - // END - break; - - case 0x0011: - // Bottom | Left - targetType = enumWireType.turn; - rotation += 90; - break; - - case 0x0100: - // Right - rotation += 90; - break; - - case 0x0101: - // Right | Left - rotation += 90; - break; - - case 0x0110: - // Right | Bottom - targetType = enumWireType.turn; - break; - - case 0x0111: - // Right | Bottom | Left - targetType = enumWireType.split; - break; - - case 0x1000: - // Top - break; - - case 0x1001: - // Top | Left - targetType = enumWireType.turn; - rotation += 180; - break; - - case 0x1010: - // Top | Bottom - break; - - case 0x1011: - // Top | Bottom | Left - targetType = enumWireType.split; - rotation += 90; - break; - - case 0x1100: - // Top | Right - targetType = enumWireType.turn; - rotation -= 90; - break; - - case 0x1101: - // Top | Right | Left - targetType = enumWireType.split; - rotation += 180; - break; - - case 0x1110: - // Top | Right | Bottom - targetType = enumWireType.split; - rotation -= 90; - break; - - case 0x1111: - // Top | Right | Bottom | Left - targetType = enumWireType.cross; - break; - } - - return { - // Clamp rotation - rotation: (rotation + 360 * 10) % 360, - rotationVariant: arrayWireRotationVariantToType.indexOf(targetType), - }; - } -} +import { Loader } from "../../core/loader"; +import { generateMatrixRotations } from "../../core/utils"; +import { enumDirection, Vector } from "../../core/vector"; +import { SOUNDS } from "../../platform/sound"; +import { enumWireType, enumWireVariant, WireComponent } from "../components/wire"; +import { Entity } from "../entity"; +import { defaultBuildingVariant, MetaBuilding } from "../meta_building"; +import { GameRoot } from "../root"; +import { enumHubGoalRewards } from "../tutorial_goals"; + +export const arrayWireRotationVariantToType = [ + enumWireType.forward, + enumWireType.turn, + enumWireType.split, + enumWireType.cross, +]; + +export const wireOverlayMatrices = { + [enumWireType.forward]: generateMatrixRotations([0, 1, 0, 0, 1, 0, 0, 1, 0]), + [enumWireType.split]: generateMatrixRotations([0, 0, 0, 1, 1, 1, 0, 1, 0]), + [enumWireType.turn]: generateMatrixRotations([0, 0, 0, 0, 1, 1, 0, 1, 0]), + [enumWireType.cross]: generateMatrixRotations([0, 1, 0, 1, 1, 1, 0, 1, 0]), +}; + +/** @enum {string} */ +export const wireVariants = { + second: "second", +}; + +const enumWireVariantToVariant = { + [defaultBuildingVariant]: enumWireVariant.first, + [wireVariants.second]: enumWireVariant.second, +}; + +export class MetaWireBuilding extends MetaBuilding { + constructor() { + super("wire"); + } + + static getAllVariantCombinations() { + return [ + { + internalId: 27, + variant: defaultBuildingVariant, + rotationVariant: 0, + }, + { + internalId: 28, + variant: defaultBuildingVariant, + rotationVariant: 1, + }, + { + internalId: 29, + variant: defaultBuildingVariant, + rotationVariant: 2, + }, + { + internalId: 30, + variant: defaultBuildingVariant, + rotationVariant: 3, + }, + { + internalId: 52, + variant: enumWireVariant.second, + rotationVariant: 0, + }, + { + internalId: 53, + variant: enumWireVariant.second, + rotationVariant: 1, + }, + { + internalId: 54, + variant: enumWireVariant.second, + rotationVariant: 2, + }, + { + internalId: 55, + variant: enumWireVariant.second, + rotationVariant: 3, + }, + ]; + } + + getHasDirectionLockAvailable() { + return true; + } + + getSilhouetteColor() { + return "#61ef6f"; + } + + getAvailableVariants() { + return [defaultBuildingVariant, wireVariants.second]; + } + + getDimensions() { + return new Vector(1, 1); + } + + getStayInPlacementMode() { + return true; + } + + getPlacementSound() { + return SOUNDS.placeBelt; + } + + getRotateAutomaticallyWhilePlacing() { + return true; + } + + /** @returns {"wires"} **/ + getLayer() { + return "wires"; + } + + getSprite() { + return null; + } + + getIsReplaceable() { + return true; + } + + /** + * @param {GameRoot} root + */ + getIsUnlocked(root) { + return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_wires_painter_and_levers); + } + + /** + * Creates the entity at the given location + * @param {Entity} entity + */ + setupEntityComponents(entity) { + entity.addComponent(new WireComponent({})); + } + + /** + * + * @param {Entity} entity + * @param {number} rotationVariant + * @param {string} variant + */ + updateVariants(entity, rotationVariant, variant) { + entity.components.Wire.type = arrayWireRotationVariantToType[rotationVariant]; + entity.components.Wire.variant = enumWireVariantToVariant[variant]; + } + + /** + * + * @param {number} rotation + * @param {number} rotationVariant + * @param {string} variant + * @param {Entity} entity + */ + getSpecialOverlayRenderMatrix(rotation, rotationVariant, variant, entity) { + return wireOverlayMatrices[entity.components.Wire.type][rotation]; + } + + /** + * + * @param {number} rotationVariant + * @param {string} variant + * @returns {import("../../core/draw_utils").AtlasSprite} + */ + getPreviewSprite(rotationVariant, variant) { + const wireVariant = enumWireVariantToVariant[variant]; + switch (arrayWireRotationVariantToType[rotationVariant]) { + case enumWireType.forward: { + return Loader.getSprite("sprites/wires/sets/" + wireVariant + "_forward.png"); + } + case enumWireType.turn: { + return Loader.getSprite("sprites/wires/sets/" + wireVariant + "_turn.png"); + } + case enumWireType.split: { + return Loader.getSprite("sprites/wires/sets/" + wireVariant + "_split.png"); + } + case enumWireType.cross: { + return Loader.getSprite("sprites/wires/sets/" + wireVariant + "_cross.png"); + } + default: { + assertAlways(false, "Invalid wire rotation variant"); + } + } + } + + getBlueprintSprite(rotationVariant, variant) { + return this.getPreviewSprite(rotationVariant, variant); + } + + /** + * Should compute the optimal rotation variant on the given tile + * @param {object} param0 + * @param {GameRoot} param0.root + * @param {Vector} param0.tile + * @param {number} param0.rotation + * @param {string} param0.variant + * @param {string} param0.layer + * @return {{ rotation: number, rotationVariant: number, connectedEntities?: Array }} + */ + computeOptimalDirectionAndRotationVariantAtTile({ root, tile, rotation, variant, layer }) { + const wireVariant = enumWireVariantToVariant[variant]; + const connections = { + top: root.logic.computeWireEdgeStatus({ tile, wireVariant, edge: enumDirection.top }), + right: root.logic.computeWireEdgeStatus({ tile, wireVariant, edge: enumDirection.right }), + bottom: root.logic.computeWireEdgeStatus({ tile, wireVariant, edge: enumDirection.bottom }), + left: root.logic.computeWireEdgeStatus({ tile, wireVariant, edge: enumDirection.left }), + }; + + let flag = 0; + flag |= connections.top ? 0x1000 : 0; + flag |= connections.right ? 0x100 : 0; + flag |= connections.bottom ? 0x10 : 0; + flag |= connections.left ? 0x1 : 0; + + let targetType = enumWireType.forward; + + // First, reset rotation + rotation = 0; + + switch (flag) { + case 0x0000: + // Nothing + break; + + case 0x0001: + // Left + rotation += 90; + break; + + case 0x0010: + // Bottom + // END + break; + + case 0x0011: + // Bottom | Left + targetType = enumWireType.turn; + rotation += 90; + break; + + case 0x0100: + // Right + rotation += 90; + break; + + case 0x0101: + // Right | Left + rotation += 90; + break; + + case 0x0110: + // Right | Bottom + targetType = enumWireType.turn; + break; + + case 0x0111: + // Right | Bottom | Left + targetType = enumWireType.split; + break; + + case 0x1000: + // Top + break; + + case 0x1001: + // Top | Left + targetType = enumWireType.turn; + rotation += 180; + break; + + case 0x1010: + // Top | Bottom + break; + + case 0x1011: + // Top | Bottom | Left + targetType = enumWireType.split; + rotation += 90; + break; + + case 0x1100: + // Top | Right + targetType = enumWireType.turn; + rotation -= 90; + break; + + case 0x1101: + // Top | Right | Left + targetType = enumWireType.split; + rotation += 180; + break; + + case 0x1110: + // Top | Right | Bottom + targetType = enumWireType.split; + rotation -= 90; + break; + + case 0x1111: + // Top | Right | Bottom | Left + targetType = enumWireType.cross; + break; + } + + return { + // Clamp rotation + rotation: (rotation + 360 * 10) % 360, + rotationVariant: arrayWireRotationVariantToType.indexOf(targetType), + }; + } +} diff --git a/src/js/game/buildings/wire_tunnel.js b/src/js/game/buildings/wire_tunnel.js index 80faa9df..36197622 100644 --- a/src/js/game/buildings/wire_tunnel.js +++ b/src/js/game/buildings/wire_tunnel.js @@ -1,67 +1,67 @@ -import { generateMatrixRotations } from "../../core/utils"; -import { Vector } from "../../core/vector"; -import { WireTunnelComponent } from "../components/wire_tunnel"; -import { Entity } from "../entity"; -import { defaultBuildingVariant, MetaBuilding } from "../meta_building"; -import { GameRoot } from "../root"; -import { enumHubGoalRewards } from "../tutorial_goals"; - -const wireTunnelOverlayMatrix = generateMatrixRotations([0, 1, 0, 1, 1, 1, 0, 1, 0]); - -export class MetaWireTunnelBuilding extends MetaBuilding { - constructor() { - super("wire_tunnel"); - } - - static getAllVariantCombinations() { - return [ - { - internalId: 39, - variant: defaultBuildingVariant, - }, - ]; - } - - getSilhouetteColor() { - return "#777a86"; - } - - /** - * @param {GameRoot} root - */ - getIsUnlocked(root) { - return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_wires_painter_and_levers); - } - - /** - * - * @param {number} rotation - * @param {number} rotationVariant - * @param {string} variant - * @param {Entity} entity - */ - getSpecialOverlayRenderMatrix(rotation, rotationVariant, variant, entity) { - return wireTunnelOverlayMatrix[rotation]; - } - - getIsRotateable() { - return false; - } - - getDimensions() { - return new Vector(1, 1); - } - - /** @returns {"wires"} **/ - getLayer() { - return "wires"; - } - - /** - * Creates the entity at the given location - * @param {Entity} entity - */ - setupEntityComponents(entity) { - entity.addComponent(new WireTunnelComponent()); - } -} +import { generateMatrixRotations } from "../../core/utils"; +import { Vector } from "../../core/vector"; +import { WireTunnelComponent } from "../components/wire_tunnel"; +import { Entity } from "../entity"; +import { defaultBuildingVariant, MetaBuilding } from "../meta_building"; +import { GameRoot } from "../root"; +import { enumHubGoalRewards } from "../tutorial_goals"; + +const wireTunnelOverlayMatrix = generateMatrixRotations([0, 1, 0, 1, 1, 1, 0, 1, 0]); + +export class MetaWireTunnelBuilding extends MetaBuilding { + constructor() { + super("wire_tunnel"); + } + + static getAllVariantCombinations() { + return [ + { + internalId: 39, + variant: defaultBuildingVariant, + }, + ]; + } + + getSilhouetteColor() { + return "#777a86"; + } + + /** + * @param {GameRoot} root + */ + getIsUnlocked(root) { + return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_wires_painter_and_levers); + } + + /** + * + * @param {number} rotation + * @param {number} rotationVariant + * @param {string} variant + * @param {Entity} entity + */ + getSpecialOverlayRenderMatrix(rotation, rotationVariant, variant, entity) { + return wireTunnelOverlayMatrix[rotation]; + } + + getIsRotateable() { + return false; + } + + getDimensions() { + return new Vector(1, 1); + } + + /** @returns {"wires"} **/ + getLayer() { + return "wires"; + } + + /** + * Creates the entity at the given location + * @param {Entity} entity + */ + setupEntityComponents(entity) { + entity.addComponent(new WireTunnelComponent()); + } +} diff --git a/src/js/game/camera.js b/src/js/game/camera.js index fc08d73f..b5001f45 100644 --- a/src/js/game/camera.js +++ b/src/js/game/camera.js @@ -1,1039 +1,1041 @@ -import { clickDetectorGlobals } from "../core/click_detector"; -import { globalConfig, SUPPORT_TOUCH } from "../core/config"; -import { createLogger } from "../core/logging"; -import { Rectangle } from "../core/rectangle"; -import { Signal, STOP_PROPAGATION } from "../core/signal"; -import { clamp } from "../core/utils"; -import { mixVector, Vector } from "../core/vector"; -import { BasicSerializableObject, types } from "../savegame/serialization"; -import { KEYMAPPINGS } from "./key_action_mapper"; -import { GameRoot } from "./root"; - -const logger = createLogger("camera"); - -export const USER_INTERACT_MOVE = "move"; -export const USER_INTERACT_ZOOM = "zoom"; -export const USER_INTERACT_TOUCHEND = "touchend"; - -const velocitySmoothing = 0.5; -const velocityFade = 0.98; -const velocityStrength = 0.4; -const velocityMax = 20; -const ticksBeforeErasingVelocity = 10; - -/** - * @enum {string} - */ -export const enumMouseButton = { - left: "left", - middle: "middle", - right: "right", -}; - -export class Camera extends BasicSerializableObject { - constructor(root) { - super(); - - /** @type {GameRoot} */ - this.root = root; - - // Zoom level, 2 means double size - - // Find optimal initial zoom - - this.zoomLevel = this.findInitialZoom(); - this.clampZoomLevel(); - - /** @type {Vector} */ - this.center = new Vector(0, 0); - - // Input handling - this.currentlyMoving = false; - this.lastMovingPosition = null; - this.lastMovingPositionLastTick = null; - this.numTicksStandingStill = null; - this.cameraUpdateTimeBucket = 0.0; - this.didMoveSinceTouchStart = false; - this.currentlyPinching = false; - this.lastPinchPositions = null; - - this.keyboardForce = new Vector(); - - // Signal which gets emitted once the user changed something - this.userInteraction = new Signal(); - - /** @type {Vector} */ - this.currentShake = new Vector(0, 0); - - /** @type {Vector} */ - this.currentPan = new Vector(0, 0); - - // Set desired pan (camera movement) - /** @type {Vector} */ - this.desiredPan = new Vector(0, 0); - - // Set desired camera center - /** @type {Vector} */ - this.desiredCenter = null; - - // Set desired camera zoom - /** @type {number} */ - this.desiredZoom = null; - - /** @type {Vector} */ - this.touchPostMoveVelocity = new Vector(0, 0); - - // Handlers - this.downPreHandler = /** @type {TypedSignal<[Vector, enumMouseButton]>} */ (new Signal()); - this.movePreHandler = /** @type {TypedSignal<[Vector]>} */ (new Signal()); - // this.pinchPreHandler = /** @type {TypedSignal<[Vector]>} */ (new Signal()); - this.upPostHandler = /** @type {TypedSignal<[Vector]>} */ (new Signal()); - - this.internalInitEvents(); - this.clampZoomLevel(); - this.bindKeys(); - if (G_IS_DEV) { - window.addEventListener("keydown", ev => { - if (ev.key === "i") { - this.zoomLevel = 3; - } - }); - } - } - - // Serialization - static getId() { - return "Camera"; - } - - static getSchema() { - return { - zoomLevel: types.float, - center: types.vector, - }; - } - - deserialize(data) { - const errorCode = super.deserialize(data); - if (errorCode) { - return errorCode; - } - - // Safety - this.clampZoomLevel(); - } - - // Simple getters & setters - - addScreenShake(amount) { - const currentShakeAmount = this.currentShake.length(); - const scale = 1 / (1 + 3 * currentShakeAmount); - this.currentShake.x = this.currentShake.x + 2 * (Math.random() - 0.5) * scale * amount; - this.currentShake.y = this.currentShake.y + 2 * (Math.random() - 0.5) * scale * amount; - } - - /** - * Sets a point in world space to focus on - * @param {Vector} center - */ - setDesiredCenter(center) { - this.desiredCenter = center.copy(); - this.currentlyMoving = false; - } - - /** - * Sets a desired zoom level - * @param {number} zoom - */ - setDesiredZoom(zoom) { - this.desiredZoom = zoom; - } - - /** - * Returns if this camera is currently moving by a non-user interaction - */ - isCurrentlyMovingToDesiredCenter() { - return this.desiredCenter !== null; - } - - /** - * Sets the camera pan, every frame the camera will move by this amount - * @param {Vector} pan - */ - setPan(pan) { - this.desiredPan = pan.copy(); - } - - /** - * Finds a good initial zoom level - */ - findInitialZoom() { - let desiredWorldSpaceWidth = 18 * globalConfig.tileSize; - if (window.innerWidth < 1000) { - desiredWorldSpaceWidth = 12 * globalConfig.tileSize; - } - - const zoomLevelX = this.root.gameWidth / desiredWorldSpaceWidth; - const zoomLevelY = this.root.gameHeight / desiredWorldSpaceWidth; - - const finalLevel = Math.min(zoomLevelX, zoomLevelY); - assert( - Number.isFinite(finalLevel) && finalLevel > 0, - "Invalid zoom level computed for initial zoom: " + finalLevel - ); - return finalLevel; - } - - /** - * Clears all animations - */ - clearAnimations() { - this.touchPostMoveVelocity.x = 0; - this.touchPostMoveVelocity.y = 0; - this.desiredCenter = null; - this.desiredPan.x = 0; - this.desiredPan.y = 0; - this.currentPan.x = 0; - this.currentPan.y = 0; - this.currentlyPinching = false; - this.currentlyMoving = false; - this.lastMovingPosition = null; - this.didMoveSinceTouchStart = false; - this.desiredZoom = null; - } - - /** - * Returns if the user is currently interacting with the camera - * @returns {boolean} true if the user interacts - */ - isCurrentlyInteracting() { - if (this.currentlyPinching) { - return true; - } - if (this.currentlyMoving) { - // Only interacting if moved at least once - return this.didMoveSinceTouchStart; - } - if (this.touchPostMoveVelocity.lengthSquare() > 1) { - return true; - } - return false; - } - - /** - * Returns if in the next frame the viewport will change - * @returns {boolean} true if it willchange - */ - viewportWillChange() { - return this.desiredCenter !== null || this.desiredZoom !== null || this.isCurrentlyInteracting(); - } - - /** - * Cancels all interactions, that is user interaction and non user interaction - */ - cancelAllInteractions() { - this.touchPostMoveVelocity = new Vector(0, 0); - this.desiredCenter = null; - this.currentlyMoving = false; - this.currentlyPinching = false; - this.desiredZoom = null; - } - - /** - * Returns effective viewport width - */ - getViewportWidth() { - return this.root.gameWidth / this.zoomLevel; - } - - /** - * Returns effective viewport height - */ - getViewportHeight() { - return this.root.gameHeight / this.zoomLevel; - } - - /** - * Returns effective world space viewport left - */ - getViewportLeft() { - return this.center.x - this.getViewportWidth() / 2 + (this.currentShake.x * 10) / this.zoomLevel; - } - - /** - * Returns effective world space viewport right - */ - getViewportRight() { - return this.center.x + this.getViewportWidth() / 2 + (this.currentShake.x * 10) / this.zoomLevel; - } - - /** - * Returns effective world space viewport top - */ - getViewportTop() { - return this.center.y - this.getViewportHeight() / 2 + (this.currentShake.x * 10) / this.zoomLevel; - } - - /** - * Returns effective world space viewport bottom - */ - getViewportBottom() { - return this.center.y + this.getViewportHeight() / 2 + (this.currentShake.x * 10) / this.zoomLevel; - } - - /** - * Returns the visible world space rect - * @returns {Rectangle} - */ - getVisibleRect() { - return Rectangle.fromTRBL( - Math.floor(this.getViewportTop()), - Math.ceil(this.getViewportRight()), - Math.ceil(this.getViewportBottom()), - Math.floor(this.getViewportLeft()) - ); - } - - getIsMapOverlayActive() { - return this.zoomLevel < globalConfig.mapChunkOverviewMinZoom; - } - - /** - * Attaches all event listeners - */ - internalInitEvents() { - this.eventListenerTouchStart = this.onTouchStart.bind(this); - this.eventListenerTouchEnd = this.onTouchEnd.bind(this); - this.eventListenerTouchMove = this.onTouchMove.bind(this); - this.eventListenerMousewheel = this.onMouseWheel.bind(this); - this.eventListenerMouseDown = this.onMouseDown.bind(this); - this.eventListenerMouseMove = this.onMouseMove.bind(this); - this.eventListenerMouseUp = this.onMouseUp.bind(this); - - if (SUPPORT_TOUCH) { - this.root.canvas.addEventListener("touchstart", this.eventListenerTouchStart); - this.root.canvas.addEventListener("touchend", this.eventListenerTouchEnd); - this.root.canvas.addEventListener("touchcancel", this.eventListenerTouchEnd); - this.root.canvas.addEventListener("touchmove", this.eventListenerTouchMove); - } - - this.root.canvas.addEventListener("wheel", this.eventListenerMousewheel); - this.root.canvas.addEventListener("mousedown", this.eventListenerMouseDown); - this.root.canvas.addEventListener("mousemove", this.eventListenerMouseMove); - window.addEventListener("mouseup", this.eventListenerMouseUp); - // this.root.canvas.addEventListener("mouseout", this.eventListenerMouseUp); - } - - /** - * Cleans up all event listeners - */ - cleanup() { - if (SUPPORT_TOUCH) { - this.root.canvas.removeEventListener("touchstart", this.eventListenerTouchStart); - this.root.canvas.removeEventListener("touchend", this.eventListenerTouchEnd); - this.root.canvas.removeEventListener("touchcancel", this.eventListenerTouchEnd); - this.root.canvas.removeEventListener("touchmove", this.eventListenerTouchMove); - } - - this.root.canvas.removeEventListener("wheel", this.eventListenerMousewheel); - this.root.canvas.removeEventListener("mousedown", this.eventListenerMouseDown); - this.root.canvas.removeEventListener("mousemove", this.eventListenerMouseMove); - window.removeEventListener("mouseup", this.eventListenerMouseUp); - // this.root.canvas.removeEventListener("mouseout", this.eventListenerMouseUp); - } - - /** - * Binds the arrow keys - */ - bindKeys() { - const mapper = this.root.keyMapper; - mapper.getBinding(KEYMAPPINGS.navigation.mapMoveUp).add(() => (this.keyboardForce.y = -1)); - mapper.getBinding(KEYMAPPINGS.navigation.mapMoveDown).add(() => (this.keyboardForce.y = 1)); - mapper.getBinding(KEYMAPPINGS.navigation.mapMoveRight).add(() => (this.keyboardForce.x = 1)); - mapper.getBinding(KEYMAPPINGS.navigation.mapMoveLeft).add(() => (this.keyboardForce.x = -1)); - - mapper - .getBinding(KEYMAPPINGS.navigation.mapZoomIn) - .add(() => (this.desiredZoom = this.zoomLevel * 1.2)); - mapper - .getBinding(KEYMAPPINGS.navigation.mapZoomOut) - .add(() => (this.desiredZoom = this.zoomLevel / 1.2)); - - mapper.getBinding(KEYMAPPINGS.navigation.centerMap).add(() => this.centerOnMap()); - } - - centerOnMap() { - this.desiredCenter = new Vector(0, 0); - } - - /** - * Converts from screen to world space - * @param {Vector} screen - * @returns {Vector} world space - */ - screenToWorld(screen) { - const centerSpace = screen.subScalars(this.root.gameWidth / 2, this.root.gameHeight / 2); - return centerSpace.divideScalar(this.zoomLevel).add(this.center); - } - - /** - * Converts from world to screen space - * @param {Vector} world - * @returns {Vector} screen space - */ - worldToScreen(world) { - const screenSpace = world.sub(this.center).multiplyScalar(this.zoomLevel); - return screenSpace.addScalars(this.root.gameWidth / 2, this.root.gameHeight / 2); - } - - /** - * Returns if a point is on screen - * @param {Vector} point - * @returns {boolean} true if its on screen - */ - isWorldPointOnScreen(point) { - const rect = this.getVisibleRect(); - return rect.containsPoint(point.x, point.y); - } - - getMaximumZoom() { - return this.root.gameMode.getMaximumZoom(); - } - - getMinimumZoom() { - return this.root.gameMode.getMinimumZoom(); - } - - /** - * Returns if we can further zoom in - * @returns {boolean} - */ - canZoomIn() { - return this.zoomLevel <= this.getMaximumZoom() - 0.01; - } - - /** - * Returns if we can further zoom out - * @returns {boolean} - */ - canZoomOut() { - return this.zoomLevel >= this.getMinimumZoom() + 0.01; - } - - // EVENTS - - /** - * Checks if the mouse event is too close after a touch event and thus - * should get ignored - */ - checkPreventDoubleMouse() { - if (performance.now() - clickDetectorGlobals.lastTouchTime < 1000.0) { - return false; - } - return true; - } - - /** - * Mousedown handler - * @param {MouseEvent} event - */ - onMouseDown(event) { - if (event.cancelable) { - event.preventDefault(); - // event.stopPropagation(); - } - - if (!this.checkPreventDoubleMouse()) { - return; - } - - this.touchPostMoveVelocity = new Vector(0, 0); - if (event.button === 0) { - this.combinedSingleTouchStartHandler(event.clientX, event.clientY); - } else if (event.button === 1) { - this.downPreHandler.dispatch(new Vector(event.clientX, event.clientY), enumMouseButton.middle); - } else if (event.button === 2) { - this.downPreHandler.dispatch(new Vector(event.clientX, event.clientY), enumMouseButton.right); - } - return false; - } - - /** - * Mousemove handler - * @param {MouseEvent} event - */ - onMouseMove(event) { - if (event.cancelable) { - event.preventDefault(); - // event.stopPropagation(); - } - - if (!this.checkPreventDoubleMouse()) { - return; - } - - if (event.button === 0) { - this.combinedSingleTouchMoveHandler(event.clientX, event.clientY); - } - - // Clamp everything afterwards - this.clampZoomLevel(); - this.clampToBounds(); - return false; - } - - /** - * Mouseup handler - * @param {MouseEvent=} event - */ - onMouseUp(event) { - if (event) { - if (event.cancelable) { - event.preventDefault(); - // event.stopPropagation(); - } - } - - if (!this.checkPreventDoubleMouse()) { - return; - } - - this.combinedSingleTouchStopHandler(event.clientX, event.clientY); - return false; - } - - /** - * Mousewheel event - * @param {WheelEvent} event - */ - onMouseWheel(event) { - if (event.cancelable) { - event.preventDefault(); - // event.stopPropagation(); - } - const prevZoom = this.zoomLevel; - - const scale = 1 + 0.15 * this.root.app.settings.getScrollWheelSensitivity(); - assert(Number.isFinite(scale), "Got invalid scale in mouse wheel event: " + event.deltaY); - assert(Number.isFinite(this.zoomLevel), "Got invalid zoom level *before* wheel: " + this.zoomLevel); - this.zoomLevel *= event.deltaY < 0 ? scale : 1 / scale; - assert(Number.isFinite(this.zoomLevel), "Got invalid zoom level *after* wheel: " + this.zoomLevel); - - this.clampZoomLevel(); - this.desiredZoom = null; - - let mousePosition = this.root.app.mousePosition; - if (!this.root.app.settings.getAllSettings().zoomToCursor) { - mousePosition = new Vector(this.root.gameWidth / 2, this.root.gameHeight / 2); - } - - if (mousePosition) { - const worldPos = this.root.camera.screenToWorld(mousePosition); - const worldDelta = worldPos.sub(this.center); - const actualDelta = this.zoomLevel / prevZoom - 1; - this.center = this.center.add(worldDelta.multiplyScalar(actualDelta)); - this.desiredCenter = null; - } - - return false; - } - - /** - * Touch start handler - * @param {TouchEvent} event - */ - onTouchStart(event) { - if (event.cancelable) { - event.preventDefault(); - // event.stopPropagation(); - } - - clickDetectorGlobals.lastTouchTime = performance.now(); - this.touchPostMoveVelocity = new Vector(0, 0); - - if (event.touches.length === 1) { - const touch = event.touches[0]; - this.combinedSingleTouchStartHandler(touch.clientX, touch.clientY); - } else if (event.touches.length === 2) { - // if (this.pinchPreHandler.dispatch() === STOP_PROPAGATION) { - // // Something prevented pinching - // return false; - // } - - const touch1 = event.touches[0]; - const touch2 = event.touches[1]; - this.currentlyMoving = false; - this.currentlyPinching = true; - this.lastPinchPositions = [ - new Vector(touch1.clientX, touch1.clientY), - new Vector(touch2.clientX, touch2.clientY), - ]; - } - return false; - } - - /** - * Touch move handler - * @param {TouchEvent} event - */ - onTouchMove(event) { - if (event.cancelable) { - event.preventDefault(); - // event.stopPropagation(); - } - - clickDetectorGlobals.lastTouchTime = performance.now(); - - if (event.touches.length === 1) { - const touch = event.touches[0]; - this.combinedSingleTouchMoveHandler(touch.clientX, touch.clientY); - } else if (event.touches.length === 2) { - if (this.currentlyPinching) { - const touch1 = event.touches[0]; - const touch2 = event.touches[1]; - - const newPinchPositions = [ - new Vector(touch1.clientX, touch1.clientY), - new Vector(touch2.clientX, touch2.clientY), - ]; - - // Get distance of taps last time and now - const lastDistance = this.lastPinchPositions[0].distance(this.lastPinchPositions[1]); - const thisDistance = newPinchPositions[0].distance(newPinchPositions[1]); - - // IMPORTANT to do math max here to avoid NaN and causing an invalid zoom level - const difference = thisDistance / Math.max(0.001, lastDistance); - - // Find old center of zoom - let oldCenter = this.lastPinchPositions[0].centerPoint(this.lastPinchPositions[1]); - - // Find new center of zoom - let center = newPinchPositions[0].centerPoint(newPinchPositions[1]); - - // Compute movement - let movement = oldCenter.sub(center); - this.center.x += movement.x / this.zoomLevel; - this.center.y += movement.y / this.zoomLevel; - - // Compute zoom - center = center.sub(new Vector(this.root.gameWidth / 2, this.root.gameHeight / 2)); - - // Apply zoom - assert( - Number.isFinite(difference), - "Invalid pinch difference: " + - difference + - "(last=" + - lastDistance + - ", new = " + - thisDistance + - ")" - ); - this.zoomLevel *= difference; - - // Stick to pivot point - const correcture = center.multiplyScalar(difference - 1).divideScalar(this.zoomLevel); - - this.center = this.center.add(correcture); - this.lastPinchPositions = newPinchPositions; - this.userInteraction.dispatch(USER_INTERACT_MOVE); - - // Since we zoomed, abort any programmed zooming - if (this.desiredZoom) { - this.desiredZoom = null; - } - } - } - - // Clamp everything afterwards - this.clampZoomLevel(); - return false; - } - - /** - * Touch end and cancel handler - * @param {TouchEvent=} event - */ - onTouchEnd(event) { - if (event) { - if (event.cancelable) { - event.preventDefault(); - // event.stopPropagation(); - } - } - - clickDetectorGlobals.lastTouchTime = performance.now(); - if (event.changedTouches.length === 0) { - logger.warn("Touch end without changed touches"); - } - - const touch = event.changedTouches[0]; - this.combinedSingleTouchStopHandler(touch.clientX, touch.clientY); - return false; - } - - /** - * Internal touch start handler - * @param {number} x - * @param {number} y - */ - combinedSingleTouchStartHandler(x, y) { - const pos = new Vector(x, y); - if (this.downPreHandler.dispatch(pos, enumMouseButton.left) === STOP_PROPAGATION) { - // Somebody else captured it - return; - } - - this.touchPostMoveVelocity = new Vector(0, 0); - this.currentlyMoving = true; - this.lastMovingPosition = pos; - this.lastMovingPositionLastTick = null; - this.numTicksStandingStill = 0; - this.didMoveSinceTouchStart = false; - } - - /** - * Internal touch move handler - * @param {number} x - * @param {number} y - */ - combinedSingleTouchMoveHandler(x, y) { - const pos = new Vector(x, y); - if (this.movePreHandler.dispatch(pos) === STOP_PROPAGATION) { - // Somebody else captured it - return; - } - - if (!this.currentlyMoving) { - return false; - } - - let delta = this.lastMovingPosition.sub(pos).divideScalar(this.zoomLevel); - if (G_IS_DEV && globalConfig.debug.testCulling) { - // When testing culling, we see everything from the same distance - delta = delta.multiplyScalar(this.zoomLevel * -2); - } - - this.didMoveSinceTouchStart = this.didMoveSinceTouchStart || delta.length() > 0; - this.center = this.center.add(delta); - - this.touchPostMoveVelocity = this.touchPostMoveVelocity - .multiplyScalar(velocitySmoothing) - .add(delta.multiplyScalar(1 - velocitySmoothing)); - - this.lastMovingPosition = pos; - this.userInteraction.dispatch(USER_INTERACT_MOVE); - - // Since we moved, abort any programmed moving - if (this.desiredCenter) { - this.desiredCenter = null; - } - } - - /** - * Internal touch stop handler - */ - combinedSingleTouchStopHandler(x, y) { - if (this.currentlyMoving || this.currentlyPinching) { - this.currentlyMoving = false; - this.currentlyPinching = false; - this.lastMovingPosition = null; - this.lastMovingPositionLastTick = null; - this.numTicksStandingStill = 0; - this.lastPinchPositions = null; - this.userInteraction.dispatch(USER_INTERACT_TOUCHEND); - this.didMoveSinceTouchStart = false; - } - this.upPostHandler.dispatch(new Vector(x, y)); - } - - /** - * Clamps the camera zoom level within the allowed range - */ - clampZoomLevel() { - if (G_IS_DEV && globalConfig.debug.disableZoomLimits) { - return; - } - assert(Number.isFinite(this.zoomLevel), "Invalid zoom level *before* clamp: " + this.zoomLevel); - this.zoomLevel = clamp(this.zoomLevel, this.getMinimumZoom(), this.getMaximumZoom()); - assert(Number.isFinite(this.zoomLevel), "Invalid zoom level *after* clamp: " + this.zoomLevel); - - if (this.desiredZoom) { - this.desiredZoom = clamp(this.desiredZoom, this.getMinimumZoom(), this.getMaximumZoom()); - } - } - - /** - * Clamps the center within set boundaries - */ - clampToBounds() { - const bounds = this.root.gameMode.getCameraBounds(); - if (!bounds) { - return; - } - - const tileScaleBounds = this.root.gameMode.getCameraBounds().allScaled(globalConfig.tileSize); - this.center.x = clamp(this.center.x, tileScaleBounds.x, tileScaleBounds.x + tileScaleBounds.w); - this.center.y = clamp(this.center.y, tileScaleBounds.y, tileScaleBounds.y + tileScaleBounds.h); - } - - /** - * Updates the camera - * @param {number} dt Delta time in milliseconds - */ - update(dt) { - dt = Math.min(dt, 33); - this.cameraUpdateTimeBucket += dt; - - // Simulate movement of N FPS - const updatesPerFrame = 4; - const physicsStepSizeMs = 1000.0 / (60.0 * updatesPerFrame); - - let now = this.root.time.systemNow() - 3 * physicsStepSizeMs; - - while (this.cameraUpdateTimeBucket > physicsStepSizeMs) { - now += physicsStepSizeMs; - this.cameraUpdateTimeBucket -= physicsStepSizeMs; - - this.internalUpdatePanning(now, physicsStepSizeMs); - this.internalUpdateMousePanning(now, physicsStepSizeMs); - this.internalUpdateZooming(now, physicsStepSizeMs); - this.internalUpdateCentering(now, physicsStepSizeMs); - this.internalUpdateShake(now, physicsStepSizeMs); - this.internalUpdateKeyboardForce(now, physicsStepSizeMs); - } - this.clampZoomLevel(); - } - - /** - * Prepares a context to transform it - * @param {CanvasRenderingContext2D} context - */ - transform(context) { - if (G_IS_DEV && globalConfig.debug.testCulling) { - context.transform(1, 0, 0, 1, 100, 100); - return; - } - - this.clampZoomLevel(); - const zoom = this.zoomLevel; - - context.transform( - // Scale, skew, rotate - zoom, - 0, - 0, - zoom, - - // Translate - -zoom * this.getViewportLeft(), - -zoom * this.getViewportTop() - ); - } - - /** - * Internal shake handler - * @param {number} now Time now in seconds - * @param {number} dt Delta time - */ - internalUpdateShake(now, dt) { - this.currentShake = this.currentShake.multiplyScalar(0.92); - } - - /** - * Internal pan handler - * @param {number} now Time now in seconds - * @param {number} dt Delta time - */ - internalUpdatePanning(now, dt) { - const baseStrength = velocityStrength * this.root.app.platformWrapper.getTouchPanStrength(); - - this.touchPostMoveVelocity = this.touchPostMoveVelocity.multiplyScalar(velocityFade); - - // Check if the camera is being dragged but standing still: if not, zero out `touchPostMoveVelocity`. - if (this.currentlyMoving && this.desiredCenter === null) { - if ( - this.lastMovingPositionLastTick !== null && - this.lastMovingPositionLastTick.equalsEpsilon(this.lastMovingPosition) - ) { - this.numTicksStandingStill++; - } else { - this.numTicksStandingStill = 0; - } - this.lastMovingPositionLastTick = this.lastMovingPosition.copy(); - - if (this.numTicksStandingStill >= ticksBeforeErasingVelocity) { - this.touchPostMoveVelocity.x = 0; - this.touchPostMoveVelocity.y = 0; - } - } - // Check influence of past points - if (!this.currentlyMoving && !this.currentlyPinching) { - const len = this.touchPostMoveVelocity.length(); - if (len >= velocityMax) { - this.touchPostMoveVelocity.x = (this.touchPostMoveVelocity.x * velocityMax) / len; - this.touchPostMoveVelocity.y = (this.touchPostMoveVelocity.y * velocityMax) / len; - } - - this.center = this.center.add(this.touchPostMoveVelocity.multiplyScalar(baseStrength)); - - // Panning - this.currentPan = mixVector(this.currentPan, this.desiredPan, 0.06); - this.center = this.center.add(this.currentPan.multiplyScalar((0.5 * dt) / this.zoomLevel)); - this.clampToBounds(); - } - } - - /** - * Internal screen panning handler - * @param {number} now - * @param {number} dt - */ - internalUpdateMousePanning(now, dt) { - if (!this.root.app.focused) { - return; - } - - if (!this.root.app.settings.getAllSettings().enableMousePan) { - // Not enabled - return; - } - - const mousePos = this.root.app.mousePosition; - if (!mousePos) { - return; - } - - if (this.root.hud.shouldPauseGame() || this.root.hud.hasBlockingOverlayOpen()) { - return; - } - - if (this.desiredCenter || this.desiredZoom || this.currentlyMoving || this.currentlyPinching) { - // Performing another method of movement right now - return; - } - - if ( - mousePos.x < 0 || - mousePos.y < 0 || - mousePos.x > this.root.gameWidth || - mousePos.y > this.root.gameHeight - ) { - // Out of screen - return; - } - - const panAreaPixels = 2; - - const panVelocity = new Vector(); - if (mousePos.x < panAreaPixels) { - panVelocity.x -= 1; - } - if (mousePos.x > this.root.gameWidth - panAreaPixels) { - panVelocity.x += 1; - } - - if (mousePos.y < panAreaPixels) { - panVelocity.y -= 1; - } - if (mousePos.y > this.root.gameHeight - panAreaPixels) { - panVelocity.y += 1; - } - - this.center = this.center.add( - panVelocity.multiplyScalar( - ((0.5 * dt) / this.zoomLevel) * this.root.app.settings.getMovementSpeed() - ) - ); - - this.clampToBounds(); - } - - /** - * Updates the non user interaction zooming - * @param {number} now Time now in seconds - * @param {number} dt Delta time - */ - internalUpdateZooming(now, dt) { - if (!this.currentlyPinching && this.desiredZoom !== null) { - const diff = this.zoomLevel - this.desiredZoom; - if (Math.abs(diff) > 0.0001) { - let fade = 0.94; - if (diff > 0) { - // Zoom out faster than in - fade = 0.9; - } - - assert(Number.isFinite(this.desiredZoom), "Desired zoom is NaN: " + this.desiredZoom); - assert(Number.isFinite(fade), "Zoom fade is NaN: " + fade); - this.zoomLevel = this.zoomLevel * fade + this.desiredZoom * (1 - fade); - assert(Number.isFinite(this.zoomLevel), "Zoom level is NaN after fade: " + this.zoomLevel); - } else { - this.zoomLevel = this.desiredZoom; - this.desiredZoom = null; - } - } - } - - /** - * Updates the non user interaction centering - * @param {number} now Time now in seconds - * @param {number} dt Delta time - */ - internalUpdateCentering(now, dt) { - if (!this.currentlyMoving && this.desiredCenter !== null) { - const diff = this.center.direction(this.desiredCenter); - const length = diff.length(); - const tolerance = 1 / this.zoomLevel; - if (length > tolerance) { - const movement = diff.multiplyScalar(Math.min(1, dt * 0.008)); - this.center.x += movement.x; - this.center.y += movement.y; - } else { - this.desiredCenter = null; - } - } - } - - /** - * Updates the keyboard forces - * @param {number} now - * @param {number} dt Delta time - */ - internalUpdateKeyboardForce(now, dt) { - if (!this.currentlyMoving && this.desiredCenter == null) { - const limitingDimension = Math.min(this.root.gameWidth, this.root.gameHeight); - - const moveAmount = ((limitingDimension / 2048) * dt) / this.zoomLevel; - - let forceX = 0; - let forceY = 0; - - const actionMapper = this.root.keyMapper; - if (actionMapper.getBinding(KEYMAPPINGS.navigation.mapMoveUp).pressed) { - forceY -= 1; - } - - if (actionMapper.getBinding(KEYMAPPINGS.navigation.mapMoveDown).pressed) { - forceY += 1; - } - - if (actionMapper.getBinding(KEYMAPPINGS.navigation.mapMoveLeft).pressed) { - forceX -= 1; - } - - if (actionMapper.getBinding(KEYMAPPINGS.navigation.mapMoveRight).pressed) { - forceX += 1; - } - - let movementSpeed = - this.root.app.settings.getMovementSpeed() * - (actionMapper.getBinding(KEYMAPPINGS.navigation.mapMoveFaster).pressed ? 4 : 1); - - this.center.x += moveAmount * forceX * movementSpeed; - this.center.y += moveAmount * forceY * movementSpeed; - - this.clampToBounds(); - } - } -} +import { clickDetectorGlobals } from "../core/click_detector"; +import { globalConfig, SUPPORT_TOUCH } from "../core/config"; +import { createLogger } from "../core/logging"; +import { Rectangle } from "../core/rectangle"; +import { Signal, STOP_PROPAGATION } from "../core/signal"; +import { clamp } from "../core/utils"; +import { mixVector, Vector } from "../core/vector"; +import { BasicSerializableObject, types } from "../savegame/serialization"; +import { KEYMAPPINGS } from "./key_action_mapper"; +import { GameRoot } from "./root"; + +const logger = createLogger("camera"); + +// @TODO: unused signal +export const USER_INTERACT_MOVE = "move"; +export const USER_INTERACT_ZOOM = "zoom"; +export const USER_INTERACT_TOUCHEND = "touchend"; + +const velocitySmoothing = 0.5; +const velocityFade = 0.98; +const velocityStrength = 0.4; +const velocityMax = 20; +const ticksBeforeErasingVelocity = 10; + +/** + * @enum {string} + */ +export const enumMouseButton = { + left: "left", + middle: "middle", + right: "right", +}; + +export class Camera extends BasicSerializableObject { + constructor(root) { + super(); + + /** @type {GameRoot} */ + this.root = root; + + // Zoom level, 2 means double size + + // Find optimal initial zoom + + this.zoomLevel = this.findInitialZoom(); + this.clampZoomLevel(); + + /** @type {Vector} */ + this.center = new Vector(0, 0); + + // Input handling + this.currentlyMoving = false; + this.lastMovingPosition = null; + this.lastMovingPositionLastTick = null; + this.numTicksStandingStill = null; + this.cameraUpdateTimeBucket = 0.0; + this.didMoveSinceTouchStart = false; + this.currentlyPinching = false; + this.lastPinchPositions = null; + + this.keyboardForce = new Vector(); + + // Signal which gets emitted once the user changed something + /** @type {Signal<[string]>} */ + this.userInteraction = new Signal(); + + /** @type {Vector} */ + this.currentShake = new Vector(0, 0); + + /** @type {Vector} */ + this.currentPan = new Vector(0, 0); + + // Set desired pan (camera movement) + /** @type {Vector} */ + this.desiredPan = new Vector(0, 0); + + // Set desired camera center + /** @type {Vector} */ + this.desiredCenter = null; + + // Set desired camera zoom + /** @type {number} */ + this.desiredZoom = null; + + /** @type {Vector} */ + this.touchPostMoveVelocity = new Vector(0, 0); + + // Handlers + this.downPreHandler = /** @type {Signal<[Vector, enumMouseButton]>} */ (new Signal()); + this.movePreHandler = /** @type {Signal<[Vector]>} */ (new Signal()); + // this.pinchPreHandler = /** @type {Signal<[Vector]>} */ (new Signal()); + this.upPostHandler = /** @type {Signal<[Vector]>} */ (new Signal()); + + this.internalInitEvents(); + this.clampZoomLevel(); + this.bindKeys(); + if (G_IS_DEV) { + window.addEventListener("keydown", ev => { + if (ev.key === "i") { + this.zoomLevel = 3; + } + }); + } + } + + // Serialization + static getId() { + return "Camera"; + } + + static getSchema() { + return { + zoomLevel: types.float, + center: types.vector, + }; + } + + deserialize(data) { + const errorCode = super.deserialize(data); + if (errorCode) { + return errorCode; + } + + // Safety + this.clampZoomLevel(); + } + + // Simple getters & setters + + addScreenShake(amount) { + const currentShakeAmount = this.currentShake.length(); + const scale = 1 / (1 + 3 * currentShakeAmount); + this.currentShake.x = this.currentShake.x + 2 * (Math.random() - 0.5) * scale * amount; + this.currentShake.y = this.currentShake.y + 2 * (Math.random() - 0.5) * scale * amount; + } + + /** + * Sets a point in world space to focus on + * @param {Vector} center + */ + setDesiredCenter(center) { + this.desiredCenter = center.copy(); + this.currentlyMoving = false; + } + + /** + * Sets a desired zoom level + * @param {number} zoom + */ + setDesiredZoom(zoom) { + this.desiredZoom = zoom; + } + + /** + * Returns if this camera is currently moving by a non-user interaction + */ + isCurrentlyMovingToDesiredCenter() { + return this.desiredCenter !== null; + } + + /** + * Sets the camera pan, every frame the camera will move by this amount + * @param {Vector} pan + */ + setPan(pan) { + this.desiredPan = pan.copy(); + } + + /** + * Finds a good initial zoom level + */ + findInitialZoom() { + let desiredWorldSpaceWidth = 18 * globalConfig.tileSize; + if (window.innerWidth < 1000) { + desiredWorldSpaceWidth = 12 * globalConfig.tileSize; + } + + const zoomLevelX = this.root.gameWidth / desiredWorldSpaceWidth; + const zoomLevelY = this.root.gameHeight / desiredWorldSpaceWidth; + + const finalLevel = Math.min(zoomLevelX, zoomLevelY); + assert( + Number.isFinite(finalLevel) && finalLevel > 0, + "Invalid zoom level computed for initial zoom: " + finalLevel + ); + return finalLevel; + } + + /** + * Clears all animations + */ + clearAnimations() { + this.touchPostMoveVelocity.x = 0; + this.touchPostMoveVelocity.y = 0; + this.desiredCenter = null; + this.desiredPan.x = 0; + this.desiredPan.y = 0; + this.currentPan.x = 0; + this.currentPan.y = 0; + this.currentlyPinching = false; + this.currentlyMoving = false; + this.lastMovingPosition = null; + this.didMoveSinceTouchStart = false; + this.desiredZoom = null; + } + + /** + * Returns if the user is currently interacting with the camera + * @returns {boolean} true if the user interacts + */ + isCurrentlyInteracting() { + if (this.currentlyPinching) { + return true; + } + if (this.currentlyMoving) { + // Only interacting if moved at least once + return this.didMoveSinceTouchStart; + } + if (this.touchPostMoveVelocity.lengthSquare() > 1) { + return true; + } + return false; + } + + /** + * Returns if in the next frame the viewport will change + * @returns {boolean} true if it willchange + */ + viewportWillChange() { + return this.desiredCenter !== null || this.desiredZoom !== null || this.isCurrentlyInteracting(); + } + + /** + * Cancels all interactions, that is user interaction and non user interaction + */ + cancelAllInteractions() { + this.touchPostMoveVelocity = new Vector(0, 0); + this.desiredCenter = null; + this.currentlyMoving = false; + this.currentlyPinching = false; + this.desiredZoom = null; + } + + /** + * Returns effective viewport width + */ + getViewportWidth() { + return this.root.gameWidth / this.zoomLevel; + } + + /** + * Returns effective viewport height + */ + getViewportHeight() { + return this.root.gameHeight / this.zoomLevel; + } + + /** + * Returns effective world space viewport left + */ + getViewportLeft() { + return this.center.x - this.getViewportWidth() / 2 + (this.currentShake.x * 10) / this.zoomLevel; + } + + /** + * Returns effective world space viewport right + */ + getViewportRight() { + return this.center.x + this.getViewportWidth() / 2 + (this.currentShake.x * 10) / this.zoomLevel; + } + + /** + * Returns effective world space viewport top + */ + getViewportTop() { + return this.center.y - this.getViewportHeight() / 2 + (this.currentShake.x * 10) / this.zoomLevel; + } + + /** + * Returns effective world space viewport bottom + */ + getViewportBottom() { + return this.center.y + this.getViewportHeight() / 2 + (this.currentShake.x * 10) / this.zoomLevel; + } + + /** + * Returns the visible world space rect + * @returns {Rectangle} + */ + getVisibleRect() { + return Rectangle.fromTRBL( + Math.floor(this.getViewportTop()), + Math.ceil(this.getViewportRight()), + Math.ceil(this.getViewportBottom()), + Math.floor(this.getViewportLeft()) + ); + } + + getIsMapOverlayActive() { + return this.zoomLevel < globalConfig.mapChunkOverviewMinZoom; + } + + /** + * Attaches all event listeners + */ + internalInitEvents() { + this.eventListenerTouchStart = this.onTouchStart.bind(this); + this.eventListenerTouchEnd = this.onTouchEnd.bind(this); + this.eventListenerTouchMove = this.onTouchMove.bind(this); + this.eventListenerMousewheel = this.onMouseWheel.bind(this); + this.eventListenerMouseDown = this.onMouseDown.bind(this); + this.eventListenerMouseMove = this.onMouseMove.bind(this); + this.eventListenerMouseUp = this.onMouseUp.bind(this); + + if (SUPPORT_TOUCH) { + this.root.canvas.addEventListener("touchstart", this.eventListenerTouchStart); + this.root.canvas.addEventListener("touchend", this.eventListenerTouchEnd); + this.root.canvas.addEventListener("touchcancel", this.eventListenerTouchEnd); + this.root.canvas.addEventListener("touchmove", this.eventListenerTouchMove); + } + + this.root.canvas.addEventListener("wheel", this.eventListenerMousewheel); + this.root.canvas.addEventListener("mousedown", this.eventListenerMouseDown); + this.root.canvas.addEventListener("mousemove", this.eventListenerMouseMove); + window.addEventListener("mouseup", this.eventListenerMouseUp); + // this.root.canvas.addEventListener("mouseout", this.eventListenerMouseUp); + } + + /** + * Cleans up all event listeners + */ + cleanup() { + if (SUPPORT_TOUCH) { + this.root.canvas.removeEventListener("touchstart", this.eventListenerTouchStart); + this.root.canvas.removeEventListener("touchend", this.eventListenerTouchEnd); + this.root.canvas.removeEventListener("touchcancel", this.eventListenerTouchEnd); + this.root.canvas.removeEventListener("touchmove", this.eventListenerTouchMove); + } + + this.root.canvas.removeEventListener("wheel", this.eventListenerMousewheel); + this.root.canvas.removeEventListener("mousedown", this.eventListenerMouseDown); + this.root.canvas.removeEventListener("mousemove", this.eventListenerMouseMove); + window.removeEventListener("mouseup", this.eventListenerMouseUp); + // this.root.canvas.removeEventListener("mouseout", this.eventListenerMouseUp); + } + + /** + * Binds the arrow keys + */ + bindKeys() { + const mapper = this.root.keyMapper; + mapper.getBinding(KEYMAPPINGS.navigation.mapMoveUp).add(() => (this.keyboardForce.y = -1)); + mapper.getBinding(KEYMAPPINGS.navigation.mapMoveDown).add(() => (this.keyboardForce.y = 1)); + mapper.getBinding(KEYMAPPINGS.navigation.mapMoveRight).add(() => (this.keyboardForce.x = 1)); + mapper.getBinding(KEYMAPPINGS.navigation.mapMoveLeft).add(() => (this.keyboardForce.x = -1)); + + mapper + .getBinding(KEYMAPPINGS.navigation.mapZoomIn) + .add(() => (this.desiredZoom = this.zoomLevel * 1.2)); + mapper + .getBinding(KEYMAPPINGS.navigation.mapZoomOut) + .add(() => (this.desiredZoom = this.zoomLevel / 1.2)); + + mapper.getBinding(KEYMAPPINGS.navigation.centerMap).add(() => this.centerOnMap()); + } + + centerOnMap() { + this.desiredCenter = new Vector(0, 0); + } + + /** + * Converts from screen to world space + * @param {Vector} screen + * @returns {Vector} world space + */ + screenToWorld(screen) { + const centerSpace = screen.subScalars(this.root.gameWidth / 2, this.root.gameHeight / 2); + return centerSpace.divideScalar(this.zoomLevel).add(this.center); + } + + /** + * Converts from world to screen space + * @param {Vector} world + * @returns {Vector} screen space + */ + worldToScreen(world) { + const screenSpace = world.sub(this.center).multiplyScalar(this.zoomLevel); + return screenSpace.addScalars(this.root.gameWidth / 2, this.root.gameHeight / 2); + } + + /** + * Returns if a point is on screen + * @param {Vector} point + * @returns {boolean} true if its on screen + */ + isWorldPointOnScreen(point) { + const rect = this.getVisibleRect(); + return rect.containsPoint(point.x, point.y); + } + + getMaximumZoom() { + return this.root.gameMode.getMaximumZoom(); + } + + getMinimumZoom() { + return this.root.gameMode.getMinimumZoom(); + } + + /** + * Returns if we can further zoom in + * @returns {boolean} + */ + canZoomIn() { + return this.zoomLevel <= this.getMaximumZoom() - 0.01; + } + + /** + * Returns if we can further zoom out + * @returns {boolean} + */ + canZoomOut() { + return this.zoomLevel >= this.getMinimumZoom() + 0.01; + } + + // EVENTS + + /** + * Checks if the mouse event is too close after a touch event and thus + * should get ignored + */ + checkPreventDoubleMouse() { + if (performance.now() - clickDetectorGlobals.lastTouchTime < 1000.0) { + return false; + } + return true; + } + + /** + * Mousedown handler + * @param {MouseEvent} event + */ + onMouseDown(event) { + if (event.cancelable) { + event.preventDefault(); + // event.stopPropagation(); + } + + if (!this.checkPreventDoubleMouse()) { + return; + } + + this.touchPostMoveVelocity = new Vector(0, 0); + if (event.button === 0) { + this.combinedSingleTouchStartHandler(event.clientX, event.clientY); + } else if (event.button === 1) { + this.downPreHandler.dispatch(new Vector(event.clientX, event.clientY), enumMouseButton.middle); + } else if (event.button === 2) { + this.downPreHandler.dispatch(new Vector(event.clientX, event.clientY), enumMouseButton.right); + } + return false; + } + + /** + * Mousemove handler + * @param {MouseEvent} event + */ + onMouseMove(event) { + if (event.cancelable) { + event.preventDefault(); + // event.stopPropagation(); + } + + if (!this.checkPreventDoubleMouse()) { + return; + } + + if (event.button === 0) { + this.combinedSingleTouchMoveHandler(event.clientX, event.clientY); + } + + // Clamp everything afterwards + this.clampZoomLevel(); + this.clampToBounds(); + return false; + } + + /** + * Mouseup handler + * @param {MouseEvent=} event + */ + onMouseUp(event) { + if (event) { + if (event.cancelable) { + event.preventDefault(); + // event.stopPropagation(); + } + } + + if (!this.checkPreventDoubleMouse()) { + return; + } + + this.combinedSingleTouchStopHandler(event.clientX, event.clientY); + return false; + } + + /** + * Mousewheel event + * @param {WheelEvent} event + */ + onMouseWheel(event) { + if (event.cancelable) { + event.preventDefault(); + // event.stopPropagation(); + } + const prevZoom = this.zoomLevel; + + const scale = 1 + 0.15 * this.root.app.settings.getScrollWheelSensitivity(); + assert(Number.isFinite(scale), "Got invalid scale in mouse wheel event: " + event.deltaY); + assert(Number.isFinite(this.zoomLevel), "Got invalid zoom level *before* wheel: " + this.zoomLevel); + this.zoomLevel *= event.deltaY < 0 ? scale : 1 / scale; + assert(Number.isFinite(this.zoomLevel), "Got invalid zoom level *after* wheel: " + this.zoomLevel); + + this.clampZoomLevel(); + this.desiredZoom = null; + + let mousePosition = this.root.app.mousePosition; + if (!this.root.app.settings.getAllSettings().zoomToCursor) { + mousePosition = new Vector(this.root.gameWidth / 2, this.root.gameHeight / 2); + } + + if (mousePosition) { + const worldPos = this.root.camera.screenToWorld(mousePosition); + const worldDelta = worldPos.sub(this.center); + const actualDelta = this.zoomLevel / prevZoom - 1; + this.center = this.center.add(worldDelta.multiplyScalar(actualDelta)); + this.desiredCenter = null; + } + + return false; + } + + /** + * Touch start handler + * @param {TouchEvent} event + */ + onTouchStart(event) { + if (event.cancelable) { + event.preventDefault(); + // event.stopPropagation(); + } + + clickDetectorGlobals.lastTouchTime = performance.now(); + this.touchPostMoveVelocity = new Vector(0, 0); + + if (event.touches.length === 1) { + const touch = event.touches[0]; + this.combinedSingleTouchStartHandler(touch.clientX, touch.clientY); + } else if (event.touches.length === 2) { + // if (this.pinchPreHandler.dispatch() === STOP_PROPAGATION) { + // // Something prevented pinching + // return false; + // } + + const touch1 = event.touches[0]; + const touch2 = event.touches[1]; + this.currentlyMoving = false; + this.currentlyPinching = true; + this.lastPinchPositions = [ + new Vector(touch1.clientX, touch1.clientY), + new Vector(touch2.clientX, touch2.clientY), + ]; + } + return false; + } + + /** + * Touch move handler + * @param {TouchEvent} event + */ + onTouchMove(event) { + if (event.cancelable) { + event.preventDefault(); + // event.stopPropagation(); + } + + clickDetectorGlobals.lastTouchTime = performance.now(); + + if (event.touches.length === 1) { + const touch = event.touches[0]; + this.combinedSingleTouchMoveHandler(touch.clientX, touch.clientY); + } else if (event.touches.length === 2) { + if (this.currentlyPinching) { + const touch1 = event.touches[0]; + const touch2 = event.touches[1]; + + const newPinchPositions = [ + new Vector(touch1.clientX, touch1.clientY), + new Vector(touch2.clientX, touch2.clientY), + ]; + + // Get distance of taps last time and now + const lastDistance = this.lastPinchPositions[0].distance(this.lastPinchPositions[1]); + const thisDistance = newPinchPositions[0].distance(newPinchPositions[1]); + + // IMPORTANT to do math max here to avoid NaN and causing an invalid zoom level + const difference = thisDistance / Math.max(0.001, lastDistance); + + // Find old center of zoom + let oldCenter = this.lastPinchPositions[0].centerPoint(this.lastPinchPositions[1]); + + // Find new center of zoom + let center = newPinchPositions[0].centerPoint(newPinchPositions[1]); + + // Compute movement + let movement = oldCenter.sub(center); + this.center.x += movement.x / this.zoomLevel; + this.center.y += movement.y / this.zoomLevel; + + // Compute zoom + center = center.sub(new Vector(this.root.gameWidth / 2, this.root.gameHeight / 2)); + + // Apply zoom + assert( + Number.isFinite(difference), + "Invalid pinch difference: " + + difference + + "(last=" + + lastDistance + + ", new = " + + thisDistance + + ")" + ); + this.zoomLevel *= difference; + + // Stick to pivot point + const correcture = center.multiplyScalar(difference - 1).divideScalar(this.zoomLevel); + + this.center = this.center.add(correcture); + this.lastPinchPositions = newPinchPositions; + this.userInteraction.dispatch(USER_INTERACT_MOVE); + + // Since we zoomed, abort any programmed zooming + if (this.desiredZoom) { + this.desiredZoom = null; + } + } + } + + // Clamp everything afterwards + this.clampZoomLevel(); + return false; + } + + /** + * Touch end and cancel handler + * @param {TouchEvent=} event + */ + onTouchEnd(event) { + if (event) { + if (event.cancelable) { + event.preventDefault(); + // event.stopPropagation(); + } + } + + clickDetectorGlobals.lastTouchTime = performance.now(); + if (event.changedTouches.length === 0) { + logger.warn("Touch end without changed touches"); + } + + const touch = event.changedTouches[0]; + this.combinedSingleTouchStopHandler(touch.clientX, touch.clientY); + return false; + } + + /** + * Internal touch start handler + * @param {number} x + * @param {number} y + */ + combinedSingleTouchStartHandler(x, y) { + const pos = new Vector(x, y); + if (this.downPreHandler.dispatch(pos, enumMouseButton.left) === STOP_PROPAGATION) { + // Somebody else captured it + return; + } + + this.touchPostMoveVelocity = new Vector(0, 0); + this.currentlyMoving = true; + this.lastMovingPosition = pos; + this.lastMovingPositionLastTick = null; + this.numTicksStandingStill = 0; + this.didMoveSinceTouchStart = false; + } + + /** + * Internal touch move handler + * @param {number} x + * @param {number} y + */ + combinedSingleTouchMoveHandler(x, y) { + const pos = new Vector(x, y); + if (this.movePreHandler.dispatch(pos) === STOP_PROPAGATION) { + // Somebody else captured it + return; + } + + if (!this.currentlyMoving) { + return false; + } + + let delta = this.lastMovingPosition.sub(pos).divideScalar(this.zoomLevel); + if (G_IS_DEV && globalConfig.debug.testCulling) { + // When testing culling, we see everything from the same distance + delta = delta.multiplyScalar(this.zoomLevel * -2); + } + + this.didMoveSinceTouchStart = this.didMoveSinceTouchStart || delta.length() > 0; + this.center = this.center.add(delta); + + this.touchPostMoveVelocity = this.touchPostMoveVelocity + .multiplyScalar(velocitySmoothing) + .add(delta.multiplyScalar(1 - velocitySmoothing)); + + this.lastMovingPosition = pos; + this.userInteraction.dispatch(USER_INTERACT_MOVE); + + // Since we moved, abort any programmed moving + if (this.desiredCenter) { + this.desiredCenter = null; + } + } + + /** + * Internal touch stop handler + */ + combinedSingleTouchStopHandler(x, y) { + if (this.currentlyMoving || this.currentlyPinching) { + this.currentlyMoving = false; + this.currentlyPinching = false; + this.lastMovingPosition = null; + this.lastMovingPositionLastTick = null; + this.numTicksStandingStill = 0; + this.lastPinchPositions = null; + this.userInteraction.dispatch(USER_INTERACT_TOUCHEND); + this.didMoveSinceTouchStart = false; + } + this.upPostHandler.dispatch(new Vector(x, y)); + } + + /** + * Clamps the camera zoom level within the allowed range + */ + clampZoomLevel() { + if (G_IS_DEV && globalConfig.debug.disableZoomLimits) { + return; + } + assert(Number.isFinite(this.zoomLevel), "Invalid zoom level *before* clamp: " + this.zoomLevel); + this.zoomLevel = clamp(this.zoomLevel, this.getMinimumZoom(), this.getMaximumZoom()); + assert(Number.isFinite(this.zoomLevel), "Invalid zoom level *after* clamp: " + this.zoomLevel); + + if (this.desiredZoom) { + this.desiredZoom = clamp(this.desiredZoom, this.getMinimumZoom(), this.getMaximumZoom()); + } + } + + /** + * Clamps the center within set boundaries + */ + clampToBounds() { + const bounds = this.root.gameMode.getCameraBounds(); + if (!bounds) { + return; + } + + const tileScaleBounds = this.root.gameMode.getCameraBounds().allScaled(globalConfig.tileSize); + this.center.x = clamp(this.center.x, tileScaleBounds.x, tileScaleBounds.x + tileScaleBounds.w); + this.center.y = clamp(this.center.y, tileScaleBounds.y, tileScaleBounds.y + tileScaleBounds.h); + } + + /** + * Updates the camera + * @param {number} dt Delta time in milliseconds + */ + update(dt) { + dt = Math.min(dt, 33); + this.cameraUpdateTimeBucket += dt; + + // Simulate movement of N FPS + const updatesPerFrame = 4; + const physicsStepSizeMs = 1000.0 / (60.0 * updatesPerFrame); + + let now = this.root.time.systemNow() - 3 * physicsStepSizeMs; + + while (this.cameraUpdateTimeBucket > physicsStepSizeMs) { + now += physicsStepSizeMs; + this.cameraUpdateTimeBucket -= physicsStepSizeMs; + + this.internalUpdatePanning(now, physicsStepSizeMs); + this.internalUpdateMousePanning(now, physicsStepSizeMs); + this.internalUpdateZooming(now, physicsStepSizeMs); + this.internalUpdateCentering(now, physicsStepSizeMs); + this.internalUpdateShake(now, physicsStepSizeMs); + this.internalUpdateKeyboardForce(now, physicsStepSizeMs); + } + this.clampZoomLevel(); + } + + /** + * Prepares a context to transform it + * @param {CanvasRenderingContext2D} context + */ + transform(context) { + if (G_IS_DEV && globalConfig.debug.testCulling) { + context.transform(1, 0, 0, 1, 100, 100); + return; + } + + this.clampZoomLevel(); + const zoom = this.zoomLevel; + + context.transform( + // Scale, skew, rotate + zoom, + 0, + 0, + zoom, + + // Translate + -zoom * this.getViewportLeft(), + -zoom * this.getViewportTop() + ); + } + + /** + * Internal shake handler + * @param {number} now Time now in seconds + * @param {number} dt Delta time + */ + internalUpdateShake(now, dt) { + this.currentShake = this.currentShake.multiplyScalar(0.92); + } + + /** + * Internal pan handler + * @param {number} now Time now in seconds + * @param {number} dt Delta time + */ + internalUpdatePanning(now, dt) { + const baseStrength = velocityStrength * this.root.app.platformWrapper.getTouchPanStrength(); + + this.touchPostMoveVelocity = this.touchPostMoveVelocity.multiplyScalar(velocityFade); + + // Check if the camera is being dragged but standing still: if not, zero out `touchPostMoveVelocity`. + if (this.currentlyMoving && this.desiredCenter === null) { + if ( + this.lastMovingPositionLastTick !== null && + this.lastMovingPositionLastTick.equalsEpsilon(this.lastMovingPosition) + ) { + this.numTicksStandingStill++; + } else { + this.numTicksStandingStill = 0; + } + this.lastMovingPositionLastTick = this.lastMovingPosition.copy(); + + if (this.numTicksStandingStill >= ticksBeforeErasingVelocity) { + this.touchPostMoveVelocity.x = 0; + this.touchPostMoveVelocity.y = 0; + } + } + // Check influence of past points + if (!this.currentlyMoving && !this.currentlyPinching) { + const len = this.touchPostMoveVelocity.length(); + if (len >= velocityMax) { + this.touchPostMoveVelocity.x = (this.touchPostMoveVelocity.x * velocityMax) / len; + this.touchPostMoveVelocity.y = (this.touchPostMoveVelocity.y * velocityMax) / len; + } + + this.center = this.center.add(this.touchPostMoveVelocity.multiplyScalar(baseStrength)); + + // Panning + this.currentPan = mixVector(this.currentPan, this.desiredPan, 0.06); + this.center = this.center.add(this.currentPan.multiplyScalar((0.5 * dt) / this.zoomLevel)); + this.clampToBounds(); + } + } + + /** + * Internal screen panning handler + * @param {number} now + * @param {number} dt + */ + internalUpdateMousePanning(now, dt) { + if (!this.root.app.focused) { + return; + } + + if (!this.root.app.settings.getAllSettings().enableMousePan) { + // Not enabled + return; + } + + const mousePos = this.root.app.mousePosition; + if (!mousePos) { + return; + } + + if (this.root.hud.shouldPauseGame() || this.root.hud.hasBlockingOverlayOpen()) { + return; + } + + if (this.desiredCenter || this.desiredZoom || this.currentlyMoving || this.currentlyPinching) { + // Performing another method of movement right now + return; + } + + if ( + mousePos.x < 0 || + mousePos.y < 0 || + mousePos.x > this.root.gameWidth || + mousePos.y > this.root.gameHeight + ) { + // Out of screen + return; + } + + const panAreaPixels = 2; + + const panVelocity = new Vector(); + if (mousePos.x < panAreaPixels) { + panVelocity.x -= 1; + } + if (mousePos.x > this.root.gameWidth - panAreaPixels) { + panVelocity.x += 1; + } + + if (mousePos.y < panAreaPixels) { + panVelocity.y -= 1; + } + if (mousePos.y > this.root.gameHeight - panAreaPixels) { + panVelocity.y += 1; + } + + this.center = this.center.add( + panVelocity.multiplyScalar( + ((0.5 * dt) / this.zoomLevel) * this.root.app.settings.getMovementSpeed() + ) + ); + + this.clampToBounds(); + } + + /** + * Updates the non user interaction zooming + * @param {number} now Time now in seconds + * @param {number} dt Delta time + */ + internalUpdateZooming(now, dt) { + if (!this.currentlyPinching && this.desiredZoom !== null) { + const diff = this.zoomLevel - this.desiredZoom; + if (Math.abs(diff) > 0.0001) { + let fade = 0.94; + if (diff > 0) { + // Zoom out faster than in + fade = 0.9; + } + + assert(Number.isFinite(this.desiredZoom), "Desired zoom is NaN: " + this.desiredZoom); + assert(Number.isFinite(fade), "Zoom fade is NaN: " + fade); + this.zoomLevel = this.zoomLevel * fade + this.desiredZoom * (1 - fade); + assert(Number.isFinite(this.zoomLevel), "Zoom level is NaN after fade: " + this.zoomLevel); + } else { + this.zoomLevel = this.desiredZoom; + this.desiredZoom = null; + } + } + } + + /** + * Updates the non user interaction centering + * @param {number} now Time now in seconds + * @param {number} dt Delta time + */ + internalUpdateCentering(now, dt) { + if (!this.currentlyMoving && this.desiredCenter !== null) { + const diff = this.center.direction(this.desiredCenter); + const length = diff.length(); + const tolerance = 1 / this.zoomLevel; + if (length > tolerance) { + const movement = diff.multiplyScalar(Math.min(1, dt * 0.008)); + this.center.x += movement.x; + this.center.y += movement.y; + } else { + this.desiredCenter = null; + } + } + } + + /** + * Updates the keyboard forces + * @param {number} now + * @param {number} dt Delta time + */ + internalUpdateKeyboardForce(now, dt) { + if (!this.currentlyMoving && this.desiredCenter == null) { + const limitingDimension = Math.min(this.root.gameWidth, this.root.gameHeight); + + const moveAmount = ((limitingDimension / 2048) * dt) / this.zoomLevel; + + let forceX = 0; + let forceY = 0; + + const actionMapper = this.root.keyMapper; + if (actionMapper.getBinding(KEYMAPPINGS.navigation.mapMoveUp).pressed) { + forceY -= 1; + } + + if (actionMapper.getBinding(KEYMAPPINGS.navigation.mapMoveDown).pressed) { + forceY += 1; + } + + if (actionMapper.getBinding(KEYMAPPINGS.navigation.mapMoveLeft).pressed) { + forceX -= 1; + } + + if (actionMapper.getBinding(KEYMAPPINGS.navigation.mapMoveRight).pressed) { + forceX += 1; + } + + let movementSpeed = + this.root.app.settings.getMovementSpeed() * + (actionMapper.getBinding(KEYMAPPINGS.navigation.mapMoveFaster).pressed ? 4 : 1); + + this.center.x += moveAmount * forceX * movementSpeed; + this.center.y += moveAmount * forceY * movementSpeed; + + this.clampToBounds(); + } + } +} diff --git a/src/js/game/component_registry.js b/src/js/game/component_registry.js index 1add879e..4146ea45 100644 --- a/src/js/game/component_registry.js +++ b/src/js/game/component_registry.js @@ -53,7 +53,7 @@ export function initComponentRegistry() { assert( // @ts-ignore - require.context("./components", false, /.*\.js/i).keys().length === + import.meta.webpackContext("./components", { recursive: false, regExp: /.*\.js/i }).keys().length === gComponentRegistry.getNumEntries(), "Not all components are registered" ); diff --git a/src/js/game/components/belt_reader.js b/src/js/game/components/belt_reader.js index 187e53cd..0026f2b8 100644 --- a/src/js/game/components/belt_reader.js +++ b/src/js/game/components/belt_reader.js @@ -1,53 +1,53 @@ -import { Component } from "../component"; -import { BaseItem } from "../base_item"; -import { typeItemSingleton } from "../item_resolver"; -import { types } from "../../savegame/serialization"; - -/** @enum {string} */ -export const enumBeltReaderType = { - wired: "wired", - wireless: "wireless", -}; - -export class BeltReaderComponent extends Component { - static getId() { - return "BeltReader"; - } - - static getSchema() { - return { - lastItem: types.nullable(typeItemSingleton), - }; - } - - constructor() { - super(); - this.clear(); - } - - clear() { - /** - * Which items went through the reader, we only store the time - * @type {Array} - */ - this.lastItemTimes = []; - - /** - * Which item passed the reader last - * @type {BaseItem} - */ - this.lastItem = null; - - /** - * Stores the last throughput we computed - * @type {number} - */ - this.lastThroughput = 0; - - /** - * Stores when we last computed the throughput - * @type {number} - */ - this.lastThroughputComputation = 0; - } -} +import { Component } from "../component"; +import { BaseItem } from "../base_item"; +import { typeItemSingleton } from "../item_resolver"; +import { types } from "../../savegame/serialization"; + +/** @enum {string} */ +export const enumBeltReaderType = { + wired: "wired", + wireless: "wireless", +}; + +export class BeltReaderComponent extends Component { + static getId() { + return "BeltReader"; + } + + static getSchema() { + return { + lastItem: types.nullable(typeItemSingleton), + }; + } + + constructor() { + super(); + this.clear(); + } + + clear() { + /** + * Which items went through the reader, we only store the time + * @type {Array} + */ + this.lastItemTimes = []; + + /** + * Which item passed the reader last + * @type {BaseItem} + */ + this.lastItem = null; + + /** + * Stores the last throughput we computed + * @type {number} + */ + this.lastThroughput = 0; + + /** + * Stores when we last computed the throughput + * @type {number} + */ + this.lastThroughputComputation = 0; + } +} diff --git a/src/js/game/components/belt_underlays.js b/src/js/game/components/belt_underlays.js index 63b265d0..ccce4dd4 100644 --- a/src/js/game/components/belt_underlays.js +++ b/src/js/game/components/belt_underlays.js @@ -1,41 +1,41 @@ -import { enumDirection, Vector } from "../../core/vector"; -import { Component } from "../component"; - -/** - * Store which type an underlay is, this is cached so we can easily - * render it. - * - * Full: Render underlay at top and bottom of tile - * Bottom Only: Only render underlay at the bottom half - * Top Only: - * @enum {string} - */ -export const enumClippedBeltUnderlayType = { - full: "full", - bottomOnly: "bottomOnly", - topOnly: "topOnly", - none: "none", -}; - -/** - * @typedef {{ - * pos: Vector, - * direction: enumDirection, - * cachedType?: enumClippedBeltUnderlayType - * }} BeltUnderlayTile - */ - -export class BeltUnderlaysComponent extends Component { - static getId() { - return "BeltUnderlays"; - } - - /** - * @param {object} param0 - * @param {Array=} param0.underlays Where to render belt underlays - */ - constructor({ underlays = [] }) { - super(); - this.underlays = underlays; - } -} +import { enumDirection, Vector } from "../../core/vector"; +import { Component } from "../component"; + +/** + * Store which type an underlay is, this is cached so we can easily + * render it. + * + * Full: Render underlay at top and bottom of tile + * Bottom Only: Only render underlay at the bottom half + * Top Only: + * @enum {string} + */ +export const enumClippedBeltUnderlayType = { + full: "full", + bottomOnly: "bottomOnly", + topOnly: "topOnly", + none: "none", +}; + +/** + * @typedef {{ + * pos: Vector, + * direction: enumDirection, + * cachedType?: enumClippedBeltUnderlayType + * }} BeltUnderlayTile + */ + +export class BeltUnderlaysComponent extends Component { + static getId() { + return "BeltUnderlays"; + } + + /** + * @param {object} param0 + * @param {Array=} param0.underlays Where to render belt underlays + */ + constructor({ underlays = [] }) { + super(); + this.underlays = underlays; + } +} diff --git a/src/js/game/components/filter.js b/src/js/game/components/filter.js index 8a22a076..d90e05e9 100644 --- a/src/js/game/components/filter.js +++ b/src/js/game/components/filter.js @@ -1,59 +1,59 @@ -import { types } from "../../savegame/serialization"; -import { BaseItem } from "../base_item"; -import { Component } from "../component"; -import { typeItemSingleton } from "../item_resolver"; - -/** - * @typedef {{ - * item: BaseItem, - * progress: number - * }} PendingFilterItem - */ - -export class FilterComponent extends Component { - static getId() { - return "Filter"; - } - - duplicateWithoutContents() { - return new FilterComponent(); - } - - static getSchema() { - return { - pendingItemsToLeaveThrough: types.array( - types.structured({ - item: typeItemSingleton, - progress: types.ufloat, - }) - ), - - pendingItemsToReject: types.array( - types.structured({ - item: typeItemSingleton, - progress: types.ufloat, - }) - ), - }; - } - - constructor() { - super(); - - this.clear(); - } - - clear() { - /** - * Items in queue to leave through - * @type {Array} - */ - this.pendingItemsToLeaveThrough = []; - - /** - * Items in queue to reject - * @type {Array} - */ - this.pendingItemsToReject = []; - } -} +import { types } from "../../savegame/serialization"; +import { BaseItem } from "../base_item"; +import { Component } from "../component"; +import { typeItemSingleton } from "../item_resolver"; + +/** + * @typedef {{ + * item: BaseItem, + * progress: number + * }} PendingFilterItem + */ + +export class FilterComponent extends Component { + static getId() { + return "Filter"; + } + + duplicateWithoutContents() { + return new FilterComponent(); + } + + static getSchema() { + return { + pendingItemsToLeaveThrough: types.array( + types.structured({ + item: typeItemSingleton, + progress: types.ufloat, + }) + ), + + pendingItemsToReject: types.array( + types.structured({ + item: typeItemSingleton, + progress: types.ufloat, + }) + ), + }; + } + + constructor() { + super(); + + this.clear(); + } + + clear() { + /** + * Items in queue to leave through + * @type {Array} + */ + this.pendingItemsToLeaveThrough = []; + + /** + * Items in queue to reject + * @type {Array} + */ + this.pendingItemsToReject = []; + } +} diff --git a/src/js/game/components/goal_acceptor.js b/src/js/game/components/goal_acceptor.js index fa5f5908..c5aeb974 100644 --- a/src/js/game/components/goal_acceptor.js +++ b/src/js/game/components/goal_acceptor.js @@ -1,67 +1,67 @@ -import { globalConfig } from "../../core/config"; -import { BaseItem } from "../base_item"; -import { Component } from "../component"; -import { typeItemSingleton } from "../item_resolver"; - -export class GoalAcceptorComponent extends Component { - static getId() { - return "GoalAcceptor"; - } - - static getSchema() { - return { - item: typeItemSingleton, - }; - } - - /** - * @param {object} param0 - * @param {BaseItem=} param0.item - * @param {number=} param0.rate - */ - constructor({ item = null, rate = null }) { - super(); - - // ths item to produce - /** @type {BaseItem | undefined} */ - this.item = item; - - this.clear(); - } - - clear() { - /** - * The last item we delivered - * @type {{ item: BaseItem; time: number; } | null} */ - this.lastDelivery = null; - - // The amount of items we delivered so far - this.currentDeliveredItems = 0; - - // Used for animations - this.displayPercentage = 0; - } - - /** - * Clears items but doesn't instantly reset the progress bar - */ - clearItems() { - this.lastDelivery = null; - this.currentDeliveredItems = 0; - } - - getRequiredSecondsPerItem() { - return ( - globalConfig.goalAcceptorsPerProducer / - (globalConfig.puzzleModeSpeed * globalConfig.beltSpeedItemsPerSecond) - ); - } - - /** - * Copy the current state to another component - * @param {GoalAcceptorComponent} otherComponent - */ - copyAdditionalStateTo(otherComponent) { - otherComponent.item = this.item; - } -} +import { globalConfig } from "../../core/config"; +import { BaseItem } from "../base_item"; +import { Component } from "../component"; +import { typeItemSingleton } from "../item_resolver"; + +export class GoalAcceptorComponent extends Component { + static getId() { + return "GoalAcceptor"; + } + + static getSchema() { + return { + item: typeItemSingleton, + }; + } + + /** + * @param {object} param0 + * @param {BaseItem=} param0.item + * @param {number=} param0.rate + */ + constructor({ item = null, rate = null }) { + super(); + + // ths item to produce + /** @type {BaseItem | undefined} */ + this.item = item; + + this.clear(); + } + + clear() { + /** + * The last item we delivered + * @type {{ item: BaseItem; time: number; } | null} */ + this.lastDelivery = null; + + // The amount of items we delivered so far + this.currentDeliveredItems = 0; + + // Used for animations + this.displayPercentage = 0; + } + + /** + * Clears items but doesn't instantly reset the progress bar + */ + clearItems() { + this.lastDelivery = null; + this.currentDeliveredItems = 0; + } + + getRequiredSecondsPerItem() { + return ( + globalConfig.goalAcceptorsPerProducer / + (globalConfig.puzzleModeSpeed * globalConfig.beltSpeedItemsPerSecond) + ); + } + + /** + * Copy the current state to another component + * @param {GoalAcceptorComponent} otherComponent + */ + copyAdditionalStateTo(otherComponent) { + otherComponent.item = this.item; + } +} diff --git a/src/js/game/components/item_acceptor.js b/src/js/game/components/item_acceptor.js index d3df3763..e984fefb 100644 --- a/src/js/game/components/item_acceptor.js +++ b/src/js/game/components/item_acceptor.js @@ -1,136 +1,136 @@ -import { enumDirection, enumInvertedDirections, Vector } from "../../core/vector"; -import { types } from "../../savegame/serialization"; -import { BaseItem } from "../base_item"; -import { Component } from "../component"; - -/** - * @typedef {{ - * pos: Vector, - * direction: enumDirection, - * filter?: ItemType - * }} ItemAcceptorSlot */ - -/** - * Contains information about a slot plus its location - * @typedef {{ - * slot: ItemAcceptorSlot, - * index: number, - * }} ItemAcceptorLocatedSlot */ - -/** - * @typedef {{ - * pos: Vector, - * direction: enumDirection, - * filter?: ItemType - * }} ItemAcceptorSlotConfig */ - -export class ItemAcceptorComponent extends Component { - static getId() { - return "ItemAcceptor"; - } - - /** - * - * @param {object} param0 - * @param {Array} param0.slots The slots from which we accept items - */ - constructor({ slots = [] }) { - super(); - - this.setSlots(slots); - this.clear(); - } - - clear() { - /** - * Fixes belt animations - * @type {Array<{ - * item: BaseItem, - * slotIndex: number, - * animProgress: number, - * direction: enumDirection - * }>} - */ - this.itemConsumptionAnimations = []; - } - - /** - * - * @param {Array} slots - */ - setSlots(slots) { - /** @type {Array} */ - this.slots = []; - for (let i = 0; i < slots.length; ++i) { - const slot = slots[i]; - this.slots.push({ - pos: slot.pos, - direction: slot.direction, - - // Which type of item to accept (shape | color | all) @see ItemType - filter: slot.filter, - }); - } - } - - /** - * Returns if this acceptor can accept a new item at slot N - * - * NOTICE: The belt path ignores this for performance reasons and does his own check - * @param {number} slotIndex - * @param {BaseItem=} item - */ - canAcceptItem(slotIndex, item) { - const slot = this.slots[slotIndex]; - return !slot.filter || slot.filter === item.getItemType(); - } - - /** - * Called when an item has been accepted so that - * @param {number} slotIndex - * @param {enumDirection} direction - * @param {BaseItem} item - * @param {number} remainingProgress World space remaining progress, can be set to set the start position of the item - */ - onItemAccepted(slotIndex, direction, item, remainingProgress = 0.0) { - this.itemConsumptionAnimations.push({ - item, - slotIndex, - direction, - animProgress: Math.min(1, remainingProgress * 2), - }); - } - - /** - * Tries to find a slot which accepts the current item - * @param {Vector} targetLocalTile - * @param {enumDirection} fromLocalDirection - * @returns {ItemAcceptorLocatedSlot|null} - */ - findMatchingSlot(targetLocalTile, fromLocalDirection) { - // We need to invert our direction since the acceptor specifies *from* which direction - // it accepts items, but the ejector specifies *into* which direction it ejects items. - // E.g.: Ejector ejects into "right" direction but acceptor accepts from "left" direction. - const desiredDirection = enumInvertedDirections[fromLocalDirection]; - - // Go over all slots and try to find a target slot - for (let slotIndex = 0; slotIndex < this.slots.length; ++slotIndex) { - const slot = this.slots[slotIndex]; - - // Make sure the acceptor slot is on the right position - if (!slot.pos.equals(targetLocalTile)) { - continue; - } - - // Check if the acceptor slot accepts items from our direction - if (desiredDirection === slot.direction) { - return { - slot, - index: slotIndex, - }; - } - } - - return null; - } -} +import { enumDirection, enumInvertedDirections, Vector } from "../../core/vector"; +import { types } from "../../savegame/serialization"; +import { BaseItem } from "../base_item"; +import { Component } from "../component"; + +/** + * @typedef {{ + * pos: Vector, + * direction: enumDirection, + * filter?: ItemType + * }} ItemAcceptorSlot */ + +/** + * Contains information about a slot plus its location + * @typedef {{ + * slot: ItemAcceptorSlot, + * index: number, + * }} ItemAcceptorLocatedSlot */ + +/** + * @typedef {{ + * pos: Vector, + * direction: enumDirection, + * filter?: ItemType + * }} ItemAcceptorSlotConfig */ + +export class ItemAcceptorComponent extends Component { + static getId() { + return "ItemAcceptor"; + } + + /** + * + * @param {object} param0 + * @param {Array} param0.slots The slots from which we accept items + */ + constructor({ slots = [] }) { + super(); + + this.setSlots(slots); + this.clear(); + } + + clear() { + /** + * Fixes belt animations + * @type {Array<{ + * item: BaseItem, + * slotIndex: number, + * animProgress: number, + * direction: enumDirection + * }>} + */ + this.itemConsumptionAnimations = []; + } + + /** + * + * @param {Array} slots + */ + setSlots(slots) { + /** @type {Array} */ + this.slots = []; + for (let i = 0; i < slots.length; ++i) { + const slot = slots[i]; + this.slots.push({ + pos: slot.pos, + direction: slot.direction, + + // Which type of item to accept (shape | color | all) @see ItemType + filter: slot.filter, + }); + } + } + + /** + * Returns if this acceptor can accept a new item at slot N + * + * NOTICE: The belt path ignores this for performance reasons and does his own check + * @param {number} slotIndex + * @param {BaseItem=} item + */ + canAcceptItem(slotIndex, item) { + const slot = this.slots[slotIndex]; + return !slot.filter || slot.filter === item.getItemType(); + } + + /** + * Called when an item has been accepted so that + * @param {number} slotIndex + * @param {enumDirection} direction + * @param {BaseItem} item + * @param {number} remainingProgress World space remaining progress, can be set to set the start position of the item + */ + onItemAccepted(slotIndex, direction, item, remainingProgress = 0.0) { + this.itemConsumptionAnimations.push({ + item, + slotIndex, + direction, + animProgress: Math.min(1, remainingProgress * 2), + }); + } + + /** + * Tries to find a slot which accepts the current item + * @param {Vector} targetLocalTile + * @param {enumDirection} fromLocalDirection + * @returns {ItemAcceptorLocatedSlot|null} + */ + findMatchingSlot(targetLocalTile, fromLocalDirection) { + // We need to invert our direction since the acceptor specifies *from* which direction + // it accepts items, but the ejector specifies *into* which direction it ejects items. + // E.g.: Ejector ejects into "right" direction but acceptor accepts from "left" direction. + const desiredDirection = enumInvertedDirections[fromLocalDirection]; + + // Go over all slots and try to find a target slot + for (let slotIndex = 0; slotIndex < this.slots.length; ++slotIndex) { + const slot = this.slots[slotIndex]; + + // Make sure the acceptor slot is on the right position + if (!slot.pos.equals(targetLocalTile)) { + continue; + } + + // Check if the acceptor slot accepts items from our direction + if (desiredDirection === slot.direction) { + return { + slot, + index: slotIndex, + }; + } + } + + return null; + } +} diff --git a/src/js/game/components/item_ejector.js b/src/js/game/components/item_ejector.js index bfc54cd8..c8c4ec94 100644 --- a/src/js/game/components/item_ejector.js +++ b/src/js/game/components/item_ejector.js @@ -1,154 +1,154 @@ -import { enumDirection, enumDirectionToVector, Vector } from "../../core/vector"; -import { types } from "../../savegame/serialization"; -import { BaseItem } from "../base_item"; -import { BeltPath } from "../belt_path"; -import { Component } from "../component"; -import { Entity } from "../entity"; -import { typeItemSingleton } from "../item_resolver"; - -/** - * @typedef {{ - * pos: Vector, - * direction: enumDirection, - * item: BaseItem, - * lastItem: BaseItem, - * progress: number?, - * cachedDestSlot?: import("./item_acceptor").ItemAcceptorLocatedSlot, - * cachedBeltPath?: BeltPath, - * cachedTargetEntity?: Entity - * }} ItemEjectorSlot - */ - -export class ItemEjectorComponent extends Component { - static getId() { - return "ItemEjector"; - } - - static getSchema() { - // The cachedDestSlot, cachedTargetEntity fields are not serialized. - return { - slots: types.fixedSizeArray( - types.structured({ - item: types.nullable(typeItemSingleton), - progress: types.float, - }) - ), - }; - } - - /** - * - * @param {object} param0 - * @param {Array<{pos: Vector, direction: enumDirection }>=} param0.slots The slots to eject on - * @param {boolean=} param0.renderFloatingItems Whether to render items even if they are not connected - */ - constructor({ slots = [], renderFloatingItems = true }) { - super(); - - this.setSlots(slots); - this.renderFloatingItems = renderFloatingItems; - } - - clear() { - for (const slot of this.slots) { - slot.item = null; - slot.lastItem = null; - slot.progress = 0; - } - } - - /** - * @param {Array<{pos: Vector, direction: enumDirection }>} slots The slots to eject on - */ - setSlots(slots) { - /** @type {Array} */ - this.slots = []; - for (let i = 0; i < slots.length; ++i) { - const slot = slots[i]; - this.slots.push({ - pos: slot.pos, - direction: slot.direction, - item: null, - lastItem: null, - progress: 0, - cachedDestSlot: null, - cachedTargetEntity: null, - }); - } - } - - /** - * Returns where this slot ejects to - * @param {ItemEjectorSlot} slot - * @returns {Vector} - */ - getSlotTargetLocalTile(slot) { - const directionVector = enumDirectionToVector[slot.direction]; - return slot.pos.add(directionVector); - } - - /** - * Returns whether any slot ejects to the given local tile - * @param {Vector} tile - */ - anySlotEjectsToLocalTile(tile) { - for (let i = 0; i < this.slots.length; ++i) { - if (this.getSlotTargetLocalTile(this.slots[i]).equals(tile)) { - return true; - } - } - return false; - } - - /** - * Returns if we can eject on a given slot - * @param {number} slotIndex - * @returns {boolean} - */ - canEjectOnSlot(slotIndex) { - assert(slotIndex >= 0 && slotIndex < this.slots.length, "Invalid ejector slot: " + slotIndex); - return !this.slots[slotIndex].item; - } - - /** - * Returns the first free slot on this ejector or null if there is none - * @returns {number?} - */ - getFirstFreeSlot() { - for (let i = 0; i < this.slots.length; ++i) { - if (this.canEjectOnSlot(i)) { - return i; - } - } - return null; - } - - /** - * Tries to eject a given item - * @param {number} slotIndex - * @param {BaseItem} item - * @returns {boolean} - */ - tryEject(slotIndex, item) { - if (!this.canEjectOnSlot(slotIndex)) { - return false; - } - this.slots[slotIndex].item = item; - this.slots[slotIndex].lastItem = item; - this.slots[slotIndex].progress = 0; - return true; - } - - /** - * Clears the given slot and returns the item it had - * @param {number} slotIndex - * @returns {BaseItem|null} - */ - takeSlotItem(slotIndex) { - const slot = this.slots[slotIndex]; - const item = slot.item; - slot.item = null; - slot.progress = 0.0; - return item; - } -} +import { enumDirection, enumDirectionToVector, Vector } from "../../core/vector"; +import { types } from "../../savegame/serialization"; +import { BaseItem } from "../base_item"; +import { BeltPath } from "../belt_path"; +import { Component } from "../component"; +import { Entity } from "../entity"; +import { typeItemSingleton } from "../item_resolver"; + +/** + * @typedef {{ + * pos: Vector, + * direction: enumDirection, + * item: BaseItem, + * lastItem: BaseItem, + * progress: number?, + * cachedDestSlot?: import("./item_acceptor").ItemAcceptorLocatedSlot, + * cachedBeltPath?: BeltPath, + * cachedTargetEntity?: Entity + * }} ItemEjectorSlot + */ + +export class ItemEjectorComponent extends Component { + static getId() { + return "ItemEjector"; + } + + static getSchema() { + // The cachedDestSlot, cachedTargetEntity fields are not serialized. + return { + slots: types.fixedSizeArray( + types.structured({ + item: types.nullable(typeItemSingleton), + progress: types.float, + }) + ), + }; + } + + /** + * + * @param {object} param0 + * @param {Array<{pos: Vector, direction: enumDirection }>=} param0.slots The slots to eject on + * @param {boolean=} param0.renderFloatingItems Whether to render items even if they are not connected + */ + constructor({ slots = [], renderFloatingItems = true }) { + super(); + + this.setSlots(slots); + this.renderFloatingItems = renderFloatingItems; + } + + clear() { + for (const slot of this.slots) { + slot.item = null; + slot.lastItem = null; + slot.progress = 0; + } + } + + /** + * @param {Array<{pos: Vector, direction: enumDirection }>} slots The slots to eject on + */ + setSlots(slots) { + /** @type {Array} */ + this.slots = []; + for (let i = 0; i < slots.length; ++i) { + const slot = slots[i]; + this.slots.push({ + pos: slot.pos, + direction: slot.direction, + item: null, + lastItem: null, + progress: 0, + cachedDestSlot: null, + cachedTargetEntity: null, + }); + } + } + + /** + * Returns where this slot ejects to + * @param {ItemEjectorSlot} slot + * @returns {Vector} + */ + getSlotTargetLocalTile(slot) { + const directionVector = enumDirectionToVector[slot.direction]; + return slot.pos.add(directionVector); + } + + /** + * Returns whether any slot ejects to the given local tile + * @param {Vector} tile + */ + anySlotEjectsToLocalTile(tile) { + for (let i = 0; i < this.slots.length; ++i) { + if (this.getSlotTargetLocalTile(this.slots[i]).equals(tile)) { + return true; + } + } + return false; + } + + /** + * Returns if we can eject on a given slot + * @param {number} slotIndex + * @returns {boolean} + */ + canEjectOnSlot(slotIndex) { + assert(slotIndex >= 0 && slotIndex < this.slots.length, "Invalid ejector slot: " + slotIndex); + return !this.slots[slotIndex].item; + } + + /** + * Returns the first free slot on this ejector or null if there is none + * @returns {number?} + */ + getFirstFreeSlot() { + for (let i = 0; i < this.slots.length; ++i) { + if (this.canEjectOnSlot(i)) { + return i; + } + } + return null; + } + + /** + * Tries to eject a given item + * @param {number} slotIndex + * @param {BaseItem} item + * @returns {boolean} + */ + tryEject(slotIndex, item) { + if (!this.canEjectOnSlot(slotIndex)) { + return false; + } + this.slots[slotIndex].item = item; + this.slots[slotIndex].lastItem = item; + this.slots[slotIndex].progress = 0; + return true; + } + + /** + * Clears the given slot and returns the item it had + * @param {number} slotIndex + * @returns {BaseItem|null} + */ + takeSlotItem(slotIndex) { + const slot = this.slots[slotIndex]; + const item = slot.item; + slot.item = null; + slot.progress = 0.0; + return item; + } +} diff --git a/src/js/game/components/item_processor.js b/src/js/game/components/item_processor.js index be7d1ce4..e512d666 100644 --- a/src/js/game/components/item_processor.js +++ b/src/js/game/components/item_processor.js @@ -7,9 +7,9 @@ export const enumItemProcessorTypes = { balancer: "balancer", cutter: "cutter", cutterQuad: "cutterQuad", - rotater: "rotater", - rotaterCCW: "rotaterCCW", - rotater180: "rotater180", + rotator: "rotator", + rotatorCCW: "rotatorCCW", + rotator180: "rotator180", stacker: "stacker", trash: "trash", mixer: "mixer", diff --git a/src/js/game/components/item_producer.js b/src/js/game/components/item_producer.js index ef3571e2..3c1ccf0c 100644 --- a/src/js/game/components/item_producer.js +++ b/src/js/game/components/item_producer.js @@ -1,7 +1,7 @@ -import { Component } from "../component"; - -export class ItemProducerComponent extends Component { - static getId() { - return "ItemProducer"; - } -} +import { Component } from "../component"; + +export class ItemProducerComponent extends Component { + static getId() { + return "ItemProducer"; + } +} diff --git a/src/js/game/components/lever.js b/src/js/game/components/lever.js index 106cbbdd..cdef5a58 100644 --- a/src/js/game/components/lever.js +++ b/src/js/game/components/lever.js @@ -1,31 +1,31 @@ -import { Component } from "../component"; -import { types } from "../../savegame/serialization"; - -export class LeverComponent extends Component { - static getId() { - return "Lever"; - } - - static getSchema() { - return { - toggled: types.bool, - }; - } - - /** - * Copy the current state to another component - * @param {LeverComponent} otherComponent - */ - copyAdditionalStateTo(otherComponent) { - otherComponent.toggled = this.toggled; - } - - /** - * @param {object} param0 - * @param {boolean=} param0.toggled - */ - constructor({ toggled = false }) { - super(); - this.toggled = toggled; - } -} +import { Component } from "../component"; +import { types } from "../../savegame/serialization"; + +export class LeverComponent extends Component { + static getId() { + return "Lever"; + } + + static getSchema() { + return { + toggled: types.bool, + }; + } + + /** + * Copy the current state to another component + * @param {LeverComponent} otherComponent + */ + copyAdditionalStateTo(otherComponent) { + otherComponent.toggled = this.toggled; + } + + /** + * @param {object} param0 + * @param {boolean=} param0.toggled + */ + constructor({ toggled = false }) { + super(); + this.toggled = toggled; + } +} diff --git a/src/js/game/components/logic_gate.js b/src/js/game/components/logic_gate.js index 62cd3365..83611706 100644 --- a/src/js/game/components/logic_gate.js +++ b/src/js/game/components/logic_gate.js @@ -1,34 +1,34 @@ -import { Component } from "../component"; - -/** @enum {string} */ -export const enumLogicGateType = { - and: "and", - not: "not", - xor: "xor", - or: "or", - transistor: "transistor", - - analyzer: "analyzer", - rotater: "rotater", - unstacker: "unstacker", - cutter: "cutter", - compare: "compare", - stacker: "stacker", - painter: "painter", -}; - -export class LogicGateComponent extends Component { - static getId() { - return "LogicGate"; - } - - /** - * - * @param {object} param0 - * @param {enumLogicGateType=} param0.type - */ - constructor({ type = enumLogicGateType.and }) { - super(); - this.type = type; - } -} +import { Component } from "../component"; + +/** @enum {string} */ +export const enumLogicGateType = { + and: "and", + not: "not", + xor: "xor", + or: "or", + transistor: "transistor", + + analyzer: "analyzer", + rotator: "rotator", + unstacker: "unstacker", + cutter: "cutter", + compare: "compare", + stacker: "stacker", + painter: "painter", +}; + +export class LogicGateComponent extends Component { + static getId() { + return "LogicGate"; + } + + /** + * + * @param {object} param0 + * @param {enumLogicGateType=} param0.type + */ + constructor({ type = enumLogicGateType.and }) { + super(); + this.type = type; + } +} diff --git a/src/js/game/components/miner.js b/src/js/game/components/miner.js index 5321ae11..cc2edb2c 100644 --- a/src/js/game/components/miner.js +++ b/src/js/game/components/miner.js @@ -1,64 +1,64 @@ -import { types } from "../../savegame/serialization"; -import { BaseItem } from "../base_item"; -import { Component } from "../component"; -import { Entity } from "../entity"; -import { typeItemSingleton } from "../item_resolver"; - -const chainBufferSize = 6; - -export class MinerComponent extends Component { - static getId() { - return "Miner"; - } - - static getSchema() { - // cachedMinedItem is not serialized. - return { - lastMiningTime: types.ufloat, - itemChainBuffer: types.array(typeItemSingleton), - }; - } - - constructor({ chainable = false }) { - super(); - this.lastMiningTime = 0; - this.chainable = chainable; - - /** - * @type {BaseItem} - */ - this.cachedMinedItem = null; - - /** - * Which miner this miner ejects to, in case its a chainable one. - * If the value is false, it means there is no entity, and we don't have to re-check - * @type {Entity|null|false} - */ - this.cachedChainedMiner = null; - - this.clear(); - } - - clear() { - /** - * Stores items from other miners which were chained to this - * miner. - * @type {Array} - */ - this.itemChainBuffer = []; - } - - /** - * - * @param {BaseItem} item - */ - tryAcceptChainedItem(item) { - if (this.itemChainBuffer.length > chainBufferSize) { - // Well, this one is full - return false; - } - - this.itemChainBuffer.push(item); - return true; - } -} +import { types } from "../../savegame/serialization"; +import { BaseItem } from "../base_item"; +import { Component } from "../component"; +import { Entity } from "../entity"; +import { typeItemSingleton } from "../item_resolver"; + +const chainBufferSize = 6; + +export class MinerComponent extends Component { + static getId() { + return "Miner"; + } + + static getSchema() { + // cachedMinedItem is not serialized. + return { + lastMiningTime: types.ufloat, + itemChainBuffer: types.array(typeItemSingleton), + }; + } + + constructor({ chainable = false }) { + super(); + this.lastMiningTime = 0; + this.chainable = chainable; + + /** + * @type {BaseItem} + */ + this.cachedMinedItem = null; + + /** + * Which miner this miner ejects to, in case its a chainable one. + * If the value is false, it means there is no entity, and we don't have to re-check + * @type {Entity|null|false} + */ + this.cachedChainedMiner = null; + + this.clear(); + } + + clear() { + /** + * Stores items from other miners which were chained to this + * miner. + * @type {Array} + */ + this.itemChainBuffer = []; + } + + /** + * + * @param {BaseItem} item + */ + tryAcceptChainedItem(item) { + if (this.itemChainBuffer.length > chainBufferSize) { + // Well, this one is full + return false; + } + + this.itemChainBuffer.push(item); + return true; + } +} diff --git a/src/js/game/components/static_map_entity.js b/src/js/game/components/static_map_entity.js index a3d6a8ca..f92fcc48 100644 --- a/src/js/game/components/static_map_entity.js +++ b/src/js/game/components/static_map_entity.js @@ -255,8 +255,16 @@ export class StaticMapEntityComponent extends Component { * @param {AtlasSprite} sprite * @param {number=} extrudePixels How many pixels to extrude the sprite * @param {Vector=} overridePosition Whether to drwa the entity at a different location + * @param {boolean=} pixelAligned + * Whether to round the canvas coordinates, to avoid issues with transparency between tiling images */ - drawSpriteOnBoundsClipped(parameters, sprite, extrudePixels = 0, overridePosition = null) { + drawSpriteOnBoundsClipped( + parameters, + sprite, + extrudePixels = 0, + overridePosition = null, + pixelAligned = false + ) { if (!this.shouldBeDrawn(parameters) && !overridePosition) { return; } @@ -269,31 +277,74 @@ export class StaticMapEntityComponent extends Component { worldY = overridePosition.y * globalConfig.tileSize; } - if (this.rotation === 0) { - // Early out, is faster - sprite.drawCached( - parameters, - worldX - extrudePixels * size.x, - worldY - extrudePixels * size.y, - globalConfig.tileSize * size.x + 2 * extrudePixels * size.x, - globalConfig.tileSize * size.y + 2 * extrudePixels * size.y - ); - } else { - const rotationCenterX = worldX + globalConfig.halfTileSize; - const rotationCenterY = worldY + globalConfig.halfTileSize; + if (!pixelAligned) { + if (this.rotation === 0) { + // Early out, is faster + sprite.drawCached( + parameters, + worldX - extrudePixels * size.x, + worldY - extrudePixels * size.y, + globalConfig.tileSize * size.x + 2 * extrudePixels * size.x, + globalConfig.tileSize * size.y + 2 * extrudePixels * size.y + ); + } else { + const rotationCenterX = worldX + globalConfig.halfTileSize; + const rotationCenterY = worldY + globalConfig.halfTileSize; - parameters.context.translate(rotationCenterX, rotationCenterY); - parameters.context.rotate(Math.radians(this.rotation)); - sprite.drawCached( - parameters, - -globalConfig.halfTileSize - extrudePixels * size.x, - -globalConfig.halfTileSize - extrudePixels * size.y, - globalConfig.tileSize * size.x + 2 * extrudePixels * size.x, - globalConfig.tileSize * size.y + 2 * extrudePixels * size.y, - false // no clipping possible here - ); - parameters.context.rotate(-Math.radians(this.rotation)); - parameters.context.translate(-rotationCenterX, -rotationCenterY); + parameters.context.translate(rotationCenterX, rotationCenterY); + parameters.context.rotate(Math.radians(this.rotation)); + sprite.drawCached( + parameters, + -globalConfig.halfTileSize - extrudePixels * size.x, + -globalConfig.halfTileSize - extrudePixels * size.y, + globalConfig.tileSize * size.x + 2 * extrudePixels * size.x, + globalConfig.tileSize * size.y + 2 * extrudePixels * size.y, + false // no clipping possible here + ); + parameters.context.rotate(-Math.radians(this.rotation)); + parameters.context.translate(-rotationCenterX, -rotationCenterY); + } + return; } + + const tileCenter = new Vector(globalConfig.halfTileSize, globalConfig.halfTileSize); + const rotatedTileCenter = tileCenter.rotateFastMultipleOf90(this.rotation); + const cornerX = worldX + tileCenter.x - rotatedTileCenter.x; + const cornerY = worldY + tileCenter.y - rotatedTileCenter.y; + const offset = size.rotateFastMultipleOf90(this.rotation); + + const transform = parameters.context.getTransform(); + const matrix = new DOMMatrix().rotate(0, 0, -this.rotation).multiplySelf(transform); + let { x: x1, y: y1 } = matrix.transformPoint( + new DOMPoint(cornerX - extrudePixels * offset.x, cornerY - extrudePixels * offset.y) + ); + let { x: x2, y: y2 } = matrix.transformPoint( + new DOMPoint( + cornerX + globalConfig.tileSize * offset.x + extrudePixels * offset.x, + cornerY + globalConfig.tileSize * offset.y + extrudePixels * offset.y + ) + ); + if (x1 > x2) { + [x1, x2] = [x2, x1]; + } + if (y1 > y2) { + [y1, y2] = [y2, y1]; + } + // Even though drawCached may scale the coordinates, + // that scaling is for sprites that don't take up their full tile space, + // so they should be interpolated exactly between the rounded tile coordinates. + // E.g. rounding in drawCached causes curved belts to look misaligned. + x1 = Math.round(x1); + y1 = Math.round(y1); + x2 = Math.round(x2); + y2 = Math.round(y2); + if (x2 - x1 == 0 || y2 - y1 == 0) { + return; + } + + parameters.context.resetTransform(); + parameters.context.rotate(Math.radians(this.rotation)); + sprite.drawCached(parameters, x1, y1, x2 - x1, y2 - y1, false); + parameters.context.setTransform(transform); } } diff --git a/src/js/game/components/underground_belt.js b/src/js/game/components/underground_belt.js index 2b744edd..fe465350 100644 --- a/src/js/game/components/underground_belt.js +++ b/src/js/game/components/underground_belt.js @@ -57,7 +57,7 @@ export class UndergroundBeltComponent extends Component { /** * Used on both receiver and sender. - * Reciever: Used to store the next item to transfer, and to block input while doing this + * Receiver: Used to store the next item to transfer, and to block input while doing this * Sender: Used to store which items are currently "travelling" * @type {Array<[BaseItem, number]>} Format is [Item, ingame time to eject the item] */ diff --git a/src/js/game/core.js b/src/js/game/core.js index 7283710e..e349f5b2 100644 --- a/src/js/game/core.js +++ b/src/js/game/core.js @@ -1,576 +1,523 @@ -/* typehints:start */ -import { Application } from "../application"; -/* typehints:end */ -import { BufferMaintainer } from "../core/buffer_maintainer"; -import { - disableImageSmoothing, - enableImageSmoothing, - getBufferStats, - registerCanvas, -} from "../core/buffer_utils"; -import { globalConfig } from "../core/config"; -import { getDeviceDPI, resizeHighDPICanvas } from "../core/dpi_manager"; -import { DrawParameters } from "../core/draw_parameters"; -import { gMetaBuildingRegistry } from "../core/global_registries"; -import { createLogger } from "../core/logging"; -import { Rectangle } from "../core/rectangle"; -import { ORIGINAL_SPRITE_SCALE } from "../core/sprites"; -import { lerp, randomInt, round2Digits } from "../core/utils"; -import { Vector } from "../core/vector"; -import { Savegame } from "../savegame/savegame"; -import { SavegameSerializer } from "../savegame/savegame_serializer"; -import { AutomaticSave } from "./automatic_save"; -import { MetaHubBuilding } from "./buildings/hub"; -import { Camera } from "./camera"; -import { DynamicTickrate } from "./dynamic_tickrate"; -import { EntityManager } from "./entity_manager"; -import { GameSystemManager } from "./game_system_manager"; -import { HubGoals } from "./hub_goals"; -import { GameHUD } from "./hud/hud"; -import { KeyActionMapper } from "./key_action_mapper"; -import { GameLogic } from "./logic"; -import { MapView } from "./map_view"; -import { defaultBuildingVariant } from "./meta_building"; -import { GameMode } from "./game_mode"; -import { ProductionAnalytics } from "./production_analytics"; -import { GameRoot } from "./root"; -import { ShapeDefinitionManager } from "./shape_definition_manager"; -import { AchievementProxy } from "./achievement_proxy"; -import { SoundProxy } from "./sound_proxy"; -import { GameTime } from "./time/game_time"; -import { MOD_SIGNALS } from "../mods/mod_signals"; - -const logger = createLogger("ingame/core"); - -// Store the canvas so we can reuse it later -/** @type {HTMLCanvasElement} */ -let lastCanvas = null; -/** @type {CanvasRenderingContext2D} */ -let lastContext = null; - -/** - * The core manages the root and represents the whole game. It wraps the root, since - * the root class is just a data holder. - */ -export class GameCore { - /** @param {Application} app */ - constructor(app) { - this.app = app; - - /** @type {GameRoot} */ - this.root = null; - - /** - * Set to true at the beginning of a logic update and cleared when its finished. - * This is to prevent doing a recursive logic update which can lead to unexpected - * behaviour. - */ - this.duringLogicUpdate = false; - - // Cached - this.boundInternalTick = this.updateLogic.bind(this); - - /** - * Opacity of the overview alpha - * @TODO Doesn't belong here - */ - this.overlayAlpha = 0; - } - - /** - * Initializes the root object which stores all game related data. The state - * is required as a back reference (used sometimes) - * @param {import("../states/ingame").InGameState} parentState - * @param {Savegame} savegame - */ - initializeRoot(parentState, savegame, gameModeId) { - logger.log("initializing root"); - - // Construct the root element, this is the data representation of the game - this.root = new GameRoot(this.app); - this.root.gameState = parentState; - this.root.keyMapper = parentState.keyActionMapper; - this.root.savegame = savegame; - this.root.gameWidth = this.app.screenWidth; - this.root.gameHeight = this.app.screenHeight; - - // Initialize canvas element & context - this.internalInitCanvas(); - - // Members - const root = this.root; - - // This isn't nice, but we need it right here - root.keyMapper = new KeyActionMapper(root, this.root.gameState.inputReciever); - - // Init game mode - root.gameMode = GameMode.create(root, gameModeId, parentState.creationPayload.gameModeParameters); - - // Needs to come first - root.dynamicTickrate = new DynamicTickrate(root); - - // Init classes - root.camera = new Camera(root); - root.map = new MapView(root); - root.logic = new GameLogic(root); - root.hud = new GameHUD(root); - root.time = new GameTime(root); - root.achievementProxy = new AchievementProxy(root); - root.automaticSave = new AutomaticSave(root); - root.soundProxy = new SoundProxy(root); - - // Init managers - root.entityMgr = new EntityManager(root); - root.systemMgr = new GameSystemManager(root); - root.shapeDefinitionMgr = new ShapeDefinitionManager(root); - root.hubGoals = new HubGoals(root); - root.productionAnalytics = new ProductionAnalytics(root); - root.buffers = new BufferMaintainer(root); - - // Initialize the hud once everything is loaded - this.root.hud.initialize(); - - // Initial resize event, it might be possible that the screen - // resized later during init tho, which is why will emit it later - // again anyways - this.resize(this.app.screenWidth, this.app.screenHeight); - - if (G_IS_DEV) { - // @ts-ignore - window.globalRoot = root; - } - - // @todo Find better place - if (G_IS_DEV && globalConfig.debug.manualTickOnly) { - this.root.gameState.inputReciever.keydown.add(key => { - if (key.keyCode === 84) { - // 'T' - - // Extract current real time - this.root.time.updateRealtimeNow(); - - // Perform logic ticks - this.root.time.performTicks(this.root.dynamicTickrate.deltaMs, this.boundInternalTick); - - // Update analytics - root.productionAnalytics.update(); - - // Check achievements - root.achievementProxy.update(); - } - }); - } - - logger.log("root initialized"); - MOD_SIGNALS.gameInitialized.dispatch(root); - } - - /** - * Initializes a new game, this means creating a new map and centering on the - * playerbase - * */ - initNewGame() { - logger.log("Initializing new game"); - this.root.gameIsFresh = true; - this.root.map.seed = randomInt(0, 100000); - - if (!this.root.gameMode.hasHub()) { - return; - } - - // Place the hub - const hub = gMetaBuildingRegistry.findByClass(MetaHubBuilding).createEntity({ - root: this.root, - origin: new Vector(-2, -2), - rotation: 0, - originalRotation: 0, - rotationVariant: 0, - variant: defaultBuildingVariant, - }); - this.root.map.placeStaticEntity(hub); - this.root.entityMgr.registerEntity(hub); - this.root.camera.center = new Vector(-5, 2).multiplyScalar(globalConfig.tileSize); - } - - /** - * Inits an existing game by loading the raw savegame data and deserializing it. - * Also runs basic validity checks. - */ - initExistingGame() { - logger.log("Initializing existing game"); - const serializer = new SavegameSerializer(); - - try { - const status = serializer.deserialize(this.root.savegame.getCurrentDump(), this.root); - if (!status.isGood()) { - logger.error("savegame-deserialize-failed:" + status.reason); - return false; - } - } catch (ex) { - logger.error("Exception during deserialization:", ex); - return false; - } - this.root.gameIsFresh = false; - return true; - } - - /** - * Initializes the render canvas - */ - internalInitCanvas() { - let canvas, context; - if (!lastCanvas) { - logger.log("Creating new canvas"); - canvas = document.createElement("canvas"); - canvas.id = "ingame_Canvas"; - canvas.setAttribute("opaque", "true"); - canvas.setAttribute("webkitOpaque", "true"); - canvas.setAttribute("mozOpaque", "true"); - this.root.gameState.getDivElement().appendChild(canvas); - context = canvas.getContext("2d", { alpha: false }); - - lastCanvas = canvas; - lastContext = context; - } else { - logger.log("Reusing canvas"); - if (lastCanvas.parentElement) { - lastCanvas.parentElement.removeChild(lastCanvas); - } - this.root.gameState.getDivElement().appendChild(lastCanvas); - - canvas = lastCanvas; - context = lastContext; - - lastContext.clearRect(0, 0, lastCanvas.width, lastCanvas.height); - } - - canvas.classList.toggle("smoothed", globalConfig.smoothing.smoothMainCanvas); - - // Oof, use :not() instead - canvas.classList.toggle("unsmoothed", !globalConfig.smoothing.smoothMainCanvas); - - if (globalConfig.smoothing.smoothMainCanvas) { - enableImageSmoothing(context); - } else { - disableImageSmoothing(context); - } - - this.root.canvas = canvas; - this.root.context = context; - - registerCanvas(canvas, context); - } - - /** - * Destructs the root, freeing all resources - */ - destruct() { - if (lastCanvas && lastCanvas.parentElement) { - lastCanvas.parentElement.removeChild(lastCanvas); - } - - this.root.destruct(); - delete this.root; - this.root = null; - this.app = null; - } - - tick(deltaMs) { - const root = this.root; - - // Extract current real time - root.time.updateRealtimeNow(); - - // Camera is always updated, no matter what - root.camera.update(deltaMs); - - if (!(G_IS_DEV && globalConfig.debug.manualTickOnly)) { - // Perform logic ticks - this.root.time.performTicks(deltaMs, this.boundInternalTick); - - // Update analytics - root.productionAnalytics.update(); - - // Check achievements - root.achievementProxy.update(); - } - - // Update automatic save after everything finished - root.automaticSave.update(); - - return true; - } - - shouldRender() { - if (this.root.queue.requireRedraw) { - return true; - } - if (this.root.hud.shouldPauseRendering()) { - return false; - } - - // Do not render - if (!this.app.isRenderable()) { - return false; - } - - return true; - } - - updateLogic() { - const root = this.root; - - root.dynamicTickrate.beginTick(); - - if (G_IS_DEV && globalConfig.debug.disableLogicTicks) { - root.dynamicTickrate.endTick(); - return true; - } - - this.duringLogicUpdate = true; - - // Update entities, this removes destroyed entities - root.entityMgr.update(); - - // IMPORTANT: At this point, the game might be game over. Stop if this is the case - if (!this.root) { - logger.log("Root destructed, returning false"); - root.dynamicTickrate.endTick(); - - return false; - } - - root.systemMgr.update(); - // root.particleMgr.update(); - - this.duringLogicUpdate = false; - root.dynamicTickrate.endTick(); - return true; - } - - resize(w, h) { - this.root.gameWidth = w; - this.root.gameHeight = h; - resizeHighDPICanvas(this.root.canvas, w, h, globalConfig.smoothing.smoothMainCanvas); - this.root.signals.resized.dispatch(w, h); - this.root.queue.requireRedraw = true; - } - - postLoadHook() { - logger.log("Dispatching post load hook"); - this.root.signals.postLoadHook.dispatch(); - - if (!this.root.gameIsFresh) { - // Also dispatch game restored hook on restored savegames - this.root.signals.gameRestored.dispatch(); - } - - this.root.gameInitialized = true; - } - - draw() { - const root = this.root; - const systems = root.systemMgr.systems; - - this.root.dynamicTickrate.onFrameRendered(); - - if (!this.shouldRender()) { - // Always update hud tho - root.hud.update(); - return; - } - - this.root.signals.gameFrameStarted.dispatch(); - - root.queue.requireRedraw = false; - - // Gather context and save all state - const context = root.context; - context.save(); - if (G_IS_DEV) { - context.fillStyle = "#a10000"; - context.fillRect(0, 0, window.innerWidth * 3, window.innerHeight * 3); - } - - // Compute optimal zoom level and atlas scale - const zoomLevel = root.camera.zoomLevel; - const lowQuality = root.app.settings.getAllSettings().lowQualityTextures; - const effectiveZoomLevel = - (zoomLevel / globalConfig.assetsDpi) * getDeviceDPI() * globalConfig.assetsSharpness; - - let desiredAtlasScale = "0.25"; - if (effectiveZoomLevel > 0.5 && !lowQuality) { - desiredAtlasScale = ORIGINAL_SPRITE_SCALE; - } else if (effectiveZoomLevel > 0.35 && !lowQuality) { - desiredAtlasScale = "0.5"; - } - - // Construct parameters required for drawing - const params = new DrawParameters({ - context: context, - visibleRect: root.camera.getVisibleRect(), - desiredAtlasScale, - zoomLevel, - root: root, - }); - - if (G_IS_DEV && globalConfig.debug.testCulling) { - context.clearRect(0, 0, root.gameWidth, root.gameHeight); - } - - // Transform to world space - - if (G_IS_DEV && globalConfig.debug.testClipping) { - params.visibleRect = params.visibleRect.expandedInAllDirections( - -200 / this.root.camera.zoomLevel - ); - } - - root.camera.transform(context); - - assert(context.globalAlpha === 1.0, "Global alpha not 1 on frame start"); - - // Update hud - root.hud.update(); - - // Main rendering order - // ----- - - const desiredOverlayAlpha = this.root.camera.getIsMapOverlayActive() ? 1 : 0; - this.overlayAlpha = lerp(this.overlayAlpha, desiredOverlayAlpha, 0.25); - - // On low performance, skip the fade - if (this.root.entityMgr.entities.length > 5000 || this.root.dynamicTickrate.averageFps < 50) { - this.overlayAlpha = desiredOverlayAlpha; - } - - if (this.overlayAlpha < 0.99) { - // Background (grid, resources, etc) - root.map.drawBackground(params); - - // Belt items - systems.belt.drawBeltItems(params); - - // Miner & Static map entities etc. - root.map.drawForeground(params); - - // HUB Overlay - systems.hub.draw(params); - - // Green wires overlay - if (root.hud.parts.wiresOverlay) { - root.hud.parts.wiresOverlay.draw(params); - } - - if (this.root.currentLayer === "wires") { - // Static map entities - root.map.drawWiresForegroundLayer(params); - } - } - - if (this.overlayAlpha > 0.01) { - // Map overview - context.globalAlpha = this.overlayAlpha; - root.map.drawOverlay(params); - context.globalAlpha = 1; - } - - if (G_IS_DEV) { - root.map.drawStaticEntityDebugOverlays(params); - } - - if (G_IS_DEV && globalConfig.debug.renderBeltPaths) { - systems.belt.drawBeltPathDebug(params); - } - - // END OF GAME CONTENT - // ----- - - // Finally, draw the hud. Nothing should come after that - root.hud.draw(params); - - assert(context.globalAlpha === 1.0, "Global alpha not 1 on frame end before restore"); - - // Restore to screen space - context.restore(); - - // Restore parameters - params.zoomLevel = 1; - params.desiredAtlasScale = ORIGINAL_SPRITE_SCALE; - params.visibleRect = new Rectangle(0, 0, this.root.gameWidth, this.root.gameHeight); - if (G_IS_DEV && globalConfig.debug.testClipping) { - params.visibleRect = params.visibleRect.expandedInAllDirections(-200); - } - - // Draw overlays, those are screen space - root.hud.drawOverlays(params); - - assert(context.globalAlpha === 1.0, "context.globalAlpha not 1 on frame end"); - - if (G_IS_DEV && globalConfig.debug.simulateSlowRendering) { - let sum = 0; - for (let i = 0; i < 1e8; ++i) { - sum += i; - } - if (Math.random() > 0.95) { - console.log(sum); - } - } - - if (G_IS_DEV && globalConfig.debug.showAtlasInfo) { - context.font = "13px GameFont"; - context.fillStyle = "blue"; - context.fillText( - "Atlas: " + - desiredAtlasScale + - " / Zoom: " + - round2Digits(zoomLevel) + - " / Effective Zoom: " + - round2Digits(effectiveZoomLevel), - 20, - 600 - ); - - const stats = this.root.buffers.getStats(); - - context.fillText( - "Maintained Buffers: " + - stats.rootKeys + - " root keys / " + - stats.subKeys + - " buffers / VRAM: " + - round2Digits(stats.vramBytes / (1024 * 1024)) + - " MB", - 20, - 620 - ); - const internalStats = getBufferStats(); - context.fillText( - "Total Buffers: " + - internalStats.bufferCount + - " buffers / " + - internalStats.backlogSize + - " backlog / " + - internalStats.backlogKeys + - " keys in backlog / VRAM " + - round2Digits(internalStats.vramUsage / (1024 * 1024)) + - " MB / Backlog " + - round2Digits(internalStats.backlogVramUsage / (1024 * 1024)) + - " MB / Created " + - internalStats.numCreated + - " / Reused " + - internalStats.numReused, - 20, - 640 - ); - } - - if (G_IS_DEV && globalConfig.debug.testClipping) { - context.strokeStyle = "red"; - context.lineWidth = 1; - context.beginPath(); - context.rect(200, 200, this.root.gameWidth - 400, this.root.gameHeight - 400); - context.stroke(); - } - } -} +/* typehints:start */ +import { Application } from "../application"; +/* typehints:end */ +import { BufferMaintainer } from "../core/buffer_maintainer"; +import { getBufferStats } from "../core/buffer_utils"; +import { globalConfig } from "../core/config"; +import { getDeviceDPI, resizeHighDPICanvas } from "../core/dpi_manager"; +import { DrawParameters } from "../core/draw_parameters"; +import { gMetaBuildingRegistry } from "../core/global_registries"; +import { createLogger } from "../core/logging"; +import { Rectangle } from "../core/rectangle"; +import { ORIGINAL_SPRITE_SCALE } from "../core/sprites"; +import { lerp, randomInt, round2Digits } from "../core/utils"; +import { Vector } from "../core/vector"; +import { MOD_SIGNALS } from "../mods/mod_signals"; +import { Savegame } from "../savegame/savegame"; +import { SavegameSerializer } from "../savegame/savegame_serializer"; +import { AutomaticSave } from "./automatic_save"; +import { MetaHubBuilding } from "./buildings/hub"; +import { Camera } from "./camera"; +import { DynamicTickrate } from "./dynamic_tickrate"; +import { EntityManager } from "./entity_manager"; +import { GameMode } from "./game_mode"; +import { GameSystemManager } from "./game_system_manager"; +import { HubGoals } from "./hub_goals"; +import { GameHUD } from "./hud/hud"; +import { KeyActionMapper } from "./key_action_mapper"; +import { GameLogic } from "./logic"; +import { MapView } from "./map_view"; +import { defaultBuildingVariant } from "./meta_building"; +import { ProductionAnalytics } from "./production_analytics"; +import { GameRoot } from "./root"; +import { ShapeDefinitionManager } from "./shape_definition_manager"; +import { SoundProxy } from "./sound_proxy"; +import { GameTime } from "./time/game_time"; + +const logger = createLogger("ingame/core"); + +/** + * The core manages the root and represents the whole game. It wraps the root, since + * the root class is just a data holder. + */ +export class GameCore { + /** @param {Application} app */ + constructor(app) { + this.app = app; + + /** @type {GameRoot} */ + this.root = null; + + /** + * Set to true at the beginning of a logic update and cleared when its finished. + * This is to prevent doing a recursive logic update which can lead to unexpected + * behaviour. + */ + this.duringLogicUpdate = false; + + // Cached + this.boundInternalTick = this.updateLogic.bind(this); + + /** + * Opacity of the overview alpha + * @TODO Doesn't belong here + */ + this.overlayAlpha = 0; + } + + /** + * Initializes the root object which stores all game related data. The state + * is required as a back reference (used sometimes) + * @param {import("../states/ingame").InGameState} parentState + * @param {Savegame} savegame + */ + initializeRoot(parentState, savegame, gameModeId) { + logger.log("initializing root"); + + // Construct the root element, this is the data representation of the game + this.root = new GameRoot(this.app); + this.root.gameState = parentState; + this.root.keyMapper = parentState.keyActionMapper; + this.root.savegame = savegame; + this.root.gameWidth = this.app.screenWidth; + this.root.gameHeight = this.app.screenHeight; + + // Initialize canvas element & context + this.internalInitCanvas(); + + // Members + const root = this.root; + + // This isn't nice, but we need it right here + root.keyMapper = new KeyActionMapper(root, this.root.gameState.inputReceiver); + + // Init game mode + root.gameMode = GameMode.create(root, gameModeId, parentState.creationPayload.gameModeParameters); + + // Needs to come first + root.dynamicTickrate = new DynamicTickrate(root); + + // Init classes + root.camera = new Camera(root); + root.map = new MapView(root); + root.logic = new GameLogic(root); + root.hud = new GameHUD(root); + root.time = new GameTime(root); + root.automaticSave = new AutomaticSave(root); + root.soundProxy = new SoundProxy(root); + + // Init managers + root.entityMgr = new EntityManager(root); + root.systemMgr = new GameSystemManager(root); + root.shapeDefinitionMgr = new ShapeDefinitionManager(root); + root.hubGoals = new HubGoals(root); + root.productionAnalytics = new ProductionAnalytics(root); + root.buffers = new BufferMaintainer(root); + + // Initialize the hud once everything is loaded + this.root.hud.initialize(); + + // Initial resize event, it might be possible that the screen + // resized later during init tho, which is why will emit it later + // again anyways + this.resize(this.app.screenWidth, this.app.screenHeight); + + if (G_IS_DEV) { + // @ts-ignore + window.globalRoot = root; + } + + // @todo Find better place + if (G_IS_DEV && globalConfig.debug.manualTickOnly) { + this.root.gameState.inputReceiver.keydown.add(key => { + if (key.keyCode === 84) { + // 'T' + + // Extract current real time + this.root.time.updateRealtimeNow(); + + // Perform logic ticks + this.root.time.performTicks(this.root.dynamicTickrate.deltaMs, this.boundInternalTick); + + // Update analytics + root.productionAnalytics.update(); + } + }); + } + + logger.log("root initialized"); + MOD_SIGNALS.gameInitialized.dispatch(root); + } + + /** + * Initializes a new game, this means creating a new map and centering on the + * playerbase + * */ + initNewGame() { + logger.log("Initializing new game"); + this.root.gameIsFresh = true; + this.root.map.seed = randomInt(0, 100000); + + if (!this.root.gameMode.hasHub()) { + return; + } + + // Place the hub + const hub = gMetaBuildingRegistry.findByClass(MetaHubBuilding).createEntity({ + root: this.root, + origin: new Vector(-2, -2), + rotation: 0, + originalRotation: 0, + rotationVariant: 0, + variant: defaultBuildingVariant, + }); + this.root.map.placeStaticEntity(hub); + this.root.entityMgr.registerEntity(hub); + this.root.camera.center = new Vector(-5, 2).multiplyScalar(globalConfig.tileSize); + } + + /** + * Inits an existing game by loading the raw savegame data and deserializing it. + * Also runs basic validity checks. + */ + initExistingGame() { + logger.log("Initializing existing game"); + const serializer = new SavegameSerializer(); + + try { + const status = serializer.deserialize(this.root.savegame.getCurrentDump(), this.root); + if (!status.isGood()) { + logger.error("savegame-deserialize-failed:" + status.reason); + return false; + } + } catch (ex) { + logger.error("Exception during deserialization:", ex); + return false; + } + this.root.gameIsFresh = false; + return true; + } + + /** + * Initializes the render canvas + */ + internalInitCanvas() { + logger.log("Creating new canvas"); + const canvas = document.createElement("canvas"); + canvas.id = "ingame_Canvas"; + this.root.gameState.getDivElement().appendChild(canvas); + const context = canvas.getContext("2d", { alpha: false }); + + context.imageSmoothingEnabled = globalConfig.smoothing.smoothMainCanvas; + context.imageSmoothingQuality = globalConfig.smoothing.quality; + + this.root.canvas = canvas; + this.root.context = context; + } + + /** + * Destructs the root, freeing all resources + */ + destruct() { + this.root.destruct(); + delete this.root; + this.root = null; + this.app = null; + } + + tick(deltaMs) { + const root = this.root; + + // Extract current real time + root.time.updateRealtimeNow(); + + // Camera is always updated, no matter what + root.camera.update(deltaMs); + + if (!(G_IS_DEV && globalConfig.debug.manualTickOnly)) { + // Perform logic ticks + this.root.time.performTicks(deltaMs, this.boundInternalTick); + + // Update analytics + root.productionAnalytics.update(); + } + + // Update automatic save after everything finished + root.automaticSave.update(); + + return true; + } + + shouldRender() { + if (this.root.queue.requireRedraw) { + return true; + } + if (this.root.hud.shouldPauseRendering()) { + return false; + } + + // Do not render + if (!this.app.isRenderable()) { + return false; + } + + return true; + } + + updateLogic() { + const root = this.root; + + root.dynamicTickrate.beginTick(); + + if (G_IS_DEV && globalConfig.debug.disableLogicTicks) { + root.dynamicTickrate.endTick(); + return true; + } + + this.duringLogicUpdate = true; + + // Update entities, this removes destroyed entities + root.entityMgr.update(); + + // IMPORTANT: At this point, the game might be game over. Stop if this is the case + if (!this.root) { + logger.log("Root destructed, returning false"); + root.dynamicTickrate.endTick(); + + return false; + } + + root.systemMgr.update(); + // root.particleMgr.update(); + + this.duringLogicUpdate = false; + root.dynamicTickrate.endTick(); + return true; + } + + resize(w, h) { + this.root.gameWidth = w; + this.root.gameHeight = h; + resizeHighDPICanvas(this.root.canvas, w, h, globalConfig.smoothing.smoothMainCanvas); + this.root.signals.resized.dispatch(w, h); + this.root.queue.requireRedraw = true; + } + + postLoadHook() { + logger.log("Dispatching post load hook"); + this.root.signals.postLoadHook.dispatch(); + + if (!this.root.gameIsFresh) { + // Also dispatch game restored hook on restored savegames + this.root.signals.gameRestored.dispatch(); + } + + this.root.gameInitialized = true; + } + + draw() { + const root = this.root; + const systems = root.systemMgr.systems; + + this.root.dynamicTickrate.onFrameRendered(); + + if (!this.shouldRender()) { + // Always update hud tho + root.hud.update(); + return; + } + + this.root.signals.gameFrameStarted.dispatch(); + + root.queue.requireRedraw = false; + + // Gather context and save all state + const context = root.context; + context.save(); + if (G_IS_DEV) { + context.fillStyle = "#a10000"; + context.fillRect(0, 0, window.innerWidth * 3, window.innerHeight * 3); + } + + // Compute optimal zoom level and atlas scale + const zoomLevel = root.camera.zoomLevel; + const lowQuality = root.app.settings.getAllSettings().lowQualityTextures; + const effectiveZoomLevel = + (zoomLevel / globalConfig.assetsDpi) * getDeviceDPI() * globalConfig.assetsSharpness; + + let desiredAtlasScale = "0.25"; + if (effectiveZoomLevel > 0.5 && !lowQuality) { + desiredAtlasScale = ORIGINAL_SPRITE_SCALE; + } else if (effectiveZoomLevel > 0.35 && !lowQuality) { + desiredAtlasScale = "0.5"; + } + + // Construct parameters required for drawing + const params = new DrawParameters({ + context: context, + visibleRect: root.camera.getVisibleRect(), + desiredAtlasScale, + zoomLevel, + root: root, + }); + + if (G_IS_DEV && globalConfig.debug.testCulling) { + context.clearRect(0, 0, root.gameWidth, root.gameHeight); + } + + // Transform to world space + + if (G_IS_DEV && globalConfig.debug.testClipping) { + params.visibleRect = params.visibleRect.expandedInAllDirections( + -200 / this.root.camera.zoomLevel + ); + } + + root.camera.transform(context); + + assert(context.globalAlpha === 1.0, "Global alpha not 1 on frame start"); + + // Update hud + root.hud.update(); + + // Main rendering order + // ----- + + const desiredOverlayAlpha = this.root.camera.getIsMapOverlayActive() ? 1 : 0; + this.overlayAlpha = lerp(this.overlayAlpha, desiredOverlayAlpha, 0.25); + + // On low performance, skip the fade + if (this.root.entityMgr.entities.size > 5000 || this.root.dynamicTickrate.averageFps < 50) { + this.overlayAlpha = desiredOverlayAlpha; + } + + if (this.overlayAlpha < 0.99) { + // Background (grid, resources, etc) + root.map.drawBackground(params); + + // Belt items + systems.belt.drawBeltItems(params); + + // Miner & Static map entities etc. + root.map.drawForeground(params); + + // HUB Overlay + systems.hub.draw(params); + + // Green wires overlay + if (root.hud.parts.wiresOverlay) { + root.hud.parts.wiresOverlay.draw(params); + } + + if (this.root.currentLayer === "wires") { + // Static map entities + root.map.drawWiresForegroundLayer(params); + } + } + + if (this.overlayAlpha > 0.01) { + // Map overview + context.globalAlpha = this.overlayAlpha; + root.map.drawOverlay(params); + context.globalAlpha = 1; + } + + if (G_IS_DEV) { + root.map.drawStaticEntityDebugOverlays(params); + } + + if (G_IS_DEV && globalConfig.debug.renderBeltPaths) { + systems.belt.drawBeltPathDebug(params); + } + + // END OF GAME CONTENT + // ----- + + // Finally, draw the hud. Nothing should come after that + root.hud.draw(params); + + assert(context.globalAlpha === 1.0, "Global alpha not 1 on frame end before restore"); + + // Restore to screen space + context.restore(); + + // Restore parameters + params.zoomLevel = 1; + params.desiredAtlasScale = ORIGINAL_SPRITE_SCALE; + params.visibleRect = new Rectangle(0, 0, this.root.gameWidth, this.root.gameHeight); + if (G_IS_DEV && globalConfig.debug.testClipping) { + params.visibleRect = params.visibleRect.expandedInAllDirections(-200); + } + + // Draw overlays, those are screen space + root.hud.drawOverlays(params); + + assert(context.globalAlpha === 1.0, "context.globalAlpha not 1 on frame end"); + + if (G_IS_DEV && globalConfig.debug.simulateSlowRendering) { + let sum = 0; + for (let i = 0; i < 1e8; ++i) { + sum += i; + } + if (Math.random() > 0.95) { + console.log(sum); + } + } + + if (G_IS_DEV && globalConfig.debug.showAtlasInfo) { + context.font = "13px GameFont"; + context.fillStyle = "blue"; + context.fillText( + "Atlas: " + + desiredAtlasScale + + " / Zoom: " + + round2Digits(zoomLevel) + + " / Effective Zoom: " + + round2Digits(effectiveZoomLevel), + 20, + 600 + ); + + const stats = this.root.buffers.getStats(); + + context.fillText( + "Maintained Buffers: " + + stats.rootKeys + + " root keys / " + + stats.subKeys + + " buffers / VRAM: " + + round2Digits(stats.vramBytes / (1024 * 1024)) + + " MB", + 20, + 620 + ); + const internalStats = getBufferStats(); + context.fillText( + "Total Buffers: " + + internalStats.bufferCount + + " buffers / " + + internalStats.backlogSize + + " backlog / " + + internalStats.backlogKeys + + " keys in backlog / VRAM " + + round2Digits(internalStats.vramUsage / (1024 * 1024)) + + " MB / Backlog " + + round2Digits(internalStats.backlogVramUsage / (1024 * 1024)) + + " MB / Created " + + internalStats.numCreated + + " / Reused " + + internalStats.numReused, + 20, + 640 + ); + } + + if (G_IS_DEV && globalConfig.debug.testClipping) { + context.strokeStyle = "red"; + context.lineWidth = 1; + context.beginPath(); + context.rect(200, 200, this.root.gameWidth - 400, this.root.gameHeight - 400); + context.stroke(); + } + } +} diff --git a/src/js/game/dynamic_tickrate.js b/src/js/game/dynamic_tickrate.js index c76fa2e1..c68a3ec9 100644 --- a/src/js/game/dynamic_tickrate.js +++ b/src/js/game/dynamic_tickrate.js @@ -1,129 +1,116 @@ -import { GameRoot } from "./root"; -import { createLogger } from "../core/logging"; -import { globalConfig } from "../core/config"; - -const logger = createLogger("dynamic_tickrate"); - -const fpsAccumulationTime = 1000; - -export class DynamicTickrate { - /** - * - * @param {GameRoot} root - */ - constructor(root) { - this.root = root; - - this.currentTickStart = null; - this.capturedTicks = []; - this.averageTickDuration = 0; - - this.accumulatedFps = 0; - this.accumulatedFpsLastUpdate = 0; - - this.averageFps = 60; - - const fixedRate = this.root.gameMode.getFixedTickrate(); - if (fixedRate) { - logger.log("Setting fixed tickrate of", fixedRate); - this.setTickRate(fixedRate); - } else { - this.setTickRate(this.root.app.settings.getDesiredFps()); - - if (G_IS_DEV && globalConfig.debug.renderForTrailer) { - this.setTickRate(300); - } - } - } - - onFrameRendered() { - ++this.accumulatedFps; - - const now = performance.now(); - const timeDuration = now - this.accumulatedFpsLastUpdate; - if (timeDuration > fpsAccumulationTime) { - const avgFps = (this.accumulatedFps / fpsAccumulationTime) * 1000; - this.averageFps = avgFps; - this.accumulatedFps = 0; - this.accumulatedFpsLastUpdate = now; - } - } - - /** - * Sets the tick rate to N updates per second - * @param {number} rate - */ - setTickRate(rate) { - logger.log("Applying tick-rate of", rate); - this.currentTickRate = rate; - this.deltaMs = 1000.0 / this.currentTickRate; - this.deltaSeconds = 1.0 / this.currentTickRate; - } - - /** - * Increases the tick rate marginally - */ - increaseTickRate() { - if (G_IS_DEV && globalConfig.debug.renderForTrailer) { - return; - } - - const desiredFps = this.root.app.settings.getDesiredFps(); - this.setTickRate(Math.round(Math.min(desiredFps, this.currentTickRate * 1.2))); - } - - /** - * Decreases the tick rate marginally - */ - decreaseTickRate() { - if (G_IS_DEV && globalConfig.debug.renderForTrailer) { - return; - } - - const desiredFps = this.root.app.settings.getDesiredFps(); - this.setTickRate(Math.round(Math.max(desiredFps / 2, this.currentTickRate * 0.8))); - } - - /** - * Call whenever a tick began - */ - beginTick() { - assert(this.currentTickStart === null, "BeginTick called twice"); - this.currentTickStart = performance.now(); - - if (this.capturedTicks.length > this.currentTickRate * 2) { - // Take only a portion of the ticks - this.capturedTicks.sort(); - this.capturedTicks.splice(0, 10); - this.capturedTicks.splice(this.capturedTicks.length - 11, 10); - - let average = 0; - for (let i = 0; i < this.capturedTicks.length; ++i) { - average += this.capturedTicks[i]; - } - average /= this.capturedTicks.length; - - this.averageTickDuration = average; - - // Disabled for now: Dynamically adjusting tick rate - // if (this.averageFps > desiredFps * 0.9) { - // // if (average < maxTickDuration) { - // this.increaseTickRate(); - // } else if (this.averageFps < desiredFps * 0.7) { - // this.decreaseTickRate(); - // } - - this.capturedTicks = []; - } - } - - /** - * Call whenever a tick ended - */ - endTick() { - assert(this.currentTickStart !== null, "EndTick called without BeginTick"); - const duration = performance.now() - this.currentTickStart; - this.capturedTicks.push(duration); - this.currentTickStart = null; - } -} +import { createLogger } from "../core/logging"; +import { GameRoot } from "./root"; + +const logger = createLogger("dynamic_tickrate"); + +const fpsAccumulationTime = 1000; + +export class DynamicTickrate { + /** + * + * @param {GameRoot} root + */ + constructor(root) { + this.root = root; + + this.currentTickStart = null; + this.capturedTicks = []; + this.averageTickDuration = 0; + + this.accumulatedFps = 0; + this.accumulatedFpsLastUpdate = 0; + + this.averageFps = 60; + + const fixedRate = this.root.gameMode.getFixedTickrate(); + if (fixedRate) { + logger.log("Setting fixed tickrate of", fixedRate); + this.setTickRate(fixedRate); + } else { + this.setTickRate(this.root.app.settings.getDesiredFps()); + } + } + + onFrameRendered() { + ++this.accumulatedFps; + + const now = performance.now(); + const timeDuration = now - this.accumulatedFpsLastUpdate; + if (timeDuration > fpsAccumulationTime) { + const avgFps = (this.accumulatedFps / fpsAccumulationTime) * 1000; + this.averageFps = avgFps; + this.accumulatedFps = 0; + this.accumulatedFpsLastUpdate = now; + } + } + + /** + * Sets the tick rate to N updates per second + * @param {number} rate + */ + setTickRate(rate) { + logger.log("Applying tick-rate of", rate); + this.currentTickRate = rate; + this.deltaMs = 1000.0 / this.currentTickRate; + this.deltaSeconds = 1.0 / this.currentTickRate; + } + + /** + * Increases the tick rate marginally + */ + increaseTickRate() { + const desiredFps = this.root.app.settings.getDesiredFps(); + this.setTickRate(Math.round(Math.min(desiredFps, this.currentTickRate * 1.2))); + } + + /** + * Decreases the tick rate marginally + */ + decreaseTickRate() { + const desiredFps = this.root.app.settings.getDesiredFps(); + this.setTickRate(Math.round(Math.max(desiredFps / 2, this.currentTickRate * 0.8))); + } + + /** + * Call whenever a tick began + */ + beginTick() { + assert(this.currentTickStart === null, "BeginTick called twice"); + this.currentTickStart = performance.now(); + + if (this.capturedTicks.length > this.currentTickRate * 2) { + // Take only a portion of the ticks + this.capturedTicks.sort(); + this.capturedTicks.splice(0, 10); + this.capturedTicks.splice(this.capturedTicks.length - 11, 10); + + let average = 0; + for (let i = 0; i < this.capturedTicks.length; ++i) { + average += this.capturedTicks[i]; + } + average /= this.capturedTicks.length; + + this.averageTickDuration = average; + + // Disabled for now: Dynamically adjusting tick rate + // if (this.averageFps > desiredFps * 0.9) { + // // if (average < maxTickDuration) { + // this.increaseTickRate(); + // } else if (this.averageFps < desiredFps * 0.7) { + // this.decreaseTickRate(); + // } + + this.capturedTicks = []; + } + } + + /** + * Call whenever a tick ended + */ + endTick() { + assert(this.currentTickStart !== null, "EndTick called without BeginTick"); + const duration = performance.now() - this.currentTickStart; + this.capturedTicks.push(duration); + this.currentTickStart = null; + } +} diff --git a/src/js/game/entity.js b/src/js/game/entity.js index 9acaf26b..e9373918 100644 --- a/src/js/game/entity.js +++ b/src/js/game/entity.js @@ -1,232 +1,232 @@ -/* typehints:start */ -import { DrawParameters } from "../core/draw_parameters"; -import { Component } from "./component"; -/* typehints:end */ - -import { GameRoot } from "./root"; -import { globalConfig } from "../core/config"; -import { enumDirectionToVector, enumDirectionToAngle } from "../core/vector"; -import { BasicSerializableObject, types } from "../savegame/serialization"; -import { EntityComponentStorage } from "./entity_components"; -import { Loader } from "../core/loader"; -import { drawRotatedSprite } from "../core/draw_utils"; -import { gComponentRegistry } from "../core/global_registries"; -import { getBuildingDataFromCode } from "./building_codes"; - -export class Entity extends BasicSerializableObject { - /** - * @param {GameRoot} root - */ - constructor(root) { - super(); - - /** - * Handle to the global game root - */ - this.root = root; - - /** - * The components of the entity - */ - this.components = new EntityComponentStorage(); - - /** - * Whether this entity was registered on the @see EntityManager so far - */ - this.registered = false; - - /** - * On which layer this entity is - * @type {Layer} - */ - this.layer = "regular"; - - /** - * Internal entity unique id, set by the @see EntityManager - */ - this.uid = 0; - - /* typehints:start */ - - /** - * Stores if this entity is destroyed, set by the @see EntityManager - * @type {boolean} */ - this.destroyed; - - /** - * Stores if this entity is queued to get destroyed in the next tick - * of the @see EntityManager - * @type {boolean} */ - this.queuedForDestroy; - - /** - * Stores the reason why this entity was destroyed - * @type {string} */ - this.destroyReason; - - /* typehints:end */ - } - - static getId() { - return "Entity"; - } - - /** - * @see BasicSerializableObject.getSchema - * @returns {import("../savegame/serialization").Schema} - */ - static getSchema() { - return { - uid: types.uint, - components: types.keyValueMap(types.objData(gComponentRegistry), false), - }; - } - - /** - * Returns a clone of this entity - */ - clone() { - const staticComp = this.components.StaticMapEntity; - const buildingData = getBuildingDataFromCode(staticComp.code); - - const clone = buildingData.metaInstance.createEntity({ - root: this.root, - origin: staticComp.origin, - originalRotation: staticComp.originalRotation, - rotation: staticComp.rotation, - rotationVariant: buildingData.rotationVariant, - variant: buildingData.variant, - }); - - for (const key in this.components) { - /** @type {Component} */ (this.components[key]).copyAdditionalStateTo(clone.components[key]); - } - - return clone; - } - - /** - * Adds a new component, only possible until the entity is registered on the entity manager, - * after that use @see EntityManager.addDynamicComponent - * @param {Component} componentInstance - * @param {boolean} force Used by the entity manager. Internal parameter, do not change - */ - addComponent(componentInstance, force = false) { - if (!force && this.registered) { - this.root.entityMgr.attachDynamicComponent(this, componentInstance); - return; - } - assert(force || !this.registered, "Entity already registered, use EntityManager.addDynamicComponent"); - const id = /** @type {typeof Component} */ (componentInstance.constructor).getId(); - assert(!this.components[id], "Component already present"); - this.components[id] = componentInstance; - } - - /** - * Removes a given component, only possible until the entity is registered on the entity manager, - * after that use @see EntityManager.removeDynamicComponent - * @param {typeof Component} componentClass - * @param {boolean} force - */ - removeComponent(componentClass, force = false) { - if (!force && this.registered) { - this.root.entityMgr.removeDynamicComponent(this, componentClass); - return; - } - assert( - force || !this.registered, - "Entity already registered, use EntityManager.removeDynamicComponent" - ); - const id = componentClass.getId(); - assert(this.components[id], "Component does not exist on entity"); - delete this.components[id]; - } - - /** - * Draws the entity, to override use @see Entity.drawImpl - * @param {DrawParameters} parameters - */ - drawDebugOverlays(parameters) { - const context = parameters.context; - const staticComp = this.components.StaticMapEntity; - - if (G_IS_DEV && staticComp && globalConfig.debug.showEntityBounds) { - if (staticComp) { - const transformed = staticComp.getTileSpaceBounds(); - context.strokeStyle = "rgba(255, 0, 0, 0.5)"; - context.lineWidth = 2; - // const boundsSize = 20; - context.beginPath(); - context.rect( - transformed.x * globalConfig.tileSize, - transformed.y * globalConfig.tileSize, - transformed.w * globalConfig.tileSize, - transformed.h * globalConfig.tileSize - ); - context.stroke(); - } - } - - if (G_IS_DEV && staticComp && globalConfig.debug.showAcceptorEjectors) { - const ejectorComp = this.components.ItemEjector; - - if (ejectorComp) { - const ejectorSprite = Loader.getSprite("sprites/debug/ejector_slot.png"); - for (let i = 0; i < ejectorComp.slots.length; ++i) { - const slot = ejectorComp.slots[i]; - const slotTile = staticComp.localTileToWorld(slot.pos); - const direction = staticComp.localDirectionToWorld(slot.direction); - const directionVector = enumDirectionToVector[direction]; - const angle = Math.radians(enumDirectionToAngle[direction]); - - context.globalAlpha = slot.item ? 1 : 0.2; - drawRotatedSprite({ - parameters, - sprite: ejectorSprite, - x: (slotTile.x + 0.5 + directionVector.x * 0.37) * globalConfig.tileSize, - y: (slotTile.y + 0.5 + directionVector.y * 0.37) * globalConfig.tileSize, - angle, - size: globalConfig.tileSize * 0.25, - }); - } - } - const acceptorComp = this.components.ItemAcceptor; - - if (acceptorComp) { - const acceptorSprite = Loader.getSprite("sprites/misc/acceptor_slot.png"); - for (let i = 0; i < acceptorComp.slots.length; ++i) { - const slot = acceptorComp.slots[i]; - const slotTile = staticComp.localTileToWorld(slot.pos); - const direction = staticComp.localDirectionToWorld(slot.direction); - const directionVector = enumDirectionToVector[direction]; - const angle = Math.radians(enumDirectionToAngle[direction] + 180); - context.globalAlpha = 0.4; - drawRotatedSprite({ - parameters, - sprite: acceptorSprite, - x: (slotTile.x + 0.5 + directionVector.x * 0.37) * globalConfig.tileSize, - y: (slotTile.y + 0.5 + directionVector.y * 0.37) * globalConfig.tileSize, - angle, - size: globalConfig.tileSize * 0.25, - }); - } - } - - context.globalAlpha = 1; - } - // this.drawImpl(parameters); - } - - ///// Helper interfaces - - ///// Interface to override by subclasses - - /** - * override, should draw the entity - * @param {DrawParameters} parameters - * @abstract - */ - drawImpl(parameters) { - abstract; - } -} +/* typehints:start */ +import { DrawParameters } from "../core/draw_parameters"; +import { Component } from "./component"; +/* typehints:end */ + +import { GameRoot } from "./root"; +import { globalConfig } from "../core/config"; +import { enumDirectionToVector, enumDirectionToAngle } from "../core/vector"; +import { BasicSerializableObject, types } from "../savegame/serialization"; +import { Loader } from "../core/loader"; +import { drawRotatedSprite } from "../core/draw_utils"; +import { gComponentRegistry } from "../core/global_registries"; +import { getBuildingDataFromCode } from "./building_codes"; + +export class Entity extends BasicSerializableObject { + /** + * @param {GameRoot} root + */ + constructor(root) { + super(); + + /** + * Handle to the global game root + */ + this.root = root; + + /** + * The components of the entity + * @type {import("./entity_components").EntityComponentStorage} + */ + this.components = {}; + + /** + * Whether this entity was registered on the @see EntityManager so far + */ + this.registered = false; + + /** + * On which layer this entity is + * @type {Layer} + */ + this.layer = "regular"; + + /** + * Internal entity unique id, set by the @see EntityManager + */ + this.uid = 0; + + /* typehints:start */ + + /** + * Stores if this entity is destroyed, set by the @see EntityManager + * @type {boolean} */ + this.destroyed; + + /** + * Stores if this entity is queued to get destroyed in the next tick + * of the @see EntityManager + * @type {boolean} */ + this.queuedForDestroy; + + /** + * Stores the reason why this entity was destroyed + * @type {string} */ + this.destroyReason; + + /* typehints:end */ + } + + static getId() { + return "Entity"; + } + + /** + * @see BasicSerializableObject.getSchema + * @returns {import("../savegame/serialization").Schema} + */ + static getSchema() { + return { + uid: types.uint, + components: types.keyValueMap(types.objData(gComponentRegistry), false), + }; + } + + /** + * Returns a clone of this entity + */ + clone() { + const staticComp = this.components.StaticMapEntity; + const buildingData = getBuildingDataFromCode(staticComp.code); + + const clone = buildingData.metaInstance.createEntity({ + root: this.root, + origin: staticComp.origin, + originalRotation: staticComp.originalRotation, + rotation: staticComp.rotation, + rotationVariant: buildingData.rotationVariant, + variant: buildingData.variant, + }); + + for (const key in this.components) { + this.components[key].copyAdditionalStateTo(clone.components[key]); + } + + return clone; + } + + /** + * Adds a new component, only possible until the entity is registered on the entity manager, + * after that use @see EntityManager.addDynamicComponent + * @param {Component} componentInstance + * @param {boolean} force Used by the entity manager. Internal parameter, do not change + */ + addComponent(componentInstance, force = false) { + if (!force && this.registered) { + this.root.entityMgr.attachDynamicComponent(this, componentInstance); + return; + } + assert(force || !this.registered, "Entity already registered, use EntityManager.addDynamicComponent"); + const id = /** @type {typeof Component} */ (componentInstance.constructor).getId(); + assert(!this.components[id], "Component already present"); + this.components[id] = componentInstance; + } + + /** + * Removes a given component, only possible until the entity is registered on the entity manager, + * after that use @see EntityManager.removeDynamicComponent + * @param {typeof Component} componentClass + * @param {boolean} force + */ + removeComponent(componentClass, force = false) { + if (!force && this.registered) { + this.root.entityMgr.removeDynamicComponent(this, componentClass); + return; + } + assert( + force || !this.registered, + "Entity already registered, use EntityManager.removeDynamicComponent" + ); + const id = componentClass.getId(); + assert(this.components[id], "Component does not exist on entity"); + delete this.components[id]; + } + + /** + * Draws the entity, to override use @see Entity.drawImpl + * @param {DrawParameters} parameters + */ + drawDebugOverlays(parameters) { + const context = parameters.context; + const staticComp = this.components.StaticMapEntity; + + if (G_IS_DEV && staticComp && globalConfig.debug.showEntityBounds) { + if (staticComp) { + const transformed = staticComp.getTileSpaceBounds(); + context.strokeStyle = "rgba(255, 0, 0, 0.5)"; + context.lineWidth = 2; + // const boundsSize = 20; + context.beginPath(); + context.rect( + transformed.x * globalConfig.tileSize, + transformed.y * globalConfig.tileSize, + transformed.w * globalConfig.tileSize, + transformed.h * globalConfig.tileSize + ); + context.stroke(); + } + } + + if (G_IS_DEV && staticComp && globalConfig.debug.showAcceptorEjectors) { + const ejectorComp = this.components.ItemEjector; + + if (ejectorComp) { + const ejectorSprite = Loader.getSprite("sprites/debug/ejector_slot.png"); + for (let i = 0; i < ejectorComp.slots.length; ++i) { + const slot = ejectorComp.slots[i]; + const slotTile = staticComp.localTileToWorld(slot.pos); + const direction = staticComp.localDirectionToWorld(slot.direction); + const directionVector = enumDirectionToVector[direction]; + const angle = Math.radians(enumDirectionToAngle[direction]); + + context.globalAlpha = slot.item ? 1 : 0.2; + drawRotatedSprite({ + parameters, + sprite: ejectorSprite, + x: (slotTile.x + 0.5 + directionVector.x * 0.37) * globalConfig.tileSize, + y: (slotTile.y + 0.5 + directionVector.y * 0.37) * globalConfig.tileSize, + angle, + size: globalConfig.tileSize * 0.25, + }); + } + } + const acceptorComp = this.components.ItemAcceptor; + + if (acceptorComp) { + const acceptorSprite = Loader.getSprite("sprites/misc/acceptor_slot.png"); + for (let i = 0; i < acceptorComp.slots.length; ++i) { + const slot = acceptorComp.slots[i]; + const slotTile = staticComp.localTileToWorld(slot.pos); + const direction = staticComp.localDirectionToWorld(slot.direction); + const directionVector = enumDirectionToVector[direction]; + const angle = Math.radians(enumDirectionToAngle[direction] + 180); + context.globalAlpha = 0.4; + drawRotatedSprite({ + parameters, + sprite: acceptorSprite, + x: (slotTile.x + 0.5 + directionVector.x * 0.37) * globalConfig.tileSize, + y: (slotTile.y + 0.5 + directionVector.y * 0.37) * globalConfig.tileSize, + angle, + size: globalConfig.tileSize * 0.25, + }); + } + } + + context.globalAlpha = 1; + } + // this.drawImpl(parameters); + } + + ///// Helper interfaces + + ///// Interface to override by subclasses + + /** + * override, should draw the entity + * @param {DrawParameters} parameters + * @abstract + */ + drawImpl(parameters) { + abstract; + } +} diff --git a/src/js/game/entity_components.js b/src/js/game/entity_components.js deleted file mode 100644 index 163be9f9..00000000 --- a/src/js/game/entity_components.js +++ /dev/null @@ -1,98 +0,0 @@ -/* typehints:start */ -import { BeltComponent } from "./components/belt"; -import { BeltUnderlaysComponent } from "./components/belt_underlays"; -import { HubComponent } from "./components/hub"; -import { ItemAcceptorComponent } from "./components/item_acceptor"; -import { ItemEjectorComponent } from "./components/item_ejector"; -import { ItemProcessorComponent } from "./components/item_processor"; -import { MinerComponent } from "./components/miner"; -import { StaticMapEntityComponent } from "./components/static_map_entity"; -import { StorageComponent } from "./components/storage"; -import { UndergroundBeltComponent } from "./components/underground_belt"; -import { WiredPinsComponent } from "./components/wired_pins"; -import { WireComponent } from "./components/wire"; -import { ConstantSignalComponent } from "./components/constant_signal"; -import { LogicGateComponent } from "./components/logic_gate"; -import { LeverComponent } from "./components/lever"; -import { WireTunnelComponent } from "./components/wire_tunnel"; -import { DisplayComponent } from "./components/display"; -import { BeltReaderComponent } from "./components/belt_reader"; -import { FilterComponent } from "./components/filter"; -import { ItemProducerComponent } from "./components/item_producer"; -import { GoalAcceptorComponent } from "./components/goal_acceptor"; -/* typehints:end */ - -/** - * Typedefs for all entity components. These are not actually present on the entity, - * thus they are undefined by default - */ -export class EntityComponentStorage { - constructor() { - /* typehints:start */ - - /** @type {StaticMapEntityComponent} */ - this.StaticMapEntity; - - /** @type {BeltComponent} */ - this.Belt; - - /** @type {ItemEjectorComponent} */ - this.ItemEjector; - - /** @type {ItemAcceptorComponent} */ - this.ItemAcceptor; - - /** @type {MinerComponent} */ - this.Miner; - - /** @type {ItemProcessorComponent} */ - this.ItemProcessor; - - /** @type {UndergroundBeltComponent} */ - this.UndergroundBelt; - - /** @type {HubComponent} */ - this.Hub; - - /** @type {StorageComponent} */ - this.Storage; - - /** @type {WiredPinsComponent} */ - this.WiredPins; - - /** @type {BeltUnderlaysComponent} */ - this.BeltUnderlays; - - /** @type {WireComponent} */ - this.Wire; - - /** @type {ConstantSignalComponent} */ - this.ConstantSignal; - - /** @type {LogicGateComponent} */ - this.LogicGate; - - /** @type {LeverComponent} */ - this.Lever; - - /** @type {WireTunnelComponent} */ - this.WireTunnel; - - /** @type {DisplayComponent} */ - this.Display; - - /** @type {BeltReaderComponent} */ - this.BeltReader; - - /** @type {FilterComponent} */ - this.Filter; - - /** @type {ItemProducerComponent} */ - this.ItemProducer; - - /** @type {GoalAcceptorComponent} */ - this.GoalAcceptor; - - /* typehints:end */ - } -} diff --git a/src/js/game/entity_components.ts b/src/js/game/entity_components.ts new file mode 100644 index 00000000..e057dcf4 --- /dev/null +++ b/src/js/game/entity_components.ts @@ -0,0 +1,51 @@ +import type { BeltComponent } from "./components/belt"; +import type { BeltUnderlaysComponent } from "./components/belt_underlays"; +import type { HubComponent } from "./components/hub"; +import type { ItemAcceptorComponent } from "./components/item_acceptor"; +import type { ItemEjectorComponent } from "./components/item_ejector"; +import type { ItemProcessorComponent } from "./components/item_processor"; +import type { MinerComponent } from "./components/miner"; +import type { StaticMapEntityComponent } from "./components/static_map_entity"; +import type { StorageComponent } from "./components/storage"; +import type { UndergroundBeltComponent } from "./components/underground_belt"; +import type { WiredPinsComponent } from "./components/wired_pins"; +import type { WireComponent } from "./components/wire"; +import type { ConstantSignalComponent } from "./components/constant_signal"; +import type { LogicGateComponent } from "./components/logic_gate"; +import type { LeverComponent } from "./components/lever"; +import type { WireTunnelComponent } from "./components/wire_tunnel"; +import type { DisplayComponent } from "./components/display"; +import type { BeltReaderComponent } from "./components/belt_reader"; +import type { FilterComponent } from "./components/filter"; +import type { ItemProducerComponent } from "./components/item_producer"; +import type { GoalAcceptorComponent } from "./components/goal_acceptor"; +import type { Component } from "./component"; + +/** + * Typedefs for all entity components. + */ +export interface EntityComponentStorage { + StaticMapEntity?: StaticMapEntityComponent; + Belt?: BeltComponent; + ItemEjector?: ItemEjectorComponent; + ItemAcceptor?: ItemAcceptorComponent; + Miner?: MinerComponent; + ItemProcessor?: ItemProcessorComponent; + UndergroundBelt?: UndergroundBeltComponent; + Hub?: HubComponent; + Storage?: StorageComponent; + WiredPins?: WiredPinsComponent; + BeltUnderlays?: BeltUnderlaysComponent; + Wire?: WireComponent; + ConstantSignal?: ConstantSignalComponent; + LogicGate?: LogicGateComponent; + Lever?: LeverComponent; + WireTunnel?: WireTunnelComponent; + Display?: DisplayComponent; + BeltReader?: BeltReaderComponent; + Filter?: FilterComponent; + ItemProducer?: ItemProducerComponent; + GoalAcceptor?: GoalAcceptorComponent; + + [k: string]: Component; +} diff --git a/src/js/game/entity_manager.js b/src/js/game/entity_manager.ts similarity index 50% rename from src/js/game/entity_manager.js rename to src/js/game/entity_manager.ts index b4101fc8..756f5bce 100644 --- a/src/js/game/entity_manager.js +++ b/src/js/game/entity_manager.ts @@ -1,241 +1,218 @@ -import { arrayDeleteValue, newEmptyMap, fastArrayDeleteValue } from "../core/utils"; -import { Component } from "./component"; -import { GameRoot } from "./root"; -import { Entity } from "./entity"; -import { BasicSerializableObject, types } from "../savegame/serialization"; -import { createLogger } from "../core/logging"; -import { globalConfig } from "../core/config"; - -const logger = createLogger("entity_manager"); - -// Manages all entities - -// NOTICE: We use arrayDeleteValue instead of fastArrayDeleteValue since that does not preserve the order -// This is slower but we need it for the street path generation - -export class EntityManager extends BasicSerializableObject { - constructor(root) { - super(); - - /** @type {GameRoot} */ - this.root = root; - - /** @type {Array} */ - this.entities = []; - - // We store a separate list with entities to destroy, since we don't destroy - // them instantly - /** @type {Array} */ - this.destroyList = []; - - // Store a map from componentid to entities - This is used by the game system - // for faster processing - /** @type {Object.>} */ - this.componentToEntity = newEmptyMap(); - - // Store the next uid to use - this.nextUid = 10000; - } - - static getId() { - return "EntityManager"; - } - - static getSchema() { - return { - nextUid: types.uint, - }; - } - - getStatsText() { - return this.entities.length + " entities [" + this.destroyList.length + " to kill]"; - } - - // Main update - update() { - this.processDestroyList(); - } - - /** - * Registers a new entity - * @param {Entity} entity - * @param {number=} uid Optional predefined uid - */ - registerEntity(entity, uid = null) { - if (G_IS_DEV && !globalConfig.debug.disableSlowAsserts) { - assert(this.entities.indexOf(entity) < 0, `RegisterEntity() called twice for entity ${entity}`); - } - assert(!entity.destroyed, `Attempting to register destroyed entity ${entity}`); - - if (G_IS_DEV && !globalConfig.debug.disableSlowAsserts && uid !== null) { - assert(!this.findByUid(uid, false), "Entity uid already taken: " + uid); - assert(uid >= 0 && uid < Number.MAX_SAFE_INTEGER, "Invalid uid passed: " + uid); - } - - this.entities.push(entity); - - // Register into the componentToEntity map - for (const componentId in entity.components) { - if (entity.components[componentId]) { - if (this.componentToEntity[componentId]) { - this.componentToEntity[componentId].push(entity); - } else { - this.componentToEntity[componentId] = [entity]; - } - } - } - - // Give each entity a unique id - entity.uid = uid ? uid : this.generateUid(); - entity.registered = true; - - this.root.signals.entityAdded.dispatch(entity); - } - - /** - * Generates a new uid - * @returns {number} - */ - generateUid() { - return this.nextUid++; - } - - /** - * Call to attach a new component after the creation of the entity - * @param {Entity} entity - * @param {Component} component - */ - attachDynamicComponent(entity, component) { - entity.addComponent(component, true); - const componentId = /** @type {typeof Component} */ (component.constructor).getId(); - if (this.componentToEntity[componentId]) { - this.componentToEntity[componentId].push(entity); - } else { - this.componentToEntity[componentId] = [entity]; - } - this.root.signals.entityGotNewComponent.dispatch(entity); - } - - /** - * Call to remove a component after the creation of the entity - * @param {Entity} entity - * @param {typeof Component} component - */ - removeDynamicComponent(entity, component) { - entity.removeComponent(component, true); - const componentId = /** @type {typeof Component} */ (component.constructor).getId(); - - fastArrayDeleteValue(this.componentToEntity[componentId], entity); - this.root.signals.entityComponentRemoved.dispatch(entity); - } - - /** - * Finds an entity buy its uid, kinda slow since it loops over all entities - * @param {number} uid - * @param {boolean=} errorWhenNotFound - * @returns {Entity} - */ - findByUid(uid, errorWhenNotFound = true) { - const arr = this.entities; - for (let i = 0, len = arr.length; i < len; ++i) { - const entity = arr[i]; - if (entity.uid === uid) { - if (entity.queuedForDestroy || entity.destroyed) { - if (errorWhenNotFound) { - logger.warn("Entity with UID", uid, "not found (destroyed)"); - } - return null; - } - return entity; - } - } - if (errorWhenNotFound) { - logger.warn("Entity with UID", uid, "not found"); - } - return null; - } - - /** - * Returns a map which gives a mapping from UID to Entity. - * This map is not updated. - * - * @returns {Map} - */ - getFrozenUidSearchMap() { - const result = new Map(); - const array = this.entities; - for (let i = 0, len = array.length; i < len; ++i) { - const entity = array[i]; - if (!entity.queuedForDestroy && !entity.destroyed) { - result.set(entity.uid, entity); - } - } - return result; - } - - /** - * Returns all entities having the given component - * @param {typeof Component} componentHandle - * @returns {Array} entities - */ - getAllWithComponent(componentHandle) { - return this.componentToEntity[componentHandle.getId()] || []; - } - - /** - * Unregisters all components of an entity from the component to entity mapping - * @param {Entity} entity - */ - unregisterEntityComponents(entity) { - for (const componentId in entity.components) { - if (entity.components[componentId]) { - arrayDeleteValue(this.componentToEntity[componentId], entity); - } - } - } - - // Processes the entities to destroy and actually destroys them - /* eslint-disable max-statements */ - processDestroyList() { - for (let i = 0; i < this.destroyList.length; ++i) { - const entity = this.destroyList[i]; - - // Remove from entities list - arrayDeleteValue(this.entities, entity); - - // Remove from componentToEntity list - this.unregisterEntityComponents(entity); - - entity.registered = false; - entity.destroyed = true; - - this.root.signals.entityDestroyed.dispatch(entity); - } - - this.destroyList = []; - } - - /** - * Queues an entity for destruction - * @param {Entity} entity - */ - destroyEntity(entity) { - if (entity.destroyed) { - logger.error("Tried to destroy already destroyed entity:", entity.uid); - return; - } - - if (entity.queuedForDestroy) { - logger.error("Trying to destroy entity which is already queued for destroy!", entity.uid); - return; - } - - if (this.destroyList.indexOf(entity) < 0) { - this.destroyList.push(entity); - entity.queuedForDestroy = true; - this.root.signals.entityQueuedForDestroy.dispatch(entity); - } else { - assert(false, "Trying to destroy entity twice"); - } - } -} +import { globalConfig } from "../core/config"; +import { createLogger } from "../core/logging"; +import { newEmptyMap } from "../core/utils"; +import { BasicSerializableObject, types } from "../savegame/serialization"; +import { Component } from "./component"; +import { Entity } from "./entity"; +import { GameRoot } from "./root"; + +const logger = createLogger("entity_manager"); + +// Manages all entities + +// NOTICE: We use arrayDeleteValue instead of fastArrayDeleteValue since that does not preserve the order +// This is slower but we need it for the street path generation + +export class EntityManager extends BasicSerializableObject { + readonly root: GameRoot; + readonly entities = new Map(); + + // We store a separate list with entities to destroy, since we don't destroy + // them instantly + private destroyList: Entity[] = []; + + // Store a map from componentid to entities - This is used by the game system + // for faster processing + private readonly componentToEntity: Record> = newEmptyMap(); + + // Store the next uid to use + private nextUid = 10000; + + constructor(root: GameRoot) { + super(); + this.root = root; + } + + static getId() { + return "EntityManager"; + } + + static getSchema() { + return { + nextUid: types.uint, + }; + } + + getStatsText() { + return this.entities.size + " entities [" + this.destroyList.length + " to kill]"; + } + + // Main update + update() { + this.processDestroyList(); + } + + /** + * Registers a new entity + * @param uid Optional predefined uid + */ + registerEntity(entity: Entity, uid = this.generateUid()) { + if (G_IS_DEV && !globalConfig.debug.disableSlowAsserts) { + assert( + this.entities.get(entity.uid) !== entity, + `RegisterEntity() called twice for entity ${entity}` + ); + } + assert(!entity.destroyed, `Attempting to register destroyed entity ${entity}`); + + if (G_IS_DEV && !globalConfig.debug.disableSlowAsserts) { + assert(!this.findByUid(uid, false), "Entity uid already taken: " + uid); + assert(uid >= 0 && uid < Number.MAX_SAFE_INTEGER, "Invalid uid passed: " + uid); + } + + // Give each entity a unique id + entity.uid = uid; + entity.registered = true; + + this.entities.set(entity.uid, entity); + + // Register into the componentToEntity map + for (const componentId in entity.components) { + if (entity.components[componentId]) { + const set = (this.componentToEntity[componentId] ??= new Set()); + set.add(entity); + } + } + + this.root.signals.entityAdded.dispatch(entity); + } + + /** + * Generates a new uid + */ + generateUid(): number { + return this.nextUid++; + } + + /** + * Call to attach a new component after the creation of the entity + */ + attachDynamicComponent(entity: Entity, component: Component) { + entity.addComponent(component, true); + const componentId = /** @type {typeof Component} */ component.constructor.getId(); + const set = (this.componentToEntity[componentId] ??= new Set()); + set.add(entity); + this.root.signals.entityGotNewComponent.dispatch(entity); + } + + /** + * Call to remove a component after the creation of the entity + */ + removeDynamicComponent(entity: Entity, component: typeof Component) { + entity.removeComponent(component, true); + const componentId = /** @type {typeof Component} */ component.constructor.getId(); + + this.componentToEntity[componentId].delete(entity); + this.root.signals.entityComponentRemoved.dispatch(entity); + } + + /** + * Finds an entity by its uid + */ + findByUid(uid: number, errorWhenNotFound = true): Entity { + const entity = this.entities.get(uid); + + if (entity === undefined || entity.queuedForDestroy || entity.destroyed) { + if (errorWhenNotFound) { + logger.warn("Entity with UID", uid, "not found (destroyed)"); + } + + return null; + } + + return entity; + } + + /** + * Returns a map which gives a mapping from UID to Entity. + * This map is not updated. + */ + getFrozenUidSearchMap(): Map { + const result = new Map(); + for (const [uid, entity] of this.entities) { + if (!entity.queuedForDestroy && !entity.destroyed) { + result.set(uid, entity); + } + } + return result; + } + + /** + * Returns all entities having the given component + * @deprecated use {@link getEntitiesWithComponent} instead + */ + getAllWithComponent(componentHandle: typeof Component): Entity[] { + return [...(this.componentToEntity[componentHandle.getId()] ?? new Set())]; + } + + /** + * A version of {@link getAllWithComponent} that returns a Set + */ + getEntitiesWithComponent(componentHandle: typeof Component): Set { + return new Set(this.componentToEntity[componentHandle.getId()] ?? []); + } + + /** + * Unregisters all components of an entity from the component to entity mapping + */ + unregisterEntityComponents(entity: Entity) { + for (const componentId in entity.components) { + if (entity.components[componentId]) { + this.componentToEntity[componentId].delete(entity); + } + } + } + + // Processes the entities to destroy and actually destroys them + processDestroyList() { + for (let i = 0; i < this.destroyList.length; ++i) { + const entity = this.destroyList[i]; + + // Remove from entities list + this.entities.delete(entity.uid); + + // Remove from componentToEntity list + this.unregisterEntityComponents(entity); + + entity.registered = false; + entity.destroyed = true; + + this.root.signals.entityDestroyed.dispatch(entity); + } + + this.destroyList = []; + } + + /** + * Queues an entity for destruction + */ + destroyEntity(entity: Entity) { + if (entity.destroyed) { + logger.error("Tried to destroy already destroyed entity:", entity.uid); + return; + } + + if (entity.queuedForDestroy) { + logger.error("Trying to destroy entity which is already queued for destroy!", entity.uid); + return; + } + + if (this.destroyList.indexOf(entity) < 0) { + this.destroyList.push(entity); + entity.queuedForDestroy = true; + this.root.signals.entityQueuedForDestroy.dispatch(entity); + } else { + assert(false, "Trying to destroy entity twice"); + } + } +} diff --git a/src/js/game/game_mode.js b/src/js/game/game_mode.js index 2c4527e3..a49d94f3 100644 --- a/src/js/game/game_mode.js +++ b/src/js/game/game_mode.js @@ -1,198 +1,193 @@ -/* typehints:start */ -import { GameRoot } from "./root"; -/* typehints:end */ - -import { Rectangle } from "../core/rectangle"; -import { gGameModeRegistry } from "../core/global_registries"; -import { types, BasicSerializableObject } from "../savegame/serialization"; -import { MetaBuilding } from "./meta_building"; -import { MetaItemProducerBuilding } from "./buildings/item_producer"; -import { BaseHUDPart } from "./hud/base_hud_part"; - -/** @enum {string} */ -export const enumGameModeIds = { - puzzleEdit: "puzzleEditMode", - puzzlePlay: "puzzlePlayMode", - regular: "regularMode", -}; - -/** @enum {string} */ -export const enumGameModeTypes = { - default: "defaultModeType", - puzzle: "puzzleModeType", -}; - -export class GameMode extends BasicSerializableObject { - /** @returns {string} */ - static getId() { - abstract; - return "unknownMode"; - } - - /** @returns {string} */ - static getType() { - abstract; - return "unknownType"; - } - /** - * @param {GameRoot} root - * @param {string} [id=Regular] - * @param {object|undefined} payload - */ - static create(root, id = enumGameModeIds.regular, payload = undefined) { - return new (gGameModeRegistry.findById(id))(root, payload); - } - - /** - * @param {GameRoot} root - */ - constructor(root) { - super(); - this.root = root; - - /** - * @type {Record} - */ - this.additionalHudParts = {}; - - /** @type {typeof MetaBuilding[]} */ - this.hiddenBuildings = [MetaItemProducerBuilding]; - } - - /** @returns {object} */ - serialize() { - return { - $: this.getId(), - data: super.serialize(), - }; - } - - /** @param {object} savedata */ - deserialize({ data }) { - super.deserialize(data, this.root); - } - - /** @returns {string} */ - getId() { - // @ts-ignore - return this.constructor.getId(); - } - - /** @returns {string} */ - getType() { - // @ts-ignore - return this.constructor.getType(); - } - - /** - * @param {typeof MetaBuilding} building - Class name of building - * @returns {boolean} - */ - isBuildingExcluded(building) { - return this.hiddenBuildings.indexOf(building) >= 0; - } - - /** @returns {undefined|Rectangle[]} */ - getBuildableZones() { - return; - } - - /** @returns {Rectangle|undefined} */ - getCameraBounds() { - return; - } - - /** @returns {boolean} */ - hasHub() { - return true; - } - - /** @returns {boolean} */ - hasResources() { - return true; - } - - /** @returns {boolean} */ - hasAchievements() { - return false; - } - - /** @returns {number} */ - getMinimumZoom() { - return 0.06; - } - - /** @returns {number} */ - getMaximumZoom() { - return 3.5; - } - - /** @returns {Object} */ - getUpgrades() { - return { - belt: [], - miner: [], - processors: [], - painting: [], - }; - } - - throughputDoesNotMatter() { - return false; - } - - /** - * @param {number} w - * @param {number} h - * @abstract - */ - adjustZone(w = 0, h = 0) { - abstract; - return; - } - - /** @returns {array} */ - getLevelDefinitions() { - return []; - } - - /** @returns {boolean} */ - getIsFreeplayAvailable() { - return false; - } - - /** @returns {boolean} */ - getIsSaveable() { - return true; - } - - /** @returns {boolean} */ - getHasFreeCopyPaste() { - return false; - } - - /** @returns {boolean} */ - getSupportsWires() { - return true; - } - - /** @returns {boolean} */ - getIsEditor() { - return false; - } - - /** @returns {boolean} */ - getIsDeterministic() { - return false; - } - - /** @returns {number | undefined} */ - getFixedTickrate() { - return; - } - - /** @returns {string} */ - getBlueprintShapeKey() { - return "CbCbCbRb:CwCwCwCw"; - } -} +/* typehints:start */ +import { GameRoot } from "./root"; +/* typehints:end */ + +import { gGameModeRegistry } from "../core/global_registries"; +import { Rectangle } from "../core/rectangle"; +import { BasicSerializableObject } from "../savegame/serialization"; +import { MetaItemProducerBuilding } from "./buildings/item_producer"; +import { BaseHUDPart } from "./hud/base_hud_part"; +import { MetaBuilding } from "./meta_building"; + +/** @enum {string} */ +export const enumGameModeIds = { + puzzleEdit: "puzzleEditMode", + puzzlePlay: "puzzlePlayMode", + regular: "regularMode", +}; + +/** @enum {string} */ +export const enumGameModeTypes = { + default: "defaultModeType", + puzzle: "puzzleModeType", +}; + +export class GameMode extends BasicSerializableObject { + /** @returns {string} */ + static getId() { + abstract; + return "unknownMode"; + } + + /** @returns {string} */ + static getType() { + abstract; + return "unknownType"; + } + /** + * @param {GameRoot} root + * @param {string} [id=Regular] + * @param {object|undefined} payload + */ + static create(root, id = enumGameModeIds.regular, payload = undefined) { + return new (gGameModeRegistry.findById(id))(root, payload); + } + + /** + * @param {GameRoot} root + */ + constructor(root) { + super(); + this.root = root; + + /** + * @type {Record} + */ + this.additionalHudParts = {}; + + /** @type {typeof MetaBuilding[]} */ + this.hiddenBuildings = [MetaItemProducerBuilding]; + } + + /** @returns {object} */ + serialize() { + return { + $: this.getId(), + data: super.serialize(), + }; + } + + /** @param {object} savedata */ + deserialize({ data }) { + super.deserialize(data, this.root); + } + + /** @returns {string} */ + getId() { + // @ts-ignore + return this.constructor.getId(); + } + + /** @returns {string} */ + getType() { + // @ts-ignore + return this.constructor.getType(); + } + + /** + * @param {typeof MetaBuilding} building - Class name of building + * @returns {boolean} + */ + isBuildingExcluded(building) { + return this.hiddenBuildings.indexOf(building) >= 0; + } + + /** @returns {undefined|Rectangle[]} */ + getBuildableZones() { + return; + } + + /** @returns {Rectangle|undefined} */ + getCameraBounds() { + return; + } + + /** @returns {boolean} */ + hasHub() { + return true; + } + + /** @returns {boolean} */ + hasResources() { + return true; + } + + /** @returns {number} */ + getMinimumZoom() { + return 0.06; + } + + /** @returns {number} */ + getMaximumZoom() { + return 3.5; + } + + /** @returns {Object} */ + getUpgrades() { + return { + belt: [], + miner: [], + processors: [], + painting: [], + }; + } + + throughputDoesNotMatter() { + return false; + } + + /** + * @param {number} w + * @param {number} h + * @abstract + */ + adjustZone(w = 0, h = 0) { + abstract; + return; + } + + /** @returns {array} */ + getLevelDefinitions() { + return []; + } + + /** @returns {boolean} */ + getIsFreeplayAvailable() { + return false; + } + + /** @returns {boolean} */ + getIsSaveable() { + return true; + } + + /** @returns {boolean} */ + getHasFreeCopyPaste() { + return false; + } + + /** @returns {boolean} */ + getSupportsWires() { + return true; + } + + /** @returns {boolean} */ + getIsEditor() { + return false; + } + + /** @returns {boolean} */ + getIsDeterministic() { + return false; + } + + /** @returns {number | undefined} */ + getFixedTickrate() { + return; + } + + /** @returns {string} */ + getBlueprintShapeKey() { + return "CbCbCbRb:CwCwCwCw"; + } +} diff --git a/src/js/game/game_system_manager.js b/src/js/game/game_system_manager.js index a799b42a..0c9d5f11 100644 --- a/src/js/game/game_system_manager.js +++ b/src/js/game/game_system_manager.js @@ -1,232 +1,232 @@ -/* typehints:start */ -import { GameSystem } from "./game_system"; -import { GameRoot } from "./root"; -/* typehints:end */ - -import { createLogger } from "../core/logging"; -import { BeltSystem } from "./systems/belt"; -import { ItemEjectorSystem } from "./systems/item_ejector"; -import { MapResourcesSystem } from "./systems/map_resources"; -import { MinerSystem } from "./systems/miner"; -import { ItemProcessorSystem } from "./systems/item_processor"; -import { UndergroundBeltSystem } from "./systems/underground_belt"; -import { HubSystem } from "./systems/hub"; -import { StaticMapEntitySystem } from "./systems/static_map_entity"; -import { ItemAcceptorSystem } from "./systems/item_acceptor"; -import { StorageSystem } from "./systems/storage"; -import { WiredPinsSystem } from "./systems/wired_pins"; -import { BeltUnderlaysSystem } from "./systems/belt_underlays"; -import { WireSystem } from "./systems/wire"; -import { ConstantSignalSystem } from "./systems/constant_signal"; -import { LogicGateSystem } from "./systems/logic_gate"; -import { LeverSystem } from "./systems/lever"; -import { DisplaySystem } from "./systems/display"; -import { ItemProcessorOverlaysSystem } from "./systems/item_processor_overlays"; -import { BeltReaderSystem } from "./systems/belt_reader"; -import { FilterSystem } from "./systems/filter"; -import { ItemProducerSystem } from "./systems/item_producer"; -import { ConstantProducerSystem } from "./systems/constant_producer"; -import { GoalAcceptorSystem } from "./systems/goal_acceptor"; -import { ZoneSystem } from "./systems/zone"; - -const logger = createLogger("game_system_manager"); - -/** - * @type {Object GameSystem}>>} - */ -export const MODS_ADDITIONAL_SYSTEMS = {}; - -export class GameSystemManager { - /** - * - * @param {GameRoot} root - */ - constructor(root) { - this.root = root; - - this.systems = { - /* typehints:start */ - /** @type {BeltSystem} */ - belt: null, - - /** @type {ItemEjectorSystem} */ - itemEjector: null, - - /** @type {MapResourcesSystem} */ - mapResources: null, - - /** @type {MinerSystem} */ - miner: null, - - /** @type {ItemProcessorSystem} */ - itemProcessor: null, - - /** @type {UndergroundBeltSystem} */ - undergroundBelt: null, - - /** @type {HubSystem} */ - hub: null, - - /** @type {StaticMapEntitySystem} */ - staticMapEntities: null, - - /** @type {ItemAcceptorSystem} */ - itemAcceptor: null, - - /** @type {StorageSystem} */ - storage: null, - - /** @type {WiredPinsSystem} */ - wiredPins: null, - - /** @type {BeltUnderlaysSystem} */ - beltUnderlays: null, - - /** @type {WireSystem} */ - wire: null, - - /** @type {ConstantSignalSystem} */ - constantSignal: null, - - /** @type {LogicGateSystem} */ - logicGate: null, - - /** @type {LeverSystem} */ - lever: null, - - /** @type {DisplaySystem} */ - display: null, - - /** @type {ItemProcessorOverlaysSystem} */ - itemProcessorOverlays: null, - - /** @type {BeltReaderSystem} */ - beltReader: null, - - /** @type {FilterSystem} */ - filter: null, - - /** @type {ItemProducerSystem} */ - itemProducer: null, - - /** @type {ConstantProducerSystem} */ - ConstantProducer: null, - - /** @type {GoalAcceptorSystem} */ - GoalAcceptor: null, - - /** @type {ZoneSystem} */ - zone: null, - - /* typehints:end */ - }; - this.systemUpdateOrder = []; - - this.internalInitSystems(); - } - - /** - * Initializes all systems - */ - internalInitSystems() { - const addBefore = id => { - const systems = MODS_ADDITIONAL_SYSTEMS[id]; - if (systems) { - systems.forEach(({ id, systemClass }) => add(id, systemClass)); - } - }; - - const add = (id, systemClass) => { - addBefore(id); - this.systems[id] = new systemClass(this.root); - this.systemUpdateOrder.push(id); - }; - - // Order is important! - - // IMPORTANT: Item acceptor must be before the belt, because it may not tick after the belt - // has put in the item into the acceptor animation, otherwise its off - add("itemAcceptor", ItemAcceptorSystem); - - add("belt", BeltSystem); - - add("undergroundBelt", UndergroundBeltSystem); - - add("miner", MinerSystem); - - add("storage", StorageSystem); - - add("itemProcessor", ItemProcessorSystem); - - add("filter", FilterSystem); - - add("itemProducer", ItemProducerSystem); - - add("itemEjector", ItemEjectorSystem); - - if (this.root.gameMode.hasResources()) { - add("mapResources", MapResourcesSystem); - } - - add("hub", HubSystem); - - add("staticMapEntities", StaticMapEntitySystem); - - add("wiredPins", WiredPinsSystem); - - add("beltUnderlays", BeltUnderlaysSystem); - - add("constantSignal", ConstantSignalSystem); - - // WIRES section - add("lever", LeverSystem); - - // Wires must be before all gate, signal etc logic! - add("wire", WireSystem); - - // IMPORTANT: We have 2 phases: In phase 1 we compute the output values of all gates, - // processors etc. In phase 2 we propagate it through the wires network - add("logicGate", LogicGateSystem); - - add("beltReader", BeltReaderSystem); - - add("display", DisplaySystem); - - add("itemProcessorOverlays", ItemProcessorOverlaysSystem); - - add("constantProducer", ConstantProducerSystem); - - add("goalAcceptor", GoalAcceptorSystem); - - if (this.root.gameMode.getBuildableZones()) { - add("zone", ZoneSystem); - } - - addBefore("end"); - - for (const key in MODS_ADDITIONAL_SYSTEMS) { - if (!this.systems[key] && key !== "end") { - logger.error("Mod system not attached due to invalid 'before': ", key); - } - } - - logger.log("📦 There are", this.systemUpdateOrder.length, "game systems"); - } - - /** - * Updates all systems - */ - update() { - for (let i = 0; i < this.systemUpdateOrder.length; ++i) { - const system = this.systems[this.systemUpdateOrder[i]]; - system.update(); - } - } - - refreshCaches() { - for (let i = 0; i < this.systemUpdateOrder.length; ++i) { - const system = this.systems[this.systemUpdateOrder[i]]; - system.refreshCaches(); - } - } -} +/* typehints:start */ +import { GameSystem } from "./game_system"; +import { GameRoot } from "./root"; +/* typehints:end */ + +import { createLogger } from "../core/logging"; +import { BeltSystem } from "./systems/belt"; +import { ItemEjectorSystem } from "./systems/item_ejector"; +import { MapResourcesSystem } from "./systems/map_resources"; +import { MinerSystem } from "./systems/miner"; +import { ItemProcessorSystem } from "./systems/item_processor"; +import { UndergroundBeltSystem } from "./systems/underground_belt"; +import { HubSystem } from "./systems/hub"; +import { StaticMapEntitySystem } from "./systems/static_map_entity"; +import { ItemAcceptorSystem } from "./systems/item_acceptor"; +import { StorageSystem } from "./systems/storage"; +import { WiredPinsSystem } from "./systems/wired_pins"; +import { BeltUnderlaysSystem } from "./systems/belt_underlays"; +import { WireSystem } from "./systems/wire"; +import { ConstantSignalSystem } from "./systems/constant_signal"; +import { LogicGateSystem } from "./systems/logic_gate"; +import { LeverSystem } from "./systems/lever"; +import { DisplaySystem } from "./systems/display"; +import { ItemProcessorOverlaysSystem } from "./systems/item_processor_overlays"; +import { BeltReaderSystem } from "./systems/belt_reader"; +import { FilterSystem } from "./systems/filter"; +import { ItemProducerSystem } from "./systems/item_producer"; +import { ConstantProducerSystem } from "./systems/constant_producer"; +import { GoalAcceptorSystem } from "./systems/goal_acceptor"; +import { ZoneSystem } from "./systems/zone"; + +const logger = createLogger("game_system_manager"); + +/** + * @type {Object GameSystem}>>} + */ +export const MODS_ADDITIONAL_SYSTEMS = {}; + +export class GameSystemManager { + /** + * + * @param {GameRoot} root + */ + constructor(root) { + this.root = root; + + this.systems = { + /* typehints:start */ + /** @type {BeltSystem} */ + belt: null, + + /** @type {ItemEjectorSystem} */ + itemEjector: null, + + /** @type {MapResourcesSystem} */ + mapResources: null, + + /** @type {MinerSystem} */ + miner: null, + + /** @type {ItemProcessorSystem} */ + itemProcessor: null, + + /** @type {UndergroundBeltSystem} */ + undergroundBelt: null, + + /** @type {HubSystem} */ + hub: null, + + /** @type {StaticMapEntitySystem} */ + staticMapEntities: null, + + /** @type {ItemAcceptorSystem} */ + itemAcceptor: null, + + /** @type {StorageSystem} */ + storage: null, + + /** @type {WiredPinsSystem} */ + wiredPins: null, + + /** @type {BeltUnderlaysSystem} */ + beltUnderlays: null, + + /** @type {WireSystem} */ + wire: null, + + /** @type {ConstantSignalSystem} */ + constantSignal: null, + + /** @type {LogicGateSystem} */ + logicGate: null, + + /** @type {LeverSystem} */ + lever: null, + + /** @type {DisplaySystem} */ + display: null, + + /** @type {ItemProcessorOverlaysSystem} */ + itemProcessorOverlays: null, + + /** @type {BeltReaderSystem} */ + beltReader: null, + + /** @type {FilterSystem} */ + filter: null, + + /** @type {ItemProducerSystem} */ + itemProducer: null, + + /** @type {ConstantProducerSystem} */ + ConstantProducer: null, + + /** @type {GoalAcceptorSystem} */ + GoalAcceptor: null, + + /** @type {ZoneSystem} */ + zone: null, + + /* typehints:end */ + }; + this.systemUpdateOrder = []; + + this.internalInitSystems(); + } + + /** + * Initializes all systems + */ + internalInitSystems() { + const addBefore = id => { + const systems = MODS_ADDITIONAL_SYSTEMS[id]; + if (systems) { + systems.forEach(({ id, systemClass }) => add(id, systemClass)); + } + }; + + const add = (id, systemClass) => { + addBefore(id); + this.systems[id] = new systemClass(this.root); + this.systemUpdateOrder.push(id); + }; + + // Order is important! + + // IMPORTANT: Item acceptor must be before the belt, because it may not tick after the belt + // has put in the item into the acceptor animation, otherwise its off + add("itemAcceptor", ItemAcceptorSystem); + + add("belt", BeltSystem); + + add("undergroundBelt", UndergroundBeltSystem); + + add("miner", MinerSystem); + + add("storage", StorageSystem); + + add("itemProcessor", ItemProcessorSystem); + + add("filter", FilterSystem); + + add("itemProducer", ItemProducerSystem); + + add("itemEjector", ItemEjectorSystem); + + if (this.root.gameMode.hasResources()) { + add("mapResources", MapResourcesSystem); + } + + add("hub", HubSystem); + + add("staticMapEntities", StaticMapEntitySystem); + + add("wiredPins", WiredPinsSystem); + + add("beltUnderlays", BeltUnderlaysSystem); + + add("constantSignal", ConstantSignalSystem); + + // WIRES section + add("lever", LeverSystem); + + // Wires must be before all gate, signal etc logic! + add("wire", WireSystem); + + // IMPORTANT: We have 2 phases: In phase 1 we compute the output values of all gates, + // processors etc. In phase 2 we propagate it through the wires network + add("logicGate", LogicGateSystem); + + add("beltReader", BeltReaderSystem); + + add("display", DisplaySystem); + + add("itemProcessorOverlays", ItemProcessorOverlaysSystem); + + add("constantProducer", ConstantProducerSystem); + + add("goalAcceptor", GoalAcceptorSystem); + + if (this.root.gameMode.getBuildableZones()) { + add("zone", ZoneSystem); + } + + addBefore("end"); + + for (const key in MODS_ADDITIONAL_SYSTEMS) { + if (!this.systems[key] && key !== "end") { + logger.error("Mod system not attached due to invalid 'before': ", key); + } + } + + logger.log("📦 There are", this.systemUpdateOrder.length, "game systems"); + } + + /** + * Updates all systems + */ + update() { + for (let i = 0; i < this.systemUpdateOrder.length; ++i) { + const system = this.systems[this.systemUpdateOrder[i]]; + system.update(); + } + } + + refreshCaches() { + for (let i = 0; i < this.systemUpdateOrder.length; ++i) { + const system = this.systems[this.systemUpdateOrder[i]]; + system.refreshCaches(); + } + } +} diff --git a/src/js/game/game_system_with_filter.js b/src/js/game/game_system_with_filter.js index a6efeffd..38f6eb50 100644 --- a/src/js/game/game_system_with_filter.js +++ b/src/js/game/game_system_with_filter.js @@ -1,137 +1,137 @@ -/* typehints:start */ -import { Component } from "./component"; -import { Entity } from "./entity"; -/* typehints:end */ - -import { GameRoot } from "./root"; -import { GameSystem } from "./game_system"; -import { arrayDelete, arrayDeleteValue } from "../core/utils"; -import { globalConfig } from "../core/config"; - -export class GameSystemWithFilter extends GameSystem { - /** - * Constructs a new game system with the given component filter. It will process - * all entities which have *all* of the passed components - * @param {GameRoot} root - * @param {Array} requiredComponents - */ - constructor(root, requiredComponents) { - super(root); - this.requiredComponents = requiredComponents; - this.requiredComponentIds = requiredComponents.map(component => component.getId()); - - /** - * All entities which match the current components - * @type {Array} - */ - this.allEntities = []; - - this.root.signals.entityAdded.add(this.internalPushEntityIfMatching, this); - this.root.signals.entityGotNewComponent.add(this.internalReconsiderEntityToAdd, this); - this.root.signals.entityComponentRemoved.add(this.internalCheckEntityAfterComponentRemoval, this); - this.root.signals.entityQueuedForDestroy.add(this.internalPopEntityIfMatching, this); - - this.root.signals.postLoadHook.add(this.internalPostLoadHook, this); - this.root.signals.bulkOperationFinished.add(this.refreshCaches, this); - } - - /** - * @param {Entity} entity - */ - internalPushEntityIfMatching(entity) { - for (let i = 0; i < this.requiredComponentIds.length; ++i) { - if (!entity.components[this.requiredComponentIds[i]]) { - return; - } - } - - // This is slow! - if (G_IS_DEV && !globalConfig.debug.disableSlowAsserts) { - assert(this.allEntities.indexOf(entity) < 0, "entity already in list: " + entity); - } - - this.internalRegisterEntity(entity); - } - - /** - * - * @param {Entity} entity - */ - internalCheckEntityAfterComponentRemoval(entity) { - if (this.allEntities.indexOf(entity) < 0) { - // Entity wasn't interesting anyways - return; - } - - for (let i = 0; i < this.requiredComponentIds.length; ++i) { - if (!entity.components[this.requiredComponentIds[i]]) { - // Entity is not interesting anymore - arrayDeleteValue(this.allEntities, entity); - } - } - } - - /** - * - * @param {Entity} entity - */ - internalReconsiderEntityToAdd(entity) { - for (let i = 0; i < this.requiredComponentIds.length; ++i) { - if (!entity.components[this.requiredComponentIds[i]]) { - return; - } - } - if (this.allEntities.indexOf(entity) >= 0) { - return; - } - this.internalRegisterEntity(entity); - } - - refreshCaches() { - // Remove all entities which are queued for destroy - for (let i = 0; i < this.allEntities.length; ++i) { - const entity = this.allEntities[i]; - if (entity.queuedForDestroy || entity.destroyed) { - this.allEntities.splice(i, 1); - i -= 1; - } - } - - this.allEntities.sort((a, b) => a.uid - b.uid); - } - - /** - * Recomputes all target entities after the game has loaded - */ - internalPostLoadHook() { - this.refreshCaches(); - } - - /** - * - * @param {Entity} entity - */ - internalRegisterEntity(entity) { - this.allEntities.push(entity); - - if (this.root.gameInitialized && !this.root.bulkOperationRunning) { - // Sort entities by uid so behaviour is predictable - this.allEntities.sort((a, b) => a.uid - b.uid); - } - } - - /** - * - * @param {Entity} entity - */ - internalPopEntityIfMatching(entity) { - if (this.root.bulkOperationRunning) { - // We do this in refreshCaches afterwards - return; - } - const index = this.allEntities.indexOf(entity); - if (index >= 0) { - arrayDelete(this.allEntities, index); - } - } -} +/* typehints:start */ +import { Component } from "./component"; +import { Entity } from "./entity"; +/* typehints:end */ + +import { GameRoot } from "./root"; +import { GameSystem } from "./game_system"; +import { arrayDelete, arrayDeleteValue } from "../core/utils"; +import { globalConfig } from "../core/config"; + +export class GameSystemWithFilter extends GameSystem { + /** + * Constructs a new game system with the given component filter. It will process + * all entities which have *all* of the passed components + * @param {GameRoot} root + * @param {Array} requiredComponents + */ + constructor(root, requiredComponents) { + super(root); + this.requiredComponents = requiredComponents; + this.requiredComponentIds = requiredComponents.map(component => component.getId()); + + /** + * All entities which match the current components + * @type {Array} + */ + this.allEntities = []; + + this.root.signals.entityAdded.add(this.internalPushEntityIfMatching, this); + this.root.signals.entityGotNewComponent.add(this.internalReconsiderEntityToAdd, this); + this.root.signals.entityComponentRemoved.add(this.internalCheckEntityAfterComponentRemoval, this); + this.root.signals.entityQueuedForDestroy.add(this.internalPopEntityIfMatching, this); + + this.root.signals.postLoadHook.add(this.internalPostLoadHook, this); + this.root.signals.bulkOperationFinished.add(this.refreshCaches, this); + } + + /** + * @param {Entity} entity + */ + internalPushEntityIfMatching(entity) { + for (let i = 0; i < this.requiredComponentIds.length; ++i) { + if (!entity.components[this.requiredComponentIds[i]]) { + return; + } + } + + // This is slow! + if (G_IS_DEV && !globalConfig.debug.disableSlowAsserts) { + assert(this.allEntities.indexOf(entity) < 0, "entity already in list: " + entity); + } + + this.internalRegisterEntity(entity); + } + + /** + * + * @param {Entity} entity + */ + internalCheckEntityAfterComponentRemoval(entity) { + if (this.allEntities.indexOf(entity) < 0) { + // Entity wasn't interesting anyways + return; + } + + for (let i = 0; i < this.requiredComponentIds.length; ++i) { + if (!entity.components[this.requiredComponentIds[i]]) { + // Entity is not interesting anymore + arrayDeleteValue(this.allEntities, entity); + } + } + } + + /** + * + * @param {Entity} entity + */ + internalReconsiderEntityToAdd(entity) { + for (let i = 0; i < this.requiredComponentIds.length; ++i) { + if (!entity.components[this.requiredComponentIds[i]]) { + return; + } + } + if (this.allEntities.indexOf(entity) >= 0) { + return; + } + this.internalRegisterEntity(entity); + } + + refreshCaches() { + // Remove all entities which are queued for destroy + for (let i = 0; i < this.allEntities.length; ++i) { + const entity = this.allEntities[i]; + if (entity.queuedForDestroy || entity.destroyed) { + this.allEntities.splice(i, 1); + i -= 1; + } + } + + this.allEntities.sort((a, b) => a.uid - b.uid); + } + + /** + * Recomputes all target entities after the game has loaded + */ + internalPostLoadHook() { + this.refreshCaches(); + } + + /** + * + * @param {Entity} entity + */ + internalRegisterEntity(entity) { + this.allEntities.push(entity); + + if (this.root.gameInitialized && !this.root.bulkOperationRunning) { + // Sort entities by uid so behaviour is predictable + this.allEntities.sort((a, b) => a.uid - b.uid); + } + } + + /** + * + * @param {Entity} entity + */ + internalPopEntityIfMatching(entity) { + if (this.root.bulkOperationRunning) { + // We do this in refreshCaches afterwards + return; + } + const index = this.allEntities.indexOf(entity); + if (index >= 0) { + arrayDelete(this.allEntities, index); + } + } +} diff --git a/src/js/game/hints.js b/src/js/game/hints.js index c2e7e4e5..ebf209c1 100644 --- a/src/js/game/hints.js +++ b/src/js/game/hints.js @@ -1,22 +1,22 @@ -import { randomChoice } from "../core/utils"; -import { T } from "../translations"; - -const hintsShown = []; - -/** - * Finds a new hint to show about the game which the user hasn't seen within this session - */ -export function getRandomHint() { - let maxTries = 100 * T.tips.length; - - while (maxTries-- > 0) { - const hint = randomChoice(T.tips); - if (!hintsShown.includes(hint)) { - hintsShown.push(hint); - return hint; - } - } - - // All tips shown so far - return randomChoice(T.tips); -} +import { randomChoice } from "../core/utils"; +import { T } from "../translations"; + +const hintsShown = []; + +/** + * Finds a new hint to show about the game which the user hasn't seen within this session + */ +export function getRandomHint() { + let maxTries = 100 * T.tips.length; + + while (maxTries-- > 0) { + const hint = randomChoice(T.tips); + if (!hintsShown.includes(hint)) { + hintsShown.push(hint); + return hint; + } + } + + // All tips shown so far + return randomChoice(T.tips); +} diff --git a/src/js/game/hub_goals.js b/src/js/game/hub_goals.js index 3ce607fa..1db5b05f 100644 --- a/src/js/game/hub_goals.js +++ b/src/js/game/hub_goals.js @@ -1,576 +1,567 @@ -import { globalConfig } from "../core/config"; -import { RandomNumberGenerator } from "../core/rng"; -import { clamp } from "../core/utils"; -import { BasicSerializableObject, types } from "../savegame/serialization"; -import { enumColors } from "./colors"; -import { enumItemProcessorTypes } from "./components/item_processor"; -import { enumAnalyticsDataSource } from "./production_analytics"; -import { GameRoot } from "./root"; -import { enumSubShape, ShapeDefinition } from "./shape_definition"; -import { enumHubGoalRewards } from "./tutorial_goals"; - -export const MOD_ITEM_PROCESSOR_SPEEDS = {}; - -export class HubGoals extends BasicSerializableObject { - static getId() { - return "HubGoals"; - } - - static getSchema() { - return { - level: types.uint, - storedShapes: types.keyValueMap(types.uint), - upgradeLevels: types.keyValueMap(types.uint), - }; - } - - /** - * - * @param {*} data - * @param {GameRoot} root - */ - deserialize(data, root) { - const errorCode = super.deserialize(data); - if (errorCode) { - return errorCode; - } - - const levels = root.gameMode.getLevelDefinitions(); - - // If freeplay is not available, clamp the level - if (!root.gameMode.getIsFreeplayAvailable()) { - this.level = Math.min(this.level, levels.length); - } - - // Compute gained rewards - for (let i = 0; i < this.level - 1; ++i) { - if (i < levels.length) { - const reward = levels[i].reward; - this.gainedRewards[reward] = (this.gainedRewards[reward] || 0) + 1; - } - } - - // Compute upgrade improvements - const upgrades = this.root.gameMode.getUpgrades(); - for (const upgradeId in upgrades) { - const tiers = upgrades[upgradeId]; - const level = this.upgradeLevels[upgradeId] || 0; - let totalImprovement = 1; - for (let i = 0; i < level; ++i) { - totalImprovement += tiers[i].improvement; - } - this.upgradeImprovements[upgradeId] = totalImprovement; - } - - // Compute current goal - this.computeNextGoal(); - } - - /** - * @param {GameRoot} root - */ - constructor(root) { - super(); - - this.root = root; - - this.level = 1; - - /** - * Which story rewards we already gained - * @type {Object.} - */ - this.gainedRewards = {}; - - /** - * Mapping from shape hash -> amount - * @type {Object} - */ - this.storedShapes = {}; - - /** - * Stores the levels for all upgrades - * @type {Object} - */ - this.upgradeLevels = {}; - - /** - * Stores the improvements for all upgrades - * @type {Object} - */ - this.upgradeImprovements = {}; - - // Reset levels first - const upgrades = this.root.gameMode.getUpgrades(); - for (const key in upgrades) { - this.upgradeLevels[key] = 0; - this.upgradeImprovements[key] = 1; - } - - this.computeNextGoal(); - - // Allow quickly switching goals in dev mode - if (G_IS_DEV) { - window.addEventListener("keydown", ev => { - if (ev.key === "p") { - // root is not guaranteed to exist within ~0.5s after loading in - if (this.root && this.root.app && this.root.app.gameAnalytics) { - if (!this.isEndOfDemoReached()) { - this.onGoalCompleted(); - } - } - } - }); - } - } - - /** - * Returns whether the end of the demo is reached - * @returns {boolean} - */ - isEndOfDemoReached() { - return ( - !this.root.gameMode.getIsFreeplayAvailable() && - this.level >= this.root.gameMode.getLevelDefinitions().length - ); - } - - /** - * Returns how much of the current shape is stored - * @param {ShapeDefinition} definition - * @returns {number} - */ - getShapesStored(definition) { - return this.storedShapes[definition.getHash()] || 0; - } - - /** - * @param {string} key - * @param {number} amount - */ - takeShapeByKey(key, amount) { - assert(this.getShapesStoredByKey(key) >= amount, "Can not afford: " + key + " x " + amount); - assert(amount >= 0, "Amount < 0 for " + key); - assert(Number.isInteger(amount), "Invalid amount: " + amount); - this.storedShapes[key] = (this.storedShapes[key] || 0) - amount; - return; - } - - /** - * Returns how much of the current shape is stored - * @param {string} key - * @returns {number} - */ - getShapesStoredByKey(key) { - return this.storedShapes[key] || 0; - } - - /** - * Returns how much of the current goal was already delivered - */ - getCurrentGoalDelivered() { - if (this.currentGoal.throughputOnly) { - return ( - this.root.productionAnalytics.getCurrentShapeRateRaw( - enumAnalyticsDataSource.delivered, - this.currentGoal.definition - ) / globalConfig.analyticsSliceDurationSeconds - ); - } - - return this.getShapesStored(this.currentGoal.definition); - } - - /** - * Returns the current level of a given upgrade - * @param {string} upgradeId - */ - getUpgradeLevel(upgradeId) { - return this.upgradeLevels[upgradeId] || 0; - } - - /** - * Returns whether the given reward is already unlocked - * @param {enumHubGoalRewards} reward - */ - isRewardUnlocked(reward) { - if (G_IS_DEV && globalConfig.debug.allBuildingsUnlocked) { - return true; - } - if ( - reward === enumHubGoalRewards.reward_blueprints && - this.root.app.restrictionMgr.isLimitedVersion() - ) { - return false; - } - - if (this.root.gameMode.getLevelDefinitions().length < 1) { - // no story, so always unlocked - return true; - } - return !!this.gainedRewards[reward]; - } - - /** - * Handles the given definition, by either accounting it towards the - * goal or otherwise granting some points - * @param {ShapeDefinition} definition - */ - handleDefinitionDelivered(definition) { - const hash = definition.getHash(); - this.storedShapes[hash] = (this.storedShapes[hash] || 0) + 1; - - this.root.signals.shapeDelivered.dispatch(definition); - - // Check if we have enough for the next level - if ( - this.getCurrentGoalDelivered() >= this.currentGoal.required || - (G_IS_DEV && globalConfig.debug.rewardsInstant) - ) { - if (!this.isEndOfDemoReached()) { - this.onGoalCompleted(); - } - } - } - - /** - * Creates the next goal - */ - computeNextGoal() { - const storyIndex = this.level - 1; - const levels = this.root.gameMode.getLevelDefinitions(); - if (storyIndex < levels.length) { - const { shape, required, reward, throughputOnly } = levels[storyIndex]; - this.currentGoal = { - /** @type {ShapeDefinition} */ - definition: this.root.shapeDefinitionMgr.getShapeFromShortKey(shape), - required, - reward, - throughputOnly, - }; - return; - } - - //Floor Required amount to remove confusion - const required = Math.min(200, Math.floor(4 + (this.level - 27) * 0.25)); - this.currentGoal = { - definition: this.computeFreeplayShape(this.level), - required, - reward: enumHubGoalRewards.no_reward_freeplay, - throughputOnly: true, - }; - } - - /** - * Called when the level was completed - */ - onGoalCompleted() { - const reward = this.currentGoal.reward; - this.gainedRewards[reward] = (this.gainedRewards[reward] || 0) + 1; - - this.root.app.gameAnalytics.handleLevelCompleted(this.level); - ++this.level; - this.computeNextGoal(); - - this.root.signals.storyGoalCompleted.dispatch(this.level - 1, reward); - } - - /** - * Returns whether we are playing in free-play - */ - isFreePlay() { - return this.level >= this.root.gameMode.getLevelDefinitions().length; - } - - /** - * Returns whether a given upgrade can be unlocked - * @param {string} upgradeId - */ - canUnlockUpgrade(upgradeId) { - const tiers = this.root.gameMode.getUpgrades()[upgradeId]; - const currentLevel = this.getUpgradeLevel(upgradeId); - - if (currentLevel >= tiers.length) { - // Max level - return false; - } - - if (G_IS_DEV && globalConfig.debug.upgradesNoCost) { - return true; - } - - const tierData = tiers[currentLevel]; - - for (let i = 0; i < tierData.required.length; ++i) { - const requirement = tierData.required[i]; - if ((this.storedShapes[requirement.shape] || 0) < requirement.amount) { - return false; - } - } - return true; - } - - /** - * Returns the number of available upgrades - * @returns {number} - */ - getAvailableUpgradeCount() { - let count = 0; - for (const upgradeId in this.root.gameMode.getUpgrades()) { - if (this.canUnlockUpgrade(upgradeId)) { - ++count; - } - } - return count; - } - - /** - * Tries to unlock the given upgrade - * @param {string} upgradeId - * @returns {boolean} - */ - tryUnlockUpgrade(upgradeId) { - if (!this.canUnlockUpgrade(upgradeId)) { - return false; - } - - const upgradeTiers = this.root.gameMode.getUpgrades()[upgradeId]; - const currentLevel = this.getUpgradeLevel(upgradeId); - - const tierData = upgradeTiers[currentLevel]; - if (!tierData) { - return false; - } - - if (G_IS_DEV && globalConfig.debug.upgradesNoCost) { - // Dont take resources - } else { - for (let i = 0; i < tierData.required.length; ++i) { - const requirement = tierData.required[i]; - - // Notice: Don't have to check for hash here - this.storedShapes[requirement.shape] -= requirement.amount; - } - } - - this.upgradeLevels[upgradeId] = (this.upgradeLevels[upgradeId] || 0) + 1; - this.upgradeImprovements[upgradeId] += tierData.improvement; - - this.root.signals.upgradePurchased.dispatch(upgradeId); - - this.root.app.gameAnalytics.handleUpgradeUnlocked(upgradeId, currentLevel); - - return true; - } - - /** - * Picks random colors which are close to each other - * @param {RandomNumberGenerator} rng - */ - generateRandomColorSet(rng, allowUncolored = false) { - const colorWheel = [ - enumColors.red, - enumColors.yellow, - enumColors.green, - enumColors.cyan, - enumColors.blue, - enumColors.purple, - enumColors.red, - enumColors.yellow, - ]; - - const universalColors = [enumColors.white]; - if (allowUncolored) { - universalColors.push(enumColors.uncolored); - } - const index = rng.nextIntRange(0, colorWheel.length - 2); - const pickedColors = colorWheel.slice(index, index + 3); - pickedColors.push(rng.choice(universalColors)); - return pickedColors; - } - - /** - * Creates a (seeded) random shape - * @param {number} level - * @returns {ShapeDefinition} - */ - computeFreeplayShape(level) { - const layerCount = clamp(this.level / 25, 2, 4); - - /** @type {Array} */ - let layers = []; - - const rng = new RandomNumberGenerator(this.root.map.seed + "/" + level); - - const colors = this.generateRandomColorSet(rng, level > 35); - - let pickedSymmetry = null; // pairs of quadrants that must be the same - let availableShapes = [enumSubShape.rect, enumSubShape.circle, enumSubShape.star]; - if (rng.next() < 0.5) { - pickedSymmetry = [ - // radial symmetry - [0, 2], - [1, 3], - ]; - availableShapes.push(enumSubShape.windmill); // windmill looks good only in radial symmetry - } else { - const symmetries = [ - [ - // horizontal axis - [0, 3], - [1, 2], - ], - [ - // vertical axis - [0, 1], - [2, 3], - ], - [ - // diagonal axis - [0, 2], - [1], - [3], - ], - [ - // other diagonal axis - [1, 3], - [0], - [2], - ], - ]; - pickedSymmetry = rng.choice(symmetries); - } - - const randomColor = () => rng.choice(colors); - const randomShape = () => rng.choice(availableShapes); - - let anyIsMissingTwo = false; - - for (let i = 0; i < layerCount; ++i) { - /** @type {import("./shape_definition").ShapeLayer} */ - const layer = [null, null, null, null]; - - for (let j = 0; j < pickedSymmetry.length; ++j) { - const group = pickedSymmetry[j]; - const shape = randomShape(); - const color = randomColor(); - for (let k = 0; k < group.length; ++k) { - const quad = group[k]; - layer[quad] = { - subShape: shape, - color, - }; - } - } - - // Sometimes they actually are missing *two* ones! - // Make sure at max only one layer is missing it though, otherwise we could - // create an uncreateable shape - if (level > 75 && rng.next() > 0.95 && !anyIsMissingTwo) { - layer[rng.nextIntRange(0, 4)] = null; - anyIsMissingTwo = true; - } - - layers.push(layer); - } - - const definition = new ShapeDefinition({ layers }); - return this.root.shapeDefinitionMgr.registerOrReturnHandle(definition); - } - - ////////////// HELPERS - - /** - * Belt speed - * @returns {number} items / sec - */ - getBeltBaseSpeed() { - if (this.root.gameMode.throughputDoesNotMatter()) { - return globalConfig.beltSpeedItemsPerSecond * globalConfig.puzzleModeSpeed; - } - return globalConfig.beltSpeedItemsPerSecond * this.upgradeImprovements.belt; - } - - /** - * Underground belt speed - * @returns {number} items / sec - */ - getUndergroundBeltBaseSpeed() { - if (this.root.gameMode.throughputDoesNotMatter()) { - return globalConfig.beltSpeedItemsPerSecond * globalConfig.puzzleModeSpeed; - } - return globalConfig.beltSpeedItemsPerSecond * this.upgradeImprovements.belt; - } - - /** - * Miner speed - * @returns {number} items / sec - */ - getMinerBaseSpeed() { - if (this.root.gameMode.throughputDoesNotMatter()) { - return globalConfig.minerSpeedItemsPerSecond * globalConfig.puzzleModeSpeed; - } - return globalConfig.minerSpeedItemsPerSecond * this.upgradeImprovements.miner; - } - - /** - * Processor speed - * @param {enumItemProcessorTypes} processorType - * @returns {number} items / sec - */ - getProcessorBaseSpeed(processorType) { - if (this.root.gameMode.throughputDoesNotMatter()) { - return globalConfig.beltSpeedItemsPerSecond * globalConfig.puzzleModeSpeed * 10; - } - - switch (processorType) { - case enumItemProcessorTypes.trash: - case enumItemProcessorTypes.hub: - case enumItemProcessorTypes.goal: - return 1e30; - case enumItemProcessorTypes.balancer: - return globalConfig.beltSpeedItemsPerSecond * this.upgradeImprovements.belt * 2; - case enumItemProcessorTypes.reader: - return globalConfig.beltSpeedItemsPerSecond * this.upgradeImprovements.belt; - - case enumItemProcessorTypes.mixer: - case enumItemProcessorTypes.painter: - case enumItemProcessorTypes.painterDouble: - case enumItemProcessorTypes.painterQuad: { - assert( - globalConfig.buildingSpeeds[processorType], - "Processor type has no speed set in globalConfig.buildingSpeeds: " + processorType - ); - return ( - globalConfig.beltSpeedItemsPerSecond * - this.upgradeImprovements.painting * - globalConfig.buildingSpeeds[processorType] - ); - } - - case enumItemProcessorTypes.cutter: - case enumItemProcessorTypes.cutterQuad: - case enumItemProcessorTypes.rotater: - case enumItemProcessorTypes.rotaterCCW: - case enumItemProcessorTypes.rotater180: - case enumItemProcessorTypes.stacker: { - assert( - globalConfig.buildingSpeeds[processorType], - "Processor type has no speed set in globalConfig.buildingSpeeds: " + processorType - ); - return ( - globalConfig.beltSpeedItemsPerSecond * - this.upgradeImprovements.processors * - globalConfig.buildingSpeeds[processorType] - ); - } - default: - if (MOD_ITEM_PROCESSOR_SPEEDS[processorType]) { - return MOD_ITEM_PROCESSOR_SPEEDS[processorType](this.root); - } - assertAlways(false, "invalid processor type: " + processorType); - } - - return 1 / globalConfig.beltSpeedItemsPerSecond; - } -} +import { globalConfig } from "../core/config"; +import { RandomNumberGenerator } from "../core/rng"; +import { clamp } from "../core/utils"; +import { BasicSerializableObject, types } from "../savegame/serialization"; +import { enumColors } from "./colors"; +import { enumItemProcessorTypes } from "./components/item_processor"; +import { enumAnalyticsDataSource } from "./production_analytics"; +import { GameRoot } from "./root"; +import { enumSubShape, ShapeDefinition } from "./shape_definition"; +import { enumHubGoalRewards } from "./tutorial_goals"; + +export const MOD_ITEM_PROCESSOR_SPEEDS = {}; + +export class HubGoals extends BasicSerializableObject { + static getId() { + return "HubGoals"; + } + + static getSchema() { + return { + level: types.uint, + storedShapes: types.keyValueMap(types.uint), + upgradeLevels: types.keyValueMap(types.uint), + }; + } + + /** + * + * @param {*} data + * @param {GameRoot} root + */ + deserialize(data, root) { + const errorCode = super.deserialize(data); + if (errorCode) { + return errorCode; + } + + const levels = root.gameMode.getLevelDefinitions(); + + // If freeplay is not available, clamp the level + if (!root.gameMode.getIsFreeplayAvailable()) { + this.level = Math.min(this.level, levels.length); + } + + // Compute gained rewards + for (let i = 0; i < this.level - 1; ++i) { + if (i < levels.length) { + const reward = levels[i].reward; + this.gainedRewards[reward] = (this.gainedRewards[reward] || 0) + 1; + } + } + + // Compute upgrade improvements + const upgrades = this.root.gameMode.getUpgrades(); + for (const upgradeId in upgrades) { + const tiers = upgrades[upgradeId]; + const level = this.upgradeLevels[upgradeId] || 0; + let totalImprovement = 1; + for (let i = 0; i < level; ++i) { + totalImprovement += tiers[i].improvement; + } + this.upgradeImprovements[upgradeId] = totalImprovement; + } + + // Compute current goal + this.computeNextGoal(); + } + + /** + * @param {GameRoot} root + */ + constructor(root) { + super(); + + this.root = root; + + this.level = 1; + + /** + * Which story rewards we already gained + * @type {Object.} + */ + this.gainedRewards = {}; + + /** + * Mapping from shape hash -> amount + * @type {Object} + */ + this.storedShapes = {}; + + /** + * Stores the levels for all upgrades + * @type {Object} + */ + this.upgradeLevels = {}; + + /** + * Stores the improvements for all upgrades + * @type {Object} + */ + this.upgradeImprovements = {}; + + // Reset levels first + const upgrades = this.root.gameMode.getUpgrades(); + for (const key in upgrades) { + this.upgradeLevels[key] = 0; + this.upgradeImprovements[key] = 1; + } + + this.computeNextGoal(); + + // Allow quickly switching goals in dev mode + if (G_IS_DEV) { + window.addEventListener("keydown", ev => { + if (ev.key === "p") { + // root is not guaranteed to exist within ~0.5s after loading in + if (this.root) { + if (!this.isEndOfDemoReached()) { + this.onGoalCompleted(); + } + } + } + }); + } + } + + /** + * Returns whether the end of the demo is reached + * @returns {boolean} + */ + isEndOfDemoReached() { + return ( + !this.root.gameMode.getIsFreeplayAvailable() && + this.level >= this.root.gameMode.getLevelDefinitions().length + ); + } + + /** + * Returns how much of the current shape is stored + * @param {ShapeDefinition} definition + * @returns {number} + */ + getShapesStored(definition) { + return this.storedShapes[definition.getHash()] || 0; + } + + /** + * @param {string} key + * @param {number} amount + */ + takeShapeByKey(key, amount) { + assert(this.getShapesStoredByKey(key) >= amount, "Can not afford: " + key + " x " + amount); + assert(amount >= 0, "Amount < 0 for " + key); + assert(Number.isInteger(amount), "Invalid amount: " + amount); + this.storedShapes[key] = (this.storedShapes[key] || 0) - amount; + return; + } + + /** + * Returns how much of the current shape is stored + * @param {string} key + * @returns {number} + */ + getShapesStoredByKey(key) { + return this.storedShapes[key] || 0; + } + + /** + * Returns how much of the current goal was already delivered + */ + getCurrentGoalDelivered() { + if (this.currentGoal.throughputOnly) { + return ( + this.root.productionAnalytics.getCurrentShapeRateRaw( + enumAnalyticsDataSource.delivered, + this.currentGoal.definition + ) / globalConfig.analyticsSliceDurationSeconds + ); + } + + return this.getShapesStored(this.currentGoal.definition); + } + + /** + * Returns the current level of a given upgrade + * @param {string} upgradeId + */ + getUpgradeLevel(upgradeId) { + return this.upgradeLevels[upgradeId] || 0; + } + + /** + * Returns whether the given reward is already unlocked + * @param {enumHubGoalRewards} reward + */ + isRewardUnlocked(reward) { + if (G_IS_DEV && globalConfig.debug.allBuildingsUnlocked) { + return true; + } + + if (this.root.gameMode.getLevelDefinitions().length < 1) { + // no story, so always unlocked + return true; + } + return !!this.gainedRewards[reward]; + } + + /** + * Handles the given definition, by either accounting it towards the + * goal or otherwise granting some points + * @param {ShapeDefinition} definition + */ + handleDefinitionDelivered(definition) { + const hash = definition.getHash(); + this.storedShapes[hash] = (this.storedShapes[hash] || 0) + 1; + + this.root.signals.shapeDelivered.dispatch(definition); + + // Check if we have enough for the next level + if ( + this.getCurrentGoalDelivered() >= this.currentGoal.required || + (G_IS_DEV && globalConfig.debug.rewardsInstant) + ) { + if (!this.isEndOfDemoReached()) { + this.onGoalCompleted(); + } + } + } + + /** + * Creates the next goal + */ + computeNextGoal() { + const storyIndex = this.level - 1; + const levels = this.root.gameMode.getLevelDefinitions(); + if (storyIndex < levels.length) { + const { shape, required, reward, throughputOnly } = levels[storyIndex]; + this.currentGoal = { + /** @type {ShapeDefinition} */ + definition: this.root.shapeDefinitionMgr.getShapeFromShortKey(shape), + required, + reward, + throughputOnly, + }; + return; + } + + //Floor Required amount to remove confusion + const required = Math.min(200, Math.floor(4 + (this.level - 27) * 0.25)); + this.currentGoal = { + definition: this.computeFreeplayShape(this.level), + required, + reward: enumHubGoalRewards.no_reward_freeplay, + throughputOnly: true, + }; + } + + /** + * Called when the level was completed + */ + onGoalCompleted() { + const reward = this.currentGoal.reward; + this.gainedRewards[reward] = (this.gainedRewards[reward] || 0) + 1; + + ++this.level; + this.computeNextGoal(); + + this.root.signals.storyGoalCompleted.dispatch(this.level - 1, reward); + } + + /** + * Returns whether we are playing in free-play + */ + isFreePlay() { + return this.level >= this.root.gameMode.getLevelDefinitions().length; + } + + /** + * Returns whether a given upgrade can be unlocked + * @param {string} upgradeId + */ + canUnlockUpgrade(upgradeId) { + const tiers = this.root.gameMode.getUpgrades()[upgradeId]; + const currentLevel = this.getUpgradeLevel(upgradeId); + + if (currentLevel >= tiers.length) { + // Max level + return false; + } + + if (G_IS_DEV && globalConfig.debug.upgradesNoCost) { + return true; + } + + const tierData = tiers[currentLevel]; + + for (let i = 0; i < tierData.required.length; ++i) { + const requirement = tierData.required[i]; + if ((this.storedShapes[requirement.shape] || 0) < requirement.amount) { + return false; + } + } + return true; + } + + /** + * Returns the number of available upgrades + * @returns {number} + */ + getAvailableUpgradeCount() { + let count = 0; + for (const upgradeId in this.root.gameMode.getUpgrades()) { + if (this.canUnlockUpgrade(upgradeId)) { + ++count; + } + } + return count; + } + + /** + * Tries to unlock the given upgrade + * @param {string} upgradeId + * @returns {boolean} + */ + tryUnlockUpgrade(upgradeId) { + if (!this.canUnlockUpgrade(upgradeId)) { + return false; + } + + const upgradeTiers = this.root.gameMode.getUpgrades()[upgradeId]; + const currentLevel = this.getUpgradeLevel(upgradeId); + + const tierData = upgradeTiers[currentLevel]; + if (!tierData) { + return false; + } + + if (G_IS_DEV && globalConfig.debug.upgradesNoCost) { + // Dont take resources + } else { + for (let i = 0; i < tierData.required.length; ++i) { + const requirement = tierData.required[i]; + + // Notice: Don't have to check for hash here + this.storedShapes[requirement.shape] -= requirement.amount; + } + } + + this.upgradeLevels[upgradeId] = (this.upgradeLevels[upgradeId] || 0) + 1; + this.upgradeImprovements[upgradeId] += tierData.improvement; + + this.root.signals.upgradePurchased.dispatch(upgradeId); + + return true; + } + + /** + * Picks random colors which are close to each other + * @param {RandomNumberGenerator} rng + */ + generateRandomColorSet(rng, allowUncolored = false) { + const colorWheel = [ + enumColors.red, + enumColors.yellow, + enumColors.green, + enumColors.cyan, + enumColors.blue, + enumColors.purple, + enumColors.red, + enumColors.yellow, + ]; + + const universalColors = [enumColors.white]; + if (allowUncolored) { + universalColors.push(enumColors.uncolored); + } + const index = rng.nextIntRange(0, colorWheel.length - 2); + const pickedColors = colorWheel.slice(index, index + 3); + pickedColors.push(rng.choice(universalColors)); + return pickedColors; + } + + /** + * Creates a (seeded) random shape + * @param {number} level + * @returns {ShapeDefinition} + */ + computeFreeplayShape(level) { + const layerCount = clamp(this.level / 25, 2, 4); + + /** @type {Array} */ + let layers = []; + + const rng = new RandomNumberGenerator(this.root.map.seed + "/" + level); + + const colors = this.generateRandomColorSet(rng, level > 35); + + let pickedSymmetry = null; // pairs of quadrants that must be the same + let availableShapes = [enumSubShape.rect, enumSubShape.circle, enumSubShape.star]; + if (rng.next() < 0.5) { + pickedSymmetry = [ + // radial symmetry + [0, 2], + [1, 3], + ]; + availableShapes.push(enumSubShape.windmill); // windmill looks good only in radial symmetry + } else { + const symmetries = [ + [ + // horizontal axis + [0, 3], + [1, 2], + ], + [ + // vertical axis + [0, 1], + [2, 3], + ], + [ + // diagonal axis + [0, 2], + [1], + [3], + ], + [ + // other diagonal axis + [1, 3], + [0], + [2], + ], + ]; + pickedSymmetry = rng.choice(symmetries); + } + + const randomColor = () => rng.choice(colors); + const randomShape = () => rng.choice(availableShapes); + + let anyIsMissingTwo = false; + + for (let i = 0; i < layerCount; ++i) { + /** @type {import("./shape_definition").ShapeLayer} */ + const layer = [null, null, null, null]; + + for (let j = 0; j < pickedSymmetry.length; ++j) { + const group = pickedSymmetry[j]; + const shape = randomShape(); + const color = randomColor(); + for (let k = 0; k < group.length; ++k) { + const quad = group[k]; + layer[quad] = { + subShape: shape, + color, + }; + } + } + + // Sometimes they actually are missing *two* ones! + // Make sure at max only one layer is missing it though, otherwise we could + // create an uncreateable shape + if (level > 75 && rng.next() > 0.95 && !anyIsMissingTwo) { + layer[rng.nextIntRange(0, 4)] = null; + anyIsMissingTwo = true; + } + + layers.push(layer); + } + + const definition = new ShapeDefinition({ layers }); + return this.root.shapeDefinitionMgr.registerOrReturnHandle(definition); + } + + ////////////// HELPERS + + /** + * Belt speed + * @returns {number} items / sec + */ + getBeltBaseSpeed() { + if (this.root.gameMode.throughputDoesNotMatter()) { + return globalConfig.beltSpeedItemsPerSecond * globalConfig.puzzleModeSpeed; + } + return globalConfig.beltSpeedItemsPerSecond * this.upgradeImprovements.belt; + } + + /** + * Underground belt speed + * @returns {number} items / sec + */ + getUndergroundBeltBaseSpeed() { + if (this.root.gameMode.throughputDoesNotMatter()) { + return globalConfig.beltSpeedItemsPerSecond * globalConfig.puzzleModeSpeed; + } + return globalConfig.beltSpeedItemsPerSecond * this.upgradeImprovements.belt; + } + + /** + * Miner speed + * @returns {number} items / sec + */ + getMinerBaseSpeed() { + if (this.root.gameMode.throughputDoesNotMatter()) { + return globalConfig.minerSpeedItemsPerSecond * globalConfig.puzzleModeSpeed; + } + return globalConfig.minerSpeedItemsPerSecond * this.upgradeImprovements.miner; + } + + /** + * Processor speed + * @param {enumItemProcessorTypes} processorType + * @returns {number} items / sec + */ + getProcessorBaseSpeed(processorType) { + if (this.root.gameMode.throughputDoesNotMatter()) { + return globalConfig.beltSpeedItemsPerSecond * globalConfig.puzzleModeSpeed * 10; + } + + switch (processorType) { + case enumItemProcessorTypes.trash: + case enumItemProcessorTypes.hub: + case enumItemProcessorTypes.goal: + return 1e30; + case enumItemProcessorTypes.balancer: + return globalConfig.beltSpeedItemsPerSecond * this.upgradeImprovements.belt * 2; + case enumItemProcessorTypes.reader: + return globalConfig.beltSpeedItemsPerSecond * this.upgradeImprovements.belt; + + case enumItemProcessorTypes.mixer: + case enumItemProcessorTypes.painter: + case enumItemProcessorTypes.painterDouble: + case enumItemProcessorTypes.painterQuad: { + assert( + globalConfig.buildingSpeeds[processorType], + "Processor type has no speed set in globalConfig.buildingSpeeds: " + processorType + ); + return ( + globalConfig.beltSpeedItemsPerSecond * + this.upgradeImprovements.painting * + globalConfig.buildingSpeeds[processorType] + ); + } + + case enumItemProcessorTypes.cutter: + case enumItemProcessorTypes.cutterQuad: + case enumItemProcessorTypes.rotator: + case enumItemProcessorTypes.rotatorCCW: + case enumItemProcessorTypes.rotator180: + case enumItemProcessorTypes.stacker: { + assert( + globalConfig.buildingSpeeds[processorType], + "Processor type has no speed set in globalConfig.buildingSpeeds: " + processorType + ); + return ( + globalConfig.beltSpeedItemsPerSecond * + this.upgradeImprovements.processors * + globalConfig.buildingSpeeds[processorType] + ); + } + default: + if (MOD_ITEM_PROCESSOR_SPEEDS[processorType]) { + return MOD_ITEM_PROCESSOR_SPEEDS[processorType](this.root); + } + assertAlways(false, "invalid processor type: " + processorType); + } + + return 1 / globalConfig.beltSpeedItemsPerSecond; + } +} diff --git a/src/js/game/hud/base_hud_part.js b/src/js/game/hud/base_hud_part.js index 91b3fd3a..4c9ae059 100644 --- a/src/js/game/hud/base_hud_part.js +++ b/src/js/game/hud/base_hud_part.js @@ -101,7 +101,7 @@ export class BaseHUDPart { /** * Helper method to construct a new click detector * @param {Element} element The element to listen on - * @param {function} handler The handler to call on this object + * @param {import("../../core/signal").SignalReceiver<[]>} handler The handler to call on this object * @param {import("../../core/click_detector").ClickDetectorConstructorArgs=} args Click detector arguments * */ diff --git a/src/js/game/hud/hud.js b/src/js/game/hud/hud.js index 74214f7d..a0986724 100644 --- a/src/js/game/hud/hud.js +++ b/src/js/game/hud/hud.js @@ -1,237 +1,225 @@ -import { globalConfig } from "../../core/config"; -import { DrawParameters } from "../../core/draw_parameters"; -import { Signal } from "../../core/signal"; -import { MOD_SIGNALS } from "../../mods/mod_signals"; -import { KEYMAPPINGS } from "../key_action_mapper"; -import { MetaBuilding } from "../meta_building"; -import { GameRoot } from "../root"; -import { ShapeDefinition } from "../shape_definition"; -import { HUDBetaOverlay } from "./parts/beta_overlay"; -import { HUDBlueprintPlacer } from "./parts/blueprint_placer"; -import { HUDBuildingsToolbar } from "./parts/buildings_toolbar"; -import { HUDBuildingPlacer } from "./parts/building_placer"; -import { HUDColorBlindHelper } from "./parts/color_blind_helper"; -import { HUDChangesDebugger } from "./parts/debug_changes"; -import { HUDDebugInfo } from "./parts/debug_info"; -import { HUDEntityDebugger } from "./parts/entity_debugger"; -import { HUDModalDialogs } from "./parts/modal_dialogs"; -import { enumNotificationType } from "./parts/notifications"; -import { HUDSettingsMenu } from "./parts/settings_menu"; -import { HUDShapeTooltip } from "./parts/shape_tooltip"; -import { HUDVignetteOverlay } from "./parts/vignette_overlay"; -import { TrailerMaker } from "./trailer_maker"; - -export class GameHUD { - /** - * @param {GameRoot} root - */ - constructor(root) { - this.root = root; - } - - /** - * Initializes the hud parts - */ - initialize() { - this.signals = { - buildingSelectedForPlacement: /** @type {TypedSignal<[MetaBuilding|null]>} */ (new Signal()), - selectedPlacementBuildingChanged: /** @type {TypedSignal<[MetaBuilding|null]>} */ (new Signal()), - shapePinRequested: /** @type {TypedSignal<[ShapeDefinition]>} */ (new Signal()), - shapeUnpinRequested: /** @type {TypedSignal<[string]>} */ (new Signal()), - notification: /** @type {TypedSignal<[string, enumNotificationType]>} */ (new Signal()), - buildingsSelectedForCopy: /** @type {TypedSignal<[Array]>} */ (new Signal()), - pasteBlueprintRequested: /** @type {TypedSignal<[]>} */ (new Signal()), - viewShapeDetailsRequested: /** @type {TypedSignal<[ShapeDefinition]>} */ (new Signal()), - unlockNotificationFinished: /** @type {TypedSignal<[]>} */ (new Signal()), - }; - - this.parts = { - buildingsToolbar: new HUDBuildingsToolbar(this.root), - - blueprintPlacer: new HUDBlueprintPlacer(this.root), - buildingPlacer: new HUDBuildingPlacer(this.root), - - shapeTooltip: new HUDShapeTooltip(this.root), - - // Must always exist - settingsMenu: new HUDSettingsMenu(this.root), - debugInfo: new HUDDebugInfo(this.root), - dialogs: new HUDModalDialogs(this.root), - - // Typing hints - /* typehints:start */ - /** @type {HUDChangesDebugger} */ - changesDebugger: null, - /* typehints:end */ - }; - - if (G_IS_DEV) { - this.parts.entityDebugger = new HUDEntityDebugger(this.root); - } - - if (G_IS_DEV && globalConfig.debug.renderChanges) { - this.parts.changesDebugger = new HUDChangesDebugger(this.root); - } - - if (this.root.app.settings.getAllSettings().vignette) { - this.parts.vignetteOverlay = new HUDVignetteOverlay(this.root); - } - - if (this.root.app.settings.getAllSettings().enableColorBlindHelper) { - this.parts.colorBlindHelper = new HUDColorBlindHelper(this.root); - } - - if (!G_IS_RELEASE && !G_IS_DEV) { - this.parts.betaOverlay = new HUDBetaOverlay(this.root); - } - - const additionalParts = this.root.gameMode.additionalHudParts; - for (const [partId, part] of Object.entries(additionalParts)) { - this.parts[partId] = new part(this.root); - } - - MOD_SIGNALS.hudInitializer.dispatch(this.root); - - const frag = document.createDocumentFragment(); - for (const key in this.parts) { - MOD_SIGNALS.hudElementInitialized.dispatch(this.parts[key]); - this.parts[key].createElements(frag); - } - - document.body.appendChild(frag); - - for (const key in this.parts) { - this.parts[key].initialize(); - MOD_SIGNALS.hudElementFinalized.dispatch(this.parts[key]); - } - - this.root.keyMapper.getBinding(KEYMAPPINGS.ingame.toggleHud).add(this.toggleUi, this); - - /* dev:start */ - if (G_IS_DEV && globalConfig.debug.renderForTrailer) { - this.trailerMaker = new TrailerMaker(this.root); - } - /* dev:end*/ - } - - /** - * Attempts to close all overlays - */ - closeAllOverlays() { - for (const key in this.parts) { - this.parts[key].close(); - } - } - - /** - * Returns true if the game logic should be paused - */ - shouldPauseGame() { - for (const key in this.parts) { - if (this.parts[key].shouldPauseGame()) { - return true; - } - } - return false; - } - - /** - * Returns true if the rendering can be paused - */ - shouldPauseRendering() { - for (const key in this.parts) { - if (this.parts[key].shouldPauseRendering()) { - return true; - } - } - return false; - } - - /** - * Returns true if the rendering can be paused - */ - hasBlockingOverlayOpen() { - for (const key in this.parts) { - if (this.parts[key].isBlockingOverlay()) { - return true; - } - } - return false; - } - - /** - * Toggles the ui - */ - toggleUi() { - document.body.classList.toggle("uiHidden"); - } - - /** - * Updates all parts - */ - update() { - if (!this.root.gameInitialized) { - return; - } - - for (const key in this.parts) { - this.parts[key].update(); - } - - /* dev:start */ - if (this.trailerMaker) { - this.trailerMaker.update(); - } - /* dev:end*/ - } - - /** - * Draws all parts - * @param {DrawParameters} parameters - */ - draw(parameters) { - const partsOrder = [ - "massSelector", - "buildingPlacer", - "blueprintPlacer", - "colorBlindHelper", - "changesDebugger", - "minerHighlight", - "shapeTooltip", - "interactiveTutorial", - ]; - - for (let i = 0; i < partsOrder.length; ++i) { - if (this.parts[partsOrder[i]]) { - this.parts[partsOrder[i]].draw(parameters); - } - } - } - - /** - * Draws all part overlays - * @param {DrawParameters} parameters - */ - drawOverlays(parameters) { - const partsOrder = ["waypoints", "watermark", "wireInfo"]; - - for (let i = 0; i < partsOrder.length; ++i) { - if (this.parts[partsOrder[i]]) { - this.parts[partsOrder[i]].drawOverlays(parameters); - } - } - } - - /** - * Cleans up everything - */ - cleanup() { - for (const key in this.parts) { - this.parts[key].cleanup(); - } - - for (const key in this.signals) { - this.signals[key].removeAll(); - } - } -} +import { globalConfig } from "../../core/config"; +import { DrawParameters } from "../../core/draw_parameters"; +import { Signal } from "../../core/signal"; +import { MOD_SIGNALS } from "../../mods/mod_signals"; +import { KEYMAPPINGS } from "../key_action_mapper"; +import { MetaBuilding } from "../meta_building"; +import { GameRoot } from "../root"; +import { ShapeDefinition } from "../shape_definition"; +import { HUDBetaOverlay } from "./parts/beta_overlay"; +import { HUDBlueprintPlacer } from "./parts/blueprint_placer"; +import { HUDBuildingPlacer } from "./parts/building_placer"; +import { HUDBuildingsToolbar } from "./parts/buildings_toolbar"; +import { HUDColorBlindHelper } from "./parts/color_blind_helper"; +import { HUDChangesDebugger } from "./parts/debug_changes"; +import { HUDDebugInfo } from "./parts/debug_info"; +import { HUDEntityDebugger } from "./parts/entity_debugger"; +import { HUDModalDialogs } from "./parts/modal_dialogs"; +import { enumNotificationType } from "./parts/notifications"; +import { HUDSettingsMenu } from "./parts/settings_menu"; +import { HUDShapeTooltip } from "./parts/shape_tooltip"; +import { HUDVignetteOverlay } from "./parts/vignette_overlay"; + +export class GameHUD { + /** + * @param {GameRoot} root + */ + constructor(root) { + this.root = root; + } + + /** + * Initializes the hud parts + */ + initialize() { + this.signals = { + buildingSelectedForPlacement: /** @type {Signal<[MetaBuilding|null]>} */ (new Signal()), + selectedPlacementBuildingChanged: /** @type {Signal<[MetaBuilding|null]>} */ (new Signal()), + shapePinRequested: /** @type {Signal<[ShapeDefinition]>} */ (new Signal()), + shapeUnpinRequested: /** @type {Signal<[string]>} */ (new Signal()), + notification: /** @type {Signal<[string, enumNotificationType]>} */ (new Signal()), + buildingsSelectedForCopy: /** @type {Signal<[Array]>} */ (new Signal()), + pasteBlueprintRequested: /** @type {Signal<[]>} */ (new Signal()), + viewShapeDetailsRequested: /** @type {Signal<[ShapeDefinition]>} */ (new Signal()), + unlockNotificationFinished: /** @type {Signal<[]>} */ (new Signal()), + }; + + /** @type {import("./hud_parts").HudParts} */ + this.parts = { + buildingsToolbar: new HUDBuildingsToolbar(this.root), + + blueprintPlacer: new HUDBlueprintPlacer(this.root), + buildingPlacer: new HUDBuildingPlacer(this.root), + + shapeTooltip: new HUDShapeTooltip(this.root), + + // Must always exist + settingsMenu: new HUDSettingsMenu(this.root), + debugInfo: new HUDDebugInfo(this.root), + dialogs: new HUDModalDialogs(this.root), + + // Typing hints + /* typehints:start */ + /** @type {HUDChangesDebugger} */ + changesDebugger: null, + /* typehints:end */ + }; + + if (G_IS_DEV) { + this.parts.entityDebugger = new HUDEntityDebugger(this.root); + } + + if (G_IS_DEV && globalConfig.debug.renderChanges) { + this.parts.changesDebugger = new HUDChangesDebugger(this.root); + } + + if (this.root.app.settings.getAllSettings().vignette) { + this.parts.vignetteOverlay = new HUDVignetteOverlay(this.root); + } + + if (this.root.app.settings.getAllSettings().enableColorBlindHelper) { + this.parts.colorBlindHelper = new HUDColorBlindHelper(this.root); + } + + if (!G_IS_RELEASE && !G_IS_DEV) { + this.parts.betaOverlay = new HUDBetaOverlay(this.root); + } + + const additionalParts = this.root.gameMode.additionalHudParts; + for (const [partId, part] of Object.entries(additionalParts)) { + this.parts[partId] = new part(this.root); + } + + MOD_SIGNALS.hudInitializer.dispatch(this.root); + + const frag = document.createDocumentFragment(); + for (const key in this.parts) { + MOD_SIGNALS.hudElementInitialized.dispatch(this.parts[key]); + this.parts[key].createElements(frag); + } + + document.body.appendChild(frag); + + for (const key in this.parts) { + this.parts[key].initialize(); + MOD_SIGNALS.hudElementFinalized.dispatch(this.parts[key]); + } + + this.root.keyMapper.getBinding(KEYMAPPINGS.ingame.toggleHud).add(this.toggleUi, this); + } + + /** + * Attempts to close all overlays + */ + closeAllOverlays() { + for (const key in this.parts) { + this.parts[key].close(); + } + } + + /** + * Returns true if the game logic should be paused + */ + shouldPauseGame() { + for (const key in this.parts) { + if (this.parts[key].shouldPauseGame()) { + return true; + } + } + return false; + } + + /** + * Returns true if the rendering can be paused + */ + shouldPauseRendering() { + for (const key in this.parts) { + if (this.parts[key].shouldPauseRendering()) { + return true; + } + } + return false; + } + + /** + * Returns true if the rendering can be paused + */ + hasBlockingOverlayOpen() { + for (const key in this.parts) { + if (this.parts[key].isBlockingOverlay()) { + return true; + } + } + return false; + } + + /** + * Toggles the ui + */ + toggleUi() { + document.body.classList.toggle("uiHidden"); + } + + /** + * Updates all parts + */ + update() { + if (!this.root.gameInitialized) { + return; + } + + for (const key in this.parts) { + this.parts[key].update(); + } + } + + /** + * Draws all parts + * @param {DrawParameters} parameters + */ + draw(parameters) { + const partsOrder = [ + "massSelector", + "buildingPlacer", + "blueprintPlacer", + "colorBlindHelper", + "changesDebugger", + "minerHighlight", + "shapeTooltip", + "interactiveTutorial", + ]; + + for (let i = 0; i < partsOrder.length; ++i) { + if (this.parts[partsOrder[i]]) { + this.parts[partsOrder[i]].draw(parameters); + } + } + } + + /** + * Draws all part overlays + * @param {DrawParameters} parameters + */ + drawOverlays(parameters) { + const partsOrder = ["waypoints", "watermark", "wireInfo"]; + + for (let i = 0; i < partsOrder.length; ++i) { + if (this.parts[partsOrder[i]]) { + this.parts[partsOrder[i]].drawOverlays(parameters); + } + } + } + + /** + * Cleans up everything + */ + cleanup() { + for (const key in this.parts) { + this.parts[key].cleanup(); + } + + for (const key in this.signals) { + this.signals[key].removeAll(); + } + } +} diff --git a/src/js/game/hud/hud_parts.ts b/src/js/game/hud/hud_parts.ts new file mode 100644 index 00000000..0b6c88d6 --- /dev/null +++ b/src/js/game/hud/hud_parts.ts @@ -0,0 +1,103 @@ +import type { HUDBetaOverlay } from "./parts/beta_overlay.js"; +import type { HUDBlueprintPlacer } from "./parts/blueprint_placer.js"; +import type { HUDBuildingsToolbar } from "./parts/buildings_toolbar.js"; +import type { HUDBuildingPlacer } from "./parts/building_placer.js"; +import type { HUDColorBlindHelper } from "./parts/color_blind_helper.js"; +import type { HUDConstantSignalEdit } from "./parts/constant_signal_edit.js"; +import type { HUDChangesDebugger } from "./parts/debug_changes.js"; +import type { HUDDebugInfo } from "./parts/debug_info.js"; +import type { HUDEntityDebugger } from "./parts/entity_debugger.js"; +import type { HUDGameMenu } from "./parts/game_menu.js"; +import type { HUDInteractiveTutorial } from "./parts/interactive_tutorial.js"; +import type { HUDKeybindingOverlay } from "./parts/keybinding_overlay.js"; +import type { HUDLayerPreview } from "./parts/layer_preview.js"; +import type { HUDLeverToggle } from "./parts/lever_toggle.js"; +import type { HUDMassSelector } from "./parts/mass_selector.js"; +import type { HUDMinerHighlight } from "./parts/miner_highlight.js"; +import type { HUDModalDialogs } from "./parts/modal_dialogs.js"; +import type { HUDPuzzleNextPuzzle } from "./parts/next_puzzle.js"; +import type { HUDNotifications } from "./parts/notifications.js"; +import type { HUDPinnedShapes } from "./parts/pinned_shapes.js"; +import type { HUDPuzzleBackToMenu } from "./parts/puzzle_back_to_menu.js"; +import type { HUDPuzzleCompleteNotification } from "./parts/puzzle_complete_notification.js"; +import type { HUDPuzzleDLCLogo } from "./parts/puzzle_dlc_logo.js"; +import type { HUDPuzzleEditorControls } from "./parts/puzzle_editor_controls.js"; +import type { HUDPuzzleEditorReview } from "./parts/puzzle_editor_review.js"; +import type { HUDPuzzleEditorSettings } from "./parts/puzzle_editor_settings.js"; +import type { HUDPuzzlePlayMetadata } from "./parts/puzzle_play_metadata.js"; +import type { HUDPuzzlePlaySettings } from "./parts/puzzle_play_settings.js"; +import type { HUDScreenshotExporter } from "./parts/screenshot_exporter.js"; +import type { HUDSettingsMenu } from "./parts/settings_menu.js"; +import type { HUDShapeTooltip } from "./parts/shape_tooltip.js"; +import type { HUDShapeViewer } from "./parts/shape_viewer.js"; +import type { HUDShop } from "./parts/shop.js"; +import type { HUDStatistics } from "./parts/statistics.js"; +import type { HUDPartTutorialHints } from "./parts/tutorial_hints.js"; +import type { HUDTutorialVideoOffer } from "./parts/tutorial_video_offer.js"; +import type { HUDUnlockNotification } from "./parts/unlock_notification.js"; +import type { HUDVignetteOverlay } from "./parts/vignette_overlay.js"; +import type { HUDWaypoints } from "./parts/waypoints.js"; +import type { HUDWiresOverlay } from "./parts/wires_overlay.js"; +import type { HUDWiresToolbar } from "./parts/wires_toolbar.js"; +import type { HUDWireInfo } from "./parts/wire_info.js"; + +export interface HudParts { + buildingsToolbar: HUDBuildingsToolbar; + + blueprintPlacer: HUDBlueprintPlacer; + buildingPlacer: HUDBuildingPlacer; + + shapeTooltip: HUDShapeTooltip; + + // Must always exist + settingsMenu: HUDSettingsMenu; + debugInfo: HUDDebugInfo; + dialogs: HUDModalDialogs; + + // Dev + entityDebugger?: HUDEntityDebugger; + changesDebugger?: HUDChangesDebugger; + + vignetteOverlay?: HUDVignetteOverlay; + colorBlindHelper?: HUDColorBlindHelper; + betaOverlay?: HUDBetaOverlay; + + // Additional Hud Parts + // Shared + massSelector?: HUDMassSelector; + constantSignalEdit?: HUDConstantSignalEdit; + + // Regular + wiresToolbar?: HUDWiresToolbar; + unlockNotification?: HUDUnlockNotification; + shop?: HUDShop; + statistics?: HUDStatistics; + waypoints?: HUDWaypoints; + wireInfo?: HUDWireInfo; + leverToggle?: HUDLeverToggle; + pinnedShapes?: HUDPinnedShapes; + notifications?: HUDNotifications; + screenshotExporter?: HUDScreenshotExporter; + wiresOverlay?: HUDWiresOverlay; + shapeViewer?: HUDShapeViewer; + layerPreview?: HUDLayerPreview; + minerHighlight?: HUDMinerHighlight; + tutorialVideoOffer?: HUDTutorialVideoOffer; + gameMenu?: HUDGameMenu; + keybindingOverlay?: HUDKeybindingOverlay; + tutorialHints?: HUDPartTutorialHints; + interactiveTutorial?: HUDInteractiveTutorial; + + // Puzzle mode + puzzleBackToMenu?: HUDPuzzleBackToMenu; + puzzleDlcLogo?: HUDPuzzleDLCLogo; + + puzzleEditorControls?: HUDPuzzleEditorControls; + puzzleEditorReview?: HUDPuzzleEditorReview; + puzzleEditorSettings?: HUDPuzzleEditorSettings; + + puzzlePlayMetadata?: HUDPuzzlePlayMetadata; + puzzlePlaySettings?: HUDPuzzlePlaySettings; + puzzleCompleteNotification?: HUDPuzzleCompleteNotification; + puzzleNext?: HUDPuzzleNextPuzzle; +} diff --git a/src/js/game/hud/parts/base_toolbar.js b/src/js/game/hud/parts/base_toolbar.js index bee88574..e0d4259f 100644 --- a/src/js/game/hud/parts/base_toolbar.js +++ b/src/js/game/hud/parts/base_toolbar.js @@ -1,327 +1,331 @@ -import { gMetaBuildingRegistry } from "../../../core/global_registries"; -import { globalWarn } from "../../../core/logging"; -import { STOP_PROPAGATION } from "../../../core/signal"; -import { makeDiv, safeModulo } from "../../../core/utils"; -import { MetaBlockBuilding } from "../../buildings/block"; -import { MetaConstantProducerBuilding } from "../../buildings/constant_producer"; -import { MetaGoalAcceptorBuilding } from "../../buildings/goal_acceptor"; -import { StaticMapEntityComponent } from "../../components/static_map_entity"; -import { KEYMAPPINGS } from "../../key_action_mapper"; -import { MetaBuilding } from "../../meta_building"; -import { GameRoot } from "../../root"; -import { BaseHUDPart } from "../base_hud_part"; -import { DynamicDomAttach } from "../dynamic_dom_attach"; - -export class HUDBaseToolbar extends BaseHUDPart { - /** - * @param {GameRoot} root - * @param {object} param0 - * @param {Array} param0.primaryBuildings - * @param {Array=} param0.secondaryBuildings - * @param {function} param0.visibilityCondition - * @param {string} param0.htmlElementId - * @param {Layer=} param0.layer - */ - constructor( - root, - { primaryBuildings, secondaryBuildings = [], visibilityCondition, htmlElementId, layer = "regular" } - ) { - super(root); - - this.primaryBuildings = this.filterBuildings(primaryBuildings); - this.secondaryBuildings = this.filterBuildings(secondaryBuildings); - this.visibilityCondition = visibilityCondition; - this.htmlElementId = htmlElementId; - this.layer = layer; - - /** @type {Object.} */ - this.buildingHandles = {}; - } - - /** - * Should create all require elements - * @param {HTMLElement} parent - */ - createElements(parent) { - this.element = makeDiv(parent, this.htmlElementId, ["ingame_buildingsToolbar"], ""); - } - - /** - * @param {Array} buildings - * @returns {Array} - */ - filterBuildings(buildings) { - const filtered = []; - - for (let i = 0; i < buildings.length; i++) { - if (this.root.gameMode.isBuildingExcluded(buildings[i])) { - continue; - } - - filtered.push(buildings[i]); - } - - return filtered; - } - - /** - * Returns all buildings - * @returns {Array} - */ - get allBuildings() { - return [...this.primaryBuildings, ...this.secondaryBuildings]; - } - - initialize() { - const actionMapper = this.root.keyMapper; - let rowSecondary; - if (this.secondaryBuildings.length > 0) { - rowSecondary = makeDiv(this.element, null, ["buildings", "secondary"]); - - this.secondaryDomAttach = new DynamicDomAttach(this.root, rowSecondary, { - attachClass: "visible", - }); - } - - const rowPrimary = makeDiv(this.element, null, ["buildings", "primary"]); - - const allBuildings = this.allBuildings; - - for (let i = 0; i < allBuildings.length; ++i) { - const metaBuilding = gMetaBuildingRegistry.findByClass(allBuildings[i]); - - let rawBinding = KEYMAPPINGS.buildings[metaBuilding.getId() + "_" + this.layer]; - if (!rawBinding) { - rawBinding = KEYMAPPINGS.buildings[metaBuilding.getId()]; - } - - if (rawBinding) { - const binding = actionMapper.getBinding(rawBinding); - binding.add(() => this.selectBuildingForPlacement(metaBuilding)); - } else { - globalWarn("Building has no keybinding:", metaBuilding.getId()); - } - - const itemContainer = makeDiv( - this.primaryBuildings.includes(allBuildings[i]) ? rowPrimary : rowSecondary, - null, - ["building"] - ); - itemContainer.setAttribute("data-icon", "building_icons/" + metaBuilding.getId() + ".png"); - itemContainer.setAttribute("data-id", metaBuilding.getId()); - - const icon = makeDiv(itemContainer, null, ["icon"]); - - this.trackClicks(icon, () => this.selectBuildingForPlacement(metaBuilding), { - clickSound: null, - }); - - //lock icon for puzzle editor - if (this.root.gameMode.getIsEditor() && !this.inRequiredBuildings(metaBuilding)) { - const puzzleLock = makeDiv(itemContainer, null, ["puzzle-lock"]); - - itemContainer.classList.toggle("editor", true); - this.trackClicks(puzzleLock, () => this.toggleBuildingLock(metaBuilding), { - clickSound: null, - }); - } - - this.buildingHandles[metaBuilding.id] = { - metaBuilding: metaBuilding, - element: itemContainer, - unlocked: false, - selected: false, - index: i, - puzzleLocked: false, - }; - } - - this.root.hud.signals.selectedPlacementBuildingChanged.add( - this.onSelectedPlacementBuildingChanged, - this - ); - - this.domAttach = new DynamicDomAttach(this.root, this.element, { - timeToKeepSeconds: 0.12, - attachClass: "visible", - }); - this.lastSelectedIndex = 0; - actionMapper.getBinding(KEYMAPPINGS.placement.cycleBuildings).add(this.cycleBuildings, this); - } - - /** - * Updates the toolbar - */ - update() { - const visible = this.visibilityCondition(); - this.domAttach.update(visible); - - if (visible) { - let recomputeSecondaryToolbarVisibility = false; - for (const buildingId in this.buildingHandles) { - const handle = this.buildingHandles[buildingId]; - const newStatus = !handle.puzzleLocked && handle.metaBuilding.getIsUnlocked(this.root); - if (handle.unlocked !== newStatus) { - handle.unlocked = newStatus; - handle.element.classList.toggle("unlocked", newStatus); - recomputeSecondaryToolbarVisibility = true; - } - } - - if (recomputeSecondaryToolbarVisibility && this.secondaryDomAttach) { - let anyUnlocked = false; - for (let i = 0; i < this.secondaryBuildings.length; ++i) { - const metaClass = gMetaBuildingRegistry.findByClass(this.secondaryBuildings[i]); - if (metaClass.getIsUnlocked(this.root)) { - anyUnlocked = true; - break; - } - } - - this.secondaryDomAttach.update(anyUnlocked); - } - } - } - - /** - * Cycles through all buildings - */ - cycleBuildings() { - const visible = this.visibilityCondition(); - if (!visible) { - return; - } - - let newBuildingFound = false; - let newIndex = this.lastSelectedIndex; - const direction = this.root.keyMapper.getBinding(KEYMAPPINGS.placement.rotateInverseModifier).pressed - ? -1 - : 1; - - for (let i = 0; i <= this.primaryBuildings.length; ++i) { - newIndex = safeModulo(newIndex + direction, this.primaryBuildings.length); - const metaBuilding = gMetaBuildingRegistry.findByClass(this.primaryBuildings[newIndex]); - const handle = this.buildingHandles[metaBuilding.id]; - if (!handle.selected && handle.unlocked) { - newBuildingFound = true; - break; - } - } - if (!newBuildingFound) { - return; - } - const metaBuildingClass = this.primaryBuildings[newIndex]; - const metaBuilding = gMetaBuildingRegistry.findByClass(metaBuildingClass); - this.selectBuildingForPlacement(metaBuilding); - } - - /** - * Called when the selected building got changed - * @param {MetaBuilding} metaBuilding - */ - onSelectedPlacementBuildingChanged(metaBuilding) { - for (const buildingId in this.buildingHandles) { - const handle = this.buildingHandles[buildingId]; - const newStatus = handle.metaBuilding === metaBuilding; - if (handle.selected !== newStatus) { - handle.selected = newStatus; - handle.element.classList.toggle("selected", newStatus); - } - if (handle.selected) { - this.lastSelectedIndex = handle.index; - } - } - - this.element.classList.toggle("buildingSelected", !!metaBuilding); - } - - /** - * @param {MetaBuilding} metaBuilding - */ - selectBuildingForPlacement(metaBuilding) { - if (!this.visibilityCondition()) { - // Not active - return; - } - - if (!metaBuilding.getIsUnlocked(this.root)) { - this.root.soundProxy.playUiError(); - return STOP_PROPAGATION; - } - - const handle = this.buildingHandles[metaBuilding.getId()]; - if (handle.puzzleLocked) { - handle.puzzleLocked = false; - handle.element.classList.toggle("unlocked", false); - this.root.soundProxy.playUiClick(); - return; - } - - // Allow clicking an item again to deselect it - for (const buildingId in this.buildingHandles) { - const handle = this.buildingHandles[buildingId]; - if (handle.selected && handle.metaBuilding === metaBuilding) { - metaBuilding = null; - break; - } - } - - this.root.soundProxy.playUiClick(); - this.root.hud.signals.buildingSelectedForPlacement.dispatch(metaBuilding); - this.onSelectedPlacementBuildingChanged(metaBuilding); - } - - /** - * @param {MetaBuilding} metaBuilding - */ - toggleBuildingLock(metaBuilding) { - if (!this.visibilityCondition()) { - // Not active - return; - } - - if (this.inRequiredBuildings(metaBuilding) || !metaBuilding.getIsUnlocked(this.root)) { - this.root.soundProxy.playUiError(); - return STOP_PROPAGATION; - } - - const handle = this.buildingHandles[metaBuilding.getId()]; - handle.puzzleLocked = !handle.puzzleLocked; - handle.element.classList.toggle("unlocked", !handle.puzzleLocked); - this.root.soundProxy.playUiClick(); - - const entityManager = this.root.entityMgr; - for (const entity of entityManager.getAllWithComponent(StaticMapEntityComponent)) { - const staticComp = entity.components.StaticMapEntity; - if (staticComp.getMetaBuilding().id === metaBuilding.id) { - this.root.map.removeStaticEntity(entity); - entityManager.destroyEntity(entity); - } - } - entityManager.processDestroyList(); - - const currentMetaBuilding = this.root.hud.parts.buildingPlacer.currentMetaBuilding; - if (currentMetaBuilding.get() == metaBuilding) { - currentMetaBuilding.set(null); - } - } - - /** - * @param {MetaBuilding} metaBuilding - */ - inRequiredBuildings(metaBuilding) { - const requiredBuildings = [ - gMetaBuildingRegistry.findByClass(MetaConstantProducerBuilding), - gMetaBuildingRegistry.findByClass(MetaGoalAcceptorBuilding), - gMetaBuildingRegistry.findByClass(MetaBlockBuilding), - ]; - return requiredBuildings.includes(metaBuilding); - } -} +import { gMetaBuildingRegistry } from "../../../core/global_registries"; +import { Logger } from "../../../core/logging"; +import { STOP_PROPAGATION } from "../../../core/signal"; +import { makeDiv, safeModulo } from "../../../core/utils"; +import { MetaBlockBuilding } from "../../buildings/block"; +import { MetaConstantProducerBuilding } from "../../buildings/constant_producer"; +import { MetaGoalAcceptorBuilding } from "../../buildings/goal_acceptor"; +import { StaticMapEntityComponent } from "../../components/static_map_entity"; +import { KEYMAPPINGS } from "../../key_action_mapper"; +import { MetaBuilding } from "../../meta_building"; +import { GameRoot } from "../../root"; +import { BaseHUDPart } from "../base_hud_part"; +import { DynamicDomAttach } from "../dynamic_dom_attach"; + +const logger = new Logger("hud/base_toolbar"); + +export class HUDBaseToolbar extends BaseHUDPart { + /** + * @param {GameRoot} root + * @param {object} param0 + * @param {Array} param0.primaryBuildings + * @param {Array=} param0.secondaryBuildings + * @param {function} param0.visibilityCondition + * @param {string} param0.htmlElementId + * @param {Layer=} param0.layer + */ + constructor( + root, + { primaryBuildings, secondaryBuildings = [], visibilityCondition, htmlElementId, layer = "regular" } + ) { + super(root); + + this.primaryBuildings = this.filterBuildings(primaryBuildings); + this.secondaryBuildings = this.filterBuildings(secondaryBuildings); + this.visibilityCondition = visibilityCondition; + this.htmlElementId = htmlElementId; + this.layer = layer; + + /** @type {Object.} */ + this.buildingHandles = {}; + } + + /** + * Should create all require elements + * @param {HTMLElement} parent + */ + createElements(parent) { + this.element = makeDiv(parent, this.htmlElementId, ["ingame_buildingsToolbar"], ""); + } + + /** + * @param {Array} buildings + * @returns {Array} + */ + filterBuildings(buildings) { + const filtered = []; + + for (let i = 0; i < buildings.length; i++) { + if (this.root.gameMode.isBuildingExcluded(buildings[i])) { + continue; + } + + filtered.push(buildings[i]); + } + + return filtered; + } + + /** + * Returns all buildings + * @returns {Array} + */ + get allBuildings() { + return [...this.primaryBuildings, ...this.secondaryBuildings]; + } + + initialize() { + const actionMapper = this.root.keyMapper; + let rowSecondary; + if (this.secondaryBuildings.length > 0) { + rowSecondary = makeDiv(this.element, null, ["buildings", "secondary"]); + + this.secondaryDomAttach = new DynamicDomAttach(this.root, rowSecondary, { + attachClass: "visible", + }); + } + + const rowPrimary = makeDiv(this.element, null, ["buildings", "primary"]); + + const allBuildings = this.allBuildings; + + for (let i = 0; i < allBuildings.length; ++i) { + const metaBuilding = gMetaBuildingRegistry.findByClass(allBuildings[i]); + + let rawBinding = KEYMAPPINGS.buildings[metaBuilding.getId() + "_" + this.layer]; + if (!rawBinding) { + rawBinding = KEYMAPPINGS.buildings[metaBuilding.getId()]; + } + + if (rawBinding) { + const binding = actionMapper.getBinding(rawBinding); + binding.add(() => this.selectBuildingForPlacement(metaBuilding)); + } else { + // FIXME: This check shouldn't be here. Once registries rework is done, + // check for keybindings while finalizing the buildings registry + logger.warn("Building has no keybinding:", metaBuilding.getId()); + } + + const itemContainer = makeDiv( + this.primaryBuildings.includes(allBuildings[i]) ? rowPrimary : rowSecondary, + null, + ["building"] + ); + itemContainer.setAttribute("data-icon", "building_icons/" + metaBuilding.getId() + ".png"); + itemContainer.setAttribute("data-id", metaBuilding.getId()); + + const icon = makeDiv(itemContainer, null, ["icon"]); + + this.trackClicks(icon, () => this.selectBuildingForPlacement(metaBuilding), { + clickSound: null, + }); + + //lock icon for puzzle editor + if (this.root.gameMode.getIsEditor() && !this.inRequiredBuildings(metaBuilding)) { + const puzzleLock = makeDiv(itemContainer, null, ["puzzle-lock"]); + + itemContainer.classList.toggle("editor", true); + this.trackClicks(puzzleLock, () => this.toggleBuildingLock(metaBuilding), { + clickSound: null, + }); + } + + this.buildingHandles[metaBuilding.id] = { + metaBuilding: metaBuilding, + element: itemContainer, + unlocked: false, + selected: false, + index: i, + puzzleLocked: false, + }; + } + + this.root.hud.signals.selectedPlacementBuildingChanged.add( + this.onSelectedPlacementBuildingChanged, + this + ); + + this.domAttach = new DynamicDomAttach(this.root, this.element, { + timeToKeepSeconds: 0.12, + attachClass: "visible", + }); + this.lastSelectedIndex = 0; + actionMapper.getBinding(KEYMAPPINGS.placement.cycleBuildings).add(this.cycleBuildings, this); + } + + /** + * Updates the toolbar + */ + update() { + const visible = this.visibilityCondition(); + this.domAttach.update(visible); + + if (visible) { + let recomputeSecondaryToolbarVisibility = false; + for (const buildingId in this.buildingHandles) { + const handle = this.buildingHandles[buildingId]; + const newStatus = !handle.puzzleLocked && handle.metaBuilding.getIsUnlocked(this.root); + if (handle.unlocked !== newStatus) { + handle.unlocked = newStatus; + handle.element.classList.toggle("unlocked", newStatus); + recomputeSecondaryToolbarVisibility = true; + } + } + + if (recomputeSecondaryToolbarVisibility && this.secondaryDomAttach) { + let anyUnlocked = false; + for (let i = 0; i < this.secondaryBuildings.length; ++i) { + const metaClass = gMetaBuildingRegistry.findByClass(this.secondaryBuildings[i]); + if (metaClass.getIsUnlocked(this.root)) { + anyUnlocked = true; + break; + } + } + + this.secondaryDomAttach.update(anyUnlocked); + } + } + } + + /** + * Cycles through all buildings + */ + cycleBuildings() { + const visible = this.visibilityCondition(); + if (!visible) { + return; + } + + let newBuildingFound = false; + let newIndex = this.lastSelectedIndex; + const direction = this.root.keyMapper.getBinding(KEYMAPPINGS.placement.rotateInverseModifier).pressed + ? -1 + : 1; + + for (let i = 0; i <= this.primaryBuildings.length; ++i) { + newIndex = safeModulo(newIndex + direction, this.primaryBuildings.length); + const metaBuilding = gMetaBuildingRegistry.findByClass(this.primaryBuildings[newIndex]); + const handle = this.buildingHandles[metaBuilding.id]; + if (!handle.selected && handle.unlocked) { + newBuildingFound = true; + break; + } + } + if (!newBuildingFound) { + return; + } + const metaBuildingClass = this.primaryBuildings[newIndex]; + const metaBuilding = gMetaBuildingRegistry.findByClass(metaBuildingClass); + this.selectBuildingForPlacement(metaBuilding); + } + + /** + * Called when the selected building got changed + * @param {MetaBuilding} metaBuilding + */ + onSelectedPlacementBuildingChanged(metaBuilding) { + for (const buildingId in this.buildingHandles) { + const handle = this.buildingHandles[buildingId]; + const newStatus = handle.metaBuilding === metaBuilding; + if (handle.selected !== newStatus) { + handle.selected = newStatus; + handle.element.classList.toggle("selected", newStatus); + } + if (handle.selected) { + this.lastSelectedIndex = handle.index; + } + } + + this.element.classList.toggle("buildingSelected", !!metaBuilding); + } + + /** + * @param {MetaBuilding} metaBuilding + */ + selectBuildingForPlacement(metaBuilding) { + if (!this.visibilityCondition()) { + // Not active + return; + } + + if (!metaBuilding.getIsUnlocked(this.root)) { + this.root.soundProxy.playUiError(); + return STOP_PROPAGATION; + } + + const handle = this.buildingHandles[metaBuilding.getId()]; + if (handle.puzzleLocked) { + handle.puzzleLocked = false; + handle.element.classList.toggle("unlocked", false); + this.root.soundProxy.playUiClick(); + return; + } + + // Allow clicking an item again to deselect it + for (const buildingId in this.buildingHandles) { + const handle = this.buildingHandles[buildingId]; + if (handle.selected && handle.metaBuilding === metaBuilding) { + metaBuilding = null; + break; + } + } + + this.root.soundProxy.playUiClick(); + this.root.hud.signals.buildingSelectedForPlacement.dispatch(metaBuilding); + this.onSelectedPlacementBuildingChanged(metaBuilding); + } + + /** + * @param {MetaBuilding} metaBuilding + */ + toggleBuildingLock(metaBuilding) { + if (!this.visibilityCondition()) { + // Not active + return; + } + + if (this.inRequiredBuildings(metaBuilding) || !metaBuilding.getIsUnlocked(this.root)) { + this.root.soundProxy.playUiError(); + return STOP_PROPAGATION; + } + + const handle = this.buildingHandles[metaBuilding.getId()]; + handle.puzzleLocked = !handle.puzzleLocked; + handle.element.classList.toggle("unlocked", !handle.puzzleLocked); + this.root.soundProxy.playUiClick(); + + const entityManager = this.root.entityMgr; + for (const entity of entityManager.getAllWithComponent(StaticMapEntityComponent)) { + const staticComp = entity.components.StaticMapEntity; + if (staticComp.getMetaBuilding().id === metaBuilding.id) { + this.root.map.removeStaticEntity(entity); + entityManager.destroyEntity(entity); + } + } + entityManager.processDestroyList(); + + const currentMetaBuilding = this.root.hud.parts.buildingPlacer.currentMetaBuilding; + if (currentMetaBuilding.get() == metaBuilding) { + currentMetaBuilding.set(null); + } + } + + /** + * @param {MetaBuilding} metaBuilding + */ + inRequiredBuildings(metaBuilding) { + const requiredBuildings = [ + gMetaBuildingRegistry.findByClass(MetaConstantProducerBuilding), + gMetaBuildingRegistry.findByClass(MetaGoalAcceptorBuilding), + gMetaBuildingRegistry.findByClass(MetaBlockBuilding), + ]; + return requiredBuildings.includes(metaBuilding); + } +} diff --git a/src/js/game/hud/parts/blueprint_placer.js b/src/js/game/hud/parts/blueprint_placer.js index 4b2bafb2..617dab33 100644 --- a/src/js/game/hud/parts/blueprint_placer.js +++ b/src/js/game/hud/parts/blueprint_placer.js @@ -1,212 +1,212 @@ -import { DrawParameters } from "../../../core/draw_parameters"; -import { STOP_PROPAGATION } from "../../../core/signal"; -import { TrackedState } from "../../../core/tracked_state"; -import { makeDiv } from "../../../core/utils"; -import { Vector } from "../../../core/vector"; -import { SOUNDS } from "../../../platform/sound"; -import { T } from "../../../translations"; -import { Blueprint } from "../../blueprint"; -import { enumMouseButton } from "../../camera"; -import { KEYMAPPINGS } from "../../key_action_mapper"; -import { BaseHUDPart } from "../base_hud_part"; -import { DynamicDomAttach } from "../dynamic_dom_attach"; - -export class HUDBlueprintPlacer extends BaseHUDPart { - createElements(parent) { - const blueprintCostShape = this.root.shapeDefinitionMgr.getShapeFromShortKey( - this.root.gameMode.getBlueprintShapeKey() - ); - const blueprintCostShapeCanvas = blueprintCostShape.generateAsCanvas(80); - - this.costDisplayParent = makeDiv(parent, "ingame_HUD_BlueprintPlacer", [], ``); - - makeDiv(this.costDisplayParent, null, ["label"], T.ingame.blueprintPlacer.cost); - const costContainer = makeDiv(this.costDisplayParent, null, ["costContainer"], ""); - this.costDisplayText = makeDiv(costContainer, null, ["costText"], ""); - costContainer.appendChild(blueprintCostShapeCanvas); - } - - initialize() { - this.root.hud.signals.buildingsSelectedForCopy.add(this.createBlueprintFromBuildings, this); - - /** @type {TypedTrackedState} */ - this.currentBlueprint = new TrackedState(this.onBlueprintChanged, this); - /** @type {Blueprint?} */ - this.lastBlueprintUsed = null; - - const keyActionMapper = this.root.keyMapper; - keyActionMapper.getBinding(KEYMAPPINGS.general.back).add(this.abortPlacement, this); - keyActionMapper.getBinding(KEYMAPPINGS.placement.pipette).add(this.abortPlacement, this); - keyActionMapper.getBinding(KEYMAPPINGS.placement.rotateWhilePlacing).add(this.rotateBlueprint, this); - keyActionMapper.getBinding(KEYMAPPINGS.massSelect.pasteLastBlueprint).add(this.pasteBlueprint, this); - - this.root.camera.downPreHandler.add(this.onMouseDown, this); - this.root.camera.movePreHandler.add(this.onMouseMove, this); - - this.root.hud.signals.selectedPlacementBuildingChanged.add(this.abortPlacement, this); - this.root.signals.editModeChanged.add(this.onEditModeChanged, this); - - this.domAttach = new DynamicDomAttach(this.root, this.costDisplayParent); - this.trackedCanAfford = new TrackedState(this.onCanAffordChanged, this); - } - - getHasFreeCopyPaste() { - return this.root.gameMode.getHasFreeCopyPaste(); - } - - abortPlacement() { - if (this.currentBlueprint.get()) { - this.currentBlueprint.set(null); - - return STOP_PROPAGATION; - } - } - - /** - * Called when the layer was changed - * @param {Layer} layer - */ - onEditModeChanged(layer) { - // Check if the layer of the blueprint differs and thus we have to deselect it - const blueprint = this.currentBlueprint.get(); - if (blueprint) { - if (blueprint.layer !== layer) { - this.currentBlueprint.set(null); - } - } - } - - /** - * Called when the blueprint is now affordable or not - * @param {boolean} canAfford - */ - onCanAffordChanged(canAfford) { - this.costDisplayParent.classList.toggle("canAfford", canAfford); - } - - update() { - const currentBlueprint = this.currentBlueprint.get(); - this.domAttach.update( - !this.getHasFreeCopyPaste() && currentBlueprint && currentBlueprint.getCost() > 0 - ); - this.trackedCanAfford.set(currentBlueprint && currentBlueprint.canAfford(this.root)); - } - - /** - * Called when the blueprint was changed - * @param {Blueprint} blueprint - */ - onBlueprintChanged(blueprint) { - if (blueprint) { - this.lastBlueprintUsed = blueprint; - this.costDisplayText.innerText = "" + blueprint.getCost(); - } - } - - /** - * mouse down pre handler - * @param {Vector} pos - * @param {enumMouseButton} button - */ - onMouseDown(pos, button) { - if (button === enumMouseButton.right) { - if (this.currentBlueprint.get()) { - this.abortPlacement(); - return STOP_PROPAGATION; - } - } else if (button === enumMouseButton.left) { - const blueprint = this.currentBlueprint.get(); - if (!blueprint) { - return; - } - - if (!this.getHasFreeCopyPaste() && !blueprint.canAfford(this.root)) { - this.root.soundProxy.playUiError(); - return; - } - - const worldPos = this.root.camera.screenToWorld(pos); - const tile = worldPos.toTileSpace(); - if (blueprint.tryPlace(this.root, tile)) { - if (!this.getHasFreeCopyPaste()) { - const cost = blueprint.getCost(); - this.root.hubGoals.takeShapeByKey(this.root.gameMode.getBlueprintShapeKey(), cost); - } - this.root.soundProxy.playUi(SOUNDS.placeBuilding); - } - return STOP_PROPAGATION; - } - } - - /** - * Mouse move handler - */ - onMouseMove() { - // Prevent movement while blueprint is selected - if (this.currentBlueprint.get()) { - return STOP_PROPAGATION; - } - } - - /** - * Called when an array of bulidings was selected - * @param {Array} uids - */ - createBlueprintFromBuildings(uids) { - if (uids.length === 0) { - return; - } - this.currentBlueprint.set(Blueprint.fromUids(this.root, uids)); - } - - /** - * Attempts to rotate the current blueprint - */ - rotateBlueprint() { - if (this.currentBlueprint.get()) { - if (this.root.keyMapper.getBinding(KEYMAPPINGS.placement.rotateInverseModifier).pressed) { - this.currentBlueprint.get().rotateCcw(); - } else { - this.currentBlueprint.get().rotateCw(); - } - } - } - - /** - * Attempts to paste the last blueprint - */ - pasteBlueprint() { - if (this.lastBlueprintUsed !== null) { - if (this.lastBlueprintUsed.layer !== this.root.currentLayer) { - // Not compatible - this.root.soundProxy.playUiError(); - return; - } - - this.root.hud.signals.pasteBlueprintRequested.dispatch(); - this.currentBlueprint.set(this.lastBlueprintUsed); - } else { - this.root.soundProxy.playUiError(); - } - } - - /** - * - * @param {DrawParameters} parameters - */ - draw(parameters) { - const blueprint = this.currentBlueprint.get(); - if (!blueprint) { - return; - } - const mousePosition = this.root.app.mousePosition; - if (!mousePosition) { - // Not on screen - return; - } - - const worldPos = this.root.camera.screenToWorld(mousePosition); - const tile = worldPos.toTileSpace(); - blueprint.draw(parameters, tile); - } -} +import { DrawParameters } from "../../../core/draw_parameters"; +import { STOP_PROPAGATION } from "../../../core/signal"; +import { TrackedState } from "../../../core/tracked_state"; +import { makeDiv } from "../../../core/utils"; +import { Vector } from "../../../core/vector"; +import { SOUNDS } from "../../../platform/sound"; +import { T } from "../../../translations"; +import { Blueprint } from "../../blueprint"; +import { enumMouseButton } from "../../camera"; +import { KEYMAPPINGS } from "../../key_action_mapper"; +import { BaseHUDPart } from "../base_hud_part"; +import { DynamicDomAttach } from "../dynamic_dom_attach"; + +export class HUDBlueprintPlacer extends BaseHUDPart { + createElements(parent) { + const blueprintCostShape = this.root.shapeDefinitionMgr.getShapeFromShortKey( + this.root.gameMode.getBlueprintShapeKey() + ); + const blueprintCostShapeCanvas = blueprintCostShape.generateAsCanvas(80); + + this.costDisplayParent = makeDiv(parent, "ingame_HUD_BlueprintPlacer", [], ``); + + makeDiv(this.costDisplayParent, null, ["label"], T.ingame.blueprintPlacer.cost); + const costContainer = makeDiv(this.costDisplayParent, null, ["costContainer"], ""); + this.costDisplayText = makeDiv(costContainer, null, ["costText"], ""); + costContainer.appendChild(blueprintCostShapeCanvas); + } + + initialize() { + this.root.hud.signals.buildingsSelectedForCopy.add(this.createBlueprintFromBuildings, this); + + /** @type {TypedTrackedState} */ + this.currentBlueprint = new TrackedState(this.onBlueprintChanged, this); + /** @type {Blueprint?} */ + this.lastBlueprintUsed = null; + + const keyActionMapper = this.root.keyMapper; + keyActionMapper.getBinding(KEYMAPPINGS.general.back).add(this.abortPlacement, this); + keyActionMapper.getBinding(KEYMAPPINGS.placement.pipette).add(this.abortPlacement, this); + keyActionMapper.getBinding(KEYMAPPINGS.placement.rotateWhilePlacing).add(this.rotateBlueprint, this); + keyActionMapper.getBinding(KEYMAPPINGS.massSelect.pasteLastBlueprint).add(this.pasteBlueprint, this); + + this.root.camera.downPreHandler.add(this.onMouseDown, this); + this.root.camera.movePreHandler.add(this.onMouseMove, this); + + this.root.hud.signals.selectedPlacementBuildingChanged.add(this.abortPlacement, this); + this.root.signals.editModeChanged.add(this.onEditModeChanged, this); + + this.domAttach = new DynamicDomAttach(this.root, this.costDisplayParent); + this.trackedCanAfford = new TrackedState(this.onCanAffordChanged, this); + } + + getHasFreeCopyPaste() { + return this.root.gameMode.getHasFreeCopyPaste(); + } + + abortPlacement() { + if (this.currentBlueprint.get()) { + this.currentBlueprint.set(null); + + return STOP_PROPAGATION; + } + } + + /** + * Called when the layer was changed + * @param {Layer} layer + */ + onEditModeChanged(layer) { + // Check if the layer of the blueprint differs and thus we have to deselect it + const blueprint = this.currentBlueprint.get(); + if (blueprint) { + if (blueprint.layer !== layer) { + this.currentBlueprint.set(null); + } + } + } + + /** + * Called when the blueprint is now affordable or not + * @param {boolean} canAfford + */ + onCanAffordChanged(canAfford) { + this.costDisplayParent.classList.toggle("canAfford", canAfford); + } + + update() { + const currentBlueprint = this.currentBlueprint.get(); + this.domAttach.update( + !this.getHasFreeCopyPaste() && currentBlueprint && currentBlueprint.getCost() > 0 + ); + this.trackedCanAfford.set(currentBlueprint && currentBlueprint.canAfford(this.root)); + } + + /** + * Called when the blueprint was changed + * @param {Blueprint} blueprint + */ + onBlueprintChanged(blueprint) { + if (blueprint) { + this.lastBlueprintUsed = blueprint; + this.costDisplayText.innerText = "" + blueprint.getCost(); + } + } + + /** + * mouse down pre handler + * @param {Vector} pos + * @param {enumMouseButton} button + */ + onMouseDown(pos, button) { + if (button === enumMouseButton.right) { + if (this.currentBlueprint.get()) { + this.abortPlacement(); + return STOP_PROPAGATION; + } + } else if (button === enumMouseButton.left) { + const blueprint = this.currentBlueprint.get(); + if (!blueprint) { + return; + } + + if (!this.getHasFreeCopyPaste() && !blueprint.canAfford(this.root)) { + this.root.soundProxy.playUiError(); + return; + } + + const worldPos = this.root.camera.screenToWorld(pos); + const tile = worldPos.toTileSpace(); + if (blueprint.tryPlace(this.root, tile)) { + if (!this.getHasFreeCopyPaste()) { + const cost = blueprint.getCost(); + this.root.hubGoals.takeShapeByKey(this.root.gameMode.getBlueprintShapeKey(), cost); + } + this.root.soundProxy.playUi(SOUNDS.placeBuilding); + } + return STOP_PROPAGATION; + } + } + + /** + * Mouse move handler + */ + onMouseMove() { + // Prevent movement while blueprint is selected + if (this.currentBlueprint.get()) { + return STOP_PROPAGATION; + } + } + + /** + * Called when an array of bulidings was selected + * @param {Array} uids + */ + createBlueprintFromBuildings(uids) { + if (uids.length === 0) { + return; + } + this.currentBlueprint.set(Blueprint.fromUids(this.root, uids)); + } + + /** + * Attempts to rotate the current blueprint + */ + rotateBlueprint() { + if (this.currentBlueprint.get()) { + if (this.root.keyMapper.getBinding(KEYMAPPINGS.placement.rotateInverseModifier).pressed) { + this.currentBlueprint.get().rotateCcw(); + } else { + this.currentBlueprint.get().rotateCw(); + } + } + } + + /** + * Attempts to paste the last blueprint + */ + pasteBlueprint() { + if (this.lastBlueprintUsed !== null) { + if (this.lastBlueprintUsed.layer !== this.root.currentLayer) { + // Not compatible + this.root.soundProxy.playUiError(); + return; + } + + this.root.hud.signals.pasteBlueprintRequested.dispatch(); + this.currentBlueprint.set(this.lastBlueprintUsed); + } else { + this.root.soundProxy.playUiError(); + } + } + + /** + * + * @param {DrawParameters} parameters + */ + draw(parameters) { + const blueprint = this.currentBlueprint.get(); + if (!blueprint) { + return; + } + const mousePosition = this.root.app.mousePosition; + if (!mousePosition) { + // Not on screen + return; + } + + const worldPos = this.root.camera.screenToWorld(mousePosition); + const tile = worldPos.toTileSpace(); + blueprint.draw(parameters, tile); + } +} diff --git a/src/js/game/hud/parts/building_placer.js b/src/js/game/hud/parts/building_placer.js index d2904720..75294fb3 100644 --- a/src/js/game/hud/parts/building_placer.js +++ b/src/js/game/hud/parts/building_placer.js @@ -1,684 +1,682 @@ -import { ClickDetector } from "../../../core/click_detector"; -import { globalConfig } from "../../../core/config"; -import { DrawParameters } from "../../../core/draw_parameters"; -import { drawRotatedSprite } from "../../../core/draw_utils"; -import { Loader } from "../../../core/loader"; -import { clamp, makeDiv, removeAllChildren } from "../../../core/utils"; -import { - enumDirectionToAngle, - enumDirectionToVector, - enumInvertedDirections, - Vector, - enumDirection, -} from "../../../core/vector"; -import { T } from "../../../translations"; -import { KEYMAPPINGS } from "../../key_action_mapper"; -import { defaultBuildingVariant } from "../../meta_building"; -import { THEME } from "../../theme"; -import { DynamicDomAttach } from "../dynamic_dom_attach"; -import { HUDBuildingPlacerLogic } from "./building_placer_logic"; -import { makeOffscreenBuffer } from "../../../core/buffer_utils"; -import { layers } from "../../root"; -import { getCodeFromBuildingData } from "../../building_codes"; - -export class HUDBuildingPlacer extends HUDBuildingPlacerLogic { - /** - * @param {HTMLElement} parent - */ - createElements(parent) { - this.element = makeDiv(parent, "ingame_HUD_PlacementHints", [], ``); - - this.buildingInfoElements = {}; - this.buildingInfoElements.label = makeDiv(this.element, null, ["buildingLabel"], "Extract"); - this.buildingInfoElements.desc = makeDiv(this.element, null, ["description"], ""); - this.buildingInfoElements.descText = makeDiv(this.buildingInfoElements.desc, null, ["text"], ""); - this.buildingInfoElements.additionalInfo = makeDiv( - this.buildingInfoElements.desc, - null, - ["additionalInfo"], - "" - ); - this.buildingInfoElements.hotkey = makeDiv(this.buildingInfoElements.desc, null, ["hotkey"], ""); - this.buildingInfoElements.tutorialImage = makeDiv(this.element, null, ["buildingImage"]); - - this.variantsElement = makeDiv(parent, "ingame_HUD_PlacerVariants"); - - const compact = this.root.app.settings.getAllSettings().compactBuildingInfo; - this.element.classList.toggle("compact", compact); - this.variantsElement.classList.toggle("compact", compact); - } - - initialize() { - super.initialize(); - - // Bind to signals - this.signals.variantChanged.add(this.rerenderVariants, this); - this.root.hud.signals.buildingSelectedForPlacement.add(this.startSelection, this); - - this.domAttach = new DynamicDomAttach(this.root, this.element, { trackHover: true }); - this.variantsAttach = new DynamicDomAttach(this.root, this.variantsElement, {}); - - this.currentInterpolatedCornerTile = new Vector(); - - this.lockIndicatorSprites = {}; - [...layers, "error"].forEach(layer => { - this.lockIndicatorSprites[layer] = this.makeLockIndicatorSprite(layer); - }); - - // - - /** - * Stores the click detectors for the variants so we can clean them up later - * @type {Array} - */ - this.variantClickDetectors = []; - } - - /** - * Makes the lock indicator sprite for the given layer - * @param {string} layer - */ - makeLockIndicatorSprite(layer) { - const dims = 48; - const [canvas, context] = makeOffscreenBuffer(dims, dims, { - smooth: true, - reusable: false, - label: "lock-direction-indicator", - }); - - context.fillStyle = THEME.map.directionLock[layer].color; - context.strokeStyle = THEME.map.directionLock[layer].color; - context.lineWidth = 2; - - const padding = 5; - const height = dims * 0.5; - const bottom = (dims + height) / 2; - - context.moveTo(padding, bottom); - context.lineTo(dims / 2, bottom - height); - context.lineTo(dims - padding, bottom); - context.closePath(); - context.stroke(); - context.fill(); - - return canvas; - } - - /** - * Rerenders the building info dialog - */ - rerenderInfoDialog() { - const metaBuilding = this.currentMetaBuilding.get(); - - if (!metaBuilding) { - return; - } - - const variant = this.currentVariant.get(); - - this.buildingInfoElements.label.innerHTML = T.buildings[metaBuilding.id][variant].name; - this.buildingInfoElements.descText.innerHTML = T.buildings[metaBuilding.id][variant].description; - - const layer = this.root.currentLayer; - - let rawBinding = KEYMAPPINGS.buildings[metaBuilding.getId() + "_" + layer]; - if (!rawBinding) { - rawBinding = KEYMAPPINGS.buildings[metaBuilding.getId()]; - } - - if (rawBinding) { - const binding = this.root.keyMapper.getBinding(rawBinding); - this.buildingInfoElements.hotkey.innerHTML = T.ingame.buildingPlacement.hotkeyLabel.replace( - "", - "" + binding.getKeyCodeString() + "" - ); - } else { - this.buildingInfoElements.hotkey.innerHTML = ""; - } - - this.buildingInfoElements.tutorialImage.setAttribute( - "data-icon", - "building_tutorials/" + - metaBuilding.getId() + - (variant === defaultBuildingVariant ? "" : "-" + variant) + - ".png" - ); - - removeAllChildren(this.buildingInfoElements.additionalInfo); - const additionalInfo = metaBuilding.getAdditionalStatistics(this.root, this.currentVariant.get()); - for (let i = 0; i < additionalInfo.length; ++i) { - const [label, contents] = additionalInfo[i]; - this.buildingInfoElements.additionalInfo.innerHTML += ` - - ${contents} - `; - } - } - - cleanup() { - super.cleanup(); - this.cleanupVariantClickDetectors(); - } - - /** - * Cleans up all variant click detectors - */ - cleanupVariantClickDetectors() { - for (let i = 0; i < this.variantClickDetectors.length; ++i) { - const detector = this.variantClickDetectors[i]; - detector.cleanup(); - } - this.variantClickDetectors = []; - } - - /** - * Rerenders the variants displayed - */ - rerenderVariants() { - removeAllChildren(this.variantsElement); - this.rerenderInfoDialog(); - - const metaBuilding = this.currentMetaBuilding.get(); - - // First, clear up all click detectors - this.cleanupVariantClickDetectors(); - - if (!metaBuilding) { - return; - } - const availableVariants = metaBuilding.getAvailableVariants(this.root); - if (availableVariants.length === 1) { - return; - } - - makeDiv( - this.variantsElement, - null, - ["explanation"], - T.ingame.buildingPlacement.cycleBuildingVariants.replace( - "", - "" + - this.root.keyMapper - .getBinding(KEYMAPPINGS.placement.cycleBuildingVariants) - .getKeyCodeString() + - "" - ) - ); - - const container = makeDiv(this.variantsElement, null, ["variants"]); - - for (let i = 0; i < availableVariants.length; ++i) { - const variant = availableVariants[i]; - - const element = makeDiv(container, null, ["variant"]); - element.classList.toggle("active", variant === this.currentVariant.get()); - makeDiv(element, null, ["label"], variant); - - const iconSize = 64; - - const dimensions = metaBuilding.getDimensions(variant); - const sprite = metaBuilding.getPreviewSprite(0, variant); - const spriteWrapper = makeDiv(element, null, ["iconWrap"]); - spriteWrapper.setAttribute("data-tile-w", String(dimensions.x)); - spriteWrapper.setAttribute("data-tile-h", String(dimensions.y)); - - spriteWrapper.innerHTML = sprite.getAsHTML(iconSize * dimensions.x, iconSize * dimensions.y); - - const detector = new ClickDetector(element, { - consumeEvents: true, - targetOnly: true, - }); - detector.click.add(() => this.setVariant(variant)); - } - } - - /** - * - * @param {DrawParameters} parameters - */ - draw(parameters) { - if (this.root.camera.getIsMapOverlayActive()) { - // Dont allow placing in overview mode - this.domAttach.update(false); - this.variantsAttach.update(false); - return; - } - - this.domAttach.update(!!this.currentMetaBuilding.get()); - this.variantsAttach.update(!!this.currentMetaBuilding.get()); - const metaBuilding = this.currentMetaBuilding.get(); - - if (!metaBuilding) { - return; - } - - // Draw direction lock - if (this.isDirectionLockActive) { - this.drawDirectionLock(parameters); - } else { - this.drawRegularPlacement(parameters); - } - - if (metaBuilding.getShowWiresLayerPreview()) { - this.drawLayerPeek(parameters); - } - } - - /** - * - * @param {DrawParameters} parameters - */ - drawLayerPeek(parameters) { - const mousePosition = this.root.app.mousePosition; - if (!mousePosition) { - // Not on screen - return; - } - - const worldPosition = this.root.camera.screenToWorld(mousePosition); - - // Draw peeker - if (this.root.hud.parts.layerPreview) { - this.root.hud.parts.layerPreview.renderPreview( - parameters, - worldPosition, - 1 / this.root.camera.zoomLevel - ); - } - } - - /** - * @param {DrawParameters} parameters - */ - drawRegularPlacement(parameters) { - const mousePosition = this.root.app.mousePosition; - if (!mousePosition) { - // Not on screen - return; - } - - const metaBuilding = this.currentMetaBuilding.get(); - - const worldPos = this.root.camera.screenToWorld(mousePosition); - const mouseTile = worldPos.toTileSpace(); - - // Compute best rotation variant - const { - rotation, - rotationVariant, - connectedEntities, - } = metaBuilding.computeOptimalDirectionAndRotationVariantAtTile({ - root: this.root, - tile: mouseTile, - rotation: this.currentBaseRotation, - variant: this.currentVariant.get(), - layer: metaBuilding.getLayer(), - }); - - // Check if there are connected entities - if (connectedEntities) { - for (let i = 0; i < connectedEntities.length; ++i) { - const connectedEntity = connectedEntities[i]; - const connectedWsPoint = connectedEntity.components.StaticMapEntity.getTileSpaceBounds() - .getCenter() - .toWorldSpace(); - - const startWsPoint = mouseTile.toWorldSpaceCenterOfTile(); - - const startOffset = connectedWsPoint - .sub(startWsPoint) - .normalize() - .multiplyScalar(globalConfig.tileSize * 0.3); - const effectiveStartPoint = startWsPoint.add(startOffset); - const effectiveEndPoint = connectedWsPoint.sub(startOffset); - - parameters.context.globalAlpha = 0.6; - - // parameters.context.lineCap = "round"; - parameters.context.strokeStyle = "#7f7"; - parameters.context.lineWidth = 10; - parameters.context.beginPath(); - parameters.context.moveTo(effectiveStartPoint.x, effectiveStartPoint.y); - parameters.context.lineTo(effectiveEndPoint.x, effectiveEndPoint.y); - parameters.context.stroke(); - parameters.context.globalAlpha = 1; - // parameters.context.lineCap = "square"; - } - } - - // Synchronize rotation and origin - this.fakeEntity.layer = metaBuilding.getLayer(); - const staticComp = this.fakeEntity.components.StaticMapEntity; - staticComp.origin = mouseTile; - staticComp.rotation = rotation; - metaBuilding.updateVariants(this.fakeEntity, rotationVariant, this.currentVariant.get()); - staticComp.code = getCodeFromBuildingData( - this.currentMetaBuilding.get(), - this.currentVariant.get(), - rotationVariant - ); - - const canBuild = this.root.logic.checkCanPlaceEntity(this.fakeEntity, {}); - - // Fade in / out - parameters.context.lineWidth = 1; - - // Determine the bounds and visualize them - const entityBounds = staticComp.getTileSpaceBounds(); - const drawBorder = -3; - if (canBuild) { - parameters.context.strokeStyle = "rgba(56, 235, 111, 0.5)"; - parameters.context.fillStyle = "rgba(56, 235, 111, 0.2)"; - } else { - parameters.context.strokeStyle = "rgba(255, 0, 0, 0.2)"; - parameters.context.fillStyle = "rgba(255, 0, 0, 0.2)"; - } - - parameters.context.beginRoundedRect( - entityBounds.x * globalConfig.tileSize - drawBorder, - entityBounds.y * globalConfig.tileSize - drawBorder, - entityBounds.w * globalConfig.tileSize + 2 * drawBorder, - entityBounds.h * globalConfig.tileSize + 2 * drawBorder, - 4 - ); - parameters.context.stroke(); - // parameters.context.fill(); - parameters.context.globalAlpha = 1; - - // HACK to draw the entity sprite - const previewSprite = metaBuilding.getBlueprintSprite(rotationVariant, this.currentVariant.get()); - staticComp.origin = worldPos.divideScalar(globalConfig.tileSize).subScalars(0.5, 0.5); - staticComp.drawSpriteOnBoundsClipped(parameters, previewSprite); - staticComp.origin = mouseTile; - - // Draw ejectors - if (canBuild) { - this.drawMatchingAcceptorsAndEjectors(parameters); - } - } - - /** - * Checks if there are any entities in the way, returns true if there are - * @param {Vector} from - * @param {Vector} to - * @param {Vector[]=} ignorePositions - * @returns - */ - checkForObstales(from, to, ignorePositions = []) { - assert(from.x === to.x || from.y === to.y, "Must be a straight line"); - - const prop = from.x === to.x ? "y" : "x"; - const current = from.copy(); - - const metaBuilding = this.currentMetaBuilding.get(); - this.fakeEntity.layer = metaBuilding.getLayer(); - const staticComp = this.fakeEntity.components.StaticMapEntity; - staticComp.origin = current; - staticComp.rotation = 0; - metaBuilding.updateVariants(this.fakeEntity, 0, this.currentVariant.get()); - staticComp.code = getCodeFromBuildingData( - this.currentMetaBuilding.get(), - this.currentVariant.get(), - 0 - ); - - const start = Math.min(from[prop], to[prop]); - const end = Math.max(from[prop], to[prop]); - - for (let i = start; i <= end; i++) { - current[prop] = i; - if (ignorePositions.some(p => p.distanceSquare(current) < 0.1)) { - continue; - } - if (!this.root.logic.checkCanPlaceEntity(this.fakeEntity, { allowReplaceBuildings: false })) { - return true; - } - } - return false; - } - - /** - * @param {DrawParameters} parameters - */ - drawDirectionLock(parameters) { - const mousePosition = this.root.app.mousePosition; - if (!mousePosition) { - // Not on screen - return; - } - - const applyStyles = look => { - parameters.context.fillStyle = THEME.map.directionLock[look].color; - parameters.context.strokeStyle = THEME.map.directionLock[look].background; - parameters.context.lineWidth = 10; - }; - - if (!this.lastDragTile) { - // Not dragging yet - applyStyles(this.root.currentLayer); - const mouseWorld = this.root.camera.screenToWorld(mousePosition); - parameters.context.beginCircle(mouseWorld.x, mouseWorld.y, 4); - parameters.context.fill(); - return; - } - - const mouseWorld = this.root.camera.screenToWorld(mousePosition); - const mouseTile = mouseWorld.toTileSpace(); - const startLine = this.lastDragTile.toWorldSpaceCenterOfTile(); - const endLine = mouseTile.toWorldSpaceCenterOfTile(); - const midLine = this.currentDirectionLockCorner.toWorldSpaceCenterOfTile(); - const anyObstacle = - this.checkForObstales(this.lastDragTile, this.currentDirectionLockCorner, [ - this.lastDragTile, - mouseTile, - ]) || - this.checkForObstales(this.currentDirectionLockCorner, mouseTile, [this.lastDragTile, mouseTile]); - - if (anyObstacle) { - applyStyles("error"); - } else { - applyStyles(this.root.currentLayer); - } - - parameters.context.beginCircle(mouseWorld.x, mouseWorld.y, 4); - parameters.context.fill(); - - parameters.context.beginCircle(startLine.x, startLine.y, 8); - parameters.context.fill(); - - parameters.context.beginPath(); - parameters.context.moveTo(startLine.x, startLine.y); - parameters.context.lineTo(midLine.x, midLine.y); - parameters.context.lineTo(endLine.x, endLine.y); - parameters.context.stroke(); - - parameters.context.beginCircle(endLine.x, endLine.y, 5); - parameters.context.fill(); - - // Draw arrow - const arrowSprite = this.lockIndicatorSprites[anyObstacle ? "error" : this.root.currentLayer]; - const path = this.computeDirectionLockPath(); - for (let i = 0; i < path.length - 1; i += 1) { - const { rotation, tile } = path[i]; - const worldPos = tile.toWorldSpaceCenterOfTile(); - const angle = Math.radians(rotation); - - parameters.context.translate(worldPos.x, worldPos.y); - parameters.context.rotate(angle); - parameters.context.drawImage( - arrowSprite, - -6, - -globalConfig.halfTileSize - - clamp((this.root.time.realtimeNow() * 1.5) % 1.0, 0, 1) * 1 * globalConfig.tileSize + - globalConfig.halfTileSize - - 6, - 12, - 12 - ); - parameters.context.rotate(-angle); - parameters.context.translate(-worldPos.x, -worldPos.y); - } - } - - /** - * @param {DrawParameters} parameters - */ - drawMatchingAcceptorsAndEjectors(parameters) { - const acceptorComp = this.fakeEntity.components.ItemAcceptor; - const ejectorComp = this.fakeEntity.components.ItemEjector; - const staticComp = this.fakeEntity.components.StaticMapEntity; - const beltComp = this.fakeEntity.components.Belt; - const minerComp = this.fakeEntity.components.Miner; - - const goodArrowSprite = Loader.getSprite("sprites/misc/slot_good_arrow.png"); - const badArrowSprite = Loader.getSprite("sprites/misc/slot_bad_arrow.png"); - - // Just ignore the following code please ... thanks! - - const offsetShift = 10; - - /** - * @type {Array} - */ - let acceptorSlots = []; - /** - * @type {Array} - */ - let ejectorSlots = []; - - if (ejectorComp) { - ejectorSlots = ejectorComp.slots.slice(); - } - - if (acceptorComp) { - acceptorSlots = acceptorComp.slots.slice(); - } - - if (beltComp) { - const fakeEjectorSlot = beltComp.getFakeEjectorSlot(); - const fakeAcceptorSlot = beltComp.getFakeAcceptorSlot(); - ejectorSlots.push(fakeEjectorSlot); - acceptorSlots.push(fakeAcceptorSlot); - } - - // Go over all slots - for (let i = 0; i < acceptorSlots.length; ++i) { - const slot = acceptorSlots[i]; - - const acceptorSlotWsTile = staticComp.localTileToWorld(slot.pos); - const acceptorSlotWsPos = acceptorSlotWsTile.toWorldSpaceCenterOfTile(); - - const direction = slot.direction; - const worldDirection = staticComp.localDirectionToWorld(direction); - - // Figure out which tile ejects to this slot - const sourceTile = acceptorSlotWsTile.add(enumDirectionToVector[worldDirection]); - - let isBlocked = false; - let isConnected = false; - - // Find all entities which are on that tile - const sourceEntities = this.root.map.getLayersContentsMultipleXY(sourceTile.x, sourceTile.y); - - // Check for every entity: - for (let j = 0; j < sourceEntities.length; ++j) { - const sourceEntity = sourceEntities[j]; - const sourceEjector = sourceEntity.components.ItemEjector; - const sourceBeltComp = sourceEntity.components.Belt; - const sourceStaticComp = sourceEntity.components.StaticMapEntity; - const ejectorAcceptLocalTile = sourceStaticComp.worldToLocalTile(acceptorSlotWsTile); - - // If this entity is on the same layer as the slot - if so, it can either be - // connected, or it can not be connected and thus block the input - if (sourceEjector && sourceEjector.anySlotEjectsToLocalTile(ejectorAcceptLocalTile)) { - // This one is connected, all good - isConnected = true; - } else if ( - sourceBeltComp && - sourceStaticComp.localDirectionToWorld(sourceBeltComp.direction) === - enumInvertedDirections[worldDirection] - ) { - // Belt connected - isConnected = true; - } else { - // This one is blocked - isBlocked = true; - } - } - - const alpha = isConnected || isBlocked ? 1.0 : 0.3; - const sprite = isBlocked ? badArrowSprite : goodArrowSprite; - - parameters.context.globalAlpha = alpha; - drawRotatedSprite({ - parameters, - sprite, - x: acceptorSlotWsPos.x, - y: acceptorSlotWsPos.y, - angle: Math.radians(enumDirectionToAngle[enumInvertedDirections[worldDirection]]), - size: 13, - offsetY: offsetShift + 13, - }); - parameters.context.globalAlpha = 1; - } - - // Go over all slots - for (let ejectorSlotIndex = 0; ejectorSlotIndex < ejectorSlots.length; ++ejectorSlotIndex) { - const slot = ejectorSlots[ejectorSlotIndex]; - - const ejectorSlotLocalTile = slot.pos.add(enumDirectionToVector[slot.direction]); - const ejectorSlotWsTile = staticComp.localTileToWorld(ejectorSlotLocalTile); - - const ejectorSLotWsPos = ejectorSlotWsTile.toWorldSpaceCenterOfTile(); - const ejectorSlotWsDirection = staticComp.localDirectionToWorld(slot.direction); - - let isBlocked = false; - let isConnected = false; - - // Find all entities which are on that tile - const destEntities = this.root.map.getLayersContentsMultipleXY( - ejectorSlotWsTile.x, - ejectorSlotWsTile.y - ); - - // Check for every entity: - for (let i = 0; i < destEntities.length; ++i) { - const destEntity = destEntities[i]; - const destAcceptor = destEntity.components.ItemAcceptor; - const destStaticComp = destEntity.components.StaticMapEntity; - const destMiner = destEntity.components.Miner; - - const destLocalTile = destStaticComp.worldToLocalTile(ejectorSlotWsTile); - const destLocalDir = destStaticComp.worldDirectionToLocal(ejectorSlotWsDirection); - if (destAcceptor && destAcceptor.findMatchingSlot(destLocalTile, destLocalDir)) { - // This one is connected, all good - isConnected = true; - } else if (destEntity.components.Belt && destLocalDir === enumDirection.top) { - // Connected to a belt - isConnected = true; - } else if (minerComp && minerComp.chainable && destMiner && destMiner.chainable) { - // Chainable miners connected to eachother - isConnected = true; - } else { - // This one is blocked - isBlocked = true; - } - } - - const alpha = isConnected || isBlocked ? 1.0 : 0.3; - const sprite = isBlocked ? badArrowSprite : goodArrowSprite; - - parameters.context.globalAlpha = alpha; - drawRotatedSprite({ - parameters, - sprite, - x: ejectorSLotWsPos.x, - y: ejectorSLotWsPos.y, - angle: Math.radians(enumDirectionToAngle[ejectorSlotWsDirection]), - size: 13, - offsetY: offsetShift, - }); - parameters.context.globalAlpha = 1; - } - } -} +import { makeOffscreenBuffer } from "../../../core/buffer_utils"; +import { ClickDetector } from "../../../core/click_detector"; +import { globalConfig } from "../../../core/config"; +import { DrawParameters } from "../../../core/draw_parameters"; +import { drawRotatedSprite } from "../../../core/draw_utils"; +import { Loader } from "../../../core/loader"; +import { clamp, makeDiv, removeAllChildren } from "../../../core/utils"; +import { + Vector, + enumDirection, + enumDirectionToAngle, + enumDirectionToVector, + enumInvertedDirections, +} from "../../../core/vector"; +import { T } from "../../../translations"; +import { getCodeFromBuildingData } from "../../building_codes"; +import { KEYMAPPINGS } from "../../key_action_mapper"; +import { defaultBuildingVariant } from "../../meta_building"; +import { layers } from "../../root"; +import { THEME } from "../../theme"; +import { DynamicDomAttach } from "../dynamic_dom_attach"; +import { HUDBuildingPlacerLogic } from "./building_placer_logic"; + +export class HUDBuildingPlacer extends HUDBuildingPlacerLogic { + /** + * @param {HTMLElement} parent + */ + createElements(parent) { + this.element = makeDiv(parent, "ingame_HUD_PlacementHints", [], ``); + + this.buildingInfoElements = {}; + this.buildingInfoElements.label = makeDiv(this.element, null, ["buildingLabel"], "Extract"); + this.buildingInfoElements.desc = makeDiv(this.element, null, ["description"], ""); + this.buildingInfoElements.descText = makeDiv(this.buildingInfoElements.desc, null, ["text"], ""); + this.buildingInfoElements.additionalInfo = makeDiv( + this.buildingInfoElements.desc, + null, + ["additionalInfo"], + "" + ); + this.buildingInfoElements.hotkey = makeDiv(this.buildingInfoElements.desc, null, ["hotkey"], ""); + this.buildingInfoElements.tutorialImage = makeDiv(this.element, null, ["buildingImage"]); + + this.variantsElement = makeDiv(parent, "ingame_HUD_PlacerVariants"); + + const compact = this.root.app.settings.getAllSettings().compactBuildingInfo; + this.element.classList.toggle("compact", compact); + this.variantsElement.classList.toggle("compact", compact); + } + + initialize() { + super.initialize(); + + // Bind to signals + this.signals.variantChanged.add(this.rerenderVariants, this); + this.root.hud.signals.buildingSelectedForPlacement.add(this.startSelection, this); + + this.domAttach = new DynamicDomAttach(this.root, this.element, { trackHover: true }); + this.variantsAttach = new DynamicDomAttach(this.root, this.variantsElement, {}); + + this.currentInterpolatedCornerTile = new Vector(); + + this.lockIndicatorSprites = {}; + [...layers, "error"].forEach(layer => { + this.lockIndicatorSprites[layer] = this.makeLockIndicatorSprite(layer); + }); + + // + + /** + * Stores the click detectors for the variants so we can clean them up later + * @type {Array} + */ + this.variantClickDetectors = []; + } + + /** + * Makes the lock indicator sprite for the given layer + * @param {string} layer + */ + makeLockIndicatorSprite(layer) { + const dims = 48; + const [canvas, context] = makeOffscreenBuffer(dims, dims, { + smooth: true, + reusable: false, + label: "lock-direction-indicator", + }); + + context.fillStyle = THEME.map.directionLock[layer].color; + context.strokeStyle = THEME.map.directionLock[layer].color; + context.lineWidth = 2; + + const padding = 5; + const height = dims * 0.5; + const bottom = (dims + height) / 2; + + context.moveTo(padding, bottom); + context.lineTo(dims / 2, bottom - height); + context.lineTo(dims - padding, bottom); + context.closePath(); + context.stroke(); + context.fill(); + + return canvas; + } + + /** + * Rerenders the building info dialog + */ + rerenderInfoDialog() { + const metaBuilding = this.currentMetaBuilding.get(); + + if (!metaBuilding) { + return; + } + + const variant = this.currentVariant.get(); + + this.buildingInfoElements.label.innerHTML = T.buildings[metaBuilding.id][variant].name; + this.buildingInfoElements.descText.innerHTML = T.buildings[metaBuilding.id][variant].description; + + const layer = this.root.currentLayer; + + let rawBinding = KEYMAPPINGS.buildings[metaBuilding.getId() + "_" + layer]; + if (!rawBinding) { + rawBinding = KEYMAPPINGS.buildings[metaBuilding.getId()]; + } + + if (rawBinding) { + const binding = this.root.keyMapper.getBinding(rawBinding); + this.buildingInfoElements.hotkey.innerHTML = T.ingame.buildingPlacement.hotkeyLabel.replace( + "", + "" + binding.getKeyCodeString() + "" + ); + } else { + this.buildingInfoElements.hotkey.innerHTML = ""; + } + + this.buildingInfoElements.tutorialImage.setAttribute( + "data-icon", + "building_tutorials/" + + metaBuilding.getId() + + (variant === defaultBuildingVariant ? "" : "-" + variant) + + ".png" + ); + + removeAllChildren(this.buildingInfoElements.additionalInfo); + const additionalInfo = metaBuilding.getAdditionalStatistics(this.root, this.currentVariant.get()); + for (let i = 0; i < additionalInfo.length; ++i) { + const [label, contents] = additionalInfo[i]; + this.buildingInfoElements.additionalInfo.innerHTML += ` + + ${contents} + `; + } + } + + cleanup() { + super.cleanup(); + this.cleanupVariantClickDetectors(); + } + + /** + * Cleans up all variant click detectors + */ + cleanupVariantClickDetectors() { + for (let i = 0; i < this.variantClickDetectors.length; ++i) { + const detector = this.variantClickDetectors[i]; + detector.cleanup(); + } + this.variantClickDetectors = []; + } + + /** + * Rerenders the variants displayed + */ + rerenderVariants() { + removeAllChildren(this.variantsElement); + this.rerenderInfoDialog(); + + const metaBuilding = this.currentMetaBuilding.get(); + + // First, clear up all click detectors + this.cleanupVariantClickDetectors(); + + if (!metaBuilding) { + return; + } + const availableVariants = metaBuilding.getAvailableVariants(this.root); + if (availableVariants.length === 1) { + return; + } + + makeDiv( + this.variantsElement, + null, + ["explanation"], + T.ingame.buildingPlacement.cycleBuildingVariants.replace( + "", + "" + + this.root.keyMapper + .getBinding(KEYMAPPINGS.placement.cycleBuildingVariants) + .getKeyCodeString() + + "" + ) + ); + + const container = makeDiv(this.variantsElement, null, ["variants"]); + + for (let i = 0; i < availableVariants.length; ++i) { + const variant = availableVariants[i]; + + const element = makeDiv(container, null, ["variant"]); + element.classList.toggle("active", variant === this.currentVariant.get()); + makeDiv(element, null, ["label"], variant); + + const iconSize = 64; + + const dimensions = metaBuilding.getDimensions(variant); + const sprite = metaBuilding.getPreviewSprite(0, variant); + const spriteWrapper = makeDiv(element, null, ["iconWrap"]); + spriteWrapper.setAttribute("data-tile-w", String(dimensions.x)); + spriteWrapper.setAttribute("data-tile-h", String(dimensions.y)); + + spriteWrapper.innerHTML = sprite.getAsHTML(iconSize * dimensions.x, iconSize * dimensions.y); + + const detector = new ClickDetector(element, { + consumeEvents: true, + targetOnly: true, + }); + detector.click.add(() => this.setVariant(variant)); + } + } + + /** + * + * @param {DrawParameters} parameters + */ + draw(parameters) { + if (this.root.camera.getIsMapOverlayActive()) { + // Dont allow placing in overview mode + this.domAttach.update(false); + this.variantsAttach.update(false); + return; + } + + this.domAttach.update(!!this.currentMetaBuilding.get()); + this.variantsAttach.update(!!this.currentMetaBuilding.get()); + const metaBuilding = this.currentMetaBuilding.get(); + + if (!metaBuilding) { + return; + } + + // Draw direction lock + if (this.isDirectionLockActive) { + this.drawDirectionLock(parameters); + } else { + this.drawRegularPlacement(parameters); + } + + if (metaBuilding.getShowWiresLayerPreview()) { + this.drawLayerPeek(parameters); + } + } + + /** + * + * @param {DrawParameters} parameters + */ + drawLayerPeek(parameters) { + const mousePosition = this.root.app.mousePosition; + if (!mousePosition) { + // Not on screen + return; + } + + const worldPosition = this.root.camera.screenToWorld(mousePosition); + + // Draw peeker + if (this.root.hud.parts.layerPreview) { + this.root.hud.parts.layerPreview.renderPreview( + parameters, + worldPosition, + 1 / this.root.camera.zoomLevel + ); + } + } + + /** + * @param {DrawParameters} parameters + */ + drawRegularPlacement(parameters) { + const mousePosition = this.root.app.mousePosition; + if (!mousePosition) { + // Not on screen + return; + } + + const metaBuilding = this.currentMetaBuilding.get(); + + const worldPos = this.root.camera.screenToWorld(mousePosition); + const mouseTile = worldPos.toTileSpace(); + + // Compute best rotation variant + const { rotation, rotationVariant, connectedEntities } = + metaBuilding.computeOptimalDirectionAndRotationVariantAtTile({ + root: this.root, + tile: mouseTile, + rotation: this.currentBaseRotation, + variant: this.currentVariant.get(), + layer: metaBuilding.getLayer(), + }); + + // Check if there are connected entities + if (connectedEntities) { + for (let i = 0; i < connectedEntities.length; ++i) { + const connectedEntity = connectedEntities[i]; + const connectedWsPoint = connectedEntity.components.StaticMapEntity.getTileSpaceBounds() + .getCenter() + .toWorldSpace(); + + const startWsPoint = mouseTile.toWorldSpaceCenterOfTile(); + + const startOffset = connectedWsPoint + .sub(startWsPoint) + .normalize() + .multiplyScalar(globalConfig.tileSize * 0.3); + const effectiveStartPoint = startWsPoint.add(startOffset); + const effectiveEndPoint = connectedWsPoint.sub(startOffset); + + parameters.context.globalAlpha = 0.6; + + // parameters.context.lineCap = "round"; + parameters.context.strokeStyle = "#7f7"; + parameters.context.lineWidth = 10; + parameters.context.beginPath(); + parameters.context.moveTo(effectiveStartPoint.x, effectiveStartPoint.y); + parameters.context.lineTo(effectiveEndPoint.x, effectiveEndPoint.y); + parameters.context.stroke(); + parameters.context.globalAlpha = 1; + // parameters.context.lineCap = "square"; + } + } + + // Synchronize rotation and origin + this.fakeEntity.layer = metaBuilding.getLayer(); + const staticComp = this.fakeEntity.components.StaticMapEntity; + staticComp.origin = mouseTile; + staticComp.rotation = rotation; + metaBuilding.updateVariants(this.fakeEntity, rotationVariant, this.currentVariant.get()); + staticComp.code = getCodeFromBuildingData( + this.currentMetaBuilding.get(), + this.currentVariant.get(), + rotationVariant + ); + + const canBuild = this.root.logic.checkCanPlaceEntity(this.fakeEntity, {}); + + // Fade in / out + parameters.context.lineWidth = 1; + + // Determine the bounds and visualize them + const entityBounds = staticComp.getTileSpaceBounds(); + const drawBorder = -3; + if (canBuild) { + parameters.context.strokeStyle = "rgba(56, 235, 111, 0.5)"; + parameters.context.fillStyle = "rgba(56, 235, 111, 0.2)"; + } else { + parameters.context.strokeStyle = "rgba(255, 0, 0, 0.2)"; + parameters.context.fillStyle = "rgba(255, 0, 0, 0.2)"; + } + + parameters.context.beginPath(); + parameters.context.roundRect( + entityBounds.x * globalConfig.tileSize - drawBorder, + entityBounds.y * globalConfig.tileSize - drawBorder, + entityBounds.w * globalConfig.tileSize + 2 * drawBorder, + entityBounds.h * globalConfig.tileSize + 2 * drawBorder, + 4 + ); + parameters.context.stroke(); + // parameters.context.fill(); + parameters.context.globalAlpha = 1; + + // HACK to draw the entity sprite + const previewSprite = metaBuilding.getBlueprintSprite(rotationVariant, this.currentVariant.get()); + staticComp.origin = worldPos.divideScalar(globalConfig.tileSize).subScalars(0.5, 0.5); + staticComp.drawSpriteOnBoundsClipped(parameters, previewSprite); + staticComp.origin = mouseTile; + + // Draw ejectors + if (canBuild) { + this.drawMatchingAcceptorsAndEjectors(parameters); + } + } + + /** + * Checks if there are any entities in the way, returns true if there are + * @param {Vector} from + * @param {Vector} to + * @param {Vector[]=} ignorePositions + * @returns + */ + checkForObstales(from, to, ignorePositions = []) { + assert(from.x === to.x || from.y === to.y, "Must be a straight line"); + + const prop = from.x === to.x ? "y" : "x"; + const current = from.copy(); + + const metaBuilding = this.currentMetaBuilding.get(); + this.fakeEntity.layer = metaBuilding.getLayer(); + const staticComp = this.fakeEntity.components.StaticMapEntity; + staticComp.origin = current; + staticComp.rotation = 0; + metaBuilding.updateVariants(this.fakeEntity, 0, this.currentVariant.get()); + staticComp.code = getCodeFromBuildingData( + this.currentMetaBuilding.get(), + this.currentVariant.get(), + 0 + ); + + const start = Math.min(from[prop], to[prop]); + const end = Math.max(from[prop], to[prop]); + + for (let i = start; i <= end; i++) { + current[prop] = i; + if (ignorePositions.some(p => p.distanceSquare(current) < 0.1)) { + continue; + } + if (!this.root.logic.checkCanPlaceEntity(this.fakeEntity, { allowReplaceBuildings: false })) { + return true; + } + } + return false; + } + + /** + * @param {DrawParameters} parameters + */ + drawDirectionLock(parameters) { + const mousePosition = this.root.app.mousePosition; + if (!mousePosition) { + // Not on screen + return; + } + + const applyStyles = look => { + parameters.context.fillStyle = THEME.map.directionLock[look].color; + parameters.context.strokeStyle = THEME.map.directionLock[look].background; + parameters.context.lineWidth = 10; + }; + + if (!this.lastDragTile) { + // Not dragging yet + applyStyles(this.root.currentLayer); + const mouseWorld = this.root.camera.screenToWorld(mousePosition); + parameters.context.beginCircle(mouseWorld.x, mouseWorld.y, 4); + parameters.context.fill(); + return; + } + + const mouseWorld = this.root.camera.screenToWorld(mousePosition); + const mouseTile = mouseWorld.toTileSpace(); + const startLine = this.lastDragTile.toWorldSpaceCenterOfTile(); + const endLine = mouseTile.toWorldSpaceCenterOfTile(); + const midLine = this.currentDirectionLockCorner.toWorldSpaceCenterOfTile(); + const anyObstacle = + this.checkForObstales(this.lastDragTile, this.currentDirectionLockCorner, [ + this.lastDragTile, + mouseTile, + ]) || + this.checkForObstales(this.currentDirectionLockCorner, mouseTile, [this.lastDragTile, mouseTile]); + + if (anyObstacle) { + applyStyles("error"); + } else { + applyStyles(this.root.currentLayer); + } + + parameters.context.beginCircle(mouseWorld.x, mouseWorld.y, 4); + parameters.context.fill(); + + parameters.context.beginCircle(startLine.x, startLine.y, 8); + parameters.context.fill(); + + parameters.context.beginPath(); + parameters.context.moveTo(startLine.x, startLine.y); + parameters.context.lineTo(midLine.x, midLine.y); + parameters.context.lineTo(endLine.x, endLine.y); + parameters.context.stroke(); + + parameters.context.beginCircle(endLine.x, endLine.y, 5); + parameters.context.fill(); + + // Draw arrow + const arrowSprite = this.lockIndicatorSprites[anyObstacle ? "error" : this.root.currentLayer]; + const path = this.computeDirectionLockPath(); + for (let i = 0; i < path.length - 1; i += 1) { + const { rotation, tile } = path[i]; + const worldPos = tile.toWorldSpaceCenterOfTile(); + const angle = Math.radians(rotation); + + parameters.context.translate(worldPos.x, worldPos.y); + parameters.context.rotate(angle); + parameters.context.drawImage( + arrowSprite, + -6, + -globalConfig.halfTileSize - + clamp((this.root.time.realtimeNow() * 1.5) % 1.0, 0, 1) * 1 * globalConfig.tileSize + + globalConfig.halfTileSize - + 6, + 12, + 12 + ); + parameters.context.rotate(-angle); + parameters.context.translate(-worldPos.x, -worldPos.y); + } + } + + /** + * @param {DrawParameters} parameters + */ + drawMatchingAcceptorsAndEjectors(parameters) { + const acceptorComp = this.fakeEntity.components.ItemAcceptor; + const ejectorComp = this.fakeEntity.components.ItemEjector; + const staticComp = this.fakeEntity.components.StaticMapEntity; + const beltComp = this.fakeEntity.components.Belt; + const minerComp = this.fakeEntity.components.Miner; + + const goodArrowSprite = Loader.getSprite("sprites/misc/slot_good_arrow.png"); + const badArrowSprite = Loader.getSprite("sprites/misc/slot_bad_arrow.png"); + + // Just ignore the following code please ... thanks! + + const offsetShift = 10; + + /** + * @type {Array} + */ + let acceptorSlots = []; + /** + * @type {Array} + */ + let ejectorSlots = []; + + if (ejectorComp) { + ejectorSlots = ejectorComp.slots.slice(); + } + + if (acceptorComp) { + acceptorSlots = acceptorComp.slots.slice(); + } + + if (beltComp) { + const fakeEjectorSlot = beltComp.getFakeEjectorSlot(); + const fakeAcceptorSlot = beltComp.getFakeAcceptorSlot(); + ejectorSlots.push(fakeEjectorSlot); + acceptorSlots.push(fakeAcceptorSlot); + } + + // Go over all slots + for (let i = 0; i < acceptorSlots.length; ++i) { + const slot = acceptorSlots[i]; + + const acceptorSlotWsTile = staticComp.localTileToWorld(slot.pos); + const acceptorSlotWsPos = acceptorSlotWsTile.toWorldSpaceCenterOfTile(); + + const direction = slot.direction; + const worldDirection = staticComp.localDirectionToWorld(direction); + + // Figure out which tile ejects to this slot + const sourceTile = acceptorSlotWsTile.add(enumDirectionToVector[worldDirection]); + + let isBlocked = false; + let isConnected = false; + + // Find all entities which are on that tile + const sourceEntities = this.root.map.getLayersContentsMultipleXY(sourceTile.x, sourceTile.y); + + // Check for every entity: + for (let j = 0; j < sourceEntities.length; ++j) { + const sourceEntity = sourceEntities[j]; + const sourceEjector = sourceEntity.components.ItemEjector; + const sourceBeltComp = sourceEntity.components.Belt; + const sourceStaticComp = sourceEntity.components.StaticMapEntity; + const ejectorAcceptLocalTile = sourceStaticComp.worldToLocalTile(acceptorSlotWsTile); + + // If this entity is on the same layer as the slot - if so, it can either be + // connected, or it can not be connected and thus block the input + if (sourceEjector && sourceEjector.anySlotEjectsToLocalTile(ejectorAcceptLocalTile)) { + // This one is connected, all good + isConnected = true; + } else if ( + sourceBeltComp && + sourceStaticComp.localDirectionToWorld(sourceBeltComp.direction) === + enumInvertedDirections[worldDirection] + ) { + // Belt connected + isConnected = true; + } else { + // This one is blocked + isBlocked = true; + } + } + + const alpha = isConnected || isBlocked ? 1.0 : 0.3; + const sprite = isBlocked ? badArrowSprite : goodArrowSprite; + + parameters.context.globalAlpha = alpha; + drawRotatedSprite({ + parameters, + sprite, + x: acceptorSlotWsPos.x, + y: acceptorSlotWsPos.y, + angle: Math.radians(enumDirectionToAngle[enumInvertedDirections[worldDirection]]), + size: 13, + offsetY: offsetShift + 13, + }); + parameters.context.globalAlpha = 1; + } + + // Go over all slots + for (let ejectorSlotIndex = 0; ejectorSlotIndex < ejectorSlots.length; ++ejectorSlotIndex) { + const slot = ejectorSlots[ejectorSlotIndex]; + + const ejectorSlotLocalTile = slot.pos.add(enumDirectionToVector[slot.direction]); + const ejectorSlotWsTile = staticComp.localTileToWorld(ejectorSlotLocalTile); + + const ejectorSLotWsPos = ejectorSlotWsTile.toWorldSpaceCenterOfTile(); + const ejectorSlotWsDirection = staticComp.localDirectionToWorld(slot.direction); + + let isBlocked = false; + let isConnected = false; + + // Find all entities which are on that tile + const destEntities = this.root.map.getLayersContentsMultipleXY( + ejectorSlotWsTile.x, + ejectorSlotWsTile.y + ); + + // Check for every entity: + for (let i = 0; i < destEntities.length; ++i) { + const destEntity = destEntities[i]; + const destAcceptor = destEntity.components.ItemAcceptor; + const destStaticComp = destEntity.components.StaticMapEntity; + const destMiner = destEntity.components.Miner; + + const destLocalTile = destStaticComp.worldToLocalTile(ejectorSlotWsTile); + const destLocalDir = destStaticComp.worldDirectionToLocal(ejectorSlotWsDirection); + if (destAcceptor && destAcceptor.findMatchingSlot(destLocalTile, destLocalDir)) { + // This one is connected, all good + isConnected = true; + } else if (destEntity.components.Belt && destLocalDir === enumDirection.top) { + // Connected to a belt + isConnected = true; + } else if (minerComp && minerComp.chainable && destMiner && destMiner.chainable) { + // Chainable miners connected to eachother + isConnected = true; + } else { + // This one is blocked + isBlocked = true; + } + } + + const alpha = isConnected || isBlocked ? 1.0 : 0.3; + const sprite = isBlocked ? badArrowSprite : goodArrowSprite; + + parameters.context.globalAlpha = alpha; + drawRotatedSprite({ + parameters, + sprite, + x: ejectorSLotWsPos.x, + y: ejectorSLotWsPos.y, + angle: Math.radians(enumDirectionToAngle[ejectorSlotWsDirection]), + size: 13, + offsetY: offsetShift, + }); + parameters.context.globalAlpha = 1; + } + } +} diff --git a/src/js/game/hud/parts/building_placer_logic.js b/src/js/game/hud/parts/building_placer_logic.js index 23ac6df3..37cf8710 100644 --- a/src/js/game/hud/parts/building_placer_logic.js +++ b/src/js/game/hud/parts/building_placer_logic.js @@ -1,839 +1,838 @@ -import { globalConfig } from "../../../core/config"; -import { gMetaBuildingRegistry } from "../../../core/global_registries"; -import { Signal, STOP_PROPAGATION } from "../../../core/signal"; -import { TrackedState } from "../../../core/tracked_state"; -import { Vector } from "../../../core/vector"; -import { enumMouseButton } from "../../camera"; -import { StaticMapEntityComponent } from "../../components/static_map_entity"; -import { Entity } from "../../entity"; -import { KEYMAPPINGS } from "../../key_action_mapper"; -import { defaultBuildingVariant, MetaBuilding } from "../../meta_building"; -import { BaseHUDPart } from "../base_hud_part"; -import { SOUNDS } from "../../../platform/sound"; -import { MetaMinerBuilding, enumMinerVariants } from "../../buildings/miner"; -import { enumHubGoalRewards } from "../../tutorial_goals"; -import { getBuildingDataFromCode, getCodeFromBuildingData } from "../../building_codes"; -import { MetaHubBuilding } from "../../buildings/hub"; -import { safeModulo } from "../../../core/utils"; - -/** - * Contains all logic for the building placer - this doesn't include the rendering - * of info boxes or drawing. - */ -export class HUDBuildingPlacerLogic extends BaseHUDPart { - /** - * Initializes the logic - * @see BaseHUDPart.initialize - */ - initialize() { - /** - * We use a fake entity to get information about how a building will look - * once placed - * @type {Entity} - */ - this.fakeEntity = null; - - // Signals - this.signals = { - variantChanged: new Signal(), - draggingStarted: new Signal(), - }; - - /** - * The current building - * @type {TypedTrackedState} - */ - this.currentMetaBuilding = new TrackedState(this.onSelectedMetaBuildingChanged, this); - - /** - * The current rotation - * @type {number} - */ - this.currentBaseRotationGeneral = 0; - - /** - * The current rotation preference for each building. - * @type{Object.} - */ - this.preferredBaseRotations = {}; - - /** - * Whether we are currently dragging - * @type {boolean} - */ - this.currentlyDragging = false; - - /** - * Current building variant - * @type {TypedTrackedState} - */ - this.currentVariant = new TrackedState(() => this.signals.variantChanged.dispatch()); - - /** - * Whether we are currently drag-deleting - * @type {boolean} - */ - this.currentlyDeleting = false; - - /** - * Stores which variants for each building we prefer, this is based on what - * the user last selected - * @type {Object.} - */ - this.preferredVariants = {}; - - /** - * The tile we last dragged from - * @type {Vector} - */ - this.lastDragTile = null; - - /** - * The side for direction lock - * @type {number} (0|1) - */ - this.currentDirectionLockSide = 0; - - /** - * Whether the side for direction lock has not yet been determined. - * @type {boolean} - */ - this.currentDirectionLockSideIndeterminate = true; - - this.initializeBindings(); - } - - /** - * Initializes all bindings - */ - initializeBindings() { - // KEYBINDINGS - const keyActionMapper = this.root.keyMapper; - keyActionMapper.getBinding(KEYMAPPINGS.placement.rotateWhilePlacing).add(this.tryRotate, this); - - keyActionMapper.getBinding(KEYMAPPINGS.placement.rotateToUp).add(this.trySetRotate, this); - keyActionMapper.getBinding(KEYMAPPINGS.placement.rotateToDown).add(this.trySetRotate, this); - keyActionMapper.getBinding(KEYMAPPINGS.placement.rotateToRight).add(this.trySetRotate, this); - keyActionMapper.getBinding(KEYMAPPINGS.placement.rotateToLeft).add(this.trySetRotate, this); - - keyActionMapper.getBinding(KEYMAPPINGS.placement.cycleBuildingVariants).add(this.cycleVariants, this); - keyActionMapper - .getBinding(KEYMAPPINGS.placement.switchDirectionLockSide) - .add(this.switchDirectionLockSide, this); - keyActionMapper.getBinding(KEYMAPPINGS.general.back).add(this.abortPlacement, this); - keyActionMapper.getBinding(KEYMAPPINGS.placement.pipette).add(this.startPipette, this); - this.root.gameState.inputReciever.keyup.add(this.checkForDirectionLockSwitch, this); - - // BINDINGS TO GAME EVENTS - this.root.hud.signals.buildingsSelectedForCopy.add(this.abortPlacement, this); - this.root.hud.signals.pasteBlueprintRequested.add(this.abortPlacement, this); - this.root.signals.storyGoalCompleted.add(() => this.signals.variantChanged.dispatch()); - this.root.signals.upgradePurchased.add(() => this.signals.variantChanged.dispatch()); - this.root.signals.editModeChanged.add(this.onEditModeChanged, this); - - // MOUSE BINDINGS - this.root.camera.downPreHandler.add(this.onMouseDown, this); - this.root.camera.movePreHandler.add(this.onMouseMove, this); - this.root.camera.upPostHandler.add(this.onMouseUp, this); - } - - /** - * Called when the edit mode got changed - * @param {Layer} layer - */ - onEditModeChanged(layer) { - const metaBuilding = this.currentMetaBuilding.get(); - if (metaBuilding) { - if (metaBuilding.getLayer() !== layer) { - // This layer doesn't fit the edit mode anymore - this.currentMetaBuilding.set(null); - } - } - } - - /** - * Returns the current base rotation for the current meta-building. - * @returns {number} - */ - get currentBaseRotation() { - if (!this.root.app.settings.getAllSettings().rotationByBuilding) { - return this.currentBaseRotationGeneral; - } - const metaBuilding = this.currentMetaBuilding.get(); - if (metaBuilding && this.preferredBaseRotations.hasOwnProperty(metaBuilding.getId())) { - return this.preferredBaseRotations[metaBuilding.getId()]; - } else { - return this.currentBaseRotationGeneral; - } - } - - /** - * Sets the base rotation for the current meta-building. - * @param {number} rotation The new rotation/angle. - */ - set currentBaseRotation(rotation) { - if (!this.root.app.settings.getAllSettings().rotationByBuilding) { - this.currentBaseRotationGeneral = rotation; - } else { - const metaBuilding = this.currentMetaBuilding.get(); - if (metaBuilding) { - this.preferredBaseRotations[metaBuilding.getId()] = rotation; - } else { - this.currentBaseRotationGeneral = rotation; - } - } - } - - /** - * Returns if the direction lock is currently active - * @returns {boolean} - */ - get isDirectionLockActive() { - const metaBuilding = this.currentMetaBuilding.get(); - return ( - metaBuilding && - metaBuilding.getHasDirectionLockAvailable(this.currentVariant.get()) && - this.root.keyMapper.getBinding(KEYMAPPINGS.placementModifiers.lockBeltDirection).pressed - ); - } - - /** - * Returns the current direction lock corner, that is, the corner between - * mouse and original start point - * @returns {Vector|null} - */ - get currentDirectionLockCorner() { - const mousePosition = this.root.app.mousePosition; - if (!mousePosition) { - // Not on screen - return null; - } - - if (!this.lastDragTile) { - // Haven't dragged yet - return null; - } - - // Figure which points the line visits - const worldPos = this.root.camera.screenToWorld(mousePosition); - const mouseTile = worldPos.toTileSpace(); - - // Figure initial direction - const dx = Math.abs(this.lastDragTile.x - mouseTile.x); - const dy = Math.abs(this.lastDragTile.y - mouseTile.y); - if (dx === 0 && dy === 0) { - // Back at the start. Try a new direction. - this.currentDirectionLockSideIndeterminate = true; - } else if (this.currentDirectionLockSideIndeterminate) { - this.currentDirectionLockSideIndeterminate = false; - this.currentDirectionLockSide = dx <= dy ? 0 : 1; - } - - if (this.currentDirectionLockSide === 0) { - return new Vector(this.lastDragTile.x, mouseTile.y); - } else { - return new Vector(mouseTile.x, this.lastDragTile.y); - } - } - - /** - * Aborts the placement - */ - abortPlacement() { - if (this.currentMetaBuilding.get()) { - this.currentMetaBuilding.set(null); - return STOP_PROPAGATION; - } - } - - /** - * Aborts any dragging - */ - abortDragging() { - this.currentlyDragging = true; - this.currentlyDeleting = false; - this.initialPlacementVector = null; - this.lastDragTile = null; - } - - /** - * @see BaseHUDPart.update - */ - update() { - // Abort placement if a dialog was shown in the meantime - if (this.root.hud.hasBlockingOverlayOpen()) { - this.abortPlacement(); - return; - } - - // Always update since the camera might have moved - const mousePos = this.root.app.mousePosition; - if (mousePos) { - this.onMouseMove(mousePos); - } - - // Make sure we have nothing selected while in overview mode - if (this.root.camera.getIsMapOverlayActive()) { - if (this.currentMetaBuilding.get()) { - this.currentMetaBuilding.set(null); - } - } - } - - /** - * Tries to rotate the current building - */ - tryRotate() { - const selectedBuilding = this.currentMetaBuilding.get(); - if (selectedBuilding) { - if (this.root.keyMapper.getBinding(KEYMAPPINGS.placement.rotateInverseModifier).pressed) { - this.currentBaseRotation = (this.currentBaseRotation + 270) % 360; - } else { - this.currentBaseRotation = (this.currentBaseRotation + 90) % 360; - } - const staticComp = this.fakeEntity.components.StaticMapEntity; - staticComp.rotation = this.currentBaseRotation; - } - } - - /** - * Rotates the current building to the specified direction. - */ - trySetRotate() { - const selectedBuilding = this.currentMetaBuilding.get(); - if (selectedBuilding) { - if (this.root.keyMapper.getBinding(KEYMAPPINGS.placement.rotateToUp).pressed) { - this.currentBaseRotation = 0; - } else if (this.root.keyMapper.getBinding(KEYMAPPINGS.placement.rotateToDown).pressed) { - this.currentBaseRotation = 180; - } else if (this.root.keyMapper.getBinding(KEYMAPPINGS.placement.rotateToRight).pressed) { - this.currentBaseRotation = 90; - } else if (this.root.keyMapper.getBinding(KEYMAPPINGS.placement.rotateToLeft).pressed) { - this.currentBaseRotation = 270; - } - - const staticComp = this.fakeEntity.components.StaticMapEntity; - staticComp.rotation = this.currentBaseRotation; - } - } - - /** - * Tries to delete the building under the mouse - */ - deleteBelowCursor() { - const mousePosition = this.root.app.mousePosition; - if (!mousePosition) { - // Not on screen - return false; - } - - const worldPos = this.root.camera.screenToWorld(mousePosition); - const tile = worldPos.toTileSpace(); - const contents = this.root.map.getTileContent(tile, this.root.currentLayer); - if (contents) { - if (this.root.logic.tryDeleteBuilding(contents)) { - this.root.soundProxy.playUi(SOUNDS.destroyBuilding); - return true; - } - } - return false; - } - - /** - * Starts the pipette function - */ - startPipette() { - // Disable in overview - if (this.root.camera.getIsMapOverlayActive()) { - return; - } - - const mousePosition = this.root.app.mousePosition; - if (!mousePosition) { - // Not on screen - return; - } - - const worldPos = this.root.camera.screenToWorld(mousePosition); - const tile = worldPos.toTileSpace(); - - const contents = this.root.map.getTileContent(tile, this.root.currentLayer); - if (!contents) { - const tileBelow = this.root.map.getLowerLayerContentXY(tile.x, tile.y); - - // Check if there's a shape or color item below, if so select the miner - if ( - tileBelow && - this.root.app.settings.getAllSettings().pickMinerOnPatch && - this.root.currentLayer === "regular" && - this.root.gameMode.hasResources() - ) { - this.currentMetaBuilding.set(gMetaBuildingRegistry.findByClass(MetaMinerBuilding)); - - // Select chained miner if available, since that's always desired once unlocked - if (this.root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_miner_chainable)) { - this.currentVariant.set(enumMinerVariants.chainable); - } - } else { - this.currentMetaBuilding.set(null); - } - return; - } - - // Try to extract the building - const buildingCode = contents.components.StaticMapEntity.code; - const extracted = getBuildingDataFromCode(buildingCode); - - // Disable pipetting the hub - if (extracted.metaInstance.getId() === gMetaBuildingRegistry.findByClass(MetaHubBuilding).getId()) { - this.currentMetaBuilding.set(null); - return; - } - - // Disallow picking excluded buildings - if (this.root.gameMode.isBuildingExcluded(extracted.metaClass)) { - this.currentMetaBuilding.set(null); - return; - } - - // If the building we are picking is the same as the one we have, clear the cursor. - if ( - this.currentMetaBuilding.get() && - extracted.metaInstance.getId() === this.currentMetaBuilding.get().getId() && - extracted.variant === this.currentVariant.get() - ) { - this.currentMetaBuilding.set(null); - return; - } - - this.currentMetaBuilding.set(extracted.metaInstance); - this.currentVariant.set(extracted.variant); - this.currentBaseRotation = contents.components.StaticMapEntity.rotation; - } - - /** - * Switches the side for the direction lock manually - */ - switchDirectionLockSide() { - this.currentDirectionLockSide = 1 - this.currentDirectionLockSide; - } - - /** - * Checks if the direction lock key got released and if such, resets the placement - * @param {any} args - */ - checkForDirectionLockSwitch({ keyCode }) { - if ( - keyCode === - this.root.keyMapper.getBinding(KEYMAPPINGS.placementModifiers.lockBeltDirection).keyCode - ) { - this.abortDragging(); - } - } - - /** - * Tries to place the current building at the given tile - * @param {Vector} tile - */ - tryPlaceCurrentBuildingAt(tile) { - if (this.root.camera.getIsMapOverlayActive()) { - // Dont allow placing in overview mode - return; - } - - const metaBuilding = this.currentMetaBuilding.get(); - const { rotation, rotationVariant } = metaBuilding.computeOptimalDirectionAndRotationVariantAtTile({ - root: this.root, - tile, - rotation: this.currentBaseRotation, - variant: this.currentVariant.get(), - layer: metaBuilding.getLayer(), - }); - - const entity = this.root.logic.tryPlaceBuilding({ - origin: tile, - rotation, - rotationVariant, - originalRotation: this.currentBaseRotation, - building: this.currentMetaBuilding.get(), - variant: this.currentVariant.get(), - }); - - if (entity) { - // Succesfully placed, find which entity we actually placed - this.root.signals.entityManuallyPlaced.dispatch(entity); - - // Check if we should flip the orientation (used for tunnels) - if ( - metaBuilding.getFlipOrientationAfterPlacement() && - !this.root.keyMapper.getBinding( - KEYMAPPINGS.placementModifiers.placementDisableAutoOrientation - ).pressed - ) { - this.currentBaseRotation = (180 + this.currentBaseRotation) % 360; - } - - // Check if we should stop placement - if ( - !metaBuilding.getStayInPlacementMode() && - !this.root.keyMapper.getBinding(KEYMAPPINGS.placementModifiers.placeMultiple).pressed && - !this.root.app.settings.getAllSettings().alwaysMultiplace - ) { - // Stop placement - this.currentMetaBuilding.set(null); - } - return true; - } else { - return false; - } - } - - /** - * Cycles through the variants - */ - cycleVariants() { - const metaBuilding = this.currentMetaBuilding.get(); - if (!metaBuilding) { - this.currentVariant.set(defaultBuildingVariant); - } else { - const availableVariants = metaBuilding.getAvailableVariants(this.root); - let index = availableVariants.indexOf(this.currentVariant.get()); - if (index < 0) { - index = 0; - console.warn("Invalid variant selected:", this.currentVariant.get()); - } - const direction = this.root.keyMapper.getBinding(KEYMAPPINGS.placement.rotateInverseModifier) - .pressed - ? -1 - : 1; - - const newIndex = safeModulo(index + direction, availableVariants.length); - const newVariant = availableVariants[newIndex]; - this.setVariant(newVariant); - } - } - - /** - * Sets the current variant to the given variant - * @param {string} variant - */ - setVariant(variant) { - const metaBuilding = this.currentMetaBuilding.get(); - this.currentVariant.set(variant); - - this.preferredVariants[metaBuilding.getId()] = variant; - } - - /** - * Performs the direction locked placement between two points after - * releasing the mouse - */ - executeDirectionLockedPlacement() { - const metaBuilding = this.currentMetaBuilding.get(); - if (!metaBuilding) { - // No active building - return; - } - - // Get path to place - const path = this.computeDirectionLockPath(); - - // Store if we placed anything - let anythingPlaced = false; - - // Perform this in bulk to avoid recalculations - this.root.logic.performBulkOperation(() => { - for (let i = 0; i < path.length; ++i) { - const { rotation, tile } = path[i]; - this.currentBaseRotation = rotation; - if (this.tryPlaceCurrentBuildingAt(tile)) { - anythingPlaced = true; - } - } - }); - - if (anythingPlaced) { - this.root.soundProxy.playUi(metaBuilding.getPlacementSound()); - } - } - - /** - * Finds the path which the current direction lock will use - * @returns {Array<{ tile: Vector, rotation: number }>} - */ - computeDirectionLockPath() { - const mousePosition = this.root.app.mousePosition; - if (!mousePosition) { - // Not on screen - return []; - } - - let result = []; - - // Figure which points the line visits - const worldPos = this.root.camera.screenToWorld(mousePosition); - let endTile = worldPos.toTileSpace(); - let startTile = this.lastDragTile; - - // if the alt key is pressed, reverse belt planner direction by switching start and end tile - if (this.root.keyMapper.getBinding(KEYMAPPINGS.placementModifiers.placeInverse).pressed) { - let tmp = startTile; - startTile = endTile; - endTile = tmp; - } - - // Place from start to corner - const pathToCorner = this.currentDirectionLockCorner.sub(startTile); - const deltaToCorner = pathToCorner.normalize().round(); - const lengthToCorner = Math.round(pathToCorner.length()); - let currentPos = startTile.copy(); - - let rotation = (Math.round(Math.degrees(deltaToCorner.angle()) / 90) * 90 + 360) % 360; - - if (lengthToCorner > 0) { - for (let i = 0; i < lengthToCorner; ++i) { - result.push({ - tile: currentPos.copy(), - rotation, - }); - currentPos.addInplace(deltaToCorner); - } - } - - // Place from corner to end - const pathFromCorner = endTile.sub(this.currentDirectionLockCorner); - const deltaFromCorner = pathFromCorner.normalize().round(); - const lengthFromCorner = Math.round(pathFromCorner.length()); - - if (lengthFromCorner > 0) { - rotation = (Math.round(Math.degrees(deltaFromCorner.angle()) / 90) * 90 + 360) % 360; - for (let i = 0; i < lengthFromCorner + 1; ++i) { - result.push({ - tile: currentPos.copy(), - rotation, - }); - currentPos.addInplace(deltaFromCorner); - } - } else { - // Finish last one - result.push({ - tile: currentPos.copy(), - rotation, - }); - } - return result; - } - - /** - * Selects a given building - * @param {MetaBuilding} metaBuilding - */ - startSelection(metaBuilding) { - this.currentMetaBuilding.set(metaBuilding); - } - - /** - * Called when the selected buildings changed - * @param {MetaBuilding} metaBuilding - */ - onSelectedMetaBuildingChanged(metaBuilding) { - this.abortDragging(); - this.root.hud.signals.selectedPlacementBuildingChanged.dispatch(metaBuilding); - if (metaBuilding) { - const availableVariants = metaBuilding.getAvailableVariants(this.root); - const preferredVariant = this.preferredVariants[metaBuilding.getId()]; - - // Choose last stored variant if possible, otherwise the default one - let variant; - if (!preferredVariant || !availableVariants.includes(preferredVariant)) { - variant = availableVariants[0]; - } else { - variant = preferredVariant; - } - - this.currentVariant.set(variant); - - this.fakeEntity = new Entity(null); - metaBuilding.setupEntityComponents(this.fakeEntity, null); - - this.fakeEntity.addComponent( - new StaticMapEntityComponent({ - origin: new Vector(0, 0), - rotation: 0, - tileSize: metaBuilding.getDimensions(this.currentVariant.get()).copy(), - code: getCodeFromBuildingData(metaBuilding, variant, 0), - }) - ); - metaBuilding.updateVariants(this.fakeEntity, 0, this.currentVariant.get()); - } else { - this.fakeEntity = null; - } - - // Since it depends on both, rerender twice - this.signals.variantChanged.dispatch(); - } - - /** - * mouse down pre handler - * @param {Vector} pos - * @param {enumMouseButton} button - */ - onMouseDown(pos, button) { - if (this.root.camera.getIsMapOverlayActive()) { - // We do not allow dragging if the overlay is active - return; - } - - const metaBuilding = this.currentMetaBuilding.get(); - - // Placement - if (button === enumMouseButton.left && metaBuilding) { - this.currentlyDragging = true; - this.currentlyDeleting = false; - this.lastDragTile = this.root.camera.screenToWorld(pos).toTileSpace(); - - // Place initial building, but only if direction lock is not active - if (!this.isDirectionLockActive) { - if (this.tryPlaceCurrentBuildingAt(this.lastDragTile)) { - this.root.soundProxy.playUi(metaBuilding.getPlacementSound()); - } - } - return STOP_PROPAGATION; - } - - // Deletion - if ( - button === enumMouseButton.right && - (!metaBuilding || !this.root.app.settings.getAllSettings().clearCursorOnDeleteWhilePlacing) - ) { - this.currentlyDragging = true; - this.currentlyDeleting = true; - this.lastDragTile = this.root.camera.screenToWorld(pos).toTileSpace(); - if (this.deleteBelowCursor()) { - return STOP_PROPAGATION; - } - } - - // Cancel placement - if (button === enumMouseButton.right && metaBuilding) { - this.currentMetaBuilding.set(null); - } - } - - /** - * mouse move pre handler - * @param {Vector} pos - */ - onMouseMove(pos) { - if (this.root.camera.getIsMapOverlayActive()) { - return; - } - - // Check for direction lock - if (this.isDirectionLockActive) { - return; - } - - const metaBuilding = this.currentMetaBuilding.get(); - if ((metaBuilding || this.currentlyDeleting) && this.lastDragTile) { - const oldPos = this.lastDragTile; - let newPos = this.root.camera.screenToWorld(pos).toTileSpace(); - - // Check if camera is moving, since then we do nothing - if (this.root.camera.desiredCenter) { - this.lastDragTile = newPos; - return; - } - - // Check if anything changed - if (!oldPos.equals(newPos)) { - // Automatic Direction - if ( - metaBuilding && - metaBuilding.getRotateAutomaticallyWhilePlacing(this.currentVariant.get()) && - !this.root.keyMapper.getBinding( - KEYMAPPINGS.placementModifiers.placementDisableAutoOrientation - ).pressed - ) { - const delta = newPos.sub(oldPos); - const angleDeg = Math.degrees(delta.angle()); - this.currentBaseRotation = (Math.round(angleDeg / 90) * 90 + 360) % 360; - - // Holding alt inverts the placement - if (this.root.keyMapper.getBinding(KEYMAPPINGS.placementModifiers.placeInverse).pressed) { - this.currentBaseRotation = (180 + this.currentBaseRotation) % 360; - } - } - - // bresenham - let x0 = oldPos.x; - let y0 = oldPos.y; - let x1 = newPos.x; - let y1 = newPos.y; - - var dx = Math.abs(x1 - x0); - var dy = Math.abs(y1 - y0); - var sx = x0 < x1 ? 1 : -1; - var sy = y0 < y1 ? 1 : -1; - var err = dx - dy; - - let anythingPlaced = false; - let anythingDeleted = false; - - while (this.currentlyDeleting || this.currentMetaBuilding.get()) { - if (this.currentlyDeleting) { - // Deletion - const contents = this.root.map.getLayerContentXY(x0, y0, this.root.currentLayer); - if (contents && !contents.queuedForDestroy && !contents.destroyed) { - if (this.root.logic.tryDeleteBuilding(contents)) { - anythingDeleted = true; - } - } - } else { - // Placement - if (this.tryPlaceCurrentBuildingAt(new Vector(x0, y0))) { - anythingPlaced = true; - } - } - - if (x0 === x1 && y0 === y1) break; - var e2 = 2 * err; - if (e2 > -dy) { - err -= dy; - x0 += sx; - } - if (e2 < dx) { - err += dx; - y0 += sy; - } - } - - if (anythingPlaced) { - this.root.soundProxy.playUi(metaBuilding.getPlacementSound()); - } - if (anythingDeleted) { - this.root.soundProxy.playUi(SOUNDS.destroyBuilding); - } - } - - this.lastDragTile = newPos; - return STOP_PROPAGATION; - } - } - - /** - * Mouse up handler - */ - onMouseUp() { - if (this.root.camera.getIsMapOverlayActive()) { - return; - } - - // Check for direction lock - if (this.lastDragTile && this.currentlyDragging && this.isDirectionLockActive) { - this.executeDirectionLockedPlacement(); - } - - this.abortDragging(); - } -} +import { gMetaBuildingRegistry } from "../../../core/global_registries"; +import { Signal, STOP_PROPAGATION } from "../../../core/signal"; +import { TrackedState } from "../../../core/tracked_state"; +import { safeModulo } from "../../../core/utils"; +import { Vector } from "../../../core/vector"; +import { SOUNDS } from "../../../platform/sound"; +import { getBuildingDataFromCode, getCodeFromBuildingData } from "../../building_codes"; +import { MetaHubBuilding } from "../../buildings/hub"; +import { enumMinerVariants, MetaMinerBuilding } from "../../buildings/miner"; +import { enumMouseButton } from "../../camera"; +import { StaticMapEntityComponent } from "../../components/static_map_entity"; +import { Entity } from "../../entity"; +import { KEYMAPPINGS } from "../../key_action_mapper"; +import { defaultBuildingVariant, MetaBuilding } from "../../meta_building"; +import { enumHubGoalRewards } from "../../tutorial_goals"; +import { BaseHUDPart } from "../base_hud_part"; + +/** + * Contains all logic for the building placer - this doesn't include the rendering + * of info boxes or drawing. + */ +export class HUDBuildingPlacerLogic extends BaseHUDPart { + /** + * Initializes the logic + * @see BaseHUDPart.initialize + */ + initialize() { + /** + * We use a fake entity to get information about how a building will look + * once placed + * @type {Entity} + */ + this.fakeEntity = null; + + // Signals + this.signals = { + variantChanged: new Signal(), + draggingStarted: new Signal(), + }; + + /** + * The current building + * @type {TypedTrackedState} + */ + this.currentMetaBuilding = new TrackedState(this.onSelectedMetaBuildingChanged, this); + + /** + * The current rotation + * @type {number} + */ + this.currentBaseRotationGeneral = 0; + + /** + * The current rotation preference for each building. + * @type{Object.} + */ + this.preferredBaseRotations = {}; + + /** + * Whether we are currently dragging + * @type {boolean} + */ + this.currentlyDragging = false; + + /** + * Current building variant + * @type {TypedTrackedState} + */ + this.currentVariant = new TrackedState(() => this.signals.variantChanged.dispatch()); + + /** + * Whether we are currently drag-deleting + * @type {boolean} + */ + this.currentlyDeleting = false; + + /** + * Stores which variants for each building we prefer, this is based on what + * the user last selected + * @type {Object.} + */ + this.preferredVariants = {}; + + /** + * The tile we last dragged from + * @type {Vector} + */ + this.lastDragTile = null; + + /** + * The side for direction lock + * @type {number} (0|1) + */ + this.currentDirectionLockSide = 0; + + /** + * Whether the side for direction lock has not yet been determined. + * @type {boolean} + */ + this.currentDirectionLockSideIndeterminate = true; + + this.initializeBindings(); + } + + /** + * Initializes all bindings + */ + initializeBindings() { + // KEYBINDINGS + const keyActionMapper = this.root.keyMapper; + keyActionMapper.getBinding(KEYMAPPINGS.placement.rotateWhilePlacing).add(this.tryRotate, this); + + keyActionMapper.getBinding(KEYMAPPINGS.placement.rotateToUp).add(this.trySetRotate, this); + keyActionMapper.getBinding(KEYMAPPINGS.placement.rotateToDown).add(this.trySetRotate, this); + keyActionMapper.getBinding(KEYMAPPINGS.placement.rotateToRight).add(this.trySetRotate, this); + keyActionMapper.getBinding(KEYMAPPINGS.placement.rotateToLeft).add(this.trySetRotate, this); + + keyActionMapper.getBinding(KEYMAPPINGS.placement.cycleBuildingVariants).add(this.cycleVariants, this); + keyActionMapper + .getBinding(KEYMAPPINGS.placement.switchDirectionLockSide) + .add(this.switchDirectionLockSide, this); + keyActionMapper.getBinding(KEYMAPPINGS.general.back).add(this.abortPlacement, this); + keyActionMapper.getBinding(KEYMAPPINGS.placement.pipette).add(this.startPipette, this); + this.root.gameState.inputReceiver.keyup.add(this.checkForDirectionLockSwitch, this); + + // BINDINGS TO GAME EVENTS + this.root.hud.signals.buildingsSelectedForCopy.add(this.abortPlacement, this); + this.root.hud.signals.pasteBlueprintRequested.add(this.abortPlacement, this); + this.root.signals.storyGoalCompleted.add(() => this.signals.variantChanged.dispatch()); + this.root.signals.upgradePurchased.add(() => this.signals.variantChanged.dispatch()); + this.root.signals.editModeChanged.add(this.onEditModeChanged, this); + + // MOUSE BINDINGS + this.root.camera.downPreHandler.add(this.onMouseDown, this); + this.root.camera.movePreHandler.add(this.onMouseMove, this); + this.root.camera.upPostHandler.add(this.onMouseUp, this); + } + + /** + * Called when the edit mode got changed + * @param {Layer} layer + */ + onEditModeChanged(layer) { + const metaBuilding = this.currentMetaBuilding.get(); + if (metaBuilding) { + if (metaBuilding.getLayer() !== layer) { + // This layer doesn't fit the edit mode anymore + this.currentMetaBuilding.set(null); + } + } + } + + /** + * Returns the current base rotation for the current meta-building. + * @returns {number} + */ + get currentBaseRotation() { + if (!this.root.app.settings.getAllSettings().rotationByBuilding) { + return this.currentBaseRotationGeneral; + } + const metaBuilding = this.currentMetaBuilding.get(); + if (metaBuilding && Object.hasOwn(this.preferredBaseRotations, metaBuilding.getId())) { + return this.preferredBaseRotations[metaBuilding.getId()]; + } else { + return this.currentBaseRotationGeneral; + } + } + + /** + * Sets the base rotation for the current meta-building. + * @param {number} rotation The new rotation/angle. + */ + set currentBaseRotation(rotation) { + if (!this.root.app.settings.getAllSettings().rotationByBuilding) { + this.currentBaseRotationGeneral = rotation; + } else { + const metaBuilding = this.currentMetaBuilding.get(); + if (metaBuilding) { + this.preferredBaseRotations[metaBuilding.getId()] = rotation; + } else { + this.currentBaseRotationGeneral = rotation; + } + } + } + + /** + * Returns if the direction lock is currently active + * @returns {boolean} + */ + get isDirectionLockActive() { + const metaBuilding = this.currentMetaBuilding.get(); + return ( + metaBuilding && + metaBuilding.getHasDirectionLockAvailable(this.currentVariant.get()) && + this.root.keyMapper.getBinding(KEYMAPPINGS.placementModifiers.lockBeltDirection).pressed + ); + } + + /** + * Returns the current direction lock corner, that is, the corner between + * mouse and original start point + * @returns {Vector|null} + */ + get currentDirectionLockCorner() { + const mousePosition = this.root.app.mousePosition; + if (!mousePosition) { + // Not on screen + return null; + } + + if (!this.lastDragTile) { + // Haven't dragged yet + return null; + } + + // Figure which points the line visits + const worldPos = this.root.camera.screenToWorld(mousePosition); + const mouseTile = worldPos.toTileSpace(); + + // Figure initial direction + const dx = Math.abs(this.lastDragTile.x - mouseTile.x); + const dy = Math.abs(this.lastDragTile.y - mouseTile.y); + if (dx === 0 && dy === 0) { + // Back at the start. Try a new direction. + this.currentDirectionLockSideIndeterminate = true; + } else if (this.currentDirectionLockSideIndeterminate) { + this.currentDirectionLockSideIndeterminate = false; + this.currentDirectionLockSide = dx <= dy ? 0 : 1; + } + + if (this.currentDirectionLockSide === 0) { + return new Vector(this.lastDragTile.x, mouseTile.y); + } else { + return new Vector(mouseTile.x, this.lastDragTile.y); + } + } + + /** + * Aborts the placement + */ + abortPlacement() { + if (this.currentMetaBuilding.get()) { + this.currentMetaBuilding.set(null); + return STOP_PROPAGATION; + } + } + + /** + * Aborts any dragging + */ + abortDragging() { + this.currentlyDragging = true; + this.currentlyDeleting = false; + this.initialPlacementVector = null; + this.lastDragTile = null; + } + + /** + * @see BaseHUDPart.update + */ + update() { + // Abort placement if a dialog was shown in the meantime + if (this.root.hud.hasBlockingOverlayOpen()) { + this.abortPlacement(); + return; + } + + // Always update since the camera might have moved + const mousePos = this.root.app.mousePosition; + if (mousePos) { + this.onMouseMove(mousePos); + } + + // Make sure we have nothing selected while in overview mode + if (this.root.camera.getIsMapOverlayActive()) { + if (this.currentMetaBuilding.get()) { + this.currentMetaBuilding.set(null); + } + } + } + + /** + * Tries to rotate the current building + */ + tryRotate() { + const selectedBuilding = this.currentMetaBuilding.get(); + if (selectedBuilding) { + if (this.root.keyMapper.getBinding(KEYMAPPINGS.placement.rotateInverseModifier).pressed) { + this.currentBaseRotation = (this.currentBaseRotation + 270) % 360; + } else { + this.currentBaseRotation = (this.currentBaseRotation + 90) % 360; + } + const staticComp = this.fakeEntity.components.StaticMapEntity; + staticComp.rotation = this.currentBaseRotation; + } + } + + /** + * Rotates the current building to the specified direction. + */ + trySetRotate() { + const selectedBuilding = this.currentMetaBuilding.get(); + if (selectedBuilding) { + if (this.root.keyMapper.getBinding(KEYMAPPINGS.placement.rotateToUp).pressed) { + this.currentBaseRotation = 0; + } else if (this.root.keyMapper.getBinding(KEYMAPPINGS.placement.rotateToDown).pressed) { + this.currentBaseRotation = 180; + } else if (this.root.keyMapper.getBinding(KEYMAPPINGS.placement.rotateToRight).pressed) { + this.currentBaseRotation = 90; + } else if (this.root.keyMapper.getBinding(KEYMAPPINGS.placement.rotateToLeft).pressed) { + this.currentBaseRotation = 270; + } + + const staticComp = this.fakeEntity.components.StaticMapEntity; + staticComp.rotation = this.currentBaseRotation; + } + } + + /** + * Tries to delete the building under the mouse + */ + deleteBelowCursor() { + const mousePosition = this.root.app.mousePosition; + if (!mousePosition) { + // Not on screen + return false; + } + + const worldPos = this.root.camera.screenToWorld(mousePosition); + const tile = worldPos.toTileSpace(); + const contents = this.root.map.getTileContent(tile, this.root.currentLayer); + if (contents) { + if (this.root.logic.tryDeleteBuilding(contents)) { + this.root.soundProxy.playUi(SOUNDS.destroyBuilding); + return true; + } + } + return false; + } + + /** + * Starts the pipette function + */ + startPipette() { + // Disable in overview + if (this.root.camera.getIsMapOverlayActive()) { + return; + } + + const mousePosition = this.root.app.mousePosition; + if (!mousePosition) { + // Not on screen + return; + } + + const worldPos = this.root.camera.screenToWorld(mousePosition); + const tile = worldPos.toTileSpace(); + + const contents = this.root.map.getTileContent(tile, this.root.currentLayer); + if (!contents) { + const tileBelow = this.root.map.getLowerLayerContentXY(tile.x, tile.y); + + // Check if there's a shape or color item below, if so select the miner + if ( + tileBelow && + this.root.app.settings.getAllSettings().pickMinerOnPatch && + this.root.currentLayer === "regular" && + this.root.gameMode.hasResources() + ) { + this.currentMetaBuilding.set(gMetaBuildingRegistry.findByClass(MetaMinerBuilding)); + + // Select chained miner if available, since that's always desired once unlocked + if (this.root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_miner_chainable)) { + this.currentVariant.set(enumMinerVariants.chainable); + } + } else { + this.currentMetaBuilding.set(null); + } + return; + } + + // Try to extract the building + const buildingCode = contents.components.StaticMapEntity.code; + const extracted = getBuildingDataFromCode(buildingCode); + + // Disable pipetting the hub + if (extracted.metaInstance.getId() === gMetaBuildingRegistry.findByClass(MetaHubBuilding).getId()) { + this.currentMetaBuilding.set(null); + return; + } + + // Disallow picking excluded buildings + if (this.root.gameMode.isBuildingExcluded(extracted.metaClass)) { + this.currentMetaBuilding.set(null); + return; + } + + // If the building we are picking is the same as the one we have, clear the cursor. + if ( + this.currentMetaBuilding.get() && + extracted.metaInstance.getId() === this.currentMetaBuilding.get().getId() && + extracted.variant === this.currentVariant.get() + ) { + this.currentMetaBuilding.set(null); + return; + } + + this.currentMetaBuilding.set(extracted.metaInstance); + this.currentVariant.set(extracted.variant); + this.currentBaseRotation = contents.components.StaticMapEntity.rotation; + } + + /** + * Switches the side for the direction lock manually + */ + switchDirectionLockSide() { + this.currentDirectionLockSide = 1 - this.currentDirectionLockSide; + } + + /** + * Checks if the direction lock key got released and if such, resets the placement + * @param {any} args + */ + checkForDirectionLockSwitch({ keyCode }) { + if ( + keyCode === + this.root.keyMapper.getBinding(KEYMAPPINGS.placementModifiers.lockBeltDirection).keyCode + ) { + this.abortDragging(); + } + } + + /** + * Tries to place the current building at the given tile + * @param {Vector} tile + */ + tryPlaceCurrentBuildingAt(tile) { + if (this.root.camera.getIsMapOverlayActive()) { + // Dont allow placing in overview mode + return; + } + + const metaBuilding = this.currentMetaBuilding.get(); + const { rotation, rotationVariant } = metaBuilding.computeOptimalDirectionAndRotationVariantAtTile({ + root: this.root, + tile, + rotation: this.currentBaseRotation, + variant: this.currentVariant.get(), + layer: metaBuilding.getLayer(), + }); + + const entity = this.root.logic.tryPlaceBuilding({ + origin: tile, + rotation, + rotationVariant, + originalRotation: this.currentBaseRotation, + building: this.currentMetaBuilding.get(), + variant: this.currentVariant.get(), + }); + + if (entity) { + // Succesfully placed, find which entity we actually placed + this.root.signals.entityManuallyPlaced.dispatch(entity); + + // Check if we should flip the orientation (used for tunnels) + if ( + metaBuilding.getFlipOrientationAfterPlacement() && + !this.root.keyMapper.getBinding( + KEYMAPPINGS.placementModifiers.placementDisableAutoOrientation + ).pressed + ) { + this.currentBaseRotation = (180 + this.currentBaseRotation) % 360; + } + + // Check if we should stop placement + if ( + !metaBuilding.getStayInPlacementMode() && + !this.root.keyMapper.getBinding(KEYMAPPINGS.placementModifiers.placeMultiple).pressed && + !this.root.app.settings.getAllSettings().alwaysMultiplace + ) { + // Stop placement + this.currentMetaBuilding.set(null); + } + return true; + } else { + return false; + } + } + + /** + * Cycles through the variants + */ + cycleVariants() { + const metaBuilding = this.currentMetaBuilding.get(); + if (!metaBuilding) { + this.currentVariant.set(defaultBuildingVariant); + } else { + const availableVariants = metaBuilding.getAvailableVariants(this.root); + let index = availableVariants.indexOf(this.currentVariant.get()); + if (index < 0) { + index = 0; + console.warn("Invalid variant selected:", this.currentVariant.get()); + } + const direction = this.root.keyMapper.getBinding(KEYMAPPINGS.placement.rotateInverseModifier) + .pressed + ? -1 + : 1; + + const newIndex = safeModulo(index + direction, availableVariants.length); + const newVariant = availableVariants[newIndex]; + this.setVariant(newVariant); + } + } + + /** + * Sets the current variant to the given variant + * @param {string} variant + */ + setVariant(variant) { + const metaBuilding = this.currentMetaBuilding.get(); + this.currentVariant.set(variant); + + this.preferredVariants[metaBuilding.getId()] = variant; + } + + /** + * Performs the direction locked placement between two points after + * releasing the mouse + */ + executeDirectionLockedPlacement() { + const metaBuilding = this.currentMetaBuilding.get(); + if (!metaBuilding) { + // No active building + return; + } + + // Get path to place + const path = this.computeDirectionLockPath(); + + // Store if we placed anything + let anythingPlaced = false; + + // Perform this in bulk to avoid recalculations + this.root.logic.performBulkOperation(() => { + for (let i = 0; i < path.length; ++i) { + const { rotation, tile } = path[i]; + this.currentBaseRotation = rotation; + if (this.tryPlaceCurrentBuildingAt(tile)) { + anythingPlaced = true; + } + } + }); + + if (anythingPlaced) { + this.root.soundProxy.playUi(metaBuilding.getPlacementSound()); + } + } + + /** + * Finds the path which the current direction lock will use + * @returns {Array<{ tile: Vector, rotation: number }>} + */ + computeDirectionLockPath() { + const mousePosition = this.root.app.mousePosition; + if (!mousePosition) { + // Not on screen + return []; + } + + let result = []; + + // Figure which points the line visits + const worldPos = this.root.camera.screenToWorld(mousePosition); + let endTile = worldPos.toTileSpace(); + let startTile = this.lastDragTile; + + // if the alt key is pressed, reverse belt planner direction by switching start and end tile + if (this.root.keyMapper.getBinding(KEYMAPPINGS.placementModifiers.placeInverse).pressed) { + let tmp = startTile; + startTile = endTile; + endTile = tmp; + } + + // Place from start to corner + const pathToCorner = this.currentDirectionLockCorner.sub(startTile); + const deltaToCorner = pathToCorner.normalize().round(); + const lengthToCorner = Math.round(pathToCorner.length()); + let currentPos = startTile.copy(); + + let rotation = (Math.round(Math.degrees(deltaToCorner.angle()) / 90) * 90 + 360) % 360; + + if (lengthToCorner > 0) { + for (let i = 0; i < lengthToCorner; ++i) { + result.push({ + tile: currentPos.copy(), + rotation, + }); + currentPos.addInplace(deltaToCorner); + } + } + + // Place from corner to end + const pathFromCorner = endTile.sub(this.currentDirectionLockCorner); + const deltaFromCorner = pathFromCorner.normalize().round(); + const lengthFromCorner = Math.round(pathFromCorner.length()); + + if (lengthFromCorner > 0) { + rotation = (Math.round(Math.degrees(deltaFromCorner.angle()) / 90) * 90 + 360) % 360; + for (let i = 0; i < lengthFromCorner + 1; ++i) { + result.push({ + tile: currentPos.copy(), + rotation, + }); + currentPos.addInplace(deltaFromCorner); + } + } else { + // Finish last one + result.push({ + tile: currentPos.copy(), + rotation, + }); + } + return result; + } + + /** + * Selects a given building + * @param {MetaBuilding} metaBuilding + */ + startSelection(metaBuilding) { + this.currentMetaBuilding.set(metaBuilding); + } + + /** + * Called when the selected buildings changed + * @param {MetaBuilding} metaBuilding + */ + onSelectedMetaBuildingChanged(metaBuilding) { + this.abortDragging(); + this.root.hud.signals.selectedPlacementBuildingChanged.dispatch(metaBuilding); + if (metaBuilding) { + const availableVariants = metaBuilding.getAvailableVariants(this.root); + const preferredVariant = this.preferredVariants[metaBuilding.getId()]; + + // Choose last stored variant if possible, otherwise the default one + let variant; + if (!preferredVariant || !availableVariants.includes(preferredVariant)) { + variant = availableVariants[0]; + } else { + variant = preferredVariant; + } + + this.currentVariant.set(variant); + + this.fakeEntity = new Entity(null); + metaBuilding.setupEntityComponents(this.fakeEntity, null); + + this.fakeEntity.addComponent( + new StaticMapEntityComponent({ + origin: new Vector(0, 0), + rotation: 0, + tileSize: metaBuilding.getDimensions(this.currentVariant.get()).copy(), + code: getCodeFromBuildingData(metaBuilding, variant, 0), + }) + ); + metaBuilding.updateVariants(this.fakeEntity, 0, this.currentVariant.get()); + } else { + this.fakeEntity = null; + } + + // Since it depends on both, rerender twice + this.signals.variantChanged.dispatch(); + } + + /** + * mouse down pre handler + * @param {Vector} pos + * @param {enumMouseButton} button + */ + onMouseDown(pos, button) { + if (this.root.camera.getIsMapOverlayActive()) { + // We do not allow dragging if the overlay is active + return; + } + + const metaBuilding = this.currentMetaBuilding.get(); + + // Placement + if (button === enumMouseButton.left && metaBuilding) { + this.currentlyDragging = true; + this.currentlyDeleting = false; + this.lastDragTile = this.root.camera.screenToWorld(pos).toTileSpace(); + + // Place initial building, but only if direction lock is not active + if (!this.isDirectionLockActive) { + if (this.tryPlaceCurrentBuildingAt(this.lastDragTile)) { + this.root.soundProxy.playUi(metaBuilding.getPlacementSound()); + } + } + return STOP_PROPAGATION; + } + + // Deletion + if ( + button === enumMouseButton.right && + (!metaBuilding || !this.root.app.settings.getAllSettings().clearCursorOnDeleteWhilePlacing) + ) { + this.currentlyDragging = true; + this.currentlyDeleting = true; + this.lastDragTile = this.root.camera.screenToWorld(pos).toTileSpace(); + if (this.deleteBelowCursor()) { + return STOP_PROPAGATION; + } + } + + // Cancel placement + if (button === enumMouseButton.right && metaBuilding) { + this.currentMetaBuilding.set(null); + } + } + + /** + * mouse move pre handler + * @param {Vector} pos + */ + onMouseMove(pos) { + if (this.root.camera.getIsMapOverlayActive()) { + return; + } + + // Check for direction lock + if (this.isDirectionLockActive) { + return; + } + + const metaBuilding = this.currentMetaBuilding.get(); + if ((metaBuilding || this.currentlyDeleting) && this.lastDragTile) { + const oldPos = this.lastDragTile; + let newPos = this.root.camera.screenToWorld(pos).toTileSpace(); + + // Check if camera is moving, since then we do nothing + if (this.root.camera.desiredCenter) { + this.lastDragTile = newPos; + return; + } + + // Check if anything changed + if (!oldPos.equals(newPos)) { + // Automatic Direction + if ( + metaBuilding && + metaBuilding.getRotateAutomaticallyWhilePlacing(this.currentVariant.get()) && + !this.root.keyMapper.getBinding( + KEYMAPPINGS.placementModifiers.placementDisableAutoOrientation + ).pressed + ) { + const delta = newPos.sub(oldPos); + const angleDeg = Math.degrees(delta.angle()); + this.currentBaseRotation = (Math.round(angleDeg / 90) * 90 + 360) % 360; + + // Holding alt inverts the placement + if (this.root.keyMapper.getBinding(KEYMAPPINGS.placementModifiers.placeInverse).pressed) { + this.currentBaseRotation = (180 + this.currentBaseRotation) % 360; + } + } + + // bresenham + let x0 = oldPos.x; + let y0 = oldPos.y; + let x1 = newPos.x; + let y1 = newPos.y; + + var dx = Math.abs(x1 - x0); + var dy = Math.abs(y1 - y0); + var sx = x0 < x1 ? 1 : -1; + var sy = y0 < y1 ? 1 : -1; + var err = dx - dy; + + let anythingPlaced = false; + let anythingDeleted = false; + + while (this.currentlyDeleting || this.currentMetaBuilding.get()) { + if (this.currentlyDeleting) { + // Deletion + const contents = this.root.map.getLayerContentXY(x0, y0, this.root.currentLayer); + if (contents && !contents.queuedForDestroy && !contents.destroyed) { + if (this.root.logic.tryDeleteBuilding(contents)) { + anythingDeleted = true; + } + } + } else { + // Placement + if (this.tryPlaceCurrentBuildingAt(new Vector(x0, y0))) { + anythingPlaced = true; + } + } + + if (x0 === x1 && y0 === y1) break; + var e2 = 2 * err; + if (e2 > -dy) { + err -= dy; + x0 += sx; + } + if (e2 < dx) { + err += dx; + y0 += sy; + } + } + + if (anythingPlaced) { + this.root.soundProxy.playUi(metaBuilding.getPlacementSound()); + } + if (anythingDeleted) { + this.root.soundProxy.playUi(SOUNDS.destroyBuilding); + } + } + + this.lastDragTile = newPos; + return STOP_PROPAGATION; + } + } + + /** + * Mouse up handler + */ + onMouseUp() { + if (this.root.camera.getIsMapOverlayActive()) { + return; + } + + // Check for direction lock + if (this.lastDragTile && this.currentlyDragging && this.isDirectionLockActive) { + this.executeDirectionLockedPlacement(); + } + + this.abortDragging(); + } +} diff --git a/src/js/game/hud/parts/buildings_toolbar.js b/src/js/game/hud/parts/buildings_toolbar.js index 994a70ed..2cbc11a2 100644 --- a/src/js/game/hud/parts/buildings_toolbar.js +++ b/src/js/game/hud/parts/buildings_toolbar.js @@ -1,53 +1,53 @@ -import { MetaBeltBuilding } from "../../buildings/belt"; -import { MetaCutterBuilding } from "../../buildings/cutter"; -import { MetaDisplayBuilding } from "../../buildings/display"; -import { MetaFilterBuilding } from "../../buildings/filter"; -import { MetaLeverBuilding } from "../../buildings/lever"; -import { MetaMinerBuilding } from "../../buildings/miner"; -import { MetaMixerBuilding } from "../../buildings/mixer"; -import { MetaPainterBuilding } from "../../buildings/painter"; -import { MetaReaderBuilding } from "../../buildings/reader"; -import { MetaRotaterBuilding } from "../../buildings/rotater"; -import { MetaBalancerBuilding } from "../../buildings/balancer"; -import { MetaStackerBuilding } from "../../buildings/stacker"; -import { MetaTrashBuilding } from "../../buildings/trash"; -import { MetaUndergroundBeltBuilding } from "../../buildings/underground_belt"; -import { HUDBaseToolbar } from "./base_toolbar"; -import { MetaStorageBuilding } from "../../buildings/storage"; -import { MetaItemProducerBuilding } from "../../buildings/item_producer"; -import { MetaConstantProducerBuilding } from "../../buildings/constant_producer"; -import { MetaGoalAcceptorBuilding } from "../../buildings/goal_acceptor"; -import { MetaBlockBuilding } from "../../buildings/block"; - -export class HUDBuildingsToolbar extends HUDBaseToolbar { - constructor(root) { - super(root, { - primaryBuildings: [ - MetaConstantProducerBuilding, - MetaGoalAcceptorBuilding, - MetaBeltBuilding, - MetaBalancerBuilding, - MetaUndergroundBeltBuilding, - MetaMinerBuilding, - MetaBlockBuilding, - MetaCutterBuilding, - MetaRotaterBuilding, - MetaStackerBuilding, - MetaMixerBuilding, - MetaPainterBuilding, - MetaTrashBuilding, - MetaItemProducerBuilding, - ], - secondaryBuildings: [ - MetaStorageBuilding, - MetaReaderBuilding, - MetaLeverBuilding, - MetaFilterBuilding, - MetaDisplayBuilding, - ], - visibilityCondition: () => - !this.root.camera.getIsMapOverlayActive() && this.root.currentLayer === "regular", - htmlElementId: "ingame_HUD_BuildingsToolbar", - }); - } -} +import { MetaBalancerBuilding } from "../../buildings/balancer"; +import { MetaBeltBuilding } from "../../buildings/belt"; +import { MetaBlockBuilding } from "../../buildings/block"; +import { MetaConstantProducerBuilding } from "../../buildings/constant_producer"; +import { MetaCutterBuilding } from "../../buildings/cutter"; +import { MetaDisplayBuilding } from "../../buildings/display"; +import { MetaFilterBuilding } from "../../buildings/filter"; +import { MetaGoalAcceptorBuilding } from "../../buildings/goal_acceptor"; +import { MetaItemProducerBuilding } from "../../buildings/item_producer"; +import { MetaLeverBuilding } from "../../buildings/lever"; +import { MetaMinerBuilding } from "../../buildings/miner"; +import { MetaMixerBuilding } from "../../buildings/mixer"; +import { MetaPainterBuilding } from "../../buildings/painter"; +import { MetaReaderBuilding } from "../../buildings/reader"; +import { MetaRotatorBuilding } from "../../buildings/rotator"; +import { MetaStackerBuilding } from "../../buildings/stacker"; +import { MetaStorageBuilding } from "../../buildings/storage"; +import { MetaTrashBuilding } from "../../buildings/trash"; +import { MetaUndergroundBeltBuilding } from "../../buildings/underground_belt"; +import { HUDBaseToolbar } from "./base_toolbar"; + +export class HUDBuildingsToolbar extends HUDBaseToolbar { + constructor(root) { + super(root, { + primaryBuildings: [ + MetaConstantProducerBuilding, + MetaGoalAcceptorBuilding, + MetaBeltBuilding, + MetaBalancerBuilding, + MetaUndergroundBeltBuilding, + MetaMinerBuilding, + MetaBlockBuilding, + MetaCutterBuilding, + MetaRotatorBuilding, + MetaStackerBuilding, + MetaMixerBuilding, + MetaPainterBuilding, + MetaTrashBuilding, + MetaItemProducerBuilding, + ], + secondaryBuildings: [ + MetaStorageBuilding, + MetaReaderBuilding, + MetaLeverBuilding, + MetaFilterBuilding, + MetaDisplayBuilding, + ], + visibilityCondition: () => + !this.root.camera.getIsMapOverlayActive() && this.root.currentLayer === "regular", + htmlElementId: "ingame_HUD_BuildingsToolbar", + }); + } +} diff --git a/src/js/game/hud/parts/constant_signal_edit.js b/src/js/game/hud/parts/constant_signal_edit.js index f7099046..ac5cb3d1 100644 --- a/src/js/game/hud/parts/constant_signal_edit.js +++ b/src/js/game/hud/parts/constant_signal_edit.js @@ -1,222 +1,221 @@ -import { THIRDPARTY_URLS } from "../../../core/config"; -import { DialogWithForm } from "../../../core/modal_dialog_elements"; -import { FormElementInput, FormElementItemChooser } from "../../../core/modal_dialog_forms"; -import { STOP_PROPAGATION } from "../../../core/signal"; -import { fillInLinkIntoTranslation } from "../../../core/utils"; -import { Vector } from "../../../core/vector"; -import { T } from "../../../translations"; -import { BaseItem } from "../../base_item"; -import { enumMouseButton } from "../../camera"; -import { Entity } from "../../entity"; -import { BOOL_FALSE_SINGLETON, BOOL_TRUE_SINGLETON } from "../../items/boolean_item"; -import { COLOR_ITEM_SINGLETONS } from "../../items/color_item"; -import { BaseHUDPart } from "../base_hud_part"; -import trim from "trim"; -import { enumColors } from "../../colors"; -import { ShapeDefinition } from "../../shape_definition"; - -/** @type {{ - * [x: string]: (entity: Entity) => BaseItem - * }} */ -export const MODS_ADDITIONAL_CONSTANT_SIGNAL_RESOLVER = {}; - -export class HUDConstantSignalEdit extends BaseHUDPart { - initialize() { - this.root.camera.downPreHandler.add(this.downPreHandler, this); - } - - /** - * @param {Vector} pos - * @param {enumMouseButton} button - */ - downPreHandler(pos, button) { - if (this.root.currentLayer !== "wires") { - return; - } - - const tile = this.root.camera.screenToWorld(pos).toTileSpace(); - const contents = this.root.map.getLayerContentXY(tile.x, tile.y, "wires"); - if (contents) { - const constantComp = contents.components.ConstantSignal; - if (constantComp) { - if (button === enumMouseButton.left) { - this.editConstantSignal(contents, { - deleteOnCancel: false, - }); - return STOP_PROPAGATION; - } - } - } - } - - /** - * Asks the entity to enter a valid signal code - * @param {Entity} entity - * @param {object} param0 - * @param {boolean=} param0.deleteOnCancel - */ - editConstantSignal(entity, { deleteOnCancel = true }) { - if (!entity.components.ConstantSignal) { - return; - } - - // Ok, query, but also save the uid because it could get stale - const uid = entity.uid; - - const signal = entity.components.ConstantSignal.signal; - const signalValueInput = new FormElementInput({ - id: "signalValue", - label: fillInLinkIntoTranslation(T.dialogs.editSignal.descShortKey, THIRDPARTY_URLS.shapeViewer), - placeholder: "", - defaultValue: signal ? signal.getAsCopyableKey() : "", - validator: val => this.parseSignalCode(entity, val), - }); - - const items = [...Object.values(COLOR_ITEM_SINGLETONS)]; - - if (entity.components.WiredPins) { - items.unshift(BOOL_FALSE_SINGLETON, BOOL_TRUE_SINGLETON); - items.push( - this.root.shapeDefinitionMgr.getShapeItemFromShortKey( - this.root.gameMode.getBlueprintShapeKey() - ) - ); - } else { - // producer which can produce virtually anything - const shapes = ["CuCuCuCu", "RuRuRuRu", "WuWuWuWu", "SuSuSuSu"]; - items.unshift( - ...shapes.reverse().map(key => this.root.shapeDefinitionMgr.getShapeItemFromShortKey(key)) - ); - } - - if (this.root.gameMode.hasHub()) { - items.push( - this.root.shapeDefinitionMgr.getShapeItemFromDefinition( - this.root.hubGoals.currentGoal.definition - ) - ); - } - - if (this.root.hud.parts.pinnedShapes) { - items.push( - ...this.root.hud.parts.pinnedShapes.pinnedShapes.map(key => - this.root.shapeDefinitionMgr.getShapeItemFromShortKey(key) - ) - ); - } - - const itemInput = new FormElementItemChooser({ - id: "signalItem", - label: null, - items, - }); - - const dialog = new DialogWithForm({ - app: this.root.app, - title: T.dialogs.editConstantProducer.title, - desc: T.dialogs.editSignal.descItems, - formElements: [itemInput, signalValueInput], - buttons: ["cancel:bad:escape", "ok:good:enter"], - closeButton: false, - }); - this.root.hud.parts.dialogs.internalShowDialog(dialog); - - // When confirmed, set the signal - const closeHandler = () => { - if (!this.root || !this.root.entityMgr) { - // Game got stopped - return; - } - - const entityRef = this.root.entityMgr.findByUid(uid, false); - if (!entityRef) { - // outdated - return; - } - - const constantComp = entityRef.components.ConstantSignal; - if (!constantComp) { - // no longer interesting - return; - } - - if (itemInput.chosenItem) { - constantComp.signal = itemInput.chosenItem; - } else { - constantComp.signal = this.parseSignalCode(entity, signalValueInput.getValue()); - } - }; - - dialog.buttonSignals.ok.add(() => { - closeHandler(); - }); - dialog.valueChosen.add(() => { - dialog.closeRequested.dispatch(); - closeHandler(); - }); - - // When cancelled, destroy the entity again - if (deleteOnCancel) { - dialog.buttonSignals.cancel.add(() => { - if (!this.root || !this.root.entityMgr) { - // Game got stopped - return; - } - - const entityRef = this.root.entityMgr.findByUid(uid, false); - if (!entityRef) { - // outdated - return; - } - - const constantComp = entityRef.components.ConstantSignal; - if (!constantComp) { - // no longer interesting - return; - } - - this.root.logic.tryDeleteBuilding(entityRef); - }); - } - } - - /** - * Tries to parse a signal code - * @param {Entity} entity - * @param {string} code - * @returns {BaseItem} - */ - parseSignalCode(entity, code) { - if (!this.root || !this.root.shapeDefinitionMgr) { - // Stale reference - return null; - } - - code = trim(code); - const codeLower = code.toLowerCase(); - - if (MODS_ADDITIONAL_CONSTANT_SIGNAL_RESOLVER[codeLower]) { - return MODS_ADDITIONAL_CONSTANT_SIGNAL_RESOLVER[codeLower].apply(this, [entity]); - } - - if (enumColors[codeLower]) { - return COLOR_ITEM_SINGLETONS[codeLower]; - } - - if (entity.components.WiredPins) { - if (code === "1" || codeLower === "true") { - return BOOL_TRUE_SINGLETON; - } - - if (code === "0" || codeLower === "false") { - return BOOL_FALSE_SINGLETON; - } - } - - if (ShapeDefinition.isValidShortKey(code)) { - return this.root.shapeDefinitionMgr.getShapeItemFromShortKey(code); - } - - return null; - } -} +import { THIRDPARTY_URLS } from "../../../core/config"; +import { DialogWithForm } from "../../../core/modal_dialog_elements"; +import { FormElementInput, FormElementItemChooser } from "../../../core/modal_dialog_forms"; +import { STOP_PROPAGATION } from "../../../core/signal"; +import { fillInLinkIntoTranslation } from "../../../core/utils"; +import { Vector } from "../../../core/vector"; +import { T } from "../../../translations"; +import { BaseItem } from "../../base_item"; +import { enumMouseButton } from "../../camera"; +import { Entity } from "../../entity"; +import { BOOL_FALSE_SINGLETON, BOOL_TRUE_SINGLETON } from "../../items/boolean_item"; +import { COLOR_ITEM_SINGLETONS } from "../../items/color_item"; +import { BaseHUDPart } from "../base_hud_part"; +import { enumColors } from "../../colors"; +import { ShapeDefinition } from "../../shape_definition"; + +/** @type {{ + * [x: string]: (entity: Entity) => BaseItem + * }} */ +export const MODS_ADDITIONAL_CONSTANT_SIGNAL_RESOLVER = {}; + +export class HUDConstantSignalEdit extends BaseHUDPart { + initialize() { + this.root.camera.downPreHandler.add(this.downPreHandler, this); + } + + /** + * @param {Vector} pos + * @param {enumMouseButton} button + */ + downPreHandler(pos, button) { + if (this.root.currentLayer !== "wires") { + return; + } + + const tile = this.root.camera.screenToWorld(pos).toTileSpace(); + const contents = this.root.map.getLayerContentXY(tile.x, tile.y, "wires"); + if (contents) { + const constantComp = contents.components.ConstantSignal; + if (constantComp) { + if (button === enumMouseButton.left) { + this.editConstantSignal(contents, { + deleteOnCancel: false, + }); + return STOP_PROPAGATION; + } + } + } + } + + /** + * Asks the entity to enter a valid signal code + * @param {Entity} entity + * @param {object} param0 + * @param {boolean=} param0.deleteOnCancel + */ + editConstantSignal(entity, { deleteOnCancel = true }) { + if (!entity.components.ConstantSignal) { + return; + } + + // Ok, query, but also save the uid because it could get stale + const uid = entity.uid; + + const signal = entity.components.ConstantSignal.signal; + const signalValueInput = new FormElementInput({ + id: "signalValue", + label: fillInLinkIntoTranslation(T.dialogs.editSignal.descShortKey, THIRDPARTY_URLS.shapeViewer), + placeholder: "", + defaultValue: signal ? signal.getAsCopyableKey() : "", + validator: val => this.parseSignalCode(entity, val) !== null, + }); + + const items = [...Object.values(COLOR_ITEM_SINGLETONS)]; + + if (entity.components.WiredPins) { + items.unshift(BOOL_FALSE_SINGLETON, BOOL_TRUE_SINGLETON); + items.push( + this.root.shapeDefinitionMgr.getShapeItemFromShortKey( + this.root.gameMode.getBlueprintShapeKey() + ) + ); + } else { + // producer which can produce virtually anything + const shapes = ["CuCuCuCu", "RuRuRuRu", "WuWuWuWu", "SuSuSuSu"]; + items.unshift( + ...shapes.reverse().map(key => this.root.shapeDefinitionMgr.getShapeItemFromShortKey(key)) + ); + } + + if (this.root.gameMode.hasHub()) { + items.push( + this.root.shapeDefinitionMgr.getShapeItemFromDefinition( + this.root.hubGoals.currentGoal.definition + ) + ); + } + + if (this.root.hud.parts.pinnedShapes) { + items.push( + ...this.root.hud.parts.pinnedShapes.pinnedShapes.map(key => + this.root.shapeDefinitionMgr.getShapeItemFromShortKey(key) + ) + ); + } + + const itemInput = new FormElementItemChooser({ + id: "signalItem", + label: null, + items, + }); + + const dialog = new DialogWithForm({ + app: this.root.app, + title: T.dialogs.editConstantProducer.title, + desc: T.dialogs.editSignal.descItems, + formElements: [itemInput, signalValueInput], + buttons: ["cancel:bad:escape", "ok:good:enter"], + closeButton: false, + }); + this.root.hud.parts.dialogs.internalShowDialog(dialog); + + // When confirmed, set the signal + const closeHandler = () => { + if (!this.root || !this.root.entityMgr) { + // Game got stopped + return; + } + + const entityRef = this.root.entityMgr.findByUid(uid, false); + if (!entityRef) { + // outdated + return; + } + + const constantComp = entityRef.components.ConstantSignal; + if (!constantComp) { + // no longer interesting + return; + } + + if (itemInput.chosenItem) { + constantComp.signal = itemInput.chosenItem; + } else { + constantComp.signal = this.parseSignalCode(entity, signalValueInput.getValue()); + } + }; + + dialog.buttonSignals.ok.add(() => { + closeHandler(); + }); + dialog.valueChosen.add(() => { + dialog.closeRequested.dispatch(); + closeHandler(); + }); + + // When cancelled, destroy the entity again + if (deleteOnCancel) { + dialog.buttonSignals.cancel.add(() => { + if (!this.root || !this.root.entityMgr) { + // Game got stopped + return; + } + + const entityRef = this.root.entityMgr.findByUid(uid, false); + if (!entityRef) { + // outdated + return; + } + + const constantComp = entityRef.components.ConstantSignal; + if (!constantComp) { + // no longer interesting + return; + } + + this.root.logic.tryDeleteBuilding(entityRef); + }); + } + } + + /** + * Tries to parse a signal code + * @param {Entity} entity + * @param {string} code + * @returns {BaseItem} + */ + parseSignalCode(entity, code) { + if (!this.root || !this.root.shapeDefinitionMgr) { + // Stale reference + return null; + } + + code = code.trim(); + const codeLower = code.toLowerCase(); + + if (MODS_ADDITIONAL_CONSTANT_SIGNAL_RESOLVER[codeLower]) { + return MODS_ADDITIONAL_CONSTANT_SIGNAL_RESOLVER[codeLower].apply(this, [entity]); + } + + if (enumColors[codeLower]) { + return COLOR_ITEM_SINGLETONS[codeLower]; + } + + if (entity.components.WiredPins) { + if (code === "1" || codeLower === "true") { + return BOOL_TRUE_SINGLETON; + } + + if (code === "0" || codeLower === "false") { + return BOOL_FALSE_SINGLETON; + } + } + + if (ShapeDefinition.isValidShortKey(code)) { + return this.root.shapeDefinitionMgr.getShapeItemFromShortKey(code); + } + + return null; + } +} diff --git a/src/js/game/hud/parts/entity_debugger.js b/src/js/game/hud/parts/entity_debugger.js index debd456d..1023a335 100644 --- a/src/js/game/hud/parts/entity_debugger.js +++ b/src/js/game/hud/parts/entity_debugger.js @@ -27,7 +27,7 @@ export class HUDEntityDebugger extends BaseHUDPart { } initialize() { - this.root.gameState.inputReciever.keydown.add(key => { + this.root.gameState.inputReceiver.keydown.add(key => { if (key.keyCode === 119) { // F8 this.pickEntity(); diff --git a/src/js/game/hud/parts/game_menu.js b/src/js/game/hud/parts/game_menu.js index 2a172ab2..60ca9bf7 100644 --- a/src/js/game/hud/parts/game_menu.js +++ b/src/js/game/hud/parts/game_menu.js @@ -1,174 +1,168 @@ -import { BaseHUDPart } from "../base_hud_part"; -import { makeDiv } from "../../../core/utils"; -import { SOUNDS } from "../../../platform/sound"; -import { enumNotificationType } from "./notifications"; -import { T } from "../../../translations"; -import { KEYMAPPINGS } from "../../key_action_mapper"; -import { DynamicDomAttach } from "../dynamic_dom_attach"; -import { TrackedState } from "../../../core/tracked_state"; - -export class HUDGameMenu extends BaseHUDPart { - createElements(parent) { - this.element = makeDiv(parent, "ingame_HUD_GameMenu"); - - const buttons = [ - { - id: "shop", - label: "Upgrades", - handler: () => this.root.hud.parts.shop.show(), - keybinding: KEYMAPPINGS.ingame.menuOpenShop, - badge: () => this.root.hubGoals.getAvailableUpgradeCount(), - notification: /** @type {[string, enumNotificationType]} */ ([ - T.ingame.notifications.newUpgrade, - enumNotificationType.upgrade, - ]), - visible: () => - !this.root.app.settings.getAllSettings().offerHints || this.root.hubGoals.level >= 3, - }, - { - id: "stats", - label: "Stats", - handler: () => this.root.hud.parts.statistics.show(), - keybinding: KEYMAPPINGS.ingame.menuOpenStats, - visible: () => - !this.root.app.settings.getAllSettings().offerHints || this.root.hubGoals.level >= 3, - }, - ]; - - /** @type {Array<{ - * badge: function, - * button: HTMLElement, - * badgeElement: HTMLElement, - * lastRenderAmount: number, - * condition?: function, - * notification: [string, enumNotificationType] - * }>} */ - this.badgesToUpdate = []; - - /** @type {Array<{ - * button: HTMLElement, - * condition: function, - * domAttach: DynamicDomAttach - * }>} */ - this.visibilityToUpdate = []; - - buttons.forEach(({ id, label, handler, keybinding, badge, notification, visible }) => { - const button = document.createElement("button"); - button.classList.add(id); - this.element.appendChild(button); - this.trackClicks(button, handler); - - if (keybinding) { - const binding = this.root.keyMapper.getBinding(keybinding); - binding.add(handler); - } - - if (visible) { - this.visibilityToUpdate.push({ - button, - condition: visible, - domAttach: new DynamicDomAttach(this.root, button), - }); - } - - if (badge) { - const badgeElement = makeDiv(button, null, ["badge"]); - this.badgesToUpdate.push({ - badge, - lastRenderAmount: 0, - button, - badgeElement, - notification, - condition: visible, - }); - } - }); - - this.saveButton = makeDiv(this.element, null, ["button", "save", "animEven"]); - this.settingsButton = makeDiv(this.element, null, ["button", "settings"]); - - this.trackClicks(this.saveButton, this.startSave); - this.trackClicks(this.settingsButton, this.openSettings); - } - - initialize() { - this.root.signals.gameSaved.add(this.onGameSaved, this); - - this.trackedIsSaving = new TrackedState(this.onIsSavingChanged, this); - } - - update() { - let playSound = false; - let notifications = new Set(); - - // Check whether we are saving - this.trackedIsSaving.set(!!this.root.gameState.currentSavePromise); - - // Update visibility of buttons - for (let i = 0; i < this.visibilityToUpdate.length; ++i) { - const { condition, domAttach } = this.visibilityToUpdate[i]; - domAttach.update(condition()); - } - - // Check for notifications and badges - for (let i = 0; i < this.badgesToUpdate.length; ++i) { - const { - badge, - button, - badgeElement, - lastRenderAmount, - notification, - condition, - } = this.badgesToUpdate[i]; - - if (condition && !condition()) { - // Do not show notifications for invisible buttons - continue; - } - - // Check if the amount shown differs from the one shown last frame - const amount = badge(); - if (lastRenderAmount !== amount) { - if (amount > 0) { - badgeElement.innerText = amount; - } - // Check if the badge increased, if so play a notification - if (amount > lastRenderAmount) { - playSound = true; - if (notification) { - notifications.add(notification); - } - } - - // Rerender notifications - this.badgesToUpdate[i].lastRenderAmount = amount; - button.classList.toggle("hasBadge", amount > 0); - } - } - - if (playSound) { - this.root.soundProxy.playUi(SOUNDS.badgeNotification); - } - - notifications.forEach(([notification, type]) => { - this.root.hud.signals.notification.dispatch(notification, type); - }); - } - - onIsSavingChanged(isSaving) { - this.saveButton.classList.toggle("saving", isSaving); - } - - onGameSaved() { - this.saveButton.classList.toggle("animEven"); - this.saveButton.classList.toggle("animOdd"); - } - - startSave() { - this.root.gameState.doSave(); - } - - openSettings() { - this.root.hud.parts.settingsMenu.show(); - } -} +import { BaseHUDPart } from "../base_hud_part"; +import { makeDiv } from "../../../core/utils"; +import { SOUNDS } from "../../../platform/sound"; +import { enumNotificationType } from "./notifications"; +import { T } from "../../../translations"; +import { KEYMAPPINGS } from "../../key_action_mapper"; +import { DynamicDomAttach } from "../dynamic_dom_attach"; +import { TrackedState } from "../../../core/tracked_state"; + +export class HUDGameMenu extends BaseHUDPart { + createElements(parent) { + this.element = makeDiv(parent, "ingame_HUD_GameMenu"); + + const buttons = [ + { + id: "shop", + label: "Upgrades", + handler: () => this.root.hud.parts.shop.show(), + keybinding: KEYMAPPINGS.ingame.menuOpenShop, + badge: () => this.root.hubGoals.getAvailableUpgradeCount(), + notification: /** @type {[string, enumNotificationType]} */ ([ + T.ingame.notifications.newUpgrade, + enumNotificationType.upgrade, + ]), + visible: () => + !this.root.app.settings.getAllSettings().offerHints || this.root.hubGoals.level >= 3, + }, + { + id: "stats", + label: "Stats", + handler: () => this.root.hud.parts.statistics.show(), + keybinding: KEYMAPPINGS.ingame.menuOpenStats, + visible: () => + !this.root.app.settings.getAllSettings().offerHints || this.root.hubGoals.level >= 3, + }, + ]; + + /** @type {Array<{ + * badge: function, + * button: HTMLElement, + * badgeElement: HTMLElement, + * lastRenderAmount: number, + * condition?: function, + * notification: [string, enumNotificationType] + * }>} */ + this.badgesToUpdate = []; + + /** @type {Array<{ + * button: HTMLElement, + * condition: function, + * domAttach: DynamicDomAttach + * }>} */ + this.visibilityToUpdate = []; + + buttons.forEach(({ id, label, handler, keybinding, badge, notification, visible }) => { + const button = document.createElement("button"); + button.classList.add(id); + this.element.appendChild(button); + this.trackClicks(button, handler); + + if (keybinding) { + const binding = this.root.keyMapper.getBinding(keybinding); + binding.add(handler); + } + + if (visible) { + this.visibilityToUpdate.push({ + button, + condition: visible, + domAttach: new DynamicDomAttach(this.root, button), + }); + } + + if (badge) { + const badgeElement = makeDiv(button, null, ["badge"]); + this.badgesToUpdate.push({ + badge, + lastRenderAmount: 0, + button, + badgeElement, + notification, + condition: visible, + }); + } + }); + + this.saveButton = makeDiv(this.element, null, ["button", "save", "animEven"]); + this.settingsButton = makeDiv(this.element, null, ["button", "settings"]); + + this.trackClicks(this.saveButton, this.startSave); + this.trackClicks(this.settingsButton, this.openSettings); + } + + initialize() { + this.root.signals.gameSaved.add(this.onGameSaved, this); + + this.trackedIsSaving = new TrackedState(this.onIsSavingChanged, this); + } + + update() { + let playSound = false; + let notifications = new Set(); + + // Check whether we are saving + this.trackedIsSaving.set(!!this.root.gameState.currentSavePromise); + + // Update visibility of buttons + for (let i = 0; i < this.visibilityToUpdate.length; ++i) { + const { condition, domAttach } = this.visibilityToUpdate[i]; + domAttach.update(condition()); + } + + // Check for notifications and badges + for (let i = 0; i < this.badgesToUpdate.length; ++i) { + const { badge, button, badgeElement, lastRenderAmount, notification, condition } = + this.badgesToUpdate[i]; + + if (condition && !condition()) { + // Do not show notifications for invisible buttons + continue; + } + + // Check if the amount shown differs from the one shown last frame + const amount = badge(); + if (lastRenderAmount !== amount) { + if (amount > 0) { + badgeElement.innerText = amount; + } + // Check if the badge increased, if so play a notification + if (amount > lastRenderAmount) { + playSound = true; + if (notification) { + notifications.add(notification); + } + } + + // Rerender notifications + this.badgesToUpdate[i].lastRenderAmount = amount; + button.classList.toggle("hasBadge", amount > 0); + } + } + + if (playSound) { + this.root.soundProxy.playUi(SOUNDS.badgeNotification); + } + + notifications.forEach(([notification, type]) => { + this.root.hud.signals.notification.dispatch(notification, type); + }); + } + + onIsSavingChanged(isSaving) { + this.saveButton.classList.toggle("saving", isSaving); + } + + onGameSaved() { + this.saveButton.classList.toggle("animEven"); + this.saveButton.classList.toggle("animOdd"); + } + + startSave() { + this.root.gameState.doSave(); + } + + openSettings() { + this.root.hud.parts.settingsMenu.show(); + } +} diff --git a/src/js/game/hud/parts/interactive_tutorial.js b/src/js/game/hud/parts/interactive_tutorial.js index 7548ce43..14cffcae 100644 --- a/src/js/game/hud/parts/interactive_tutorial.js +++ b/src/js/game/hud/parts/interactive_tutorial.js @@ -1,444 +1,442 @@ -import { BaseHUDPart } from "../base_hud_part"; -import { clamp, makeDiv, smoothPulse } from "../../../core/utils"; -import { GameRoot } from "../../root"; -import { MinerComponent } from "../../components/miner"; -import { DynamicDomAttach } from "../dynamic_dom_attach"; -import { TrackedState } from "../../../core/tracked_state"; -import { cachebust } from "../../../core/cachebust"; -import { T } from "../../../translations"; -import { enumItemProcessorTypes, ItemProcessorComponent } from "../../components/item_processor"; -import { ShapeItem } from "../../items/shape_item"; -import { WireComponent } from "../../components/wire"; -import { LeverComponent } from "../../components/lever"; -import { DrawParameters } from "../../../core/draw_parameters"; -import { globalConfig } from "../../../core/config"; -import { Vector } from "../../../core/vector"; -import { MetaMinerBuilding } from "../../buildings/miner"; -import { gMetaBuildingRegistry } from "../../../core/global_registries"; -import { MetaBeltBuilding } from "../../buildings/belt"; -import { MetaTrashBuilding } from "../../buildings/trash"; -import { SOUNDS } from "../../../platform/sound"; -import { THEME } from "../../theme"; - -// @todo: Make dictionary -const tutorialsByLevel = [ - // Level 1 - [ - // 1.1. place an extractor - { - id: "1_1_extractor", - condition: /** @param {GameRoot} root */ root => - root.entityMgr.getAllWithComponent(MinerComponent).length === 0, - }, - // 1.2. connect to hub - { - id: "1_2_conveyor", - condition: /** @param {GameRoot} root */ root => { - const paths = root.systemMgr.systems.belt.beltPaths; - const miners = root.entityMgr.getAllWithComponent(MinerComponent); - for (let i = 0; i < paths.length; i++) { - const path = paths[i]; - const acceptingEntity = path.computeAcceptingEntityAndSlot().entity; - if (!acceptingEntity || !acceptingEntity.components.Hub) { - continue; - } - // Find a miner which delivers to this belt path - for (let k = 0; k < miners.length; ++k) { - const miner = miners[k]; - if (miner.components.ItemEjector.slots[0].cachedBeltPath === path) { - return false; - } - } - } - return true; - }, - }, - // 1.3 wait for completion - { - id: "1_3_expand", - condition: /** @param {GameRoot} root */ root => true, - }, - ], - // Level 2 - [ - // 2.1 place a cutter - { - id: "2_1_place_cutter", - condition: /** @param {GameRoot} root */ root => - root.entityMgr - .getAllWithComponent(ItemProcessorComponent) - .filter(e => e.components.ItemProcessor.type === enumItemProcessorTypes.cutter).length === - 0, - }, - // 2.2 place trash - { - id: "2_2_place_trash", - condition: /** @param {GameRoot} root */ root => - root.entityMgr - .getAllWithComponent(ItemProcessorComponent) - .filter(e => e.components.ItemProcessor.type === enumItemProcessorTypes.trash).length === - 0, - }, - // 2.3 place more cutters - { - id: "2_3_more_cutters", - condition: /** @param {GameRoot} root */ root => true, - }, - ], - - // Level 3 - [ - // 3.1. rectangles - { - id: "3_1_rectangles", - condition: /** @param {GameRoot} root */ root => - // 4 miners placed above rectangles and 10 delivered - root.hubGoals.getCurrentGoalDelivered() < 10 || - root.entityMgr.getAllWithComponent(MinerComponent).filter(entity => { - const tile = entity.components.StaticMapEntity.origin; - const below = root.map.getLowerLayerContentXY(tile.x, tile.y); - if (below && below.getItemType() === "shape") { - const shape = /** @type {ShapeItem} */ (below).definition.getHash(); - return shape === "RuRuRuRu"; - } - return false; - }).length < 4, - }, - ], - - [], // Level 4 - [], // Level 5 - [], // Level 6 - [], // Level 7 - [], // Level 8 - [], // Level 9 - [], // Level 10 - [], // Level 11 - [], // Level 12 - [], // Level 13 - [], // Level 14 - [], // Level 15 - [], // Level 16 - [], // Level 17 - [], // Level 18 - [], // Level 19 - [], // Level 20 - - // Level 21 - [ - // 21.1 place quad painter - { - id: "21_1_place_quad_painter", - condition: /** @param {GameRoot} root */ root => - root.entityMgr - .getAllWithComponent(ItemProcessorComponent) - .filter(e => e.components.ItemProcessor.type === enumItemProcessorTypes.painterQuad) - .length === 0, - }, - - // 21.2 switch to wires layer - { - id: "21_2_switch_to_wires", - condition: /** @param {GameRoot} root */ root => - root.entityMgr.getAllWithComponent(WireComponent).length < 5, - }, - - // 21.3 place button - { - id: "21_3_place_button", - condition: /** @param {GameRoot} root */ root => - root.entityMgr.getAllWithComponent(LeverComponent).length === 0, - }, - - // 21.4 activate button - { - id: "21_4_press_button", - condition: /** @param {GameRoot} root */ root => - root.entityMgr.getAllWithComponent(LeverComponent).some(e => !e.components.Lever.toggled), - }, - ], -]; - -export class HUDInteractiveTutorial extends BaseHUDPart { - createElements(parent) { - this.element = makeDiv( - parent, - "ingame_HUD_InteractiveTutorial", - ["animEven"], - ` - ${T.ingame.interactiveTutorial.title} - ` - ); - - this.elementDescription = makeDiv(this.element, null, ["desc"]); - this.elementGif = makeDiv(this.element, null, ["helperGif"]); - } - - cleanup() { - document.documentElement.setAttribute("data-tutorial-step", ""); - } - - initialize() { - this.domAttach = new DynamicDomAttach(this.root, this.element, { trackHover: true }); - this.currentHintId = new TrackedState(this.onHintChanged, this); - - document.documentElement.setAttribute("data-tutorial-step", ""); - } - - onHintChanged(hintId) { - this.elementDescription.innerHTML = T.ingame.interactiveTutorial.hints[hintId]; - document.documentElement.setAttribute("data-tutorial-step", hintId); - const folder = G_WEGAME_VERSION - ? "interactive_tutorial.cn.noinline" - : "interactive_tutorial.noinline"; - - this.elementGif.style.backgroundImage = - "url('" + cachebust("res/ui/" + folder + "/" + hintId + ".gif") + "')"; - this.element.classList.toggle("animEven"); - this.element.classList.toggle("animOdd"); - if (hintId) { - this.root.app.sound.playUiSound(SOUNDS.tutorialStep); - } - } - - update() { - // Compute current hint - const thisLevelHints = tutorialsByLevel[this.root.hubGoals.level - 1]; - let targetHintId = null; - - if (thisLevelHints) { - for (let i = 0; i < thisLevelHints.length; ++i) { - const hint = thisLevelHints[i]; - if (hint.condition(this.root)) { - targetHintId = hint.id; - break; - } - } - } - - this.currentHintId.set(targetHintId); - this.domAttach.update(!!targetHintId); - } - - /** - * - * @param {DrawParameters} parameters - */ - draw(parameters) { - const animation = smoothPulse(this.root.time.now()); - const currentBuilding = this.root.hud.parts.buildingPlacer.currentMetaBuilding.get(); - - if (["1_1_extractor"].includes(this.currentHintId.get())) { - if ( - currentBuilding && - currentBuilding.getId() === gMetaBuildingRegistry.findByClass(MetaMinerBuilding).getId() - ) { - // Find closest circle patch to hub - - let closest = null; - let closestDistance = 1e10; - - for (let i = 0; i > -globalConfig.mapChunkSize; --i) { - for (let j = 0; j < globalConfig.mapChunkSize; ++j) { - const resourceItem = this.root.map.getLowerLayerContentXY(i, j); - if ( - resourceItem instanceof ShapeItem && - resourceItem.definition.getHash() === "CuCuCuCu" - ) { - let distance = Math.hypot(i, j); - if (!closest || distance < closestDistance) { - const tile = new Vector(i, j); - if (!this.root.map.getTileContent(tile, "regular")) { - closest = tile; - closestDistance = distance; - } - } - } - } - } - - if (closest) { - parameters.context.fillStyle = "rgba(74, 237, 134, " + (0.5 - animation * 0.2) + ")"; - parameters.context.strokeStyle = "rgb(74, 237, 134)"; - parameters.context.lineWidth = 2; - parameters.context.beginRoundedRect( - closest.x * globalConfig.tileSize - 2 * animation, - closest.y * globalConfig.tileSize - 2 * animation, - globalConfig.tileSize + 4 * animation, - globalConfig.tileSize + 4 * animation, - 3 - ); - parameters.context.fill(); - parameters.context.stroke(); - parameters.context.globalAlpha = 1; - } - } - } - - if (this.currentHintId.get() === "1_2_conveyor") { - if ( - currentBuilding && - currentBuilding.getId() === gMetaBuildingRegistry.findByClass(MetaBeltBuilding).getId() - ) { - // Find closest miner - const miners = this.root.entityMgr.getAllWithComponent(MinerComponent); - - let closest = null; - let closestDistance = 1e10; - - for (let i = 0; i < miners.length; i++) { - const miner = miners[i]; - const distance = miner.components.StaticMapEntity.origin.lengthSquare(); - - if (![0, 90].includes(miner.components.StaticMapEntity.rotation)) { - continue; - } - if (!closest || distance < closestDistance) { - closest = miner; - } - } - - if (closest) { - // draw line from miner to hub -> But respect orientation - - const staticComp = closest.components.StaticMapEntity; - - const offset = staticComp.rotation === 0 ? new Vector(0.5, 0) : new Vector(1, 0.5); - - const anchor = - staticComp.rotation === 0 - ? new Vector(staticComp.origin.x + 0.5, 0.5) - : new Vector(-0.5, staticComp.origin.y + 0.5); - - const target = staticComp.rotation === 0 ? new Vector(-2.1, 0.5) : new Vector(-0.5, 2.1); - - parameters.context.globalAlpha = 0.1 + animation * 0.1; - parameters.context.strokeStyle = "rgb(74, 237, 134)"; - parameters.context.lineWidth = globalConfig.tileSize / 2; - parameters.context.beginPath(); - parameters.context.moveTo( - (staticComp.origin.x + offset.x) * globalConfig.tileSize, - (staticComp.origin.y + offset.y) * globalConfig.tileSize - ); - parameters.context.lineTo( - anchor.x * globalConfig.tileSize, - anchor.y * globalConfig.tileSize - ); - parameters.context.lineTo( - target.x * globalConfig.tileSize, - target.y * globalConfig.tileSize - ); - parameters.context.stroke(); - parameters.context.globalAlpha = 1; - - const arrowSprite = this.root.hud.parts.buildingPlacer.lockIndicatorSprites.regular; - - let arrows = []; - - let pos = staticComp.origin.add(offset); - let delta = anchor.sub(pos).normalize(); - let maxIter = 999; - - while (pos.distanceSquare(anchor) > 1 && maxIter-- > 0) { - pos = pos.add(delta); - arrows.push({ - pos: pos.sub(offset), - rotation: staticComp.rotation, - }); - } - - pos = anchor.copy(); - delta = target.sub(pos).normalize(); - const localDelta = - staticComp.rotation === 0 ? new Vector(-1.5, -0.5) : new Vector(-0.5, 0.5); - while (pos.distanceSquare(target) > 1 && maxIter-- > 0) { - pos = pos.add(delta); - arrows.push({ - pos: pos.add(localDelta), - rotation: 90 - staticComp.rotation, - }); - } - - for (let i = 0; i < arrows.length; i++) { - const { pos, rotation } = arrows[i]; - const worldPos = pos.toWorldSpaceCenterOfTile(); - const angle = Math.radians(rotation); - - parameters.context.translate(worldPos.x, worldPos.y); - parameters.context.rotate(angle); - parameters.context.drawImage( - arrowSprite, - -6, - -globalConfig.halfTileSize - - clamp((this.root.time.realtimeNow() * 1.5) % 1.0, 0, 1) * - 1 * - globalConfig.tileSize + - globalConfig.halfTileSize - - 6, - 12, - 12 - ); - parameters.context.rotate(-angle); - parameters.context.translate(-worldPos.x, -worldPos.y); - } - - parameters.context.fillStyle = THEME.map.tutorialDragText; - parameters.context.font = "15px GameFont"; - - if (staticComp.rotation === 0) { - const pos = staticComp.origin.toWorldSpace().subScalars(2, 10); - parameters.context.translate(pos.x, pos.y); - parameters.context.rotate(-Math.radians(90)); - parameters.context.fillText( - T.ingame.interactiveTutorial.hints["1_2_hold_and_drag"], - 0, - 0 - ); - parameters.context.rotate(Math.radians(90)); - parameters.context.translate(-pos.x, -pos.y); - } else { - const pos = staticComp.origin.toWorldSpace().addScalars(40, 50); - parameters.context.fillText( - T.ingame.interactiveTutorial.hints["1_2_hold_and_drag"], - pos.x, - pos.y - ); - } - } - } - } - - if (this.currentHintId.get() === "2_2_place_trash") { - // Find cutters - if ( - currentBuilding && - currentBuilding.getId() === gMetaBuildingRegistry.findByClass(MetaTrashBuilding).getId() - ) { - const entities = this.root.entityMgr.getAllWithComponent(ItemProcessorComponent); - for (let i = 0; i < entities.length; i++) { - const entity = entities[i]; - if (entity.components.ItemProcessor.type !== enumItemProcessorTypes.cutter) { - continue; - } - - const slot = entity.components.StaticMapEntity.localTileToWorld( - new Vector(1, -1) - ).toWorldSpace(); - parameters.context.fillStyle = "rgba(74, 237, 134, " + (0.5 - animation * 0.2) + ")"; - parameters.context.strokeStyle = "rgb(74, 237, 134)"; - parameters.context.lineWidth = 2; - parameters.context.beginRoundedRect( - slot.x - 2 * animation, - slot.y - 2 * animation, - globalConfig.tileSize + 4 * animation, - globalConfig.tileSize + 4 * animation, - 3 - ); - parameters.context.fill(); - parameters.context.stroke(); - parameters.context.globalAlpha = 1; - } - } - } - } -} +import { globalConfig } from "../../../core/config"; +import { DrawParameters } from "../../../core/draw_parameters"; +import { gMetaBuildingRegistry } from "../../../core/global_registries"; +import { TrackedState } from "../../../core/tracked_state"; +import { clamp, makeDiv, smoothPulse } from "../../../core/utils"; +import { Vector } from "../../../core/vector"; +import { SOUNDS } from "../../../platform/sound"; +import { T } from "../../../translations"; +import { MetaBeltBuilding } from "../../buildings/belt"; +import { MetaMinerBuilding } from "../../buildings/miner"; +import { MetaTrashBuilding } from "../../buildings/trash"; +import { enumItemProcessorTypes, ItemProcessorComponent } from "../../components/item_processor"; +import { LeverComponent } from "../../components/lever"; +import { MinerComponent } from "../../components/miner"; +import { WireComponent } from "../../components/wire"; +import { ShapeItem } from "../../items/shape_item"; +import { GameRoot } from "../../root"; +import { THEME } from "../../theme"; +import { BaseHUDPart } from "../base_hud_part"; +import { DynamicDomAttach } from "../dynamic_dom_attach"; + +// @todo: Make dictionary +const tutorialsByLevel = [ + // Level 1 + [ + // 1.1. place an extractor + { + id: "1_1_extractor", + condition: /** @param {GameRoot} root */ root => + root.entityMgr.getAllWithComponent(MinerComponent).length === 0, + }, + // 1.2. connect to hub + { + id: "1_2_conveyor", + condition: /** @param {GameRoot} root */ root => { + const paths = root.systemMgr.systems.belt.beltPaths; + const miners = root.entityMgr.getAllWithComponent(MinerComponent); + for (let i = 0; i < paths.length; i++) { + const path = paths[i]; + const acceptingEntity = path.computeAcceptingEntityAndSlot().entity; + if (!acceptingEntity || !acceptingEntity.components.Hub) { + continue; + } + // Find a miner which delivers to this belt path + for (let k = 0; k < miners.length; ++k) { + const miner = miners[k]; + if (miner.components.ItemEjector.slots[0].cachedBeltPath === path) { + return false; + } + } + } + return true; + }, + }, + // 1.3 wait for completion + { + id: "1_3_expand", + condition: /** @param {GameRoot} root */ root => true, + }, + ], + // Level 2 + [ + // 2.1 place a cutter + { + id: "2_1_place_cutter", + condition: /** @param {GameRoot} root */ root => + root.entityMgr + .getAllWithComponent(ItemProcessorComponent) + .filter(e => e.components.ItemProcessor.type === enumItemProcessorTypes.cutter).length === + 0, + }, + // 2.2 place trash + { + id: "2_2_place_trash", + condition: /** @param {GameRoot} root */ root => + root.entityMgr + .getAllWithComponent(ItemProcessorComponent) + .filter(e => e.components.ItemProcessor.type === enumItemProcessorTypes.trash).length === + 0, + }, + // 2.3 place more cutters + { + id: "2_3_more_cutters", + condition: /** @param {GameRoot} root */ root => true, + }, + ], + + // Level 3 + [ + // 3.1. rectangles + { + id: "3_1_rectangles", + condition: /** @param {GameRoot} root */ root => + // 4 miners placed above rectangles and 10 delivered + root.hubGoals.getCurrentGoalDelivered() < 10 || + root.entityMgr.getAllWithComponent(MinerComponent).filter(entity => { + const tile = entity.components.StaticMapEntity.origin; + const below = root.map.getLowerLayerContentXY(tile.x, tile.y); + if (below && below.getItemType() === "shape") { + const shape = /** @type {ShapeItem} */ (below).definition.getHash(); + return shape === "RuRuRuRu"; + } + return false; + }).length < 4, + }, + ], + + [], // Level 4 + [], // Level 5 + [], // Level 6 + [], // Level 7 + [], // Level 8 + [], // Level 9 + [], // Level 10 + [], // Level 11 + [], // Level 12 + [], // Level 13 + [], // Level 14 + [], // Level 15 + [], // Level 16 + [], // Level 17 + [], // Level 18 + [], // Level 19 + [], // Level 20 + + // Level 21 + [ + // 21.1 place quad painter + { + id: "21_1_place_quad_painter", + condition: /** @param {GameRoot} root */ root => + root.entityMgr + .getAllWithComponent(ItemProcessorComponent) + .filter(e => e.components.ItemProcessor.type === enumItemProcessorTypes.painterQuad) + .length === 0, + }, + + // 21.2 switch to wires layer + { + id: "21_2_switch_to_wires", + condition: /** @param {GameRoot} root */ root => + root.entityMgr.getAllWithComponent(WireComponent).length < 5, + }, + + // 21.3 place button + { + id: "21_3_place_button", + condition: /** @param {GameRoot} root */ root => + root.entityMgr.getAllWithComponent(LeverComponent).length === 0, + }, + + // 21.4 activate button + { + id: "21_4_press_button", + condition: /** @param {GameRoot} root */ root => + root.entityMgr.getAllWithComponent(LeverComponent).some(e => !e.components.Lever.toggled), + }, + ], +]; + +export class HUDInteractiveTutorial extends BaseHUDPart { + createElements(parent) { + this.element = makeDiv( + parent, + "ingame_HUD_InteractiveTutorial", + ["animEven"], + ` + ${T.ingame.interactiveTutorial.title} + ` + ); + + this.elementDescription = makeDiv(this.element, null, ["desc"]); + this.elementGif = makeDiv(this.element, null, ["helperGif"]); + } + + cleanup() { + document.documentElement.setAttribute("data-tutorial-step", ""); + } + + initialize() { + this.domAttach = new DynamicDomAttach(this.root, this.element, { trackHover: true }); + this.currentHintId = new TrackedState(this.onHintChanged, this); + + document.documentElement.setAttribute("data-tutorial-step", ""); + } + + onHintChanged(hintId) { + this.elementDescription.innerHTML = T.ingame.interactiveTutorial.hints[hintId]; + document.documentElement.setAttribute("data-tutorial-step", hintId); + + this.elementGif.style.backgroundImage = + "url('res/ui/interactive_tutorial.noinline/" + hintId + ".gif')"; + this.element.classList.toggle("animEven"); + this.element.classList.toggle("animOdd"); + if (hintId) { + this.root.app.sound.playUiSound(SOUNDS.tutorialStep); + } + } + + update() { + // Compute current hint + const thisLevelHints = tutorialsByLevel[this.root.hubGoals.level - 1]; + let targetHintId = null; + + if (thisLevelHints) { + for (let i = 0; i < thisLevelHints.length; ++i) { + const hint = thisLevelHints[i]; + if (hint.condition(this.root)) { + targetHintId = hint.id; + break; + } + } + } + + this.currentHintId.set(targetHintId); + this.domAttach.update(!!targetHintId); + } + + /** + * + * @param {DrawParameters} parameters + */ + draw(parameters) { + const animation = smoothPulse(this.root.time.now()); + const currentBuilding = this.root.hud.parts.buildingPlacer.currentMetaBuilding.get(); + + if (["1_1_extractor"].includes(this.currentHintId.get())) { + if ( + currentBuilding && + currentBuilding.getId() === gMetaBuildingRegistry.findByClass(MetaMinerBuilding).getId() + ) { + // Find closest circle patch to hub + + let closest = null; + let closestDistance = 1e10; + + for (let i = 0; i > -globalConfig.mapChunkSize; --i) { + for (let j = 0; j < globalConfig.mapChunkSize; ++j) { + const resourceItem = this.root.map.getLowerLayerContentXY(i, j); + if ( + resourceItem instanceof ShapeItem && + resourceItem.definition.getHash() === "CuCuCuCu" + ) { + let distance = Math.hypot(i, j); + if (!closest || distance < closestDistance) { + const tile = new Vector(i, j); + if (!this.root.map.getTileContent(tile, "regular")) { + closest = tile; + closestDistance = distance; + } + } + } + } + } + + if (closest) { + parameters.context.fillStyle = "rgba(74, 237, 134, " + (0.5 - animation * 0.2) + ")"; + parameters.context.strokeStyle = "rgb(74, 237, 134)"; + parameters.context.lineWidth = 2; + parameters.context.beginPath(); + parameters.context.roundRect( + closest.x * globalConfig.tileSize - 2 * animation, + closest.y * globalConfig.tileSize - 2 * animation, + globalConfig.tileSize + 4 * animation, + globalConfig.tileSize + 4 * animation, + 3 + ); + parameters.context.fill(); + parameters.context.stroke(); + parameters.context.globalAlpha = 1; + } + } + } + + if (this.currentHintId.get() === "1_2_conveyor") { + if ( + currentBuilding && + currentBuilding.getId() === gMetaBuildingRegistry.findByClass(MetaBeltBuilding).getId() + ) { + // Find closest miner + const miners = this.root.entityMgr.getAllWithComponent(MinerComponent); + + let closest = null; + let closestDistance = 1e10; + + for (let i = 0; i < miners.length; i++) { + const miner = miners[i]; + const distance = miner.components.StaticMapEntity.origin.lengthSquare(); + + if (![0, 90].includes(miner.components.StaticMapEntity.rotation)) { + continue; + } + if (!closest || distance < closestDistance) { + closest = miner; + } + } + + if (closest) { + // draw line from miner to hub -> But respect orientation + + const staticComp = closest.components.StaticMapEntity; + + const offset = staticComp.rotation === 0 ? new Vector(0.5, 0) : new Vector(1, 0.5); + + const anchor = + staticComp.rotation === 0 + ? new Vector(staticComp.origin.x + 0.5, 0.5) + : new Vector(-0.5, staticComp.origin.y + 0.5); + + const target = staticComp.rotation === 0 ? new Vector(-2.1, 0.5) : new Vector(-0.5, 2.1); + + parameters.context.globalAlpha = 0.1 + animation * 0.1; + parameters.context.strokeStyle = "rgb(74, 237, 134)"; + parameters.context.lineWidth = globalConfig.tileSize / 2; + parameters.context.beginPath(); + parameters.context.moveTo( + (staticComp.origin.x + offset.x) * globalConfig.tileSize, + (staticComp.origin.y + offset.y) * globalConfig.tileSize + ); + parameters.context.lineTo( + anchor.x * globalConfig.tileSize, + anchor.y * globalConfig.tileSize + ); + parameters.context.lineTo( + target.x * globalConfig.tileSize, + target.y * globalConfig.tileSize + ); + parameters.context.stroke(); + parameters.context.globalAlpha = 1; + + const arrowSprite = this.root.hud.parts.buildingPlacer.lockIndicatorSprites.regular; + + let arrows = []; + + let pos = staticComp.origin.add(offset); + let delta = anchor.sub(pos).normalize(); + let maxIter = 999; + + while (pos.distanceSquare(anchor) > 1 && maxIter-- > 0) { + pos = pos.add(delta); + arrows.push({ + pos: pos.sub(offset), + rotation: staticComp.rotation, + }); + } + + pos = anchor.copy(); + delta = target.sub(pos).normalize(); + const localDelta = + staticComp.rotation === 0 ? new Vector(-1.5, -0.5) : new Vector(-0.5, 0.5); + while (pos.distanceSquare(target) > 1 && maxIter-- > 0) { + pos = pos.add(delta); + arrows.push({ + pos: pos.add(localDelta), + rotation: 90 - staticComp.rotation, + }); + } + + for (let i = 0; i < arrows.length; i++) { + const { pos, rotation } = arrows[i]; + const worldPos = pos.toWorldSpaceCenterOfTile(); + const angle = Math.radians(rotation); + + parameters.context.translate(worldPos.x, worldPos.y); + parameters.context.rotate(angle); + parameters.context.drawImage( + arrowSprite, + -6, + -globalConfig.halfTileSize - + clamp((this.root.time.realtimeNow() * 1.5) % 1.0, 0, 1) * + 1 * + globalConfig.tileSize + + globalConfig.halfTileSize - + 6, + 12, + 12 + ); + parameters.context.rotate(-angle); + parameters.context.translate(-worldPos.x, -worldPos.y); + } + + parameters.context.fillStyle = THEME.map.tutorialDragText; + parameters.context.font = "15px GameFont"; + + if (staticComp.rotation === 0) { + const pos = staticComp.origin.toWorldSpace().subScalars(2, 10); + parameters.context.translate(pos.x, pos.y); + parameters.context.rotate(-Math.radians(90)); + parameters.context.fillText( + T.ingame.interactiveTutorial.hints["1_2_hold_and_drag"], + 0, + 0 + ); + parameters.context.rotate(Math.radians(90)); + parameters.context.translate(-pos.x, -pos.y); + } else { + const pos = staticComp.origin.toWorldSpace().addScalars(40, 50); + parameters.context.fillText( + T.ingame.interactiveTutorial.hints["1_2_hold_and_drag"], + pos.x, + pos.y + ); + } + } + } + } + + if (this.currentHintId.get() === "2_2_place_trash") { + // Find cutters + if ( + currentBuilding && + currentBuilding.getId() === gMetaBuildingRegistry.findByClass(MetaTrashBuilding).getId() + ) { + const entities = this.root.entityMgr.getAllWithComponent(ItemProcessorComponent); + for (let i = 0; i < entities.length; i++) { + const entity = entities[i]; + if (entity.components.ItemProcessor.type !== enumItemProcessorTypes.cutter) { + continue; + } + + const slot = entity.components.StaticMapEntity.localTileToWorld( + new Vector(1, -1) + ).toWorldSpace(); + parameters.context.fillStyle = "rgba(74, 237, 134, " + (0.5 - animation * 0.2) + ")"; + parameters.context.strokeStyle = "rgb(74, 237, 134)"; + parameters.context.lineWidth = 2; + parameters.context.beginPath(); + parameters.context.roundRect( + slot.x - 2 * animation, + slot.y - 2 * animation, + globalConfig.tileSize + 4 * animation, + globalConfig.tileSize + 4 * animation, + 3 + ); + parameters.context.fill(); + parameters.context.stroke(); + parameters.context.globalAlpha = 1; + } + } + } + } +} diff --git a/src/js/game/hud/parts/keybinding_overlay.js b/src/js/game/hud/parts/keybinding_overlay.js index 0a050484..fbce79f2 100644 --- a/src/js/game/hud/parts/keybinding_overlay.js +++ b/src/js/game/hud/parts/keybinding_overlay.js @@ -1,339 +1,334 @@ -import { makeDiv } from "../../../core/utils"; -import { T } from "../../../translations"; -import { - getStringForKeyCode, - KEYCODE_LMB, - KEYCODE_MMB, - KEYCODE_RMB, - KEYMAPPINGS, -} from "../../key_action_mapper"; -import { enumHubGoalRewards } from "../../tutorial_goals"; -import { BaseHUDPart } from "../base_hud_part"; -import { DynamicDomAttach } from "../dynamic_dom_attach"; - -const DIVIDER_TOKEN = "/"; -const ADDER_TOKEN = "+"; - -/** - * @typedef {{ keyCode: number }} KeyCode - */ - -/** - * @typedef {{ - * condition: () => boolean, - * keys: Array, - * label: string, - * cachedElement?: HTMLElement, - * cachedVisibility?: boolean - * }} KeyBinding - */ - -export class HUDKeybindingOverlay extends BaseHUDPart { - /** - * HELPER / Returns if there is a building selected for placement - * @returns {boolean} - */ - get buildingPlacementActive() { - const placer = this.root.hud.parts.buildingPlacer; - return !this.mapOverviewActive && placer && !!placer.currentMetaBuilding.get(); - } - - /** - * HELPER / Returns if there is a building selected for placement and - * it supports the belt planner - * @returns {boolean} - */ - get buildingPlacementSupportsBeltPlanner() { - const placer = this.root.hud.parts.buildingPlacer; - return ( - !this.mapOverviewActive && - placer && - placer.currentMetaBuilding.get() && - placer.currentMetaBuilding.get().getHasDirectionLockAvailable(placer.currentVariant.get()) - ); - } - - /** - * HELPER / Returns if there is a building selected for placement and - * it has multiplace enabled by default - * @returns {boolean} - */ - get buildingPlacementStaysInPlacement() { - const placer = this.root.hud.parts.buildingPlacer; - return ( - !this.mapOverviewActive && - placer && - placer.currentMetaBuilding.get() && - placer.currentMetaBuilding.get().getStayInPlacementMode() - ); - } - - /** - * HELPER / Returns if there is a blueprint selected for placement - * @returns {boolean} - */ - get blueprintPlacementActive() { - const placer = this.root.hud.parts.blueprintPlacer; - return placer && !!placer.currentBlueprint.get(); - } - - /** - * HELPER / Returns if the belt planner is currently active - * @returns {boolean} - */ - get beltPlannerActive() { - const placer = this.root.hud.parts.buildingPlacer; - return !this.mapOverviewActive && placer && placer.isDirectionLockActive; - } - - /** - * HELPER / Returns if there is a last blueprint available - * @returns {boolean} - */ - get lastBlueprintAvailable() { - const placer = this.root.hud.parts.blueprintPlacer; - return placer && !!placer.lastBlueprintUsed; - } - - /** - * HELPER / Returns if there is anything selected on the map - * @returns {boolean} - */ - get anythingSelectedOnMap() { - const selector = this.root.hud.parts.massSelector; - return selector && selector.selectedUids.size > 0; - } - - /** - * HELPER / Returns if there is a building or blueprint selected for placement - * @returns {boolean} - */ - get anyPlacementActive() { - return this.buildingPlacementActive || this.blueprintPlacementActive; - } - - /** - * HELPER / Returns if the map overview is active - * @returns {boolean} - */ - get mapOverviewActive() { - return this.root.camera.getIsMapOverlayActive(); - } - - /** - * Initializes the element - * @param {HTMLElement} parent - */ - createElements(parent) { - const mapper = this.root.keyMapper; - const k = KEYMAPPINGS; - - /** @type {Array} */ - this.keybindings = [ - { - // Move map - Including mouse - label: T.ingame.keybindingsOverlay.moveMap, - keys: [ - KEYCODE_LMB, - DIVIDER_TOKEN, - k.navigation.mapMoveUp, - k.navigation.mapMoveLeft, - k.navigation.mapMoveDown, - k.navigation.mapMoveRight, - ], - condition: () => !this.anyPlacementActive, - }, - - { - // Move map - No mouse - label: T.ingame.keybindingsOverlay.moveMap, - keys: [ - k.navigation.mapMoveUp, - k.navigation.mapMoveLeft, - k.navigation.mapMoveDown, - k.navigation.mapMoveRight, - ], - condition: () => this.anyPlacementActive, - }, - - { - // [OVERVIEW] Create marker with right click - label: T.ingame.keybindingsOverlay.createMarker, - keys: [KEYCODE_RMB], - condition: () => this.mapOverviewActive && !this.blueprintPlacementActive, - }, - - { - // Cancel placement - label: T.ingame.keybindingsOverlay.stopPlacement, - keys: [KEYCODE_RMB], - condition: () => this.anyPlacementActive, - }, - - { - // Delete with right click - label: T.ingame.keybindingsOverlay.delete, - keys: [KEYCODE_RMB], - condition: () => - !this.anyPlacementActive && !this.mapOverviewActive && !this.anythingSelectedOnMap, - }, - - { - // Pipette - label: T.ingame.keybindingsOverlay.pipette, - keys: [k.placement.pipette], - condition: () => !this.mapOverviewActive && !this.blueprintPlacementActive, - }, - - { - // Area select - label: T.ingame.keybindingsOverlay.selectBuildings, - keys: [k.massSelect.massSelectStart, ADDER_TOKEN, KEYCODE_LMB], - condition: () => !this.anyPlacementActive && !this.anythingSelectedOnMap, - }, - - { - // Place building - label: T.ingame.keybindingsOverlay.placeBuilding, - keys: [KEYCODE_LMB], - condition: () => this.anyPlacementActive, - }, - - { - // Rotate - label: T.ingame.keybindingsOverlay.rotateBuilding, - keys: [k.placement.rotateWhilePlacing], - condition: () => this.anyPlacementActive && !this.beltPlannerActive, - }, - - { - // [BELT PLANNER] Flip Side - label: T.ingame.keybindingsOverlay.plannerSwitchSide, - keys: [k.placement.switchDirectionLockSide], - condition: () => this.beltPlannerActive, - }, - - { - // Place last blueprint - label: T.ingame.keybindingsOverlay.pasteLastBlueprint, - keys: [k.massSelect.pasteLastBlueprint], - condition: () => !this.blueprintPlacementActive && this.lastBlueprintAvailable, - }, - - { - // Belt planner - label: T.ingame.keybindingsOverlay.lockBeltDirection, - keys: [k.placementModifiers.lockBeltDirection], - condition: () => this.buildingPlacementSupportsBeltPlanner && !this.beltPlannerActive, - }, - - { - // [SELECTION] Destroy - label: T.ingame.keybindingsOverlay.delete, - keys: [k.massSelect.confirmMassDelete], - condition: () => this.anythingSelectedOnMap, - }, - - { - // [SELECTION] Cancel - label: T.ingame.keybindingsOverlay.clearSelection, - keys: [k.general.back], - condition: () => this.anythingSelectedOnMap, - }, - { - // [SELECTION] Cut - label: T.ingame.keybindingsOverlay.cutSelection, - keys: [k.massSelect.massSelectCut], - condition: () => this.anythingSelectedOnMap, - }, - - { - // [SELECTION] Copy - label: T.ingame.keybindingsOverlay.copySelection, - keys: [k.massSelect.massSelectCopy], - condition: () => this.anythingSelectedOnMap, - }, - - { - // [SELECTION] Clear - label: T.ingame.keybindingsOverlay.clearBelts, - keys: [k.massSelect.massSelectClear], - condition: () => this.anythingSelectedOnMap, - }, - - { - // Switch layers - label: T.ingame.keybindingsOverlay.switchLayers, - keys: [k.ingame.switchLayers], - condition: () => - this.root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_wires_painter_and_levers), - }, - ]; - - if (!this.root.app.settings.getAllSettings().alwaysMultiplace) { - this.keybindings.push({ - // Multiplace - label: T.ingame.keybindingsOverlay.placeMultiple, - keys: [k.placementModifiers.placeMultiple], - condition: () => this.anyPlacementActive && !this.buildingPlacementStaysInPlacement, - }); - } - - this.element = makeDiv(parent, "ingame_HUD_KeybindingOverlay", []); - - for (let i = 0; i < this.keybindings.length; ++i) { - let html = ""; - const handle = this.keybindings[i]; - - for (let k = 0; k < handle.keys.length; ++k) { - const key = handle.keys[k]; - - switch (key) { - case KEYCODE_LMB: - html += ``; - break; - case KEYCODE_RMB: - html += ``; - break; - case KEYCODE_MMB: - html += ``; - break; - case DIVIDER_TOKEN: - html += ``; - break; - case ADDER_TOKEN: - html += `+`; - break; - default: - html += `${getStringForKeyCode( - mapper.getBinding(/** @type {KeyCode} */ (key)).keyCode - )}`; - } - } - html += ``; - - handle.cachedElement = makeDiv(this.element, null, ["binding"], html); - handle.cachedVisibility = false; - } - } - - initialize() { - this.domAttach = new DynamicDomAttach(this.root, this.element, { - trackHover: true, - }); - } - - update() { - for (let i = 0; i < this.keybindings.length; ++i) { - const handle = this.keybindings[i]; - const visibility = handle.condition(); - if (visibility !== handle.cachedVisibility) { - handle.cachedVisibility = visibility; - handle.cachedElement.classList.toggle("visible", visibility); - } - } - - // Required for hover - this.domAttach.update(true); - } -} +import { getStringForKeyCode, KEYCODE_LMB, KEYCODE_MMB, KEYCODE_RMB } from "@/core/keycodes"; +import { makeDiv } from "../../../core/utils"; +import { T } from "../../../translations"; +import { KEYMAPPINGS } from "../../key_action_mapper"; +import { enumHubGoalRewards } from "../../tutorial_goals"; +import { BaseHUDPart } from "../base_hud_part"; +import { DynamicDomAttach } from "../dynamic_dom_attach"; + +const DIVIDER_TOKEN = "/"; +const ADDER_TOKEN = "+"; + +/** + * @typedef {{ keyCode: number }} KeyCode + */ + +/** + * @typedef {{ + * condition: () => boolean, + * keys: Array, + * label: string, + * cachedElement?: HTMLElement, + * cachedVisibility?: boolean + * }} KeyBinding + */ + +export class HUDKeybindingOverlay extends BaseHUDPart { + /** + * HELPER / Returns if there is a building selected for placement + * @returns {boolean} + */ + get buildingPlacementActive() { + const placer = this.root.hud.parts.buildingPlacer; + return !this.mapOverviewActive && placer && !!placer.currentMetaBuilding.get(); + } + + /** + * HELPER / Returns if there is a building selected for placement and + * it supports the belt planner + * @returns {boolean} + */ + get buildingPlacementSupportsBeltPlanner() { + const placer = this.root.hud.parts.buildingPlacer; + return ( + !this.mapOverviewActive && + placer && + placer.currentMetaBuilding.get() && + placer.currentMetaBuilding.get().getHasDirectionLockAvailable(placer.currentVariant.get()) + ); + } + + /** + * HELPER / Returns if there is a building selected for placement and + * it has multiplace enabled by default + * @returns {boolean} + */ + get buildingPlacementStaysInPlacement() { + const placer = this.root.hud.parts.buildingPlacer; + return ( + !this.mapOverviewActive && + placer && + placer.currentMetaBuilding.get() && + placer.currentMetaBuilding.get().getStayInPlacementMode() + ); + } + + /** + * HELPER / Returns if there is a blueprint selected for placement + * @returns {boolean} + */ + get blueprintPlacementActive() { + const placer = this.root.hud.parts.blueprintPlacer; + return placer && !!placer.currentBlueprint.get(); + } + + /** + * HELPER / Returns if the belt planner is currently active + * @returns {boolean} + */ + get beltPlannerActive() { + const placer = this.root.hud.parts.buildingPlacer; + return !this.mapOverviewActive && placer && placer.isDirectionLockActive; + } + + /** + * HELPER / Returns if there is a last blueprint available + * @returns {boolean} + */ + get lastBlueprintAvailable() { + const placer = this.root.hud.parts.blueprintPlacer; + return placer && !!placer.lastBlueprintUsed; + } + + /** + * HELPER / Returns if there is anything selected on the map + * @returns {boolean} + */ + get anythingSelectedOnMap() { + const selector = this.root.hud.parts.massSelector; + return selector && selector.selectedUids.size > 0; + } + + /** + * HELPER / Returns if there is a building or blueprint selected for placement + * @returns {boolean} + */ + get anyPlacementActive() { + return this.buildingPlacementActive || this.blueprintPlacementActive; + } + + /** + * HELPER / Returns if the map overview is active + * @returns {boolean} + */ + get mapOverviewActive() { + return this.root.camera.getIsMapOverlayActive(); + } + + /** + * Initializes the element + * @param {HTMLElement} parent + */ + createElements(parent) { + const mapper = this.root.keyMapper; + const k = KEYMAPPINGS; + + /** @type {Array} */ + this.keybindings = [ + { + // Move map - Including mouse + label: T.ingame.keybindingsOverlay.moveMap, + keys: [ + KEYCODE_LMB, + DIVIDER_TOKEN, + k.navigation.mapMoveUp, + k.navigation.mapMoveLeft, + k.navigation.mapMoveDown, + k.navigation.mapMoveRight, + ], + condition: () => !this.anyPlacementActive, + }, + + { + // Move map - No mouse + label: T.ingame.keybindingsOverlay.moveMap, + keys: [ + k.navigation.mapMoveUp, + k.navigation.mapMoveLeft, + k.navigation.mapMoveDown, + k.navigation.mapMoveRight, + ], + condition: () => this.anyPlacementActive, + }, + + { + // [OVERVIEW] Create marker with right click + label: T.ingame.keybindingsOverlay.createMarker, + keys: [KEYCODE_RMB], + condition: () => this.mapOverviewActive && !this.blueprintPlacementActive, + }, + + { + // Cancel placement + label: T.ingame.keybindingsOverlay.stopPlacement, + keys: [KEYCODE_RMB], + condition: () => this.anyPlacementActive, + }, + + { + // Delete with right click + label: T.ingame.keybindingsOverlay.delete, + keys: [KEYCODE_RMB], + condition: () => + !this.anyPlacementActive && !this.mapOverviewActive && !this.anythingSelectedOnMap, + }, + + { + // Pipette + label: T.ingame.keybindingsOverlay.pipette, + keys: [k.placement.pipette], + condition: () => !this.mapOverviewActive && !this.blueprintPlacementActive, + }, + + { + // Area select + label: T.ingame.keybindingsOverlay.selectBuildings, + keys: [k.massSelect.massSelectStart, ADDER_TOKEN, KEYCODE_LMB], + condition: () => !this.anyPlacementActive && !this.anythingSelectedOnMap, + }, + + { + // Place building + label: T.ingame.keybindingsOverlay.placeBuilding, + keys: [KEYCODE_LMB], + condition: () => this.anyPlacementActive, + }, + + { + // Rotate + label: T.ingame.keybindingsOverlay.rotateBuilding, + keys: [k.placement.rotateWhilePlacing], + condition: () => this.anyPlacementActive && !this.beltPlannerActive, + }, + + { + // [BELT PLANNER] Flip Side + label: T.ingame.keybindingsOverlay.plannerSwitchSide, + keys: [k.placement.switchDirectionLockSide], + condition: () => this.beltPlannerActive, + }, + + { + // Place last blueprint + label: T.ingame.keybindingsOverlay.pasteLastBlueprint, + keys: [k.massSelect.pasteLastBlueprint], + condition: () => !this.blueprintPlacementActive && this.lastBlueprintAvailable, + }, + + { + // Belt planner + label: T.ingame.keybindingsOverlay.lockBeltDirection, + keys: [k.placementModifiers.lockBeltDirection], + condition: () => this.buildingPlacementSupportsBeltPlanner && !this.beltPlannerActive, + }, + + { + // [SELECTION] Destroy + label: T.ingame.keybindingsOverlay.delete, + keys: [k.massSelect.confirmMassDelete], + condition: () => this.anythingSelectedOnMap, + }, + + { + // [SELECTION] Cancel + label: T.ingame.keybindingsOverlay.clearSelection, + keys: [k.general.back], + condition: () => this.anythingSelectedOnMap, + }, + { + // [SELECTION] Cut + label: T.ingame.keybindingsOverlay.cutSelection, + keys: [k.massSelect.massSelectCut], + condition: () => this.anythingSelectedOnMap, + }, + + { + // [SELECTION] Copy + label: T.ingame.keybindingsOverlay.copySelection, + keys: [k.massSelect.massSelectCopy], + condition: () => this.anythingSelectedOnMap, + }, + + { + // [SELECTION] Clear + label: T.ingame.keybindingsOverlay.clearBelts, + keys: [k.massSelect.massSelectClear], + condition: () => this.anythingSelectedOnMap, + }, + + { + // Switch layers + label: T.ingame.keybindingsOverlay.switchLayers, + keys: [k.ingame.switchLayers], + condition: () => + this.root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_wires_painter_and_levers), + }, + ]; + + if (!this.root.app.settings.getAllSettings().alwaysMultiplace) { + this.keybindings.push({ + // Multiplace + label: T.ingame.keybindingsOverlay.placeMultiple, + keys: [k.placementModifiers.placeMultiple], + condition: () => this.anyPlacementActive && !this.buildingPlacementStaysInPlacement, + }); + } + + this.element = makeDiv(parent, "ingame_HUD_KeybindingOverlay", []); + + for (let i = 0; i < this.keybindings.length; ++i) { + let html = ""; + const handle = this.keybindings[i]; + + for (let k = 0; k < handle.keys.length; ++k) { + const key = handle.keys[k]; + + switch (key) { + case KEYCODE_LMB: + html += ``; + break; + case KEYCODE_RMB: + html += ``; + break; + case KEYCODE_MMB: + html += ``; + break; + case DIVIDER_TOKEN: + html += ``; + break; + case ADDER_TOKEN: + html += `+`; + break; + default: + html += `${getStringForKeyCode( + mapper.getBinding(/** @type {KeyCode} */ (key)).keyCode + )}`; + } + } + html += ``; + + handle.cachedElement = makeDiv(this.element, null, ["binding"], html); + handle.cachedVisibility = false; + } + } + + initialize() { + this.domAttach = new DynamicDomAttach(this.root, this.element, { + trackHover: true, + }); + } + + update() { + for (let i = 0; i < this.keybindings.length; ++i) { + const handle = this.keybindings[i]; + const visibility = handle.condition(); + if (visibility !== handle.cachedVisibility) { + handle.cachedVisibility = visibility; + handle.cachedElement.classList.toggle("visible", visibility); + } + } + + // Required for hover + this.domAttach.update(true); + } +} diff --git a/src/js/game/hud/parts/mass_selector.js b/src/js/game/hud/parts/mass_selector.js index 51a9eaa2..0a8a3633 100644 --- a/src/js/game/hud/parts/mass_selector.js +++ b/src/js/game/hud/parts/mass_selector.js @@ -1,369 +1,365 @@ -import { globalConfig } from "../../../core/config"; -import { DrawParameters } from "../../../core/draw_parameters"; -import { createLogger } from "../../../core/logging"; -import { STOP_PROPAGATION } from "../../../core/signal"; -import { formatBigNumberFull } from "../../../core/utils"; -import { Vector } from "../../../core/vector"; -import { ACHIEVEMENTS } from "../../../platform/achievement_provider"; -import { T } from "../../../translations"; -import { Blueprint } from "../../blueprint"; -import { enumMouseButton } from "../../camera"; -import { Entity } from "../../entity"; -import { KEYMAPPINGS } from "../../key_action_mapper"; -import { THEME } from "../../theme"; -import { enumHubGoalRewards } from "../../tutorial_goals"; -import { BaseHUDPart } from "../base_hud_part"; - -/* typehints:start */ -// @ts-ignore -import { Component } from "../../component"; -/* typehints:end */ - -const logger = createLogger("hud/mass_selector"); - -export class HUDMassSelector extends BaseHUDPart { - createElements(parent) {} - - initialize() { - this.currentSelectionStartWorld = null; - this.currentSelectionEnd = null; - this.selectedUids = new Set(); - - this.root.signals.entityQueuedForDestroy.add(this.onEntityDestroyed, this); - this.root.hud.signals.pasteBlueprintRequested.add(this.clearSelection, this); - - this.root.camera.downPreHandler.add(this.onMouseDown, this); - this.root.camera.movePreHandler.add(this.onMouseMove, this); - this.root.camera.upPostHandler.add(this.onMouseUp, this); - - this.root.keyMapper.getBinding(KEYMAPPINGS.general.back).addToTop(this.onBack, this); - this.root.keyMapper - .getBinding(KEYMAPPINGS.massSelect.confirmMassDelete) - .add(this.confirmDelete, this); - this.root.keyMapper.getBinding(KEYMAPPINGS.massSelect.massSelectCut).add(this.confirmCut, this); - this.root.keyMapper.getBinding(KEYMAPPINGS.massSelect.massSelectCopy).add(this.startCopy, this); - this.root.keyMapper.getBinding(KEYMAPPINGS.massSelect.massSelectClear).add(this.clearBelts, this); - - this.root.hud.signals.selectedPlacementBuildingChanged.add(this.clearSelection, this); - this.root.signals.editModeChanged.add(this.clearSelection, this); - } - - /** - * Handles the destroy callback and makes sure we clean our list - * @param {Entity} entity - */ - onEntityDestroyed(entity) { - if (this.root.bulkOperationRunning) { - return; - } - this.selectedUids.delete(entity.uid); - } - - /** - * - */ - onBack() { - // Clear entities on escape - if (this.selectedUids.size > 0) { - this.selectedUids = new Set(); - return STOP_PROPAGATION; - } - } - - /** - * Clears the entire selection - */ - clearSelection() { - this.selectedUids = new Set(); - } - - confirmDelete() { - if ( - !this.root.app.settings.getAllSettings().disableCutDeleteWarnings && - this.selectedUids.size > 100 - ) { - const { ok } = this.root.hud.parts.dialogs.showWarning( - T.dialogs.massDeleteConfirm.title, - T.dialogs.massDeleteConfirm.desc.replace( - "", - "" + formatBigNumberFull(this.selectedUids.size) - ), - ["cancel:good:escape", "ok:bad:enter"] - ); - ok.add(() => this.doDelete()); - } else { - this.doDelete(); - } - } - - doDelete() { - const entityUids = Array.from(this.selectedUids); - - // Build mapping from uid to entity - /** - * @type {Map} - */ - const mapUidToEntity = this.root.entityMgr.getFrozenUidSearchMap(); - - let count = 0; - this.root.logic.performBulkOperation(() => { - for (let i = 0; i < entityUids.length; ++i) { - const uid = entityUids[i]; - const entity = mapUidToEntity.get(uid); - if (!entity) { - logger.error("Entity not found by uid:", uid); - continue; - } - - if (!this.root.logic.tryDeleteBuilding(entity)) { - logger.error("Error in mass delete, could not remove building"); - } else { - count++; - } - } - - this.root.signals.achievementCheck.dispatch(ACHIEVEMENTS.destroy1000, count); - }); - - // Clear uids later - this.selectedUids = new Set(); - } - - showBlueprintsNotUnlocked() { - this.root.hud.parts.dialogs.showInfo( - T.dialogs.blueprintsNotUnlocked.title, - T.dialogs.blueprintsNotUnlocked.desc - ); - } - - startCopy() { - if (this.selectedUids.size > 0) { - if (!this.root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_blueprints)) { - this.showBlueprintsNotUnlocked(); - return; - } - this.root.hud.signals.buildingsSelectedForCopy.dispatch(Array.from(this.selectedUids)); - this.selectedUids = new Set(); - this.root.soundProxy.playUiClick(); - } else { - this.root.soundProxy.playUiError(); - } - } - - clearBelts() { - for (const uid of this.selectedUids) { - const entity = this.root.entityMgr.findByUid(uid); - for (const component of Object.values(entity.components)) { - /** @type {Component} */ (component).clear(); - } - } - this.selectedUids = new Set(); - } - - confirmCut() { - if (!this.root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_blueprints)) { - this.showBlueprintsNotUnlocked(); - } else if ( - !this.root.app.settings.getAllSettings().disableCutDeleteWarnings && - this.selectedUids.size > 100 - ) { - const { ok } = this.root.hud.parts.dialogs.showWarning( - T.dialogs.massCutConfirm.title, - T.dialogs.massCutConfirm.desc.replace( - "", - "" + formatBigNumberFull(this.selectedUids.size) - ), - ["cancel:good:escape", "ok:bad:enter"] - ); - ok.add(() => this.doCut()); - } else { - this.doCut(); - } - } - - doCut() { - if (this.selectedUids.size > 0) { - const entityUids = Array.from(this.selectedUids); - - const cutAction = () => { - // copy code relies on entities still existing, so must copy before deleting. - this.root.hud.signals.buildingsSelectedForCopy.dispatch(entityUids); - - for (let i = 0; i < entityUids.length; ++i) { - const uid = entityUids[i]; - const entity = this.root.entityMgr.findByUid(uid); - if (!this.root.logic.tryDeleteBuilding(entity)) { - logger.error("Error in mass cut, could not remove building"); - this.selectedUids.delete(uid); - } - } - }; - - const blueprint = Blueprint.fromUids(this.root, entityUids); - if (blueprint.canAfford(this.root)) { - cutAction(); - } else { - const { cancel, ok } = this.root.hud.parts.dialogs.showWarning( - T.dialogs.massCutInsufficientConfirm.title, - T.dialogs.massCutInsufficientConfirm.desc, - ["cancel:good:escape", "ok:bad:enter"] - ); - ok.add(cutAction); - } - - this.root.soundProxy.playUiClick(); - } else { - this.root.soundProxy.playUiError(); - } - } - - /** - * mouse down pre handler - * @param {Vector} pos - * @param {enumMouseButton} mouseButton - */ - onMouseDown(pos, mouseButton) { - if (!this.root.keyMapper.getBinding(KEYMAPPINGS.massSelect.massSelectStart).pressed) { - return; - } - - if (mouseButton !== enumMouseButton.left) { - return; - } - - if (!this.root.keyMapper.getBinding(KEYMAPPINGS.massSelect.massSelectSelectMultiple).pressed) { - // Start new selection - this.selectedUids = new Set(); - } - - this.currentSelectionStartWorld = this.root.camera.screenToWorld(pos.copy()); - this.currentSelectionEnd = pos.copy(); - return STOP_PROPAGATION; - } - - /** - * mouse move pre handler - * @param {Vector} pos - */ - onMouseMove(pos) { - if (this.currentSelectionStartWorld) { - this.currentSelectionEnd = pos.copy(); - } - } - - onMouseUp() { - if (this.currentSelectionStartWorld) { - const worldStart = this.currentSelectionStartWorld; - const worldEnd = this.root.camera.screenToWorld(this.currentSelectionEnd); - - const tileStart = worldStart.toTileSpace(); - const tileEnd = worldEnd.toTileSpace(); - - const realTileStart = tileStart.min(tileEnd); - const realTileEnd = tileStart.max(tileEnd); - - for (let x = realTileStart.x; x <= realTileEnd.x; ++x) { - for (let y = realTileStart.y; y <= realTileEnd.y; ++y) { - const contents = this.root.map.getLayerContentXY(x, y, this.root.currentLayer); - - if (contents && this.root.logic.canDeleteBuilding(contents)) { - const staticComp = contents.components.StaticMapEntity; - - if (!staticComp.getMetaBuilding().getIsRemovable(this.root)) { - continue; - } - - this.selectedUids.add(contents.uid); - } - } - } - - this.currentSelectionStartWorld = null; - this.currentSelectionEnd = null; - } - } - - /** - * - * @param {DrawParameters} parameters - */ - draw(parameters) { - const boundsBorder = 2; - - if (this.currentSelectionStartWorld) { - const worldStart = this.currentSelectionStartWorld; - const worldEnd = this.root.camera.screenToWorld(this.currentSelectionEnd); - - const realWorldStart = worldStart.min(worldEnd); - const realWorldEnd = worldStart.max(worldEnd); - - const tileStart = worldStart.toTileSpace(); - const tileEnd = worldEnd.toTileSpace(); - - const realTileStart = tileStart.min(tileEnd); - const realTileEnd = tileStart.max(tileEnd); - - parameters.context.lineWidth = 1; - parameters.context.fillStyle = THEME.map.selectionBackground; - parameters.context.strokeStyle = THEME.map.selectionOutline; - parameters.context.beginPath(); - parameters.context.rect( - realWorldStart.x, - realWorldStart.y, - realWorldEnd.x - realWorldStart.x, - realWorldEnd.y - realWorldStart.y - ); - parameters.context.fill(); - parameters.context.stroke(); - - parameters.context.fillStyle = THEME.map.selectionOverlay; - parameters.context.beginPath(); - - const renderedUids = new Set(); - - for (let x = realTileStart.x; x <= realTileEnd.x; ++x) { - for (let y = realTileStart.y; y <= realTileEnd.y; ++y) { - const contents = this.root.map.getLayerContentXY(x, y, this.root.currentLayer); - if (contents && this.root.logic.canDeleteBuilding(contents)) { - // Prevent rendering the overlay twice - const uid = contents.uid; - if (renderedUids.has(uid)) { - continue; - } - renderedUids.add(uid); - - const staticComp = contents.components.StaticMapEntity; - - if (!staticComp.getMetaBuilding().getIsRemovable(this.root)) { - continue; - } - - const bounds = staticComp.getTileSpaceBounds(); - parameters.context.rect( - bounds.x * globalConfig.tileSize + boundsBorder, - bounds.y * globalConfig.tileSize + boundsBorder, - bounds.w * globalConfig.tileSize - 2 * boundsBorder, - bounds.h * globalConfig.tileSize - 2 * boundsBorder - ); - } - } - } - parameters.context.fill(); - } - - parameters.context.fillStyle = THEME.map.selectionOverlay; - parameters.context.beginPath(); - this.selectedUids.forEach(uid => { - const entity = this.root.entityMgr.findByUid(uid); - const staticComp = entity.components.StaticMapEntity; - const bounds = staticComp.getTileSpaceBounds(); - parameters.context.rect( - bounds.x * globalConfig.tileSize + boundsBorder, - bounds.y * globalConfig.tileSize + boundsBorder, - bounds.w * globalConfig.tileSize - 2 * boundsBorder, - bounds.h * globalConfig.tileSize - 2 * boundsBorder - ); - }); - parameters.context.fill(); - } -} +import { globalConfig } from "../../../core/config"; +import { DrawParameters } from "../../../core/draw_parameters"; +import { createLogger } from "../../../core/logging"; +import { STOP_PROPAGATION } from "../../../core/signal"; +import { formatBigNumberFull } from "../../../core/utils"; +import { Vector } from "../../../core/vector"; +import { T } from "../../../translations"; +import { Blueprint } from "../../blueprint"; +import { enumMouseButton } from "../../camera"; +import { Entity } from "../../entity"; +import { KEYMAPPINGS } from "../../key_action_mapper"; +import { THEME } from "../../theme"; +import { enumHubGoalRewards } from "../../tutorial_goals"; +import { BaseHUDPart } from "../base_hud_part"; + +/* typehints:start */ +// @ts-ignore +/* typehints:end */ + +const logger = createLogger("hud/mass_selector"); + +export class HUDMassSelector extends BaseHUDPart { + createElements(parent) {} + + initialize() { + this.currentSelectionStartWorld = null; + this.currentSelectionEnd = null; + this.selectedUids = new Set(); + + this.root.signals.entityQueuedForDestroy.add(this.onEntityDestroyed, this); + this.root.hud.signals.pasteBlueprintRequested.add(this.clearSelection, this); + + this.root.camera.downPreHandler.add(this.onMouseDown, this); + this.root.camera.movePreHandler.add(this.onMouseMove, this); + this.root.camera.upPostHandler.add(this.onMouseUp, this); + + this.root.keyMapper.getBinding(KEYMAPPINGS.general.back).addToTop(this.onBack, this); + this.root.keyMapper + .getBinding(KEYMAPPINGS.massSelect.confirmMassDelete) + .add(this.confirmDelete, this); + this.root.keyMapper.getBinding(KEYMAPPINGS.massSelect.massSelectCut).add(this.confirmCut, this); + this.root.keyMapper.getBinding(KEYMAPPINGS.massSelect.massSelectCopy).add(this.startCopy, this); + this.root.keyMapper.getBinding(KEYMAPPINGS.massSelect.massSelectClear).add(this.clearBelts, this); + + this.root.hud.signals.selectedPlacementBuildingChanged.add(this.clearSelection, this); + this.root.signals.editModeChanged.add(this.clearSelection, this); + } + + /** + * Handles the destroy callback and makes sure we clean our list + * @param {Entity} entity + */ + onEntityDestroyed(entity) { + if (this.root.bulkOperationRunning) { + return; + } + this.selectedUids.delete(entity.uid); + } + + /** + * + */ + onBack() { + // Clear entities on escape + if (this.selectedUids.size > 0) { + this.selectedUids = new Set(); + return STOP_PROPAGATION; + } + } + + /** + * Clears the entire selection + */ + clearSelection() { + this.selectedUids = new Set(); + } + + confirmDelete() { + if ( + !this.root.app.settings.getAllSettings().disableCutDeleteWarnings && + this.selectedUids.size > 100 + ) { + const { ok } = this.root.hud.parts.dialogs.showWarning( + T.dialogs.massDeleteConfirm.title, + T.dialogs.massDeleteConfirm.desc.replace( + "", + "" + formatBigNumberFull(this.selectedUids.size) + ), + ["cancel:good:escape", "ok:bad:enter"] + ); + ok.add(() => this.doDelete()); + } else { + this.doDelete(); + } + } + + doDelete() { + const entityUids = Array.from(this.selectedUids); + + // Build mapping from uid to entity + /** + * @type {Map} + */ + const mapUidToEntity = this.root.entityMgr.getFrozenUidSearchMap(); + + let count = 0; + this.root.logic.performBulkOperation(() => { + for (let i = 0; i < entityUids.length; ++i) { + const uid = entityUids[i]; + const entity = mapUidToEntity.get(uid); + if (!entity) { + logger.error("Entity not found by uid:", uid); + continue; + } + + if (!this.root.logic.tryDeleteBuilding(entity)) { + logger.error("Error in mass delete, could not remove building"); + } else { + count++; + } + } + }); + + // Clear uids later + this.selectedUids = new Set(); + } + + showBlueprintsNotUnlocked() { + this.root.hud.parts.dialogs.showInfo( + T.dialogs.blueprintsNotUnlocked.title, + T.dialogs.blueprintsNotUnlocked.desc + ); + } + + startCopy() { + if (this.selectedUids.size > 0) { + if (!this.root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_blueprints)) { + this.showBlueprintsNotUnlocked(); + return; + } + this.root.hud.signals.buildingsSelectedForCopy.dispatch(Array.from(this.selectedUids)); + this.selectedUids = new Set(); + this.root.soundProxy.playUiClick(); + } else { + this.root.soundProxy.playUiError(); + } + } + + clearBelts() { + for (const uid of this.selectedUids) { + const entity = this.root.entityMgr.findByUid(uid); + for (const component of Object.values(entity.components)) { + /** @type {Component} */ (component).clear(); + } + } + this.selectedUids = new Set(); + } + + confirmCut() { + if (!this.root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_blueprints)) { + this.showBlueprintsNotUnlocked(); + } else if ( + !this.root.app.settings.getAllSettings().disableCutDeleteWarnings && + this.selectedUids.size > 100 + ) { + const { ok } = this.root.hud.parts.dialogs.showWarning( + T.dialogs.massCutConfirm.title, + T.dialogs.massCutConfirm.desc.replace( + "", + "" + formatBigNumberFull(this.selectedUids.size) + ), + ["cancel:good:escape", "ok:bad:enter"] + ); + ok.add(() => this.doCut()); + } else { + this.doCut(); + } + } + + doCut() { + if (this.selectedUids.size > 0) { + const entityUids = Array.from(this.selectedUids); + + const cutAction = () => { + // copy code relies on entities still existing, so must copy before deleting. + this.root.hud.signals.buildingsSelectedForCopy.dispatch(entityUids); + + for (let i = 0; i < entityUids.length; ++i) { + const uid = entityUids[i]; + const entity = this.root.entityMgr.findByUid(uid); + if (!this.root.logic.tryDeleteBuilding(entity)) { + logger.error("Error in mass cut, could not remove building"); + this.selectedUids.delete(uid); + } + } + }; + + const blueprint = Blueprint.fromUids(this.root, entityUids); + if (blueprint.canAfford(this.root)) { + cutAction(); + } else { + const { cancel, ok } = this.root.hud.parts.dialogs.showWarning( + T.dialogs.massCutInsufficientConfirm.title, + T.dialogs.massCutInsufficientConfirm.desc, + ["cancel:good:escape", "ok:bad:enter"] + ); + ok.add(cutAction); + } + + this.root.soundProxy.playUiClick(); + } else { + this.root.soundProxy.playUiError(); + } + } + + /** + * mouse down pre handler + * @param {Vector} pos + * @param {enumMouseButton} mouseButton + */ + onMouseDown(pos, mouseButton) { + if (!this.root.keyMapper.getBinding(KEYMAPPINGS.massSelect.massSelectStart).pressed) { + return; + } + + if (mouseButton !== enumMouseButton.left) { + return; + } + + if (!this.root.keyMapper.getBinding(KEYMAPPINGS.massSelect.massSelectSelectMultiple).pressed) { + // Start new selection + this.selectedUids = new Set(); + } + + this.currentSelectionStartWorld = this.root.camera.screenToWorld(pos.copy()); + this.currentSelectionEnd = pos.copy(); + return STOP_PROPAGATION; + } + + /** + * mouse move pre handler + * @param {Vector} pos + */ + onMouseMove(pos) { + if (this.currentSelectionStartWorld) { + this.currentSelectionEnd = pos.copy(); + } + } + + onMouseUp() { + if (this.currentSelectionStartWorld) { + const worldStart = this.currentSelectionStartWorld; + const worldEnd = this.root.camera.screenToWorld(this.currentSelectionEnd); + + const tileStart = worldStart.toTileSpace(); + const tileEnd = worldEnd.toTileSpace(); + + const realTileStart = tileStart.min(tileEnd); + const realTileEnd = tileStart.max(tileEnd); + + for (let x = realTileStart.x; x <= realTileEnd.x; ++x) { + for (let y = realTileStart.y; y <= realTileEnd.y; ++y) { + const contents = this.root.map.getLayerContentXY(x, y, this.root.currentLayer); + + if (contents && this.root.logic.canDeleteBuilding(contents)) { + const staticComp = contents.components.StaticMapEntity; + + if (!staticComp.getMetaBuilding().getIsRemovable(this.root)) { + continue; + } + + this.selectedUids.add(contents.uid); + } + } + } + + this.currentSelectionStartWorld = null; + this.currentSelectionEnd = null; + } + } + + /** + * + * @param {DrawParameters} parameters + */ + draw(parameters) { + const boundsBorder = 2; + + if (this.currentSelectionStartWorld) { + const worldStart = this.currentSelectionStartWorld; + const worldEnd = this.root.camera.screenToWorld(this.currentSelectionEnd); + + const realWorldStart = worldStart.min(worldEnd); + const realWorldEnd = worldStart.max(worldEnd); + + const tileStart = worldStart.toTileSpace(); + const tileEnd = worldEnd.toTileSpace(); + + const realTileStart = tileStart.min(tileEnd); + const realTileEnd = tileStart.max(tileEnd); + + parameters.context.lineWidth = 1; + parameters.context.fillStyle = THEME.map.selectionBackground; + parameters.context.strokeStyle = THEME.map.selectionOutline; + parameters.context.beginPath(); + parameters.context.rect( + realWorldStart.x, + realWorldStart.y, + realWorldEnd.x - realWorldStart.x, + realWorldEnd.y - realWorldStart.y + ); + parameters.context.fill(); + parameters.context.stroke(); + + parameters.context.fillStyle = THEME.map.selectionOverlay; + parameters.context.beginPath(); + + const renderedUids = new Set(); + + for (let x = realTileStart.x; x <= realTileEnd.x; ++x) { + for (let y = realTileStart.y; y <= realTileEnd.y; ++y) { + const contents = this.root.map.getLayerContentXY(x, y, this.root.currentLayer); + if (contents && this.root.logic.canDeleteBuilding(contents)) { + // Prevent rendering the overlay twice + const uid = contents.uid; + if (renderedUids.has(uid)) { + continue; + } + renderedUids.add(uid); + + const staticComp = contents.components.StaticMapEntity; + + if (!staticComp.getMetaBuilding().getIsRemovable(this.root)) { + continue; + } + + const bounds = staticComp.getTileSpaceBounds(); + parameters.context.rect( + bounds.x * globalConfig.tileSize + boundsBorder, + bounds.y * globalConfig.tileSize + boundsBorder, + bounds.w * globalConfig.tileSize - 2 * boundsBorder, + bounds.h * globalConfig.tileSize - 2 * boundsBorder + ); + } + } + } + parameters.context.fill(); + } + + parameters.context.fillStyle = THEME.map.selectionOverlay; + parameters.context.beginPath(); + this.selectedUids.forEach(uid => { + const entity = this.root.entityMgr.findByUid(uid); + const staticComp = entity.components.StaticMapEntity; + const bounds = staticComp.getTileSpaceBounds(); + parameters.context.rect( + bounds.x * globalConfig.tileSize + boundsBorder, + bounds.y * globalConfig.tileSize + boundsBorder, + bounds.w * globalConfig.tileSize - 2 * boundsBorder, + bounds.h * globalConfig.tileSize - 2 * boundsBorder + ); + }); + parameters.context.fill(); + } +} diff --git a/src/js/game/hud/parts/miner_highlight.js b/src/js/game/hud/parts/miner_highlight.js index a0c6919d..80b4b2a1 100644 --- a/src/js/game/hud/parts/miner_highlight.js +++ b/src/js/game/hud/parts/miner_highlight.js @@ -1,176 +1,178 @@ -import { globalConfig } from "../../../core/config"; -import { formatItemsPerSecond, round2Digits } from "../../../core/utils"; -import { Vector } from "../../../core/vector"; -import { T } from "../../../translations"; -import { Entity } from "../../entity"; -import { THEME } from "../../theme"; -import { BaseHUDPart } from "../base_hud_part"; - -export class HUDMinerHighlight extends BaseHUDPart { - initialize() {} - - /** - * - * @param {import("../../../core/draw_utils").DrawParameters} parameters - */ - draw(parameters) { - const mousePos = this.root.app.mousePosition; - if (!mousePos) { - // Mouse pos not ready - return; - } - - if (this.root.currentLayer !== "regular") { - // Not within the regular layer - return; - } - - if (this.root.camera.getIsMapOverlayActive()) { - // Not within the map overlay - return; - } - - const worldPos = this.root.camera.screenToWorld(mousePos); - const hoveredTile = worldPos.toTileSpace(); - - const contents = this.root.map.getTileContent(hoveredTile, "regular"); - if (!contents) { - // Empty tile - return; - } - - const minerComp = contents.components.Miner; - if (!minerComp || !minerComp.chainable) { - // Not a chainable miner - return; - } - - const lowerContents = this.root.map.getLowerLayerContentXY(hoveredTile.x, hoveredTile.y); - if (!lowerContents) { - // Not connected - return; - } - - parameters.context.fillStyle = THEME.map.connectedMiners.overlay; - - const connectedEntities = this.findConnectedMiners(contents); - - for (let i = 0; i < connectedEntities.length; ++i) { - const entity = connectedEntities[i]; - const staticComp = entity.components.StaticMapEntity; - - parameters.context.beginRoundedRect( - staticComp.origin.x * globalConfig.tileSize + 5, - staticComp.origin.y * globalConfig.tileSize + 5, - globalConfig.tileSize - 10, - globalConfig.tileSize - 10, - 3 - ); - parameters.context.fill(); - } - - const throughput = round2Digits(connectedEntities.length * this.root.hubGoals.getMinerBaseSpeed()); - - const maxThroughput = this.root.hubGoals.getBeltBaseSpeed(); - - const tooltipLocation = this.root.camera.screenToWorld(mousePos); - - const scale = (1 / this.root.camera.zoomLevel) * this.root.app.getEffectiveUiScale(); - - const isCapped = throughput > maxThroughput; - - // Background - parameters.context.fillStyle = THEME.map.connectedMiners.background; - parameters.context.beginRoundedRect( - tooltipLocation.x + 5 * scale, - tooltipLocation.y - 3 * scale, - (isCapped ? 100 : 65) * scale, - (isCapped ? 45 : 30) * scale, - 2 - ); - parameters.context.fill(); - - // Throughput - parameters.context.fillStyle = THEME.map.connectedMiners.textColor; - parameters.context.font = "bold " + scale * 10 + "px GameFont"; - parameters.context.fillText( - formatItemsPerSecond(throughput), - tooltipLocation.x + 10 * scale, - tooltipLocation.y + 10 * scale - ); - - // Amount of miners - parameters.context.globalAlpha = 0.6; - parameters.context.font = "bold " + scale * 8 + "px GameFont"; - parameters.context.fillText( - connectedEntities.length === 1 - ? T.ingame.connectedMiners.one_miner - : T.ingame.connectedMiners.n_miners.replace("", String(connectedEntities.length)), - tooltipLocation.x + 10 * scale, - tooltipLocation.y + 22 * scale - ); - - parameters.context.globalAlpha = 1; - - if (isCapped) { - parameters.context.fillStyle = THEME.map.connectedMiners.textColorCapped; - parameters.context.fillText( - T.ingame.connectedMiners.limited_items.replace( - "", - formatItemsPerSecond(maxThroughput) - ), - tooltipLocation.x + 10 * scale, - tooltipLocation.y + 34 * scale - ); - } - } - - /** - * Finds all connected miners to the given entity - * @param {Entity} entity - * @param {Set} seenUids Which entities have already been processed - * @returns {Array} The connected miners - */ - findConnectedMiners(entity, seenUids = new Set()) { - let results = []; - const origin = entity.components.StaticMapEntity.origin; - - if (!seenUids.has(entity.uid)) { - seenUids.add(entity.uid); - results.push(entity); - } - - // Check for the miner which we connect to - const connectedMiner = this.root.systemMgr.systems.miner.findChainedMiner(entity); - if (connectedMiner && !seenUids.has(connectedMiner.uid)) { - results.push(connectedMiner); - seenUids.add(connectedMiner.uid); - results.push(...this.findConnectedMiners(connectedMiner, seenUids)); - } - - // Search within a 1x1 grid - this assumes miners are always 1x1 - for (let dx = -1; dx <= 1; ++dx) { - for (let dy = -1; dy <= 1; ++dy) { - const contents = this.root.map.getTileContent( - new Vector(origin.x + dx, origin.y + dy), - "regular" - ); - if (contents) { - const minerComp = contents.components.Miner; - if (minerComp && minerComp.chainable) { - // Found a miner connected to this entity - if (!seenUids.has(contents.uid)) { - if (this.root.systemMgr.systems.miner.findChainedMiner(contents) === entity) { - results.push(contents); - seenUids.add(contents.uid); - results.push(...this.findConnectedMiners(contents, seenUids)); - } - } - } - } - } - } - - return results; - } -} +import { globalConfig } from "../../../core/config"; +import { formatItemsPerSecond, round2Digits } from "../../../core/utils"; +import { Vector } from "../../../core/vector"; +import { T } from "../../../translations"; +import { Entity } from "../../entity"; +import { THEME } from "../../theme"; +import { BaseHUDPart } from "../base_hud_part"; + +export class HUDMinerHighlight extends BaseHUDPart { + initialize() {} + + /** + * + * @param {import("../../../core/draw_utils").DrawParameters} parameters + */ + draw(parameters) { + const mousePos = this.root.app.mousePosition; + if (!mousePos) { + // Mouse pos not ready + return; + } + + if (this.root.currentLayer !== "regular") { + // Not within the regular layer + return; + } + + if (this.root.camera.getIsMapOverlayActive()) { + // Not within the map overlay + return; + } + + const worldPos = this.root.camera.screenToWorld(mousePos); + const hoveredTile = worldPos.toTileSpace(); + + const contents = this.root.map.getTileContent(hoveredTile, "regular"); + if (!contents) { + // Empty tile + return; + } + + const minerComp = contents.components.Miner; + if (!minerComp || !minerComp.chainable) { + // Not a chainable miner + return; + } + + const lowerContents = this.root.map.getLowerLayerContentXY(hoveredTile.x, hoveredTile.y); + if (!lowerContents) { + // Not connected + return; + } + + parameters.context.fillStyle = THEME.map.connectedMiners.overlay; + + const connectedEntities = this.findConnectedMiners(contents); + + for (let i = 0; i < connectedEntities.length; ++i) { + const entity = connectedEntities[i]; + const staticComp = entity.components.StaticMapEntity; + + parameters.context.beginPath(); + parameters.context.roundRect( + staticComp.origin.x * globalConfig.tileSize + 5, + staticComp.origin.y * globalConfig.tileSize + 5, + globalConfig.tileSize - 10, + globalConfig.tileSize - 10, + 3 + ); + parameters.context.fill(); + } + + const throughput = round2Digits(connectedEntities.length * this.root.hubGoals.getMinerBaseSpeed()); + + const maxThroughput = this.root.hubGoals.getBeltBaseSpeed(); + + const tooltipLocation = this.root.camera.screenToWorld(mousePos); + + const scale = (1 / this.root.camera.zoomLevel) * this.root.app.getEffectiveUiScale(); + + const isCapped = throughput > maxThroughput; + + // Background + parameters.context.fillStyle = THEME.map.connectedMiners.background; + parameters.context.beginPath(); + parameters.context.roundRect( + tooltipLocation.x + 5 * scale, + tooltipLocation.y - 3 * scale, + (isCapped ? 100 : 65) * scale, + (isCapped ? 45 : 30) * scale, + 2 + ); + parameters.context.fill(); + + // Throughput + parameters.context.fillStyle = THEME.map.connectedMiners.textColor; + parameters.context.font = "bold " + scale * 10 + "px GameFont"; + parameters.context.fillText( + formatItemsPerSecond(throughput), + tooltipLocation.x + 10 * scale, + tooltipLocation.y + 10 * scale + ); + + // Amount of miners + parameters.context.globalAlpha = 0.6; + parameters.context.font = "bold " + scale * 8 + "px GameFont"; + parameters.context.fillText( + connectedEntities.length === 1 + ? T.ingame.connectedMiners.one_miner + : T.ingame.connectedMiners.n_miners.replace("", String(connectedEntities.length)), + tooltipLocation.x + 10 * scale, + tooltipLocation.y + 22 * scale + ); + + parameters.context.globalAlpha = 1; + + if (isCapped) { + parameters.context.fillStyle = THEME.map.connectedMiners.textColorCapped; + parameters.context.fillText( + T.ingame.connectedMiners.limited_items.replace( + "", + formatItemsPerSecond(maxThroughput) + ), + tooltipLocation.x + 10 * scale, + tooltipLocation.y + 34 * scale + ); + } + } + + /** + * Finds all connected miners to the given entity + * @param {Entity} entity + * @param {Set} seenUids Which entities have already been processed + * @returns {Array} The connected miners + */ + findConnectedMiners(entity, seenUids = new Set()) { + let results = []; + const origin = entity.components.StaticMapEntity.origin; + + if (!seenUids.has(entity.uid)) { + seenUids.add(entity.uid); + results.push(entity); + } + + // Check for the miner which we connect to + const connectedMiner = this.root.systemMgr.systems.miner.findChainedMiner(entity); + if (connectedMiner && !seenUids.has(connectedMiner.uid)) { + results.push(connectedMiner); + seenUids.add(connectedMiner.uid); + results.push(...this.findConnectedMiners(connectedMiner, seenUids)); + } + + // Search within a 1x1 grid - this assumes miners are always 1x1 + for (let dx = -1; dx <= 1; ++dx) { + for (let dy = -1; dy <= 1; ++dy) { + const contents = this.root.map.getTileContent( + new Vector(origin.x + dx, origin.y + dy), + "regular" + ); + if (contents) { + const minerComp = contents.components.Miner; + if (minerComp && minerComp.chainable) { + // Found a miner connected to this entity + if (!seenUids.has(contents.uid)) { + if (this.root.systemMgr.systems.miner.findChainedMiner(contents) === entity) { + results.push(contents); + seenUids.add(contents.uid); + results.push(...this.findConnectedMiners(contents, seenUids)); + } + } + } + } + } + } + + return results; + } +} diff --git a/src/js/game/hud/parts/modal_dialogs.js b/src/js/game/hud/parts/modal_dialogs.js index 0492d8c0..9670d145 100644 --- a/src/js/game/hud/parts/modal_dialogs.js +++ b/src/js/game/hud/parts/modal_dialogs.js @@ -1,211 +1,184 @@ -/* typehints:start */ -import { Application } from "../../../application"; -/* typehints:end */ - -import { SOUNDS } from "../../../platform/sound"; -import { DynamicDomAttach } from "../dynamic_dom_attach"; -import { BaseHUDPart } from "../base_hud_part"; -import { Dialog, DialogLoading, DialogOptionChooser } from "../../../core/modal_dialog_elements"; -import { makeDiv } from "../../../core/utils"; -import { T } from "../../../translations"; -import { openStandaloneLink } from "../../../core/config"; - -export class HUDModalDialogs extends BaseHUDPart { - constructor(root, app) { - // Important: Root is not always available here! Its also used in the main menu - super(root); - - /** @type {Application} */ - this.app = root ? root.app : app; - - this.dialogParent = null; - this.dialogStack = []; - } - - // For use inside of the game, implementation of base hud part - initialize() { - this.dialogParent = document.getElementById("ingame_HUD_ModalDialogs"); - this.domWatcher = new DynamicDomAttach(this.root, this.dialogParent); - } - - shouldPauseRendering() { - // return this.dialogStack.length > 0; - // @todo: Check if change this affects anything - return false; - } - - shouldPauseGame() { - // @todo: Check if this change affects anything - return false; - } - - createElements(parent) { - return makeDiv(parent, "ingame_HUD_ModalDialogs"); - } - - // For use outside of the game - initializeToElement(element) { - assert(element, "No element for dialogs given"); - this.dialogParent = element; - } - - isBlockingOverlay() { - return this.dialogStack.length > 0; - } - - // Methods - - /** - * @param {string} title - * @param {string} text - * @param {Array} buttons - */ - showInfo(title, text, buttons = ["ok:good"]) { - const dialog = new Dialog({ - app: this.app, - title: title, - contentHTML: text, - buttons: buttons, - type: "info", - }); - this.internalShowDialog(dialog); - - if (this.app) { - this.app.sound.playUiSound(SOUNDS.dialogOk); - } - - return dialog.buttonSignals; - } - - /** - * @param {string} title - * @param {string} text - * @param {Array} buttons - */ - showWarning(title, text, buttons = ["ok:good"]) { - const dialog = new Dialog({ - app: this.app, - title: title, - contentHTML: text, - buttons: buttons, - type: "warning", - }); - this.internalShowDialog(dialog); - - if (this.app) { - this.app.sound.playUiSound(SOUNDS.dialogError); - } - - return dialog.buttonSignals; - } - - /** - * @param {string} feature - * @param {string} textPrefab - */ - showFeatureRestrictionInfo(feature, textPrefab = T.dialogs.featureRestriction.desc) { - const dialog = new Dialog({ - app: this.app, - title: T.dialogs.featureRestriction.title, - contentHTML: textPrefab.replace("", feature), - buttons: ["cancel:bad", "getStandalone:good"], - type: "warning", - }); - this.internalShowDialog(dialog); - - if (this.app) { - this.app.sound.playUiSound(SOUNDS.dialogOk); - } - - dialog.buttonSignals.getStandalone.add(() => { - openStandaloneLink(this.app, "shapez_demo_dialog"); - }); - - return dialog.buttonSignals; - } - - showOptionChooser(title, options) { - const dialog = new DialogOptionChooser({ - app: this.app, - title, - options, - }); - this.internalShowDialog(dialog); - return dialog.buttonSignals; - } - - // Returns method to be called when laoding finishd - showLoadingDialog(text = "") { - const dialog = new DialogLoading(this.app, text); - this.internalShowDialog(dialog); - return this.closeDialog.bind(this, dialog); - } - - internalShowDialog(dialog) { - const elem = dialog.createElement(); - dialog.setIndex(1000 + this.dialogStack.length); - - // Hide last dialog in queue - if (this.dialogStack.length > 0) { - this.dialogStack[this.dialogStack.length - 1].hide(); - } - - this.dialogStack.push(dialog); - - // Append dialog - dialog.show(); - dialog.closeRequested.add(this.closeDialog.bind(this, dialog)); - - // Append to HTML - this.dialogParent.appendChild(elem); - - document.body.classList.toggle("modalDialogActive", this.dialogStack.length > 0); - - // IMPORTANT: Attach element directly, otherwise double submit is possible - this.update(); - } - - update() { - if (this.domWatcher) { - this.domWatcher.update(this.dialogStack.length > 0); - } - } - - closeDialog(dialog) { - dialog.destroy(); - - let index = -1; - for (let i = 0; i < this.dialogStack.length; ++i) { - if (this.dialogStack[i] === dialog) { - index = i; - break; - } - } - assert(index >= 0, "Dialog not in dialog stack"); - this.dialogStack.splice(index, 1); - - if (this.dialogStack.length > 0) { - // Show the dialog which was previously open - this.dialogStack[this.dialogStack.length - 1].show(); - } - - document.body.classList.toggle("modalDialogActive", this.dialogStack.length > 0); - } - - close() { - for (let i = 0; i < this.dialogStack.length; ++i) { - const dialog = this.dialogStack[i]; - dialog.destroy(); - } - this.dialogStack = []; - } - - cleanup() { - super.cleanup(); - for (let i = 0; i < this.dialogStack.length; ++i) { - this.dialogStack[i].destroy(); - } - this.dialogStack = []; - this.dialogParent = null; - } -} +/* typehints:start */ +import { Application } from "../../../application"; +/* typehints:end */ + +import { SOUNDS } from "../../../platform/sound"; +import { DynamicDomAttach } from "../dynamic_dom_attach"; +import { BaseHUDPart } from "../base_hud_part"; +import { Dialog, DialogLoading, DialogOptionChooser } from "../../../core/modal_dialog_elements"; +import { makeDiv } from "../../../core/utils"; + +export class HUDModalDialogs extends BaseHUDPart { + constructor(root, app) { + // Important: Root is not always available here! Its also used in the main menu + super(root); + + /** @type {Application} */ + this.app = root ? root.app : app; + + this.dialogParent = null; + this.dialogStack = []; + } + + // For use inside of the game, implementation of base hud part + initialize() { + this.dialogParent = document.getElementById("ingame_HUD_ModalDialogs"); + this.domWatcher = new DynamicDomAttach(this.root, this.dialogParent); + } + + shouldPauseRendering() { + // return this.dialogStack.length > 0; + // @todo: Check if change this affects anything + return false; + } + + shouldPauseGame() { + // @todo: Check if this change affects anything + return false; + } + + createElements(parent) { + return makeDiv(parent, "ingame_HUD_ModalDialogs"); + } + + // For use outside of the game + initializeToElement(element) { + assert(element, "No element for dialogs given"); + this.dialogParent = element; + } + + isBlockingOverlay() { + return this.dialogStack.length > 0; + } + + // Methods + + /** + * @param {string} title + * @param {string} text + * @param {Array<`${string}:${string}`>} buttons + */ + showInfo(title, text, buttons = ["ok:good"]) { + const dialog = new Dialog({ + app: this.app, + title: title, + contentHTML: text, + buttons: buttons, + type: "info", + }); + this.internalShowDialog(dialog); + + if (this.app) { + this.app.sound.playUiSound(SOUNDS.dialogOk); + } + + return dialog.buttonSignals; + } + + /** + * @param {string} title + * @param {string} text + * @param {Array>} buttons + */ + showWarning(title, text, buttons = ["ok:good"]) { + const dialog = new Dialog({ + app: this.app, + title: title, + contentHTML: text, + buttons: buttons, + type: "warning", + }); + this.internalShowDialog(dialog); + + if (this.app) { + this.app.sound.playUiSound(SOUNDS.dialogError); + } + + return dialog.buttonSignals; + } + + showOptionChooser(title, options) { + const dialog = new DialogOptionChooser({ + app: this.app, + title, + options, + }); + this.internalShowDialog(dialog); + return dialog.buttonSignals; + } + + // Returns method to be called when laoding finishd + showLoadingDialog(text = "") { + const dialog = new DialogLoading(this.app, text); + this.internalShowDialog(dialog); + return this.closeDialog.bind(this, dialog); + } + + internalShowDialog(dialog) { + const elem = dialog.createElement(); + dialog.setIndex(1000 + this.dialogStack.length); + + // Hide last dialog in queue + if (this.dialogStack.length > 0) { + this.dialogStack[this.dialogStack.length - 1].hide(); + } + + this.dialogStack.push(dialog); + + // Append dialog + dialog.show(); + dialog.closeRequested.add(this.closeDialog.bind(this, dialog)); + + // Append to HTML + this.dialogParent.appendChild(elem); + + document.body.classList.toggle("modalDialogActive", this.dialogStack.length > 0); + + // IMPORTANT: Attach element directly, otherwise double submit is possible + this.update(); + } + + update() { + if (this.domWatcher) { + this.domWatcher.update(this.dialogStack.length > 0); + } + } + + closeDialog(dialog) { + dialog.destroy(); + + let index = -1; + for (let i = 0; i < this.dialogStack.length; ++i) { + if (this.dialogStack[i] === dialog) { + index = i; + break; + } + } + assert(index >= 0, "Dialog not in dialog stack"); + this.dialogStack.splice(index, 1); + + if (this.dialogStack.length > 0) { + // Show the dialog which was previously open + this.dialogStack[this.dialogStack.length - 1].show(); + } + + document.body.classList.toggle("modalDialogActive", this.dialogStack.length > 0); + } + + close() { + for (let i = 0; i < this.dialogStack.length; ++i) { + const dialog = this.dialogStack[i]; + dialog.destroy(); + } + this.dialogStack = []; + } + + cleanup() { + super.cleanup(); + for (let i = 0; i < this.dialogStack.length; ++i) { + this.dialogStack[i].destroy(); + } + this.dialogStack = []; + this.dialogParent = null; + } +} diff --git a/src/js/game/hud/parts/notifications.js b/src/js/game/hud/parts/notifications.js index abeab205..74fbe613 100644 --- a/src/js/game/hud/parts/notifications.js +++ b/src/js/game/hud/parts/notifications.js @@ -1,58 +1,58 @@ -import { makeDiv } from "../../../core/utils"; -import { T } from "../../../translations"; -import { BaseHUDPart } from "../base_hud_part"; - -/** @enum {string} */ -export const enumNotificationType = { - saved: "saved", - upgrade: "upgrade", - success: "success", - info: "info", - warning: "warning", - error: "error", -}; - -const notificationDuration = 3; - -export class HUDNotifications extends BaseHUDPart { - createElements(parent) { - this.element = makeDiv(parent, "ingame_HUD_Notifications", [], ``); - } - - initialize() { - this.root.hud.signals.notification.add(this.internalShowNotification, this); - - /** @type {Array<{ element: HTMLElement, expireAt: number}>} */ - this.notificationElements = []; - - // Automatic notifications - this.root.signals.gameSaved.add(() => - this.internalShowNotification(T.ingame.notifications.gameSaved, enumNotificationType.saved) - ); - } - - /** - * @param {string} message - * @param {enumNotificationType} type - */ - internalShowNotification(message, type) { - const element = makeDiv(this.element, null, ["notification", "type-" + type], message); - element.setAttribute("data-icon", "icons/notification_" + type + ".png"); - - this.notificationElements.push({ - element, - expireAt: this.root.time.realtimeNow() + notificationDuration, - }); - } - - update() { - const now = this.root.time.realtimeNow(); - for (let i = 0; i < this.notificationElements.length; ++i) { - const handle = this.notificationElements[i]; - if (handle.expireAt <= now) { - handle.element.remove(); - this.notificationElements.splice(i, 1); - } - } - } -} +import { makeDiv } from "../../../core/utils"; +import { T } from "../../../translations"; +import { BaseHUDPart } from "../base_hud_part"; + +/** @enum {string} */ +export const enumNotificationType = { + saved: "saved", + upgrade: "upgrade", + success: "success", + info: "info", + warning: "warning", + error: "error", +}; + +const notificationDuration = 3; + +export class HUDNotifications extends BaseHUDPart { + createElements(parent) { + this.element = makeDiv(parent, "ingame_HUD_Notifications", [], ``); + } + + initialize() { + this.root.hud.signals.notification.add(this.internalShowNotification, this); + + /** @type {Array<{ element: HTMLElement, expireAt: number}>} */ + this.notificationElements = []; + + // Automatic notifications + this.root.signals.gameSaved.add(() => + this.internalShowNotification(T.ingame.notifications.gameSaved, enumNotificationType.saved) + ); + } + + /** + * @param {string} message + * @param {enumNotificationType} type + */ + internalShowNotification(message, type) { + const element = makeDiv(this.element, null, ["notification", "type-" + type], message); + element.setAttribute("data-icon", "icons/notification_" + type + ".png"); + + this.notificationElements.push({ + element, + expireAt: this.root.time.realtimeNow() + notificationDuration, + }); + } + + update() { + const now = this.root.time.realtimeNow(); + for (let i = 0; i < this.notificationElements.length; ++i) { + const handle = this.notificationElements[i]; + if (handle.expireAt <= now) { + handle.element.remove(); + this.notificationElements.splice(i, 1); + } + } + } +} diff --git a/src/js/game/hud/parts/pinned_shapes.js b/src/js/game/hud/parts/pinned_shapes.js index c5bb9a82..2eddd73b 100644 --- a/src/js/game/hud/parts/pinned_shapes.js +++ b/src/js/game/hud/parts/pinned_shapes.js @@ -1,334 +1,330 @@ -import { ClickDetector } from "../../../core/click_detector"; -import { globalConfig } from "../../../core/config"; -import { arrayDeleteValue, formatBigNumber, makeDiv } from "../../../core/utils"; -import { T } from "../../../translations"; -import { enumAnalyticsDataSource } from "../../production_analytics"; -import { ShapeDefinition } from "../../shape_definition"; -import { enumHubGoalRewards } from "../../tutorial_goals"; -import { BaseHUDPart } from "../base_hud_part"; - -/** - * Manages the pinned shapes on the left side of the screen - */ -export class HUDPinnedShapes extends BaseHUDPart { - constructor(root) { - super(root); - /** - * Store a list of pinned shapes - * @type {Array} - */ - this.pinnedShapes = []; - - /** - * Store handles to the currently rendered elements, so we can update them more - * convenient. Also allows for cleaning up handles. - * @type {Array<{ - * key: string, - * definition: ShapeDefinition, - * amountLabel: HTMLElement, - * lastRenderedValue: string, - * element: HTMLElement, - * detector?: ClickDetector, - * infoDetector?: ClickDetector, - * throughputOnly?: boolean - * }>} - */ - this.handles = []; - } - - createElements(parent) { - this.element = makeDiv(parent, "ingame_HUD_PinnedShapes", []); - } - - /** - * Serializes the pinned shapes - */ - serialize() { - return { - shapes: this.pinnedShapes, - }; - } - - /** - * Deserializes the pinned shapes - * @param {{ shapes: Array}} data - */ - deserialize(data) { - if (!data || !data.shapes || !Array.isArray(data.shapes)) { - return "Invalid pinned shapes data: " + JSON.stringify(data); - } - this.pinnedShapes = data.shapes; - } - - /** - * Initializes the hud component - */ - initialize() { - // Connect to any relevant signals - this.root.signals.storyGoalCompleted.add(this.rerenderFull, this); - this.root.signals.upgradePurchased.add(this.updateShapesAfterUpgrade, this); - this.root.signals.postLoadHook.add(this.rerenderFull, this); - this.root.hud.signals.shapePinRequested.add(this.pinNewShape, this); - this.root.hud.signals.shapeUnpinRequested.add(this.unpinShape, this); - - // Perform initial render - this.updateShapesAfterUpgrade(); - } - - /** - * Updates all shapes after an upgrade has been purchased and removes the unused ones - */ - updateShapesAfterUpgrade() { - for (let i = 0; i < this.pinnedShapes.length; ++i) { - const key = this.pinnedShapes[i]; - if (key === this.root.gameMode.getBlueprintShapeKey()) { - // Ignore blueprint shapes - continue; - } - let goal = this.findGoalValueForShape(key); - if (!goal) { - // Seems no longer relevant - this.pinnedShapes.splice(i, 1); - i -= 1; - } - } - - this.rerenderFull(); - } - - /** - * Finds the current goal for the given key. If the key is the story goal, returns - * the story goal. If its the blueprint shape, no goal is returned. Otherwise - * it's searched for upgrades. - * @param {string} key - */ - findGoalValueForShape(key) { - if (key === this.root.hubGoals.currentGoal.definition.getHash()) { - return this.root.hubGoals.currentGoal.required; - } - if (key === this.root.gameMode.getBlueprintShapeKey()) { - return null; - } - - // Check if this shape is required for any upgrade - const upgrades = this.root.gameMode.getUpgrades(); - for (const upgradeId in upgrades) { - const upgradeTiers = upgrades[upgradeId]; - const currentTier = this.root.hubGoals.getUpgradeLevel(upgradeId); - const tierHandle = upgradeTiers[currentTier]; - - if (!tierHandle) { - // Max level - continue; - } - - for (let i = 0; i < tierHandle.required.length; ++i) { - const { shape, amount } = tierHandle.required[i]; - if (shape === key) { - return amount; - } - } - } - - return null; - } - - /** - * Returns whether a given shape is currently pinned - * @param {string} key - */ - isShapePinned(key) { - if ( - key === this.root.hubGoals.currentGoal.definition.getHash() || - key === this.root.gameMode.getBlueprintShapeKey() - ) { - // This is a "special" shape which is always pinned - return true; - } - - return this.pinnedShapes.indexOf(key) >= 0; - } - - /** - * Rerenders the whole component - */ - rerenderFull() { - const currentGoal = this.root.hubGoals.currentGoal; - const currentKey = currentGoal.definition.getHash(); - - // First, remove all old shapes - for (let i = 0; i < this.handles.length; ++i) { - this.handles[i].element.remove(); - const detector = this.handles[i].detector; - if (detector) { - detector.cleanup(); - } - const infoDetector = this.handles[i].infoDetector; - if (infoDetector) { - infoDetector.cleanup(); - } - } - this.handles = []; - - // Pin story goal - this.internalPinShape({ - key: currentKey, - canUnpin: false, - className: "goal", - throughputOnly: currentGoal.throughputOnly, - }); - - // Pin blueprint shape as well - if (this.root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_blueprints)) { - this.internalPinShape({ - key: this.root.gameMode.getBlueprintShapeKey(), - canUnpin: false, - className: "blueprint", - }); - } - - // Pin manually pinned shapes - for (let i = 0; i < this.pinnedShapes.length; ++i) { - const key = this.pinnedShapes[i]; - if (key !== currentKey) { - this.internalPinShape({ key }); - } - } - } - - /** - * Pins a new shape - * @param {object} param0 - * @param {string} param0.key - * @param {boolean=} param0.canUnpin - * @param {string=} param0.className - * @param {boolean=} param0.throughputOnly - */ - internalPinShape({ key, canUnpin = true, className = null, throughputOnly = false }) { - const definition = this.root.shapeDefinitionMgr.getShapeFromShortKey(key); - - const element = makeDiv(this.element, null, ["shape"]); - const canvas = definition.generateAsCanvas(120); - element.appendChild(canvas); - - if (className) { - element.classList.add(className); - } - - let detector = null; - if (canUnpin) { - const unpinButton = document.createElement("button"); - unpinButton.classList.add("unpinButton"); - element.appendChild(unpinButton); - element.classList.add("removable"); - detector = new ClickDetector(unpinButton, { - consumeEvents: true, - preventDefault: true, - targetOnly: true, - }); - detector.click.add(() => this.unpinShape(key)); - } else { - element.classList.add("marked"); - } - - // Show small info icon - let infoDetector; - if (!G_WEGAME_VERSION) { - const infoButton = document.createElement("button"); - infoButton.classList.add("infoButton"); - element.appendChild(infoButton); - infoDetector = new ClickDetector(infoButton, { - consumeEvents: true, - preventDefault: true, - targetOnly: true, - }); - infoDetector.click.add(() => - this.root.hud.signals.viewShapeDetailsRequested.dispatch(definition) - ); - } - - const amountLabel = makeDiv(element, null, ["amountLabel"], ""); - - const goal = this.findGoalValueForShape(key); - if (goal) { - makeDiv(element, null, ["goalLabel"], "/" + formatBigNumber(goal)); - } - - this.handles.push({ - key, - definition, - element, - amountLabel, - lastRenderedValue: "", - detector, - infoDetector, - throughputOnly, - }); - } - - /** - * Updates all amount labels - */ - update() { - for (let i = 0; i < this.handles.length; ++i) { - const handle = this.handles[i]; - - let currentValue = this.root.hubGoals.getShapesStoredByKey(handle.key); - let currentValueFormatted = formatBigNumber(currentValue); - - if (handle.throughputOnly) { - currentValue = - this.root.productionAnalytics.getCurrentShapeRateRaw( - enumAnalyticsDataSource.delivered, - handle.definition - ) / globalConfig.analyticsSliceDurationSeconds; - currentValueFormatted = T.ingame.statistics.shapesDisplayUnits.second.replace( - "", - String(currentValue) - ); - } - - if (currentValueFormatted !== handle.lastRenderedValue) { - handle.lastRenderedValue = currentValueFormatted; - handle.amountLabel.innerText = currentValueFormatted; - const goal = this.findGoalValueForShape(handle.key); - handle.element.classList.toggle("completed", goal && currentValue > goal); - } - } - } - - /** - * Unpins a shape - * @param {string} key - */ - unpinShape(key) { - console.log("unpin", key); - arrayDeleteValue(this.pinnedShapes, key); - this.rerenderFull(); - } - - /** - * Requests to pin a new shape - * @param {ShapeDefinition} definition - */ - pinNewShape(definition) { - const key = definition.getHash(); - if (key === this.root.hubGoals.currentGoal.definition.getHash()) { - // Can not pin current goal - return; - } - - if (key === this.root.gameMode.getBlueprintShapeKey()) { - // Can not pin the blueprint shape - return; - } - - // Check if its already pinned - if (this.pinnedShapes.indexOf(key) >= 0) { - return; - } - - this.pinnedShapes.push(key); - this.rerenderFull(); - } -} +import { ClickDetector } from "../../../core/click_detector"; +import { globalConfig } from "../../../core/config"; +import { arrayDeleteValue, formatBigNumber, makeDiv } from "../../../core/utils"; +import { T } from "../../../translations"; +import { enumAnalyticsDataSource } from "../../production_analytics"; +import { ShapeDefinition } from "../../shape_definition"; +import { enumHubGoalRewards } from "../../tutorial_goals"; +import { BaseHUDPart } from "../base_hud_part"; + +/** + * Manages the pinned shapes on the left side of the screen + */ +export class HUDPinnedShapes extends BaseHUDPart { + constructor(root) { + super(root); + /** + * Store a list of pinned shapes + * @type {Array} + */ + this.pinnedShapes = []; + + /** + * Store handles to the currently rendered elements, so we can update them more + * convenient. Also allows for cleaning up handles. + * @type {Array<{ + * key: string, + * definition: ShapeDefinition, + * amountLabel: HTMLElement, + * lastRenderedValue: string, + * element: HTMLElement, + * detector?: ClickDetector, + * infoDetector?: ClickDetector, + * throughputOnly?: boolean + * }>} + */ + this.handles = []; + } + + createElements(parent) { + this.element = makeDiv(parent, "ingame_HUD_PinnedShapes", []); + } + + /** + * Serializes the pinned shapes + */ + serialize() { + return { + shapes: this.pinnedShapes, + }; + } + + /** + * Deserializes the pinned shapes + * @param {{ shapes: Array}} data + */ + deserialize(data) { + if (!data || !data.shapes || !Array.isArray(data.shapes)) { + return "Invalid pinned shapes data: " + JSON.stringify(data); + } + this.pinnedShapes = data.shapes; + } + + /** + * Initializes the hud component + */ + initialize() { + // Connect to any relevant signals + this.root.signals.storyGoalCompleted.add(this.rerenderFull, this); + this.root.signals.upgradePurchased.add(this.updateShapesAfterUpgrade, this); + this.root.signals.postLoadHook.add(this.rerenderFull, this); + this.root.hud.signals.shapePinRequested.add(this.pinNewShape, this); + this.root.hud.signals.shapeUnpinRequested.add(this.unpinShape, this); + + // Perform initial render + this.updateShapesAfterUpgrade(); + } + + /** + * Updates all shapes after an upgrade has been purchased and removes the unused ones + */ + updateShapesAfterUpgrade() { + for (let i = 0; i < this.pinnedShapes.length; ++i) { + const key = this.pinnedShapes[i]; + if (key === this.root.gameMode.getBlueprintShapeKey()) { + // Ignore blueprint shapes + continue; + } + let goal = this.findGoalValueForShape(key); + if (!goal) { + // Seems no longer relevant + this.pinnedShapes.splice(i, 1); + i -= 1; + } + } + + this.rerenderFull(); + } + + /** + * Finds the current goal for the given key. If the key is the story goal, returns + * the story goal. If its the blueprint shape, no goal is returned. Otherwise + * it's searched for upgrades. + * @param {string} key + */ + findGoalValueForShape(key) { + if (key === this.root.hubGoals.currentGoal.definition.getHash()) { + return this.root.hubGoals.currentGoal.required; + } + if (key === this.root.gameMode.getBlueprintShapeKey()) { + return null; + } + + // Check if this shape is required for any upgrade + const upgrades = this.root.gameMode.getUpgrades(); + for (const upgradeId in upgrades) { + const upgradeTiers = upgrades[upgradeId]; + const currentTier = this.root.hubGoals.getUpgradeLevel(upgradeId); + const tierHandle = upgradeTiers[currentTier]; + + if (!tierHandle) { + // Max level + continue; + } + + for (let i = 0; i < tierHandle.required.length; ++i) { + const { shape, amount } = tierHandle.required[i]; + if (shape === key) { + return amount; + } + } + } + + return null; + } + + /** + * Returns whether a given shape is currently pinned + * @param {string} key + */ + isShapePinned(key) { + if ( + key === this.root.hubGoals.currentGoal.definition.getHash() || + key === this.root.gameMode.getBlueprintShapeKey() + ) { + // This is a "special" shape which is always pinned + return true; + } + + return this.pinnedShapes.indexOf(key) >= 0; + } + + /** + * Rerenders the whole component + */ + rerenderFull() { + const currentGoal = this.root.hubGoals.currentGoal; + const currentKey = currentGoal.definition.getHash(); + + // First, remove all old shapes + for (let i = 0; i < this.handles.length; ++i) { + this.handles[i].element.remove(); + const detector = this.handles[i].detector; + if (detector) { + detector.cleanup(); + } + const infoDetector = this.handles[i].infoDetector; + if (infoDetector) { + infoDetector.cleanup(); + } + } + this.handles = []; + + // Pin story goal + this.internalPinShape({ + key: currentKey, + canUnpin: false, + className: "goal", + throughputOnly: currentGoal.throughputOnly, + }); + + // Pin blueprint shape as well + if (this.root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_blueprints)) { + this.internalPinShape({ + key: this.root.gameMode.getBlueprintShapeKey(), + canUnpin: false, + className: "blueprint", + }); + } + + // Pin manually pinned shapes + for (let i = 0; i < this.pinnedShapes.length; ++i) { + const key = this.pinnedShapes[i]; + if (key !== currentKey) { + this.internalPinShape({ key }); + } + } + } + + /** + * Pins a new shape + * @param {object} param0 + * @param {string} param0.key + * @param {boolean=} param0.canUnpin + * @param {string=} param0.className + * @param {boolean=} param0.throughputOnly + */ + internalPinShape({ key, canUnpin = true, className = null, throughputOnly = false }) { + const definition = this.root.shapeDefinitionMgr.getShapeFromShortKey(key); + + const element = makeDiv(this.element, null, ["shape"]); + const canvas = definition.generateAsCanvas(120); + element.appendChild(canvas); + + if (className) { + element.classList.add(className); + } + + let detector = null; + if (canUnpin) { + const unpinButton = document.createElement("button"); + unpinButton.classList.add("unpinButton"); + element.appendChild(unpinButton); + element.classList.add("removable"); + detector = new ClickDetector(unpinButton, { + consumeEvents: true, + preventDefault: true, + targetOnly: true, + }); + detector.click.add(() => this.unpinShape(key)); + } else { + element.classList.add("marked"); + } + + // Show small info icon + let infoDetector; + const infoButton = document.createElement("button"); + infoButton.classList.add("infoButton"); + element.appendChild(infoButton); + infoDetector = new ClickDetector(infoButton, { + consumeEvents: true, + preventDefault: true, + targetOnly: true, + }); + infoDetector.click.add(() => this.root.hud.signals.viewShapeDetailsRequested.dispatch(definition)); + + const amountLabel = makeDiv(element, null, ["amountLabel"], ""); + + const goal = this.findGoalValueForShape(key); + if (goal) { + makeDiv(element, null, ["goalLabel"], "/" + formatBigNumber(goal)); + } + + this.handles.push({ + key, + definition, + element, + amountLabel, + lastRenderedValue: "", + detector, + infoDetector, + throughputOnly, + }); + } + + /** + * Updates all amount labels + */ + update() { + for (let i = 0; i < this.handles.length; ++i) { + const handle = this.handles[i]; + + let currentValue = this.root.hubGoals.getShapesStoredByKey(handle.key); + let currentValueFormatted = formatBigNumber(currentValue); + + if (handle.throughputOnly) { + currentValue = + this.root.productionAnalytics.getCurrentShapeRateRaw( + enumAnalyticsDataSource.delivered, + handle.definition + ) / globalConfig.analyticsSliceDurationSeconds; + currentValueFormatted = T.ingame.statistics.shapesDisplayUnits.second.replace( + "", + String(currentValue) + ); + } + + if (currentValueFormatted !== handle.lastRenderedValue) { + handle.lastRenderedValue = currentValueFormatted; + handle.amountLabel.innerText = currentValueFormatted; + const goal = this.findGoalValueForShape(handle.key); + handle.element.classList.toggle("completed", goal && currentValue > goal); + } + } + } + + /** + * Unpins a shape + * @param {string} key + */ + unpinShape(key) { + console.log("unpin", key); + arrayDeleteValue(this.pinnedShapes, key); + this.rerenderFull(); + } + + /** + * Requests to pin a new shape + * @param {ShapeDefinition} definition + */ + pinNewShape(definition) { + const key = definition.getHash(); + if (key === this.root.hubGoals.currentGoal.definition.getHash()) { + // Can not pin current goal + return; + } + + if (key === this.root.gameMode.getBlueprintShapeKey()) { + // Can not pin the blueprint shape + return; + } + + // Check if its already pinned + if (this.pinnedShapes.indexOf(key) >= 0) { + return; + } + + this.pinnedShapes.push(key); + this.rerenderFull(); + } +} diff --git a/src/js/game/hud/parts/puzzle_complete_notification.js b/src/js/game/hud/parts/puzzle_complete_notification.js index d83e33f1..37b7e50b 100644 --- a/src/js/game/hud/parts/puzzle_complete_notification.js +++ b/src/js/game/hud/parts/puzzle_complete_notification.js @@ -24,7 +24,7 @@ export class HUDPuzzleCompleteNotification extends BaseHUDPart { } createElements(parent) { - this.inputReciever = new InputReceiver("puzzle-complete"); + this.inputReceiver = new InputReceiver("puzzle-complete"); this.element = makeDiv(parent, "ingame_HUD_PuzzleCompleteNotification", ["noBlur"]); @@ -86,13 +86,13 @@ export class HUDPuzzleCompleteNotification extends BaseHUDPart { show() { this.root.soundProxy.playUi(SOUNDS.levelComplete); - this.root.app.inputMgr.makeSureAttachedAndOnTop(this.inputReciever); + this.root.app.inputMgr.makeSureAttachedAndOnTop(this.inputReceiver); this.visible = true; this.timeOfCompletion = this.root.time.now(); } cleanup() { - this.root.app.inputMgr.makeSureDetached(this.inputReciever); + this.root.app.inputMgr.makeSureDetached(this.inputReceiver); } isBlockingOverlay() { diff --git a/src/js/game/hud/parts/puzzle_dlc_logo.js b/src/js/game/hud/parts/puzzle_dlc_logo.js index 69519c0d..ec50808a 100644 --- a/src/js/game/hud/parts/puzzle_dlc_logo.js +++ b/src/js/game/hud/parts/puzzle_dlc_logo.js @@ -4,7 +4,6 @@ import { BaseHUDPart } from "../base_hud_part"; export class HUDPuzzleDLCLogo extends BaseHUDPart { createElements(parent) { this.element = makeDiv(parent, "ingame_HUD_PuzzleDLCLogo"); - this.element.classList.toggle("china", G_CHINA_VERSION || G_WEGAME_VERSION); parent.appendChild(this.element); } diff --git a/src/js/game/hud/parts/puzzle_editor_review.js b/src/js/game/hud/parts/puzzle_editor_review.js index 4a044ba5..9a7fd0b6 100644 --- a/src/js/game/hud/parts/puzzle_editor_review.js +++ b/src/js/game/hud/parts/puzzle_editor_review.js @@ -13,7 +13,6 @@ import { ShapeItem } from "../../items/shape_item"; import { ShapeDefinition } from "../../shape_definition"; import { BaseHUDPart } from "../base_hud_part"; -const trim = require("trim"); const logger = createLogger("puzzle-review"); export class HUDPuzzleEditorReview extends BaseHUDPart { @@ -106,7 +105,7 @@ export class HUDPuzzleEditorReview extends BaseHUDPart { label: T.dialogs.submitPuzzle.descName, placeholder: T.dialogs.submitPuzzle.placeholderName, defaultValue: title, - validator: val => trim(val).match(regex) && trim(val).length > 0, + validator: val => val.trim().match(regex) && val.trim().length > 0, }); let items = new Set(); @@ -135,7 +134,7 @@ export class HUDPuzzleEditorReview extends BaseHUDPart { label: null, placeholder: "CuCuCuCu", defaultValue: shortKey, - validator: val => ShapeDefinition.isValidShortKey(trim(val)), + validator: val => ShapeDefinition.isValidShortKey(val.trim()), }); const dialog = new DialogWithForm({ @@ -147,14 +146,14 @@ export class HUDPuzzleEditorReview extends BaseHUDPart { }); itemInput.valueChosen.add(value => { - shapeKeyInput.setValue(value.definition.getHash()); + shapeKeyInput.setValue(/** @type {ShapeItem} */ (value).definition.getHash()); }); this.root.hud.parts.dialogs.internalShowDialog(dialog); dialog.buttonSignals.ok.add(() => { - const title = trim(nameInput.getValue()); - const shortKey = trim(shapeKeyInput.getValue()); + const title = nameInput.getValue().trim(); + const shortKey = shapeKeyInput.getValue().trim(); this.doSubmitPuzzle(title, shortKey); }); } diff --git a/src/js/game/hud/parts/puzzle_editor_settings.js b/src/js/game/hud/parts/puzzle_editor_settings.js index bd738198..5394db93 100644 --- a/src/js/game/hud/parts/puzzle_editor_settings.js +++ b/src/js/game/hud/parts/puzzle_editor_settings.js @@ -1,231 +1,230 @@ -import { globalConfig } from "../../../core/config"; -import { gMetaBuildingRegistry } from "../../../core/global_registries"; -import { createLogger } from "../../../core/logging"; -import { Rectangle } from "../../../core/rectangle"; -import { makeDiv } from "../../../core/utils"; -import { T } from "../../../translations"; -import { MetaBlockBuilding } from "../../buildings/block"; -import { MetaConstantProducerBuilding } from "../../buildings/constant_producer"; -import { MetaGoalAcceptorBuilding } from "../../buildings/goal_acceptor"; -import { StaticMapEntityComponent } from "../../components/static_map_entity"; -import { PuzzleGameMode } from "../../modes/puzzle"; -import { BaseHUDPart } from "../base_hud_part"; - -const logger = createLogger("puzzle-editor"); - -export class HUDPuzzleEditorSettings extends BaseHUDPart { - createElements(parent) { - this.element = makeDiv(parent, "ingame_HUD_PuzzleEditorSettings"); - - if (this.root.gameMode.getBuildableZones()) { - const bind = (selector, handler) => - this.trackClicks(this.element.querySelector(selector), handler); - this.zone = makeDiv( - this.element, - null, - ["section", "zone"], - ` - - -
-
- - - - -
- -
- - - - -
- -
- - -
- -
- -
- -
` - ); - - bind(".zoneWidth .minus", () => this.modifyZone(-1, 0)); - bind(".zoneWidth .plus", () => this.modifyZone(1, 0)); - bind(".zoneHeight .minus", () => this.modifyZone(0, -1)); - bind(".zoneHeight .plus", () => this.modifyZone(0, 1)); - bind("button.trim", this.trim); - bind("button.clearItems", this.clearItems); - bind("button.resetPuzzle", this.resetPuzzle); - } - } - - clearItems() { - this.root.logic.clearAllBeltsAndItems(); - } - - resetPuzzle() { - for (const entity of this.root.entityMgr.getAllWithComponent(StaticMapEntityComponent)) { - const staticComp = entity.components.StaticMapEntity; - const goalComp = entity.components.GoalAcceptor; - - if (goalComp) { - goalComp.clear(); - } - - if ( - [MetaGoalAcceptorBuilding, MetaConstantProducerBuilding, MetaBlockBuilding] - .map(metaClass => gMetaBuildingRegistry.findByClass(metaClass).id) - .includes(staticComp.getMetaBuilding().id) - ) { - continue; - } - - this.root.map.removeStaticEntity(entity); - this.root.entityMgr.destroyEntity(entity); - } - this.root.entityMgr.processDestroyList(); - } - - trim() { - // Now, find the center - const buildings = this.root.entityMgr.entities.slice(); - - if (buildings.length === 0) { - // nothing to do - return; - } - - let minRect = null; - - for (const building of buildings) { - const staticComp = building.components.StaticMapEntity; - const bounds = staticComp.getTileSpaceBounds(); - - if (!minRect) { - minRect = bounds; - } else { - minRect = minRect.getUnion(bounds); - } - } - - const mode = /** @type {PuzzleGameMode} */ (this.root.gameMode); - const moveByInverse = minRect.getCenter().round(); - - // move buildings - if (moveByInverse.length() > 0) { - // increase area size - mode.zoneWidth = globalConfig.puzzleMaxBoundsSize; - mode.zoneHeight = globalConfig.puzzleMaxBoundsSize; - - // First, remove any items etc - this.root.logic.clearAllBeltsAndItems(); - - this.root.logic.performImmutableOperation(() => { - // 1. remove all buildings - for (const building of buildings) { - if (!this.root.logic.tryDeleteBuilding(building)) { - assertAlways(false, "Failed to remove building in trim"); - } - } - - // 2. place them again, but centered - for (const building of buildings) { - const staticComp = building.components.StaticMapEntity; - const result = this.root.logic.tryPlaceBuilding({ - origin: staticComp.origin.sub(moveByInverse), - building: staticComp.getMetaBuilding(), - originalRotation: staticComp.originalRotation, - rotation: staticComp.rotation, - rotationVariant: staticComp.getRotationVariant(), - variant: staticComp.getVariant(), - }); - if (!result) { - this.root.bulkOperationRunning = false; - assertAlways(false, "Failed to re-place building in trim"); - } - - for (const key in building.components) { - /** @type {import("../../../core/global_registries").Component} */ (building - .components[key]).copyAdditionalStateTo(result.components[key]); - } - } - }); - } - - // 3. Actually trim - let w = mode.zoneWidth; - let h = mode.zoneHeight; - - while (!this.anyBuildingOutsideZone(w - 1, h)) { - --w; - } - - while (!this.anyBuildingOutsideZone(w, h - 1)) { - --h; - } - - mode.zoneWidth = w; - mode.zoneHeight = h; - this.updateZoneValues(); - } - - initialize() { - this.visible = true; - this.updateZoneValues(); - } - - anyBuildingOutsideZone(width, height) { - if (Math.min(width, height) < globalConfig.puzzleMinBoundsSize) { - return true; - } - const newZone = Rectangle.centered(width, height); - const entities = this.root.entityMgr.getAllWithComponent(StaticMapEntityComponent); - - for (const entity of entities) { - const staticComp = entity.components.StaticMapEntity; - const bounds = staticComp.getTileSpaceBounds(); - if (!newZone.intersectsFully(bounds)) { - return true; - } - } - } - - modifyZone(deltaW, deltaH) { - const mode = /** @type {PuzzleGameMode} */ (this.root.gameMode); - - const newWidth = mode.zoneWidth + deltaW; - const newHeight = mode.zoneHeight + deltaH; - - if (Math.min(newWidth, newHeight) < globalConfig.puzzleMinBoundsSize) { - return; - } - - if (Math.max(newWidth, newHeight) > globalConfig.puzzleMaxBoundsSize) { - return; - } - - if (this.anyBuildingOutsideZone(newWidth, newHeight)) { - this.root.hud.parts.dialogs.showWarning( - T.dialogs.puzzleResizeBadBuildings.title, - T.dialogs.puzzleResizeBadBuildings.desc - ); - return; - } - - mode.zoneWidth = newWidth; - mode.zoneHeight = newHeight; - this.updateZoneValues(); - } - - updateZoneValues() { - const mode = /** @type {PuzzleGameMode} */ (this.root.gameMode); - - this.element.querySelector(".zoneWidth > .value").textContent = String(mode.zoneWidth); - this.element.querySelector(".zoneHeight > .value").textContent = String(mode.zoneHeight); - } -} +import { globalConfig } from "../../../core/config"; +import { gMetaBuildingRegistry } from "../../../core/global_registries"; +import { createLogger } from "../../../core/logging"; +import { Rectangle } from "../../../core/rectangle"; +import { makeDiv } from "../../../core/utils"; +import { T } from "../../../translations"; +import { MetaBlockBuilding } from "../../buildings/block"; +import { MetaConstantProducerBuilding } from "../../buildings/constant_producer"; +import { MetaGoalAcceptorBuilding } from "../../buildings/goal_acceptor"; +import { StaticMapEntityComponent } from "../../components/static_map_entity"; +import { PuzzleGameMode } from "../../modes/puzzle"; +import { BaseHUDPart } from "../base_hud_part"; + +const logger = createLogger("puzzle-editor"); + +export class HUDPuzzleEditorSettings extends BaseHUDPart { + createElements(parent) { + this.element = makeDiv(parent, "ingame_HUD_PuzzleEditorSettings"); + + if (this.root.gameMode.getBuildableZones()) { + const bind = (selector, handler) => + this.trackClicks(this.element.querySelector(selector), handler); + this.zone = makeDiv( + this.element, + null, + ["section", "zone"], + ` + + +
+
+ + + + +
+ +
+ + + + +
+ +
+ + +
+ +
+ +
+ +
` + ); + + bind(".zoneWidth .minus", () => this.modifyZone(-1, 0)); + bind(".zoneWidth .plus", () => this.modifyZone(1, 0)); + bind(".zoneHeight .minus", () => this.modifyZone(0, -1)); + bind(".zoneHeight .plus", () => this.modifyZone(0, 1)); + bind("button.trim", this.trim); + bind("button.clearItems", this.clearItems); + bind("button.resetPuzzle", this.resetPuzzle); + } + } + + clearItems() { + this.root.logic.clearAllBeltsAndItems(); + } + + resetPuzzle() { + for (const entity of this.root.entityMgr.getAllWithComponent(StaticMapEntityComponent)) { + const staticComp = entity.components.StaticMapEntity; + const goalComp = entity.components.GoalAcceptor; + + if (goalComp) { + goalComp.clear(); + } + + if ( + [MetaGoalAcceptorBuilding, MetaConstantProducerBuilding, MetaBlockBuilding] + .map(metaClass => gMetaBuildingRegistry.findByClass(metaClass).id) + .includes(staticComp.getMetaBuilding().id) + ) { + continue; + } + + this.root.map.removeStaticEntity(entity); + this.root.entityMgr.destroyEntity(entity); + } + this.root.entityMgr.processDestroyList(); + } + + trim() { + // Now, find the center + const buildings = [...this.root.entityMgr.entities.values()]; + + if (buildings.length === 0) { + // nothing to do + return; + } + + let minRect = null; + + for (const building of buildings) { + const staticComp = building.components.StaticMapEntity; + const bounds = staticComp.getTileSpaceBounds(); + + if (!minRect) { + minRect = bounds; + } else { + minRect = minRect.getUnion(bounds); + } + } + + const mode = /** @type {PuzzleGameMode} */ (this.root.gameMode); + const moveByInverse = minRect.getCenter().round(); + + // move buildings + if (moveByInverse.length() > 0) { + // increase area size + mode.zoneWidth = globalConfig.puzzleMaxBoundsSize; + mode.zoneHeight = globalConfig.puzzleMaxBoundsSize; + + // First, remove any items etc + this.root.logic.clearAllBeltsAndItems(); + + this.root.logic.performImmutableOperation(() => { + // 1. remove all buildings + for (const building of buildings) { + if (!this.root.logic.tryDeleteBuilding(building)) { + assertAlways(false, "Failed to remove building in trim"); + } + } + + // 2. place them again, but centered + for (const building of buildings) { + const staticComp = building.components.StaticMapEntity; + const result = this.root.logic.tryPlaceBuilding({ + origin: staticComp.origin.sub(moveByInverse), + building: staticComp.getMetaBuilding(), + originalRotation: staticComp.originalRotation, + rotation: staticComp.rotation, + rotationVariant: staticComp.getRotationVariant(), + variant: staticComp.getVariant(), + }); + if (!result) { + this.root.bulkOperationRunning = false; + assertAlways(false, "Failed to re-place building in trim"); + } + + for (const key in building.components) { + building.components[key].copyAdditionalStateTo(result.components[key]); + } + } + }); + } + + // 3. Actually trim + let w = mode.zoneWidth; + let h = mode.zoneHeight; + + while (!this.anyBuildingOutsideZone(w - 1, h)) { + --w; + } + + while (!this.anyBuildingOutsideZone(w, h - 1)) { + --h; + } + + mode.zoneWidth = w; + mode.zoneHeight = h; + this.updateZoneValues(); + } + + initialize() { + this.visible = true; + this.updateZoneValues(); + } + + anyBuildingOutsideZone(width, height) { + if (Math.min(width, height) < globalConfig.puzzleMinBoundsSize) { + return true; + } + const newZone = Rectangle.centered(width, height); + const entities = this.root.entityMgr.getAllWithComponent(StaticMapEntityComponent); + + for (const entity of entities) { + const staticComp = entity.components.StaticMapEntity; + const bounds = staticComp.getTileSpaceBounds(); + if (!newZone.intersectsFully(bounds)) { + return true; + } + } + } + + modifyZone(deltaW, deltaH) { + const mode = /** @type {PuzzleGameMode} */ (this.root.gameMode); + + const newWidth = mode.zoneWidth + deltaW; + const newHeight = mode.zoneHeight + deltaH; + + if (Math.min(newWidth, newHeight) < globalConfig.puzzleMinBoundsSize) { + return; + } + + if (Math.max(newWidth, newHeight) > globalConfig.puzzleMaxBoundsSize) { + return; + } + + if (this.anyBuildingOutsideZone(newWidth, newHeight)) { + this.root.hud.parts.dialogs.showWarning( + T.dialogs.puzzleResizeBadBuildings.title, + T.dialogs.puzzleResizeBadBuildings.desc + ); + return; + } + + mode.zoneWidth = newWidth; + mode.zoneHeight = newHeight; + this.updateZoneValues(); + } + + updateZoneValues() { + const mode = /** @type {PuzzleGameMode} */ (this.root.gameMode); + + this.element.querySelector(".zoneWidth > .value").textContent = String(mode.zoneWidth); + this.element.querySelector(".zoneHeight > .value").textContent = String(mode.zoneHeight); + } +} diff --git a/src/js/game/hud/parts/puzzle_play_metadata.js b/src/js/game/hud/parts/puzzle_play_metadata.js index 3550a1e6..a36c02e8 100644 --- a/src/js/game/hud/parts/puzzle_play_metadata.js +++ b/src/js/game/hud/parts/puzzle_play_metadata.js @@ -6,8 +6,6 @@ import { formatBigNumberFull, formatSeconds, makeDiv } from "../../../core/utils import { T } from "../../../translations"; import { BaseHUDPart } from "../base_hud_part"; -const copy = require("clipboard-copy"); - export class HUDPuzzlePlayMetadata extends BaseHUDPart { createElements(parent) { this.titleElement = makeDiv(parent, "ingame_HUD_PuzzlePlayTitle"); diff --git a/src/js/game/hud/parts/sandbox_controller.js b/src/js/game/hud/parts/sandbox_controller.js index 3689fa36..ce9f6221 100644 --- a/src/js/game/hud/parts/sandbox_controller.js +++ b/src/js/game/hud/parts/sandbox_controller.js @@ -1,161 +1,160 @@ -import { queryParamOptions } from "../../../core/query_parameters"; -import { makeDiv } from "../../../core/utils"; -import { BaseHUDPart } from "../base_hud_part"; -import { DynamicDomAttach } from "../dynamic_dom_attach"; -import { enumNotificationType } from "./notifications"; - -export class HUDSandboxController extends BaseHUDPart { - createElements(parent) { - this.element = makeDiv( - parent, - "ingame_HUD_SandboxController", - [], - ` - - Use F6 to toggle this overlay - -
-
- - - -
- -
- - - -
- -
- - - -
- -
- - - -
- -
- - - -
- -
- - -
-
- ` - ); - - const bind = (selector, handler) => this.trackClicks(this.element.querySelector(selector), handler); - - bind(".giveBlueprints", this.giveBlueprints); - bind(".maxOutAll", this.maxOutAll); - bind(".levelToggle .minus", () => this.modifyLevel(-1)); - bind(".levelToggle .plus", () => this.modifyLevel(1)); - - bind(".upgradesBelt .minus", () => this.modifyUpgrade("belt", -1)); - bind(".upgradesBelt .plus", () => this.modifyUpgrade("belt", 1)); - - bind(".upgradesExtraction .minus", () => this.modifyUpgrade("miner", -1)); - bind(".upgradesExtraction .plus", () => this.modifyUpgrade("miner", 1)); - - bind(".upgradesProcessing .minus", () => this.modifyUpgrade("processors", -1)); - bind(".upgradesProcessing .plus", () => this.modifyUpgrade("processors", 1)); - - bind(".upgradesPainting .minus", () => this.modifyUpgrade("painting", -1)); - bind(".upgradesPainting .plus", () => this.modifyUpgrade("painting", 1)); - } - - giveBlueprints() { - const shape = this.root.gameMode.getBlueprintShapeKey(); - if (!this.root.hubGoals.storedShapes[shape]) { - this.root.hubGoals.storedShapes[shape] = 0; - } - this.root.hubGoals.storedShapes[shape] += 1e9; - } - - maxOutAll() { - this.modifyUpgrade("belt", 100); - this.modifyUpgrade("miner", 100); - this.modifyUpgrade("processors", 100); - this.modifyUpgrade("painting", 100); - } - - modifyUpgrade(id, amount) { - const upgradeTiers = this.root.gameMode.getUpgrades()[id]; - const maxLevel = upgradeTiers.length; - - this.root.hubGoals.upgradeLevels[id] = Math.max( - 0, - Math.min(maxLevel, (this.root.hubGoals.upgradeLevels[id] || 0) + amount) - ); - - // Compute improvement - let improvement = 1; - for (let i = 0; i < this.root.hubGoals.upgradeLevels[id]; ++i) { - improvement += upgradeTiers[i].improvement; - } - this.root.hubGoals.upgradeImprovements[id] = improvement; - this.root.signals.upgradePurchased.dispatch(id); - this.root.hud.signals.notification.dispatch( - "Upgrade '" + id + "' is now at tier " + (this.root.hubGoals.upgradeLevels[id] + 1), - enumNotificationType.upgrade - ); - } - - modifyLevel(amount) { - const hubGoals = this.root.hubGoals; - hubGoals.level = Math.max(1, hubGoals.level + amount); - hubGoals.computeNextGoal(); - - // Clear all shapes of this level - hubGoals.storedShapes[hubGoals.currentGoal.definition.getHash()] = 0; - - if (this.root.hud.parts.pinnedShapes) { - this.root.hud.parts.pinnedShapes.rerenderFull(); - } - - // Compute gained rewards - hubGoals.gainedRewards = {}; - const levels = this.root.gameMode.getLevelDefinitions(); - for (let i = 0; i < hubGoals.level - 1; ++i) { - if (i < levels.length) { - const reward = levels[i].reward; - hubGoals.gainedRewards[reward] = (hubGoals.gainedRewards[reward] || 0) + 1; - } - } - - this.root.hud.signals.notification.dispatch( - "Changed level to " + hubGoals.level, - enumNotificationType.upgrade - ); - } - - initialize() { - // Allow toggling the controller overlay - this.root.gameState.inputReciever.keydown.add(key => { - if (key.keyCode === 117) { - // F6 - this.toggle(); - } - }); - - this.visible = false; - this.domAttach = new DynamicDomAttach(this.root, this.element); - } - - toggle() { - this.visible = !this.visible; - } - - update() { - this.domAttach.update(this.visible); - } -} +import { makeDiv } from "../../../core/utils"; +import { BaseHUDPart } from "../base_hud_part"; +import { DynamicDomAttach } from "../dynamic_dom_attach"; +import { enumNotificationType } from "./notifications"; + +export class HUDSandboxController extends BaseHUDPart { + createElements(parent) { + this.element = makeDiv( + parent, + "ingame_HUD_SandboxController", + [], + ` + + Use F6 to toggle this overlay + +
+
+ + + +
+ +
+ + + +
+ +
+ + + +
+ +
+ + + +
+ +
+ + + +
+ +
+ + +
+
+ ` + ); + + const bind = (selector, handler) => this.trackClicks(this.element.querySelector(selector), handler); + + bind(".giveBlueprints", this.giveBlueprints); + bind(".maxOutAll", this.maxOutAll); + bind(".levelToggle .minus", () => this.modifyLevel(-1)); + bind(".levelToggle .plus", () => this.modifyLevel(1)); + + bind(".upgradesBelt .minus", () => this.modifyUpgrade("belt", -1)); + bind(".upgradesBelt .plus", () => this.modifyUpgrade("belt", 1)); + + bind(".upgradesExtraction .minus", () => this.modifyUpgrade("miner", -1)); + bind(".upgradesExtraction .plus", () => this.modifyUpgrade("miner", 1)); + + bind(".upgradesProcessing .minus", () => this.modifyUpgrade("processors", -1)); + bind(".upgradesProcessing .plus", () => this.modifyUpgrade("processors", 1)); + + bind(".upgradesPainting .minus", () => this.modifyUpgrade("painting", -1)); + bind(".upgradesPainting .plus", () => this.modifyUpgrade("painting", 1)); + } + + giveBlueprints() { + const shape = this.root.gameMode.getBlueprintShapeKey(); + if (!this.root.hubGoals.storedShapes[shape]) { + this.root.hubGoals.storedShapes[shape] = 0; + } + this.root.hubGoals.storedShapes[shape] += 1e9; + } + + maxOutAll() { + this.modifyUpgrade("belt", 100); + this.modifyUpgrade("miner", 100); + this.modifyUpgrade("processors", 100); + this.modifyUpgrade("painting", 100); + } + + modifyUpgrade(id, amount) { + const upgradeTiers = this.root.gameMode.getUpgrades()[id]; + const maxLevel = upgradeTiers.length; + + this.root.hubGoals.upgradeLevels[id] = Math.max( + 0, + Math.min(maxLevel, (this.root.hubGoals.upgradeLevels[id] || 0) + amount) + ); + + // Compute improvement + let improvement = 1; + for (let i = 0; i < this.root.hubGoals.upgradeLevels[id]; ++i) { + improvement += upgradeTiers[i].improvement; + } + this.root.hubGoals.upgradeImprovements[id] = improvement; + this.root.signals.upgradePurchased.dispatch(id); + this.root.hud.signals.notification.dispatch( + "Upgrade '" + id + "' is now at tier " + (this.root.hubGoals.upgradeLevels[id] + 1), + enumNotificationType.upgrade + ); + } + + modifyLevel(amount) { + const hubGoals = this.root.hubGoals; + hubGoals.level = Math.max(1, hubGoals.level + amount); + hubGoals.computeNextGoal(); + + // Clear all shapes of this level + hubGoals.storedShapes[hubGoals.currentGoal.definition.getHash()] = 0; + + if (this.root.hud.parts.pinnedShapes) { + this.root.hud.parts.pinnedShapes.rerenderFull(); + } + + // Compute gained rewards + hubGoals.gainedRewards = {}; + const levels = this.root.gameMode.getLevelDefinitions(); + for (let i = 0; i < hubGoals.level - 1; ++i) { + if (i < levels.length) { + const reward = levels[i].reward; + hubGoals.gainedRewards[reward] = (hubGoals.gainedRewards[reward] || 0) + 1; + } + } + + this.root.hud.signals.notification.dispatch( + "Changed level to " + hubGoals.level, + enumNotificationType.upgrade + ); + } + + initialize() { + // Allow toggling the controller overlay + this.root.gameState.inputReceiver.keydown.add(key => { + if (key.keyCode === 117) { + // F6 + this.toggle(); + } + }); + + this.visible = false; + this.domAttach = new DynamicDomAttach(this.root, this.element); + } + + toggle() { + this.visible = !this.visible; + } + + update() { + this.domAttach.update(this.visible); + } +} diff --git a/src/js/game/hud/parts/screenshot_exporter.js b/src/js/game/hud/parts/screenshot_exporter.js index dd81f8b6..dcb3fbf9 100644 --- a/src/js/game/hud/parts/screenshot_exporter.js +++ b/src/js/game/hud/parts/screenshot_exporter.js @@ -19,11 +19,6 @@ export class HUDScreenshotExporter extends BaseHUDPart { } startExport() { - if (!this.root.app.restrictionMgr.getIsExportingScreenshotsPossible()) { - this.root.hud.parts.dialogs.showFeatureRestrictionInfo(T.demo.features.exportingBase); - return; - } - const { ok } = this.root.hud.parts.dialogs.showInfo( T.dialogs.exportScreenshotWarning.title, T.dialogs.exportScreenshotWarning.desc, diff --git a/src/js/game/hud/parts/settings_menu.js b/src/js/game/hud/parts/settings_menu.js index f5314cd6..f03038cf 100644 --- a/src/js/game/hud/parts/settings_menu.js +++ b/src/js/game/hud/parts/settings_menu.js @@ -1,131 +1,136 @@ -import { BaseHUDPart } from "../base_hud_part"; -import { makeDiv, formatBigNumberFull } from "../../../core/utils"; -import { DynamicDomAttach } from "../dynamic_dom_attach"; -import { InputReceiver } from "../../../core/input_receiver"; -import { KeyActionMapper, KEYMAPPINGS } from "../../key_action_mapper"; -import { T } from "../../../translations"; -import { StaticMapEntityComponent } from "../../components/static_map_entity"; -import { BeltComponent } from "../../components/belt"; - -export class HUDSettingsMenu extends BaseHUDPart { - createElements(parent) { - this.background = makeDiv(parent, "ingame_HUD_SettingsMenu", ["ingameDialog"]); - - this.menuElement = makeDiv(this.background, null, ["menuElement"]); - - if (this.root.gameMode.hasHub()) { - this.statsElement = makeDiv( - this.background, - null, - ["statsElement"], - ` - ${T.ingame.settingsMenu.beltsPlaced} - ${T.ingame.settingsMenu.buildingsPlaced} - ${T.ingame.settingsMenu.playtime} - - ` - ); - } - - this.buttonContainer = makeDiv(this.menuElement, null, ["buttons"]); - - const buttons = [ - { - id: "continue", - action: () => this.close(), - }, - { - id: "settings", - action: () => this.goToSettings(), - }, - { - id: "menu", - action: () => this.returnToMenu(), - }, - ]; - - for (let i = 0; i < buttons.length; ++i) { - const { action, id } = buttons[i]; - - const element = document.createElement("button"); - element.classList.add("styledButton"); - element.classList.add(id); - this.buttonContainer.appendChild(element); - - this.trackClicks(element, action); - } - } - - isBlockingOverlay() { - return this.visible; - } - - returnToMenu() { - this.root.app.adProvider.showVideoAd().then(() => { - this.root.gameState.goBackToMenu(); - }); - } - - goToSettings() { - this.root.gameState.goToSettings(); - } - - shouldPauseGame() { - return this.visible; - } - - shouldPauseRendering() { - return this.visible; - } - - initialize() { - this.root.keyMapper.getBinding(KEYMAPPINGS.general.back).add(this.show, this); - - this.domAttach = new DynamicDomAttach(this.root, this.background, { - attachClass: "visible", - }); - - this.inputReciever = new InputReceiver("settingsmenu"); - this.keyActionMapper = new KeyActionMapper(this.root, this.inputReciever); - this.keyActionMapper.getBinding(KEYMAPPINGS.general.back).add(this.close, this); - - this.close(); - } - - show() { - this.visible = true; - this.root.app.inputMgr.makeSureAttachedAndOnTop(this.inputReciever); - - const totalMinutesPlayed = Math.ceil(this.root.time.now() / 60); - - if (this.root.gameMode.hasHub()) { - /** @type {HTMLElement} */ - const playtimeElement = this.statsElement.querySelector(".playtime"); - /** @type {HTMLElement} */ - const buildingsPlacedElement = this.statsElement.querySelector(".buildingsPlaced"); - /** @type {HTMLElement} */ - const beltsPlacedElement = this.statsElement.querySelector(".beltsPlaced"); - - playtimeElement.innerText = T.global.time.xMinutes.replace("", `${totalMinutesPlayed}`); - - buildingsPlacedElement.innerText = formatBigNumberFull( - this.root.entityMgr.getAllWithComponent(StaticMapEntityComponent).length - - this.root.entityMgr.getAllWithComponent(BeltComponent).length - ); - - beltsPlacedElement.innerText = formatBigNumberFull( - this.root.entityMgr.getAllWithComponent(BeltComponent).length - ); - } - } - - close() { - this.visible = false; - this.root.app.inputMgr.makeSureDetached(this.inputReciever); - this.update(); - } - - update() { - this.domAttach.update(this.visible); - } -} +import { InputReceiver } from "../../../core/input_receiver"; +import { formatBigNumberFull, makeDiv } from "../../../core/utils"; +import { T } from "../../../translations"; +import { BeltComponent } from "../../components/belt"; +import { StaticMapEntityComponent } from "../../components/static_map_entity"; +import { KEYMAPPINGS, KeyActionMapper } from "../../key_action_mapper"; +import { BaseHUDPart } from "../base_hud_part"; +import { DynamicDomAttach } from "../dynamic_dom_attach"; + +export class HUDSettingsMenu extends BaseHUDPart { + createElements(parent) { + this.background = makeDiv(parent, "ingame_HUD_SettingsMenu", ["ingameDialog"]); + + this.menuElement = makeDiv(this.background, null, ["menuElement"]); + + if (this.root.gameMode.hasHub()) { + this.statsElement = makeDiv( + this.background, + null, + ["statsElement"], + ` + ${T.ingame.settingsMenu.beltsPlaced} + ${T.ingame.settingsMenu.buildingsPlaced} + ${T.ingame.settingsMenu.playtime} + + ` + ); + } + + this.buttonContainer = makeDiv(this.menuElement, null, ["buttons"]); + + const buttons = [ + { + id: "continue", + action: () => this.close(), + }, + { + id: "settings", + action: () => this.goToSettings(), + }, + { + id: "menu", + action: () => this.returnToMenu(), + }, + ]; + + for (let i = 0; i < buttons.length; ++i) { + const { action, id } = buttons[i]; + + const element = document.createElement("button"); + element.classList.add("styledButton"); + element.classList.add(id); + this.buttonContainer.appendChild(element); + + this.trackClicks(element, action); + } + } + + isBlockingOverlay() { + return this.visible; + } + + returnToMenu() { + this.root.gameState.goBackToMenu(); + } + + goToSettings() { + this.root.gameState.goToSettings(); + } + + shouldPauseGame() { + return this.visible; + } + + shouldPauseRendering() { + return this.visible; + } + + initialize() { + this.root.keyMapper.getBinding(KEYMAPPINGS.general.back).add(this.show, this); + + this.domAttach = new DynamicDomAttach(this.root, this.background, { + attachClass: "visible", + }); + + this.inputReceiver = new InputReceiver("settingsmenu"); + this.keyActionMapper = new KeyActionMapper(this.root, this.inputReceiver); + this.keyActionMapper.getBinding(KEYMAPPINGS.general.back).add(this.close, this); + + this.close(); + } + + show() { + this.visible = true; + this.root.app.inputMgr.makeSureAttachedAndOnTop(this.inputReceiver); + + const totalMinutesPlayed = Math.ceil(this.root.time.now() / 60); + + if (this.root.gameMode.hasHub()) { + /** @type {HTMLElement} */ + const playtimeElement = this.statsElement.querySelector(".playtime"); + /** @type {HTMLElement} */ + const buildingsPlacedElement = this.statsElement.querySelector(".buildingsPlaced"); + /** @type {HTMLElement} */ + const beltsPlacedElement = this.statsElement.querySelector(".beltsPlaced"); + + playtimeElement.innerText = T.global.time.xMinutes.replace("", `${totalMinutesPlayed}`); + + buildingsPlacedElement.innerText = formatBigNumberFull( + this.root.entityMgr.getAllWithComponent(StaticMapEntityComponent).length - + this.root.entityMgr.getAllWithComponent(BeltComponent).length + ); + + beltsPlacedElement.innerText = formatBigNumberFull( + this.root.entityMgr.getAllWithComponent(BeltComponent).length + ); + } + } + + close() { + this.visible = false; + this.root.app.inputMgr.makeSureDetached(this.inputReceiver); + this.update(); + } + + cleanup() { + super.cleanup(); + + // Detach the input receiver when leaving InGameState + this.root.app.inputMgr.makeSureDetached(this.inputReceiver); + } + + update() { + this.domAttach.update(this.visible); + } +} diff --git a/src/js/game/hud/parts/shape_viewer.js b/src/js/game/hud/parts/shape_viewer.js index a7f5d206..61873a9d 100644 --- a/src/js/game/hud/parts/shape_viewer.js +++ b/src/js/game/hud/parts/shape_viewer.js @@ -1,128 +1,128 @@ -import { InputReceiver } from "../../../core/input_receiver"; -import { makeDiv, removeAllChildren } from "../../../core/utils"; -import { T } from "../../../translations"; -import { KeyActionMapper, KEYMAPPINGS } from "../../key_action_mapper"; -import { ShapeDefinition } from "../../shape_definition"; -import { BaseHUDPart } from "../base_hud_part"; -import { DynamicDomAttach } from "../dynamic_dom_attach"; - -const copy = require("clipboard-copy"); - -export class HUDShapeViewer extends BaseHUDPart { - createElements(parent) { - this.background = makeDiv(parent, "ingame_HUD_ShapeViewer", ["ingameDialog"]); - - // DIALOG Inner / Wrapper - this.dialogInner = makeDiv(this.background, null, ["dialogInner"]); - this.title = makeDiv(this.dialogInner, null, ["title"], T.ingame.shapeViewer.title); - this.closeButton = makeDiv(this.title, null, ["closeButton"]); - this.trackClicks(this.closeButton, this.close); - this.contentDiv = makeDiv(this.dialogInner, null, ["content"]); - - this.renderArea = makeDiv(this.contentDiv, null, ["renderArea"]); - this.infoArea = makeDiv(this.contentDiv, null, ["infoArea"]); - - // Create button to copy the shape area - this.copyButton = document.createElement("button"); - this.copyButton.classList.add("styledButton", "copyKey"); - this.copyButton.innerText = T.ingame.shapeViewer.copyKey; - this.infoArea.appendChild(this.copyButton); - } - - initialize() { - this.root.hud.signals.viewShapeDetailsRequested.add(this.renderForShape, this); - - this.domAttach = new DynamicDomAttach(this.root, this.background, { - attachClass: "visible", - }); - - this.currentShapeKey = null; - - this.inputReciever = new InputReceiver("shape_viewer"); - this.keyActionMapper = new KeyActionMapper(this.root, this.inputReciever); - - this.keyActionMapper.getBinding(KEYMAPPINGS.general.back).add(this.close, this); - - this.trackClicks(this.copyButton, this.onCopyKeyRequested); - - this.close(); - } - - isBlockingOverlay() { - return this.visible; - } - - /** - * Called when the copying of a key was requested - */ - onCopyKeyRequested() { - if (this.currentShapeKey) { - copy(this.currentShapeKey); - this.close(); - } - } - - /** - * Closes the dialog - */ - close() { - this.visible = false; - this.root.app.inputMgr.makeSureDetached(this.inputReciever); - this.update(); - } - - /** - * Shows the viewer for a given definition - * @param {ShapeDefinition} definition - */ - renderForShape(definition) { - this.visible = true; - this.root.app.inputMgr.makeSureAttachedAndOnTop(this.inputReciever); - - removeAllChildren(this.renderArea); - - this.currentShapeKey = definition.getHash(); - - const layers = definition.layers; - this.contentDiv.setAttribute("data-layers", layers.length); - - for (let i = layers.length - 1; i >= 0; --i) { - const layerElem = makeDiv(this.renderArea, null, ["layer", "layer-" + i]); - - let fakeLayers = []; - for (let k = 0; k < i; ++k) { - fakeLayers.push([null, null, null, null]); - } - fakeLayers.push(layers[i]); - - const thisLayerOnly = new ShapeDefinition({ layers: fakeLayers }); - const thisLayerCanvas = thisLayerOnly.generateAsCanvas(160); - layerElem.appendChild(thisLayerCanvas); - - for (let quad = 0; quad < 4; ++quad) { - const quadElem = makeDiv(layerElem, null, ["quad", "quad-" + quad]); - - const contents = layers[i][quad]; - if (contents) { - const colorLabelElem = makeDiv( - quadElem, - null, - ["colorLabel"], - T.ingame.colors[contents.color] - ); - } else { - const emptyLabelElem = makeDiv( - quadElem, - null, - ["emptyLabel"], - T.ingame.shapeViewer.empty - ); - } - } - } - } - - update() { - this.domAttach.update(this.visible); - } -} +import { InputReceiver } from "../../../core/input_receiver"; +import { makeDiv, removeAllChildren } from "../../../core/utils"; +import { T } from "../../../translations"; +import { KEYMAPPINGS, KeyActionMapper } from "../../key_action_mapper"; +import { ShapeDefinition } from "../../shape_definition"; +import { BaseHUDPart } from "../base_hud_part"; +import { DynamicDomAttach } from "../dynamic_dom_attach"; + +import copy from "clipboard-copy"; + +export class HUDShapeViewer extends BaseHUDPart { + createElements(parent) { + this.background = makeDiv(parent, "ingame_HUD_ShapeViewer", ["ingameDialog"]); + + // DIALOG Inner / Wrapper + this.dialogInner = makeDiv(this.background, null, ["dialogInner"]); + this.title = makeDiv(this.dialogInner, null, ["title"], T.ingame.shapeViewer.title); + this.closeButton = makeDiv(this.title, null, ["closeButton"]); + this.trackClicks(this.closeButton, this.close); + this.contentDiv = makeDiv(this.dialogInner, null, ["content"]); + + this.renderArea = makeDiv(this.contentDiv, null, ["renderArea"]); + this.infoArea = makeDiv(this.contentDiv, null, ["infoArea"]); + + // Create button to copy the shape area + this.copyButton = document.createElement("button"); + this.copyButton.classList.add("styledButton", "copyKey"); + this.copyButton.innerText = T.ingame.shapeViewer.copyKey; + this.infoArea.appendChild(this.copyButton); + } + + initialize() { + this.root.hud.signals.viewShapeDetailsRequested.add(this.renderForShape, this); + + this.domAttach = new DynamicDomAttach(this.root, this.background, { + attachClass: "visible", + }); + + this.currentShapeKey = null; + + this.inputReceiver = new InputReceiver("shape_viewer"); + this.keyActionMapper = new KeyActionMapper(this.root, this.inputReceiver); + + this.keyActionMapper.getBinding(KEYMAPPINGS.general.back).add(this.close, this); + + this.trackClicks(this.copyButton, this.onCopyKeyRequested); + + this.close(); + } + + isBlockingOverlay() { + return this.visible; + } + + /** + * Called when the copying of a key was requested + */ + onCopyKeyRequested() { + if (this.currentShapeKey) { + copy(this.currentShapeKey); + this.close(); + } + } + + /** + * Closes the dialog + */ + close() { + this.visible = false; + this.root.app.inputMgr.makeSureDetached(this.inputReceiver); + this.update(); + } + + /** + * Shows the viewer for a given definition + * @param {ShapeDefinition} definition + */ + renderForShape(definition) { + this.visible = true; + this.root.app.inputMgr.makeSureAttachedAndOnTop(this.inputReceiver); + + removeAllChildren(this.renderArea); + + this.currentShapeKey = definition.getHash(); + + const layers = definition.layers; + this.contentDiv.setAttribute("data-layers", layers.length); + + for (let i = layers.length - 1; i >= 0; --i) { + const layerElem = makeDiv(this.renderArea, null, ["layer", "layer-" + i]); + + let fakeLayers = []; + for (let k = 0; k < i; ++k) { + fakeLayers.push([null, null, null, null]); + } + fakeLayers.push(layers[i]); + + const thisLayerOnly = new ShapeDefinition({ layers: fakeLayers }); + const thisLayerCanvas = thisLayerOnly.generateAsCanvas(160); + layerElem.appendChild(thisLayerCanvas); + + for (let quad = 0; quad < 4; ++quad) { + const quadElem = makeDiv(layerElem, null, ["quad", "quad-" + quad]); + + const contents = layers[i][quad]; + if (contents) { + const colorLabelElem = makeDiv( + quadElem, + null, + ["colorLabel"], + T.ingame.colors[contents.color] + ); + } else { + const emptyLabelElem = makeDiv( + quadElem, + null, + ["emptyLabel"], + T.ingame.shapeViewer.empty + ); + } + } + } + } + + update() { + this.domAttach.update(this.visible); + } +} diff --git a/src/js/game/hud/parts/shop.js b/src/js/game/hud/parts/shop.js index fa92b743..cb343541 100644 --- a/src/js/game/hud/parts/shop.js +++ b/src/js/game/hud/parts/shop.js @@ -1,255 +1,253 @@ -import { ClickDetector } from "../../../core/click_detector"; -import { InputReceiver } from "../../../core/input_receiver"; -import { formatBigNumber, getRomanNumber, makeDiv } from "../../../core/utils"; -import { SOUNDS } from "../../../platform/sound"; -import { T } from "../../../translations"; -import { KeyActionMapper, KEYMAPPINGS } from "../../key_action_mapper"; -import { BaseHUDPart } from "../base_hud_part"; -import { DynamicDomAttach } from "../dynamic_dom_attach"; - -export class HUDShop extends BaseHUDPart { - createElements(parent) { - this.background = makeDiv(parent, "ingame_HUD_Shop", ["ingameDialog"]); - - // DIALOG Inner / Wrapper - this.dialogInner = makeDiv(this.background, null, ["dialogInner"]); - this.title = makeDiv(this.dialogInner, null, ["title"], T.ingame.shop.title); - this.closeButton = makeDiv(this.title, null, ["closeButton"]); - this.trackClicks(this.closeButton, this.close); - this.contentDiv = makeDiv(this.dialogInner, null, ["content"]); - - this.upgradeToElements = {}; - - // Upgrades - for (const upgradeId in this.root.gameMode.getUpgrades()) { - const handle = {}; - handle.requireIndexToElement = []; - - // Wrapper - handle.elem = makeDiv(this.contentDiv, null, ["upgrade"]); - handle.elem.setAttribute("data-upgrade-id", upgradeId); - - // Title - const title = makeDiv(handle.elem, null, ["title"], T.shopUpgrades[upgradeId].name); - - // Title > Tier - handle.elemTierLabel = makeDiv(title, null, ["tier"]); - - // Icon - handle.icon = makeDiv(handle.elem, null, ["icon"]); - handle.icon.setAttribute("data-icon", "upgrades/" + upgradeId + ".png"); - - // Description - handle.elemDescription = makeDiv(handle.elem, null, ["description"], "??"); - handle.elemRequirements = makeDiv(handle.elem, null, ["requirements"]); - - // Buy button - handle.buyButton = document.createElement("button"); - handle.buyButton.classList.add("buy", "styledButton"); - handle.buyButton.innerText = T.ingame.shop.buttonUnlock; - handle.elem.appendChild(handle.buyButton); - - this.trackClicks(handle.buyButton, () => this.tryUnlockNextTier(upgradeId)); - - // Assign handle - this.upgradeToElements[upgradeId] = handle; - } - } - - rerenderFull() { - for (const upgradeId in this.upgradeToElements) { - const handle = this.upgradeToElements[upgradeId]; - const upgradeTiers = this.root.gameMode.getUpgrades()[upgradeId]; - - const currentTier = this.root.hubGoals.getUpgradeLevel(upgradeId); - const currentTierMultiplier = this.root.hubGoals.upgradeImprovements[upgradeId]; - const tierHandle = upgradeTiers[currentTier]; - - // Set tier - handle.elemTierLabel.innerText = T.ingame.shop.tier.replace( - "", - getRomanNumber(currentTier + 1) - ); - - handle.elemTierLabel.setAttribute("data-tier", currentTier); - - // Cleanup detectors - for (let i = 0; i < handle.requireIndexToElement.length; ++i) { - const requiredHandle = handle.requireIndexToElement[i]; - requiredHandle.container.remove(); - requiredHandle.pinDetector.cleanup(); - if (requiredHandle.infoDetector) { - requiredHandle.infoDetector.cleanup(); - } - } - - // Cleanup - handle.requireIndexToElement = []; - - handle.elem.classList.toggle("maxLevel", !tierHandle); - - if (!tierHandle) { - // Max level - handle.elemDescription.innerText = T.ingame.shop.maximumLevel.replace( - "", - formatBigNumber(currentTierMultiplier) - ); - continue; - } - - // Set description - handle.elemDescription.innerText = T.shopUpgrades[upgradeId].description - .replace("", currentTierMultiplier.toFixed(2)) - .replace("", (currentTierMultiplier + tierHandle.improvement).toFixed(2)); - - tierHandle.required.forEach(({ shape, amount }) => { - const container = makeDiv(handle.elemRequirements, null, ["requirement"]); - - const shapeDef = this.root.shapeDefinitionMgr.getShapeFromShortKey(shape); - const shapeCanvas = shapeDef.generateAsCanvas(120); - shapeCanvas.classList.add(); - container.appendChild(shapeCanvas); - - const progressContainer = makeDiv(container, null, ["amount"]); - const progressBar = document.createElement("label"); - progressBar.classList.add("progressBar"); - progressContainer.appendChild(progressBar); - - const progressLabel = document.createElement("label"); - progressContainer.appendChild(progressLabel); - - const pinButton = document.createElement("button"); - pinButton.classList.add("pin"); - container.appendChild(pinButton); - - let infoDetector; - if (!G_WEGAME_VERSION) { - const viewInfoButton = document.createElement("button"); - viewInfoButton.classList.add("showInfo"); - container.appendChild(viewInfoButton); - infoDetector = new ClickDetector(viewInfoButton, { - consumeEvents: true, - preventDefault: true, - }); - infoDetector.click.add(() => - this.root.hud.signals.viewShapeDetailsRequested.dispatch(shapeDef) - ); - } - - const currentGoalShape = this.root.hubGoals.currentGoal.definition.getHash(); - if (shape === currentGoalShape) { - pinButton.classList.add("isGoal"); - } else if (this.root.hud.parts.pinnedShapes.isShapePinned(shape)) { - pinButton.classList.add("alreadyPinned"); - } - - const pinDetector = new ClickDetector(pinButton, { - consumeEvents: true, - preventDefault: true, - }); - pinDetector.click.add(() => { - if (this.root.hud.parts.pinnedShapes.isShapePinned(shape)) { - this.root.hud.signals.shapeUnpinRequested.dispatch(shape); - pinButton.classList.add("unpinned"); - pinButton.classList.remove("pinned", "alreadyPinned"); - } else { - this.root.hud.signals.shapePinRequested.dispatch(shapeDef); - pinButton.classList.add("pinned"); - pinButton.classList.remove("unpinned"); - } - }); - - handle.requireIndexToElement.push({ - container, - progressLabel, - progressBar, - definition: shapeDef, - required: amount, - pinDetector, - infoDetector, - }); - }); - } - } - - renderCountsAndStatus() { - for (const upgradeId in this.upgradeToElements) { - const handle = this.upgradeToElements[upgradeId]; - for (let i = 0; i < handle.requireIndexToElement.length; ++i) { - const { progressLabel, progressBar, definition, required } = handle.requireIndexToElement[i]; - - const haveAmount = this.root.hubGoals.getShapesStored(definition); - const progress = Math.min(haveAmount / required, 1.0); - - progressLabel.innerText = formatBigNumber(haveAmount) + " / " + formatBigNumber(required); - progressBar.style.width = progress * 100.0 + "%"; - progressBar.classList.toggle("complete", progress >= 1.0); - } - - handle.buyButton.classList.toggle("buyable", this.root.hubGoals.canUnlockUpgrade(upgradeId)); - } - } - - initialize() { - this.domAttach = new DynamicDomAttach(this.root, this.background, { - attachClass: "visible", - }); - - this.inputReciever = new InputReceiver("shop"); - this.keyActionMapper = new KeyActionMapper(this.root, this.inputReciever); - - this.keyActionMapper.getBinding(KEYMAPPINGS.general.back).add(this.close, this); - this.keyActionMapper.getBinding(KEYMAPPINGS.ingame.menuClose).add(this.close, this); - this.keyActionMapper.getBinding(KEYMAPPINGS.ingame.menuOpenShop).add(this.close, this); - - this.close(); - - this.rerenderFull(); - this.root.signals.upgradePurchased.add(this.rerenderFull, this); - } - - cleanup() { - // Cleanup detectors - for (const upgradeId in this.upgradeToElements) { - const handle = this.upgradeToElements[upgradeId]; - for (let i = 0; i < handle.requireIndexToElement.length; ++i) { - const requiredHandle = handle.requireIndexToElement[i]; - requiredHandle.container.remove(); - requiredHandle.pinDetector.cleanup(); - if (requiredHandle.infoDetector) { - requiredHandle.infoDetector.cleanup(); - } - } - handle.requireIndexToElement = []; - } - } - - show() { - this.visible = true; - this.root.app.inputMgr.makeSureAttachedAndOnTop(this.inputReciever); - this.rerenderFull(); - } - - close() { - this.visible = false; - this.root.app.inputMgr.makeSureDetached(this.inputReciever); - this.update(); - } - - update() { - this.domAttach.update(this.visible); - if (this.visible) { - this.renderCountsAndStatus(); - } - } - - tryUnlockNextTier(upgradeId) { - if (this.root.hubGoals.tryUnlockUpgrade(upgradeId)) { - this.root.app.sound.playUiSound(SOUNDS.unlockUpgrade); - } - } - - isBlockingOverlay() { - return this.visible; - } -} +import { ClickDetector } from "../../../core/click_detector"; +import { InputReceiver } from "../../../core/input_receiver"; +import { formatBigNumber, getRomanNumber, makeDiv } from "../../../core/utils"; +import { SOUNDS } from "../../../platform/sound"; +import { T } from "../../../translations"; +import { KEYMAPPINGS, KeyActionMapper } from "../../key_action_mapper"; +import { BaseHUDPart } from "../base_hud_part"; +import { DynamicDomAttach } from "../dynamic_dom_attach"; + +export class HUDShop extends BaseHUDPart { + createElements(parent) { + this.background = makeDiv(parent, "ingame_HUD_Shop", ["ingameDialog"]); + + // DIALOG Inner / Wrapper + this.dialogInner = makeDiv(this.background, null, ["dialogInner"]); + this.title = makeDiv(this.dialogInner, null, ["title"], T.ingame.shop.title); + this.closeButton = makeDiv(this.title, null, ["closeButton"]); + this.trackClicks(this.closeButton, this.close); + this.contentDiv = makeDiv(this.dialogInner, null, ["content"]); + + this.upgradeToElements = {}; + + // Upgrades + for (const upgradeId in this.root.gameMode.getUpgrades()) { + const handle = {}; + handle.requireIndexToElement = []; + + // Wrapper + handle.elem = makeDiv(this.contentDiv, null, ["upgrade"]); + handle.elem.setAttribute("data-upgrade-id", upgradeId); + + // Title + const title = makeDiv(handle.elem, null, ["title"], T.shopUpgrades[upgradeId].name); + + // Title > Tier + handle.elemTierLabel = makeDiv(title, null, ["tier"]); + + // Icon + handle.icon = makeDiv(handle.elem, null, ["icon"]); + handle.icon.setAttribute("data-icon", "upgrades/" + upgradeId + ".png"); + + // Description + handle.elemDescription = makeDiv(handle.elem, null, ["description"], "??"); + handle.elemRequirements = makeDiv(handle.elem, null, ["requirements"]); + + // Buy button + handle.buyButton = document.createElement("button"); + handle.buyButton.classList.add("buy", "styledButton"); + handle.buyButton.innerText = T.ingame.shop.buttonUnlock; + handle.elem.appendChild(handle.buyButton); + + this.trackClicks(handle.buyButton, () => this.tryUnlockNextTier(upgradeId)); + + // Assign handle + this.upgradeToElements[upgradeId] = handle; + } + } + + rerenderFull() { + for (const upgradeId in this.upgradeToElements) { + const handle = this.upgradeToElements[upgradeId]; + const upgradeTiers = this.root.gameMode.getUpgrades()[upgradeId]; + + const currentTier = this.root.hubGoals.getUpgradeLevel(upgradeId); + const currentTierMultiplier = this.root.hubGoals.upgradeImprovements[upgradeId]; + const tierHandle = upgradeTiers[currentTier]; + + // Set tier + handle.elemTierLabel.innerText = T.ingame.shop.tier.replace( + "", + getRomanNumber(currentTier + 1) + ); + + handle.elemTierLabel.setAttribute("data-tier", currentTier); + + // Cleanup detectors + for (let i = 0; i < handle.requireIndexToElement.length; ++i) { + const requiredHandle = handle.requireIndexToElement[i]; + requiredHandle.container.remove(); + requiredHandle.pinDetector.cleanup(); + if (requiredHandle.infoDetector) { + requiredHandle.infoDetector.cleanup(); + } + } + + // Cleanup + handle.requireIndexToElement = []; + + handle.elem.classList.toggle("maxLevel", !tierHandle); + + if (!tierHandle) { + // Max level + handle.elemDescription.innerText = T.ingame.shop.maximumLevel.replace( + "", + formatBigNumber(currentTierMultiplier) + ); + continue; + } + + // Set description + handle.elemDescription.innerText = T.shopUpgrades[upgradeId].description + .replace("", currentTierMultiplier.toFixed(2)) + .replace("", (currentTierMultiplier + tierHandle.improvement).toFixed(2)); + + tierHandle.required.forEach(({ shape, amount }) => { + const container = makeDiv(handle.elemRequirements, null, ["requirement"]); + + const shapeDef = this.root.shapeDefinitionMgr.getShapeFromShortKey(shape); + const shapeCanvas = shapeDef.generateAsCanvas(120); + shapeCanvas.classList.add(); + container.appendChild(shapeCanvas); + + const progressContainer = makeDiv(container, null, ["amount"]); + const progressBar = document.createElement("label"); + progressBar.classList.add("progressBar"); + progressContainer.appendChild(progressBar); + + const progressLabel = document.createElement("label"); + progressContainer.appendChild(progressLabel); + + const pinButton = document.createElement("button"); + pinButton.classList.add("pin"); + container.appendChild(pinButton); + + let infoDetector; + const viewInfoButton = document.createElement("button"); + viewInfoButton.classList.add("showInfo"); + container.appendChild(viewInfoButton); + infoDetector = new ClickDetector(viewInfoButton, { + consumeEvents: true, + preventDefault: true, + }); + infoDetector.click.add(() => + this.root.hud.signals.viewShapeDetailsRequested.dispatch(shapeDef) + ); + + const currentGoalShape = this.root.hubGoals.currentGoal.definition.getHash(); + if (shape === currentGoalShape) { + pinButton.classList.add("isGoal"); + } else if (this.root.hud.parts.pinnedShapes.isShapePinned(shape)) { + pinButton.classList.add("alreadyPinned"); + } + + const pinDetector = new ClickDetector(pinButton, { + consumeEvents: true, + preventDefault: true, + }); + pinDetector.click.add(() => { + if (this.root.hud.parts.pinnedShapes.isShapePinned(shape)) { + this.root.hud.signals.shapeUnpinRequested.dispatch(shape); + pinButton.classList.add("unpinned"); + pinButton.classList.remove("pinned", "alreadyPinned"); + } else { + this.root.hud.signals.shapePinRequested.dispatch(shapeDef); + pinButton.classList.add("pinned"); + pinButton.classList.remove("unpinned"); + } + }); + + handle.requireIndexToElement.push({ + container, + progressLabel, + progressBar, + definition: shapeDef, + required: amount, + pinDetector, + infoDetector, + }); + }); + } + } + + renderCountsAndStatus() { + for (const upgradeId in this.upgradeToElements) { + const handle = this.upgradeToElements[upgradeId]; + for (let i = 0; i < handle.requireIndexToElement.length; ++i) { + const { progressLabel, progressBar, definition, required } = handle.requireIndexToElement[i]; + + const haveAmount = this.root.hubGoals.getShapesStored(definition); + const progress = Math.min(haveAmount / required, 1.0); + + progressLabel.innerText = formatBigNumber(haveAmount) + " / " + formatBigNumber(required); + progressBar.style.width = progress * 100.0 + "%"; + progressBar.classList.toggle("complete", progress >= 1.0); + } + + handle.buyButton.classList.toggle("buyable", this.root.hubGoals.canUnlockUpgrade(upgradeId)); + } + } + + initialize() { + this.domAttach = new DynamicDomAttach(this.root, this.background, { + attachClass: "visible", + }); + + this.inputReceiver = new InputReceiver("shop"); + this.keyActionMapper = new KeyActionMapper(this.root, this.inputReceiver); + + this.keyActionMapper.getBinding(KEYMAPPINGS.general.back).add(this.close, this); + this.keyActionMapper.getBinding(KEYMAPPINGS.ingame.menuClose).add(this.close, this); + this.keyActionMapper.getBinding(KEYMAPPINGS.ingame.menuOpenShop).add(this.close, this); + + this.close(); + + this.rerenderFull(); + this.root.signals.upgradePurchased.add(this.rerenderFull, this); + } + + cleanup() { + // Cleanup detectors + for (const upgradeId in this.upgradeToElements) { + const handle = this.upgradeToElements[upgradeId]; + for (let i = 0; i < handle.requireIndexToElement.length; ++i) { + const requiredHandle = handle.requireIndexToElement[i]; + requiredHandle.container.remove(); + requiredHandle.pinDetector.cleanup(); + if (requiredHandle.infoDetector) { + requiredHandle.infoDetector.cleanup(); + } + } + handle.requireIndexToElement = []; + } + } + + show() { + this.visible = true; + this.root.app.inputMgr.makeSureAttachedAndOnTop(this.inputReceiver); + this.rerenderFull(); + } + + close() { + this.visible = false; + this.root.app.inputMgr.makeSureDetached(this.inputReceiver); + this.update(); + } + + update() { + this.domAttach.update(this.visible); + if (this.visible) { + this.renderCountsAndStatus(); + } + } + + tryUnlockNextTier(upgradeId) { + if (this.root.hubGoals.tryUnlockUpgrade(upgradeId)) { + this.root.app.sound.playUiSound(SOUNDS.unlockUpgrade); + } + } + + isBlockingOverlay() { + return this.visible; + } +} diff --git a/src/js/game/hud/parts/standalone_advantages.js b/src/js/game/hud/parts/standalone_advantages.js deleted file mode 100644 index 81eccb29..00000000 --- a/src/js/game/hud/parts/standalone_advantages.js +++ /dev/null @@ -1,160 +0,0 @@ -import { globalConfig, openStandaloneLink } from "../../../core/config"; -import { InputReceiver } from "../../../core/input_receiver"; -import { ReadWriteProxy } from "../../../core/read_write_proxy"; -import { generateFileDownload, makeDiv } from "../../../core/utils"; -import { T } from "../../../translations"; -import { BaseHUDPart } from "../base_hud_part"; -import { DynamicDomAttach } from "../dynamic_dom_attach"; - -export class HUDStandaloneAdvantages extends BaseHUDPart { - createElements(parent) { - this.background = makeDiv(parent, "ingame_HUD_StandaloneAdvantages", ["ingameDialog"]); - - // DIALOG Inner / Wrapper - this.dialogInner = makeDiv(this.background, null, ["dialogInner"]); - this.title = makeDiv(this.dialogInner, null, ["title"], ""); - this.subTitle = makeDiv(this.dialogInner, null, ["subTitle"], T.ingame.standaloneAdvantages.titleV2); - - this.contentDiv = makeDiv( - this.dialogInner, - null, - ["content"], - ` -
- ${Object.entries(T.ingame.standaloneAdvantages.points) - .map( - ([key, trans]) => ` -
- ${trans.title} -

${trans.desc}

-
` - ) - .join("")} - -
- -
- -
${ - T.demoBanners.playtimeDisclaimerDownload - }
- - - -
- ` - ); - - this.trackClicks(this.contentDiv.querySelector("button.steamLinkButton"), () => { - openStandaloneLink(this.root.app, "shapez_std_advg"); - this.close(); - }); - this.trackClicks(this.contentDiv.querySelector("button.otherCloseButton"), () => { - this.close(); - }); - - this.trackClicks(this.contentDiv.querySelector(".playtimeDisclaimerDownload"), () => { - this.root.gameState.savegame.updateData(this.root); - const data = ReadWriteProxy.serializeObject(this.root.gameState.savegame.currentData); - const filename = "shapez-demo-savegame.bin"; - generateFileDownload(filename, data); - }); - } - - get showIntervalSeconds() { - if (G_IS_STANDALONE) { - return 20 * 60; - } - return 15 * 60; - } - - shouldPauseGame() { - return this.visible; - } - - shouldPauseRendering() { - return this.visible; - } - - hasBlockingOverlayOpen() { - return this.visible; - } - - initialize() { - this.domAttach = new DynamicDomAttach(this.root, this.background, { - attachClass: "visible", - }); - - this.inputReciever = new InputReceiver("standalone-advantages"); - this.close(); - - // On standalone, show popup instant - but don't do so on web, since it increases - // the amount of clicks to get into the game - if (G_IS_STEAM_DEMO) { - // show instant - this.lastShown = -1e10; - } else { - // wait for next interval - this.lastShown = 0; - } - - this.root.signals.gameRestored.add(() => { - if ( - this.root.hubGoals.level >= this.root.gameMode.getLevelDefinitions().length - 1 && - this.root.app.restrictionMgr.getIsStandaloneMarketingActive() - ) { - this.show(true); - } - }); - } - - show(final = false) { - if (!this.visible) { - this.root.app.gameAnalytics.noteMinor("game.std_advg.show"); - this.root.app.gameAnalytics.noteMinor("game.std_advg.show-" + (final ? "final" : "nonfinal")); - } - - this.lastShown = this.root.time.now(); - this.visible = true; - this.final = final; - this.root.app.inputMgr.makeSureAttachedAndOnTop(this.inputReciever); - - if (this.final) { - this.title.innerText = T.ingame.standaloneAdvantages.titleExpiredV2; - } else if (this.root.time.now() < 120) { - this.title.innerText = ""; - } else { - this.title.innerText = T.ingame.standaloneAdvantages.titleEnjoyingDemo; - } - } - - close() { - if (this.final) { - this.root.gameState.goBackToMenu(); - } else { - this.visible = false; - this.root.app.inputMgr.makeSureDetached(this.inputReciever); - - this.update(); - } - } - - update() { - if (!this.visible && this.root.time.now() - this.lastShown > this.showIntervalSeconds) { - this.show(); - } - - this.domAttach.update(this.visible); - } -} diff --git a/src/js/game/hud/parts/statistics.js b/src/js/game/hud/parts/statistics.js index 015e48b6..a1a24a94 100644 --- a/src/js/game/hud/parts/statistics.js +++ b/src/js/game/hud/parts/statistics.js @@ -1,11 +1,11 @@ import { InputReceiver } from "../../../core/input_receiver"; import { makeButton, makeDiv, removeAllChildren } from "../../../core/utils"; -import { KeyActionMapper, KEYMAPPINGS } from "../../key_action_mapper"; +import { T } from "../../../translations"; +import { KEYMAPPINGS, KeyActionMapper } from "../../key_action_mapper"; import { enumAnalyticsDataSource } from "../../production_analytics"; import { BaseHUDPart } from "../base_hud_part"; import { DynamicDomAttach } from "../dynamic_dom_attach"; -import { enumDisplayMode, HUDShapeStatisticsHandle, statisticsUnitsSeconds } from "./statistics_handle"; -import { T } from "../../../translations"; +import { HUDShapeStatisticsHandle, enumDisplayMode, statisticsUnitsSeconds } from "./statistics_handle"; /** * Capitalizes the first letter @@ -115,8 +115,8 @@ export class HUDStatistics extends BaseHUDPart { attachClass: "visible", }); - this.inputReciever = new InputReceiver("statistics"); - this.keyActionMapper = new KeyActionMapper(this.root, this.inputReciever); + this.inputReceiver = new InputReceiver("statistics"); + this.keyActionMapper = new KeyActionMapper(this.root, this.inputReceiver); this.keyActionMapper.getBinding(KEYMAPPINGS.general.back).add(this.close, this); this.keyActionMapper.getBinding(KEYMAPPINGS.ingame.menuClose).add(this.close, this); @@ -157,14 +157,14 @@ export class HUDStatistics extends BaseHUDPart { show() { this.visible = true; - this.root.app.inputMgr.makeSureAttachedAndOnTop(this.inputReciever); + this.root.app.inputMgr.makeSureAttachedAndOnTop(this.inputReceiver); this.rerenderFull(); this.update(); } close() { this.visible = false; - this.root.app.inputMgr.makeSureDetached(this.inputReciever); + this.root.app.inputMgr.makeSureDetached(this.inputReceiver); this.update(); } diff --git a/src/js/game/hud/parts/statistics_handle.js b/src/js/game/hud/parts/statistics_handle.js index 71181c81..574f1f4e 100644 --- a/src/js/game/hud/parts/statistics_handle.js +++ b/src/js/game/hud/parts/statistics_handle.js @@ -1,6 +1,6 @@ import { makeOffscreenBuffer } from "../../../core/buffer_utils"; import { globalConfig } from "../../../core/config"; -import { clamp, formatBigNumber, round2Digits } from "../../../core/utils"; +import { clamp, formatBigNumber } from "../../../core/utils"; import { T } from "../../../translations"; import { enumAnalyticsDataSource } from "../../production_analytics"; import { GameRoot } from "../../root"; @@ -92,9 +92,7 @@ export class HUDShapeStatisticsHandle { switch (dataSource) { case enumAnalyticsDataSource.stored: { - this.counter.innerText = formatBigNumber( - this.root.hubGoals.storedShapes[this.definition.getHash()] || 0 - ); + this.counter.innerText = formatBigNumber(this.root.hubGoals.getShapesStored(this.definition)); break; } case enumAnalyticsDataSource.delivered: diff --git a/src/js/game/hud/parts/tutorial_hints.js b/src/js/game/hud/parts/tutorial_hints.js index 29a07ef3..9100808a 100644 --- a/src/js/game/hud/parts/tutorial_hints.js +++ b/src/js/game/hud/parts/tutorial_hints.js @@ -1,104 +1,104 @@ -import { InputReceiver } from "../../../core/input_receiver"; -import { TrackedState } from "../../../core/tracked_state"; -import { makeDiv } from "../../../core/utils"; -import { KeyActionMapper, KEYMAPPINGS } from "../../key_action_mapper"; -import { BaseHUDPart } from "../base_hud_part"; -import { DynamicDomAttach } from "../dynamic_dom_attach"; -import { T } from "../../../translations"; - -const tutorialVideos = [3, 4, 5, 6, 7, 9, 10, 11]; - -export class HUDPartTutorialHints extends BaseHUDPart { - createElements(parent) { - this.element = makeDiv( - parent, - "ingame_HUD_TutorialHints", - [], - ` -
- ${T.ingame.tutorialHints.title} - -
- - - ` - ); - - this.videoElement = this.element.querySelector("video"); - } - - shouldPauseGame() { - return this.enlarged; - } - - initialize() { - this.trackClicks(this.element.querySelector(".toggleHint"), this.toggleHintEnlarged); - - this.videoAttach = new DynamicDomAttach(this.root, this.videoElement, { - timeToKeepSeconds: 0.3, - }); - - this.videoAttach.update(false); - this.enlarged = false; - - this.inputReciever = new InputReceiver("tutorial_hints"); - this.keyActionMapper = new KeyActionMapper(this.root, this.inputReciever); - this.keyActionMapper.getBinding(KEYMAPPINGS.general.back).add(this.close, this); - - this.domAttach = new DynamicDomAttach(this.root, this.element); - - this.currentShownLevel = new TrackedState(this.updateVideoUrl, this); - } - - updateVideoUrl(level) { - if (tutorialVideos.indexOf(level) < 0) { - this.videoElement.querySelector("source").setAttribute("src", ""); - this.videoElement.pause(); - } else { - this.videoElement - .querySelector("source") - .setAttribute("src", "https://static.shapez.io/tutorial_videos/level_" + level + ".webm"); - this.videoElement.currentTime = 0; - this.videoElement.load(); - } - } - - close() { - this.enlarged = false; - this.element.classList.remove("enlarged", "noBlur"); - this.root.app.inputMgr.makeSureDetached(this.inputReciever); - this.update(); - } - - show() { - this.element.classList.add("enlarged", "noBlur"); - this.enlarged = true; - this.root.app.inputMgr.makeSureAttachedAndOnTop(this.inputReciever); - this.update(); - - this.videoElement.currentTime = 0; - this.videoElement.play(); - } - - update() { - this.videoAttach.update(this.enlarged); - - this.currentShownLevel.set(this.root.hubGoals.level); - - const tutorialVisible = tutorialVideos.indexOf(this.root.hubGoals.level) >= 0; - this.domAttach.update(tutorialVisible); - } - - toggleHintEnlarged() { - if (this.enlarged) { - this.close(); - } else { - this.show(); - } - } -} +import { InputReceiver } from "../../../core/input_receiver"; +import { TrackedState } from "../../../core/tracked_state"; +import { makeDiv } from "../../../core/utils"; +import { T } from "../../../translations"; +import { KeyActionMapper, KEYMAPPINGS } from "../../key_action_mapper"; +import { BaseHUDPart } from "../base_hud_part"; +import { DynamicDomAttach } from "../dynamic_dom_attach"; + +const tutorialVideos = [3, 4, 5, 6, 7, 9, 10, 11]; + +export class HUDPartTutorialHints extends BaseHUDPart { + createElements(parent) { + this.element = makeDiv( + parent, + "ingame_HUD_TutorialHints", + [], + ` +
+ ${T.ingame.tutorialHints.title} + +
+ + + ` + ); + + this.videoElement = this.element.querySelector("video"); + } + + shouldPauseGame() { + return this.enlarged; + } + + initialize() { + this.trackClicks(this.element.querySelector(".toggleHint"), this.toggleHintEnlarged); + + this.videoAttach = new DynamicDomAttach(this.root, this.videoElement, { + timeToKeepSeconds: 0.3, + }); + + this.videoAttach.update(false); + this.enlarged = false; + + this.inputReceiver = new InputReceiver("tutorial_hints"); + this.keyActionMapper = new KeyActionMapper(this.root, this.inputReceiver); + this.keyActionMapper.getBinding(KEYMAPPINGS.general.back).add(this.close, this); + + this.domAttach = new DynamicDomAttach(this.root, this.element); + + this.currentShownLevel = new TrackedState(this.updateVideoUrl, this); + } + + updateVideoUrl(level) { + if (tutorialVideos.indexOf(level) < 0) { + this.videoElement.querySelector("source").setAttribute("src", ""); + this.videoElement.pause(); + } else { + this.videoElement + .querySelector("source") + .setAttribute("src", "https://static.shapez.io/tutorial_videos/level_" + level + ".webm"); + this.videoElement.currentTime = 0; + this.videoElement.load(); + } + } + + close() { + this.enlarged = false; + this.element.classList.remove("enlarged", "noBlur"); + this.root.app.inputMgr.makeSureDetached(this.inputReceiver); + this.update(); + } + + show() { + this.element.classList.add("enlarged", "noBlur"); + this.enlarged = true; + this.root.app.inputMgr.makeSureAttachedAndOnTop(this.inputReceiver); + this.update(); + + this.videoElement.currentTime = 0; + this.videoElement.play(); + } + + update() { + this.videoAttach.update(this.enlarged); + + this.currentShownLevel.set(this.root.hubGoals.level); + + const tutorialVisible = tutorialVideos.indexOf(this.root.hubGoals.level) >= 0; + this.domAttach.update(tutorialVisible); + } + + toggleHintEnlarged() { + if (this.enlarged) { + this.close(); + } else { + this.show(); + } + } +} diff --git a/src/js/game/hud/parts/tutorial_video_offer.js b/src/js/game/hud/parts/tutorial_video_offer.js index ee232a92..9750ac2d 100644 --- a/src/js/game/hud/parts/tutorial_video_offer.js +++ b/src/js/game/hud/parts/tutorial_video_offer.js @@ -1,32 +1,32 @@ -import { THIRDPARTY_URLS } from "../../../core/config"; -import { T } from "../../../translations"; -import { BaseHUDPart } from "../base_hud_part"; - -/** - * Offers to open the tutorial video after completing a level - */ -export class HUDTutorialVideoOffer extends BaseHUDPart { - createElements() {} - - initialize() { - this.root.hud.signals.unlockNotificationFinished.add(() => { - const level = this.root.hubGoals.level; - const tutorialVideoLink = THIRDPARTY_URLS.levelTutorialVideos[level]; - if (tutorialVideoLink) { - const isForeign = this.root.app.settings.getLanguage() !== "en"; - const dialogData = isForeign - ? T.dialogs.tutorialVideoAvailableForeignLanguage - : T.dialogs.tutorialVideoAvailable; - - const { ok } = this.root.hud.parts.dialogs.showInfo(dialogData.title, dialogData.desc, [ - "cancel:bad", - "ok:good", - ]); - - ok.add(() => { - this.root.app.platformWrapper.openExternalLink(tutorialVideoLink); - }); - } - }); - } -} +import { THIRDPARTY_URLS } from "../../../core/config"; +import { T } from "../../../translations"; +import { BaseHUDPart } from "../base_hud_part"; + +/** + * Offers to open the tutorial video after completing a level + */ +export class HUDTutorialVideoOffer extends BaseHUDPart { + createElements() {} + + initialize() { + this.root.hud.signals.unlockNotificationFinished.add(() => { + const level = this.root.hubGoals.level; + const tutorialVideoLink = THIRDPARTY_URLS.levelTutorialVideos[level]; + if (tutorialVideoLink) { + const isForeign = this.root.app.settings.getLanguage() !== "en"; + const dialogData = isForeign + ? T.dialogs.tutorialVideoAvailableForeignLanguage + : T.dialogs.tutorialVideoAvailable; + + const { ok } = this.root.hud.parts.dialogs.showInfo(dialogData.title, dialogData.desc, [ + "cancel:bad", + "ok:good", + ]); + + ok.add(() => { + this.root.app.platformWrapper.openExternalLink(tutorialVideoLink); + }); + } + }); + } +} diff --git a/src/js/game/hud/parts/unlock_notification.js b/src/js/game/hud/parts/unlock_notification.js index ba129f00..ce7df2e2 100644 --- a/src/js/game/hud/parts/unlock_notification.js +++ b/src/js/game/hud/parts/unlock_notification.js @@ -1,189 +1,176 @@ -import { globalConfig } from "../../../core/config"; -import { gMetaBuildingRegistry } from "../../../core/global_registries"; -import { InputReceiver } from "../../../core/input_receiver"; -import { makeDiv } from "../../../core/utils"; -import { SOUNDS } from "../../../platform/sound"; -import { T } from "../../../translations"; -import { defaultBuildingVariant } from "../../meta_building"; -import { enumHubGoalRewards } from "../../tutorial_goals"; -import { enumHubGoalRewardsToContentUnlocked } from "../../tutorial_goals_mappings"; -import { BaseHUDPart } from "../base_hud_part"; -import { DynamicDomAttach } from "../dynamic_dom_attach"; -import { enumNotificationType } from "./notifications"; - -export class HUDUnlockNotification extends BaseHUDPart { - initialize() { - this.visible = false; - - this.domAttach = new DynamicDomAttach(this.root, this.element, { - timeToKeepSeconds: 0, - }); - - if (!(G_IS_DEV && globalConfig.debug.disableUnlockDialog)) { - this.root.signals.storyGoalCompleted.add(this.showForLevel, this); - } - - this.buttonShowTimeout = null; - - this.root.app.gameAnalytics.noteMinor("game.started"); - } - - shouldPauseGame() { - return !G_IS_STANDALONE && this.visible; - } - - createElements(parent) { - this.inputReciever = new InputReceiver("unlock-notification"); - - this.element = makeDiv(parent, "ingame_HUD_UnlockNotification", ["noBlur"]); - - const dialog = makeDiv(this.element, null, ["dialog"]); - - this.elemTitle = makeDiv(dialog, null, ["title"]); - this.elemSubTitle = makeDiv(dialog, null, ["subTitle"], T.ingame.levelCompleteNotification.completed); - - this.elemContents = makeDiv(dialog, null, ["contents"]); - - this.btnClose = document.createElement("button"); - this.btnClose.classList.add("close", "styledButton"); - this.btnClose.innerText = T.ingame.levelCompleteNotification.buttonNextLevel; - dialog.appendChild(this.btnClose); - - this.trackClicks(this.btnClose, this.requestClose); - } - - /** - * @param {number} level - * @param {enumHubGoalRewards} reward - */ - showForLevel(level, reward) { - this.root.soundProxy.playUi(SOUNDS.levelComplete); - - const levels = this.root.gameMode.getLevelDefinitions(); - // Don't use getIsFreeplay() because we want the freeplay level up to show - if (level > levels.length) { - this.root.hud.signals.notification.dispatch( - T.ingame.notifications.freeplayLevelComplete.replace("", String(level)), - enumNotificationType.success - ); - return; - } - - this.root.app.gameAnalytics.noteMinor("game.level.complete-" + level); - - this.root.app.inputMgr.makeSureAttachedAndOnTop(this.inputReciever); - this.elemTitle.innerText = T.ingame.levelCompleteNotification.levelTitle.replace( - "", - ("" + level).padStart(2, "0") - ); - - const rewardName = T.storyRewards[reward].title; - - let html = ` -
- ${T.ingame.levelCompleteNotification.unlockText.replace("", rewardName)} -
- -
- ${T.storyRewards[reward].desc} -
- - `; - - html += "
"; - const gained = enumHubGoalRewardsToContentUnlocked[reward]; - if (gained) { - gained.forEach(([metaBuildingClass, variant]) => { - const metaBuilding = gMetaBuildingRegistry.findByClass(metaBuildingClass); - html += `
`; - }); - } - html += "
"; - - this.elemContents.innerHTML = html; - this.visible = true; - - if (this.buttonShowTimeout) { - clearTimeout(this.buttonShowTimeout); - } - - this.element.querySelector("button.close").classList.remove("unlocked"); - - if (this.root.app.settings.getAllSettings().offerHints) { - this.buttonShowTimeout = setTimeout( - () => this.element.querySelector("button.close").classList.add("unlocked"), - G_IS_DEV ? 100 : 1500 - ); - } else { - this.element.querySelector("button.close").classList.add("unlocked"); - } - } - - cleanup() { - this.root.app.inputMgr.makeSureDetached(this.inputReciever); - if (this.buttonShowTimeout) { - clearTimeout(this.buttonShowTimeout); - this.buttonShowTimeout = null; - } - } - - isBlockingOverlay() { - return this.visible; - } - - requestClose() { - this.root.app.adProvider.showVideoAd().then(() => { - this.close(); - - this.root.hud.signals.unlockNotificationFinished.dispatch(); - - if ( - this.root.hubGoals.level > this.root.gameMode.getLevelDefinitions().length - 1 && - this.root.app.restrictionMgr.getIsStandaloneMarketingActive() - ) { - this.root.hud.parts.standaloneAdvantages.show(true); - } - - if (!this.root.app.settings.getAllSettings().offerHints) { - return; - } - - if (this.root.hubGoals.level === 3) { - const { showUpgrades } = this.root.hud.parts.dialogs.showInfo( - T.dialogs.upgradesIntroduction.title, - T.dialogs.upgradesIntroduction.desc, - ["showUpgrades:good:timeout"] - ); - showUpgrades.add(() => this.root.hud.parts.shop.show()); - } - - if (this.root.hubGoals.level === 5) { - const { showKeybindings } = this.root.hud.parts.dialogs.showInfo( - T.dialogs.keybindingsIntroduction.title, - T.dialogs.keybindingsIntroduction.desc, - ["showKeybindings:misc", "ok:good:timeout"] - ); - showKeybindings.add(() => this.root.gameState.goToKeybindings()); - } - }); - } - - close() { - this.root.app.inputMgr.makeSureDetached(this.inputReciever); - if (this.buttonShowTimeout) { - clearTimeout(this.buttonShowTimeout); - this.buttonShowTimeout = null; - } - this.visible = false; - } - - update() { - this.domAttach.update(this.visible); - if (!this.visible && this.buttonShowTimeout) { - clearTimeout(this.buttonShowTimeout); - this.buttonShowTimeout = null; - } - } -} +import { globalConfig } from "../../../core/config"; +import { gMetaBuildingRegistry } from "../../../core/global_registries"; +import { InputReceiver } from "../../../core/input_receiver"; +import { makeDiv } from "../../../core/utils"; +import { SOUNDS } from "../../../platform/sound"; +import { T } from "../../../translations"; +import { defaultBuildingVariant } from "../../meta_building"; +import { enumHubGoalRewards } from "../../tutorial_goals"; +import { enumHubGoalRewardsToContentUnlocked } from "../../tutorial_goals_mappings"; +import { BaseHUDPart } from "../base_hud_part"; +import { DynamicDomAttach } from "../dynamic_dom_attach"; +import { enumNotificationType } from "./notifications"; + +export class HUDUnlockNotification extends BaseHUDPart { + initialize() { + this.visible = false; + + this.domAttach = new DynamicDomAttach(this.root, this.element, { + timeToKeepSeconds: 0, + }); + + if (!(G_IS_DEV && globalConfig.debug.disableUnlockDialog)) { + this.root.signals.storyGoalCompleted.add(this.showForLevel, this); + } + + this.buttonShowTimeout = null; + } + + shouldPauseGame() { + return false; + } + + createElements(parent) { + this.inputReceiver = new InputReceiver("unlock-notification"); + + this.element = makeDiv(parent, "ingame_HUD_UnlockNotification", ["noBlur"]); + + const dialog = makeDiv(this.element, null, ["dialog"]); + + this.elemTitle = makeDiv(dialog, null, ["title"]); + this.elemSubTitle = makeDiv(dialog, null, ["subTitle"], T.ingame.levelCompleteNotification.completed); + + this.elemContents = makeDiv(dialog, null, ["contents"]); + + this.btnClose = document.createElement("button"); + this.btnClose.classList.add("close", "styledButton"); + this.btnClose.innerText = T.ingame.levelCompleteNotification.buttonNextLevel; + dialog.appendChild(this.btnClose); + + this.trackClicks(this.btnClose, this.requestClose); + } + + /** + * @param {number} level + * @param {enumHubGoalRewards} reward + */ + showForLevel(level, reward) { + this.root.soundProxy.playUi(SOUNDS.levelComplete); + + const levels = this.root.gameMode.getLevelDefinitions(); + // Don't use getIsFreeplay() because we want the freeplay level up to show + if (level > levels.length) { + this.root.hud.signals.notification.dispatch( + T.ingame.notifications.freeplayLevelComplete.replace("", String(level)), + enumNotificationType.success + ); + return; + } + + this.root.app.inputMgr.makeSureAttachedAndOnTop(this.inputReceiver); + this.elemTitle.innerText = T.ingame.levelCompleteNotification.levelTitle.replace( + "", + ("" + level).padStart(2, "0") + ); + + const rewardName = T.storyRewards[reward].title; + + let html = ` +
+ ${T.ingame.levelCompleteNotification.unlockText.replace("", rewardName)} +
+ +
+ ${T.storyRewards[reward].desc} +
+ + `; + + html += "
"; + const gained = enumHubGoalRewardsToContentUnlocked[reward]; + if (gained) { + gained.forEach(([metaBuildingClass, variant]) => { + const metaBuilding = gMetaBuildingRegistry.findByClass(metaBuildingClass); + html += `
`; + }); + } + html += "
"; + + this.elemContents.innerHTML = html; + this.visible = true; + + if (this.buttonShowTimeout) { + clearTimeout(this.buttonShowTimeout); + } + + this.element.querySelector("button.close").classList.remove("unlocked"); + + if (this.root.app.settings.getAllSettings().offerHints) { + this.buttonShowTimeout = setTimeout( + () => this.element.querySelector("button.close").classList.add("unlocked"), + G_IS_DEV ? 100 : 1500 + ); + } else { + this.element.querySelector("button.close").classList.add("unlocked"); + } + } + + cleanup() { + this.root.app.inputMgr.makeSureDetached(this.inputReceiver); + if (this.buttonShowTimeout) { + clearTimeout(this.buttonShowTimeout); + this.buttonShowTimeout = null; + } + } + + isBlockingOverlay() { + return this.visible; + } + + requestClose() { + this.close(); + + this.root.hud.signals.unlockNotificationFinished.dispatch(); + + if (!this.root.app.settings.getAllSettings().offerHints) { + return; + } + + if (this.root.hubGoals.level === 3) { + const { showUpgrades } = this.root.hud.parts.dialogs.showInfo( + T.dialogs.upgradesIntroduction.title, + T.dialogs.upgradesIntroduction.desc, + ["showUpgrades:good:timeout"] + ); + showUpgrades.add(() => this.root.hud.parts.shop.show()); + } + + if (this.root.hubGoals.level === 5) { + const { showKeybindings } = this.root.hud.parts.dialogs.showInfo( + T.dialogs.keybindingsIntroduction.title, + T.dialogs.keybindingsIntroduction.desc, + ["showKeybindings:misc", "ok:good:timeout"] + ); + showKeybindings.add(() => this.root.gameState.goToKeybindings()); + } + } + + close() { + this.root.app.inputMgr.makeSureDetached(this.inputReceiver); + if (this.buttonShowTimeout) { + clearTimeout(this.buttonShowTimeout); + this.buttonShowTimeout = null; + } + this.visible = false; + } + + update() { + this.domAttach.update(this.visible); + if (!this.visible && this.buttonShowTimeout) { + clearTimeout(this.buttonShowTimeout); + this.buttonShowTimeout = null; + } + } +} diff --git a/src/js/game/hud/parts/watermark.js b/src/js/game/hud/parts/watermark.js deleted file mode 100644 index c9be8058..00000000 --- a/src/js/game/hud/parts/watermark.js +++ /dev/null @@ -1,49 +0,0 @@ -import { globalConfig, openStandaloneLink } from "../../../core/config"; -import { makeDiv } from "../../../core/utils"; -import { T } from "../../../translations"; -import { BaseHUDPart } from "../base_hud_part"; - -export class HUDWatermark extends BaseHUDPart { - createElements(parent) { - let linkText = T.ingame.watermark.get_on_steam; - - this.linkElement = makeDiv( - parent, - "ingame_HUD_WatermarkClicker", - globalConfig.currentDiscount > 0 ? ["withDiscount"] : [], - linkText + - (globalConfig.currentDiscount > 0 - ? `${T.global.discount.replace( - "", - String(globalConfig.currentDiscount) - )}` - : "") - ); - this.trackClicks(this.linkElement, () => { - openStandaloneLink(this.root.app, "shapez_watermark"); - }); - } - - initialize() {} - - update() {} - - /** - * - * @param {import("../../../core/draw_utils").DrawParameters} parameters - */ - drawOverlays(parameters) { - const w = this.root.gameWidth; - - parameters.context.fillStyle = "rgba(20, 30, 40, 0.25)"; - parameters.context.font = "bold " + this.root.app.getEffectiveUiScale() * 40 + "px GameFont"; - parameters.context.textAlign = "center"; - parameters.context.fillText( - T.demoBanners.title.toUpperCase(), - w / 2, - this.root.app.getEffectiveUiScale() * 50 - ); - - parameters.context.textAlign = "left"; - } -} diff --git a/src/js/game/hud/parts/waypoints.js b/src/js/game/hud/parts/waypoints.js index 5aac95dc..89bf9d17 100644 --- a/src/js/game/hud/parts/waypoints.js +++ b/src/js/game/hud/parts/waypoints.js @@ -1,650 +1,633 @@ -import { makeOffscreenBuffer } from "../../../core/buffer_utils"; -import { globalConfig, THIRDPARTY_URLS } from "../../../core/config"; -import { DrawParameters } from "../../../core/draw_parameters"; -import { gMetaBuildingRegistry } from "../../../core/global_registries"; -import { Loader } from "../../../core/loader"; -import { DialogWithForm } from "../../../core/modal_dialog_elements"; -import { FormElementInput } from "../../../core/modal_dialog_forms"; -import { Rectangle } from "../../../core/rectangle"; -import { STOP_PROPAGATION } from "../../../core/signal"; -import { - arrayDeleteValue, - fillInLinkIntoTranslation, - lerp, - makeDiv, - removeAllChildren, -} from "../../../core/utils"; -import { Vector } from "../../../core/vector"; -import { ACHIEVEMENTS } from "../../../platform/achievement_provider"; -import { T } from "../../../translations"; -import { BaseItem } from "../../base_item"; -import { MetaHubBuilding } from "../../buildings/hub"; -import { enumMouseButton } from "../../camera"; -import { KEYMAPPINGS } from "../../key_action_mapper"; -import { ShapeDefinition } from "../../shape_definition"; -import { BaseHUDPart } from "../base_hud_part"; -import { DynamicDomAttach } from "../dynamic_dom_attach"; -import { enumNotificationType } from "./notifications"; - -/** @typedef {{ - * label: string | null, - * center: { x: number, y: number }, - * zoomLevel: number, - * layer: Layer, - * }} Waypoint */ - -/** - * Used when a shape icon is rendered instead - */ -const MAX_LABEL_LENGTH = 71; - -export class HUDWaypoints extends BaseHUDPart { - /** - * Creates the overview of waypoints - * @param {HTMLElement} parent - */ - createElements(parent) { - // Create the helper box on the lower right when zooming out - if (this.root.app.settings.getAllSettings().offerHints && !G_WEGAME_VERSION) { - this.hintElement = makeDiv( - parent, - "ingame_HUD_Waypoints_Hint", - [], - ` - ${T.ingame.waypoints.waypoints} - ${T.ingame.waypoints.description.replace( - "", - `${this.root.keyMapper - .getBinding(KEYMAPPINGS.navigation.createMarker) - .getKeyCodeString()}` - )} - ` - ); - } - - // Create the waypoint list on the upper right - this.waypointsListElement = makeDiv(parent, "ingame_HUD_Waypoints", [], "Waypoints"); - } - - /** - * Serializes the waypoints - */ - serialize() { - return { - waypoints: this.waypoints, - }; - } - - /** - * Deserializes the waypoints - * @param {{waypoints: Array}} data - */ - deserialize(data) { - if (!data || !data.waypoints || !Array.isArray(data.waypoints)) { - return "Invalid waypoints data"; - } - this.waypoints = data.waypoints; - this.rerenderWaypointList(); - } - - /** - * Initializes everything - */ - initialize() { - // Cache the sprite for the waypoints - - this.waypointSprites = { - regular: Loader.getSprite("sprites/misc/waypoint.png"), - wires: Loader.getSprite("sprites/misc/waypoint_wires.png"), - }; - - this.directionIndicatorSprite = Loader.getSprite("sprites/misc/hub_direction_indicator.png"); - - /** @type {Array} */ - this.waypoints = []; - this.waypoints.push({ - label: null, - center: { x: 0, y: 0 }, - zoomLevel: 3, - layer: gMetaBuildingRegistry.findByClass(MetaHubBuilding).getLayer(), - }); - - // Create a buffer we can use to measure text - this.dummyBuffer = makeOffscreenBuffer(1, 1, { - reusable: false, - label: "waypoints-measure-canvas", - })[1]; - - // Dynamically attach/detach the lower right hint in the map overview - if (this.hintElement) { - this.domAttach = new DynamicDomAttach(this.root, this.hintElement); - } - - // Catch mouse and key events - if (!G_WEGAME_VERSION) { - this.root.camera.downPreHandler.add(this.onMouseDown, this); - this.root.keyMapper - .getBinding(KEYMAPPINGS.navigation.createMarker) - .add(() => this.requestSaveMarker({})); - } - - /** - * Stores at how much opacity the markers should be rendered on the map. - * This is interpolated over multiple frames so we have some sort of fade effect - */ - this.currentMarkerOpacity = 1; - this.currentCompassOpacity = 0; - - // Create buffer which is used to indicate the hub direction - const [canvas, context] = makeOffscreenBuffer(48, 48, { - smooth: true, - reusable: false, - label: "waypoints-compass", - }); - this.compassBuffer = { canvas, context }; - - /** - * Stores a cache from a shape short key to its canvas representation - */ - this.cachedKeyToCanvas = {}; - - /** - * Store cached text widths - * @type {Object} - */ - this.cachedTextWidths = {}; - - // Initial render - this.rerenderWaypointList(); - } - - /** - * Returns how long a text will be rendered - * @param {string} text - * @returns {number} - */ - getTextWidth(text) { - if (this.cachedTextWidths[text]) { - return this.cachedTextWidths[text]; - } - - this.dummyBuffer.font = "bold " + this.getTextScale() + "px GameFont"; - return (this.cachedTextWidths[text] = this.dummyBuffer.measureText(text).width); - } - - /** - * Returns how big the text should be rendered - */ - getTextScale() { - return this.getWaypointUiScale() * 12; - } - - /** - * Returns the scale for rendering waypoints - */ - getWaypointUiScale() { - return this.root.app.getEffectiveUiScale(); - } - - /** - * Re-renders the waypoint list to account for changes - */ - rerenderWaypointList() { - removeAllChildren(this.waypointsListElement); - this.cleanupClickDetectors(); - - for (let i = 0; i < this.waypoints.length; ++i) { - const waypoint = this.waypoints[i]; - const label = this.getWaypointLabel(waypoint); - - const element = makeDiv(this.waypointsListElement, null, [ - "waypoint", - "layer--" + waypoint.layer, - ]); - - if (ShapeDefinition.isValidShortKey(label)) { - const canvas = this.getWaypointCanvas(waypoint); - /** - * Create a clone of the cached canvas, as calling appendElement when a canvas is - * already in the document will move the existing canvas to the new position. - */ - const [newCanvas, context] = makeOffscreenBuffer(48, 48, { - smooth: true, - label: label + "-waypoint-" + i, - }); - context.drawImage(canvas, 0, 0); - element.appendChild(newCanvas); - element.classList.add("shapeIcon"); - } else { - element.innerText = label; - } - - if (this.isWaypointDeletable(waypoint)) { - const editButton = makeDiv(element, null, ["editButton"]); - this.trackClicks(editButton, () => this.requestSaveMarker({ waypoint })); - } - - if (!waypoint.label) { - // This must be the hub label - element.classList.add("hub"); - element.insertBefore(this.compassBuffer.canvas, element.childNodes[0]); - } - - this.trackClicks(element, () => this.moveToWaypoint(waypoint), { - targetOnly: true, - }); - } - } - - /** - * Moves the camera to a given waypoint - * @param {Waypoint} waypoint - */ - moveToWaypoint(waypoint) { - this.root.currentLayer = waypoint.layer; - this.root.camera.setDesiredCenter(new Vector(waypoint.center.x, waypoint.center.y)); - this.root.camera.setDesiredZoom(waypoint.zoomLevel); - } - - /** - * Deletes a waypoint from the list - * @param {Waypoint} waypoint - */ - deleteWaypoint(waypoint) { - arrayDeleteValue(this.waypoints, waypoint); - this.rerenderWaypointList(); - } - - /** - * Gets the canvas for a given waypoint - * @param {Waypoint} waypoint - * @returns {HTMLCanvasElement} - */ - getWaypointCanvas(waypoint) { - const key = waypoint.label; - if (this.cachedKeyToCanvas[key]) { - return this.cachedKeyToCanvas[key]; - } - - assert(ShapeDefinition.isValidShortKey(key), "Invalid short key: " + key); - const definition = this.root.shapeDefinitionMgr.getShapeFromShortKey(key); - const preRendered = definition.generateAsCanvas(48); - return (this.cachedKeyToCanvas[key] = preRendered); - } - - /** - * Requests to save a marker at the current camera position. If worldPos is set, - * uses that position instead. - * @param {object} param0 - * @param {Vector=} param0.worldPos Override the world pos, otherwise it is the camera position - * @param {Waypoint=} param0.waypoint Waypoint to be edited. If omitted, create new - */ - requestSaveMarker({ worldPos = null, waypoint = null }) { - // Construct dialog with input field - const markerNameInput = new FormElementInput({ - id: "markerName", - label: null, - placeholder: "", - defaultValue: waypoint ? waypoint.label : "", - validator: val => - val.length > 0 && (val.length < MAX_LABEL_LENGTH || ShapeDefinition.isValidShortKey(val)), - }); - const dialog = new DialogWithForm({ - app: this.root.app, - title: waypoint ? T.dialogs.createMarker.titleEdit : T.dialogs.createMarker.title, - desc: fillInLinkIntoTranslation(T.dialogs.createMarker.desc, THIRDPARTY_URLS.shapeViewer), - formElements: [markerNameInput], - buttons: waypoint ? ["delete:bad", "cancel", "ok:good"] : ["cancel", "ok:good"], - }); - this.root.hud.parts.dialogs.internalShowDialog(dialog); - - // Edit marker - if (waypoint) { - dialog.buttonSignals.ok.add(() => { - // Actually rename the waypoint - this.renameWaypoint(waypoint, markerNameInput.getValue()); - }); - dialog.buttonSignals.delete.add(() => { - // Actually delete the waypoint - this.deleteWaypoint(waypoint); - }); - } else { - // Compute where to create the marker - const center = worldPos || this.root.camera.center; - - dialog.buttonSignals.ok.add(() => { - // Show info that you can have only N markers in the demo, - // actually show this *after* entering the name so you want the - // standalone even more (I'm evil :P) - if (this.waypoints.length > this.root.app.restrictionMgr.getMaximumWaypoints()) { - this.root.hud.parts.dialogs.showFeatureRestrictionInfo( - "", - T.dialogs.markerDemoLimit.desc - ); - return; - } - - // Actually create the waypoint - this.addWaypoint(markerNameInput.getValue(), center); - }); - } - } - - /** - * Adds a new waypoint at the given location with the given label - * @param {string} label - * @param {Vector} position - */ - addWaypoint(label, position) { - this.waypoints.push({ - label, - center: { x: position.x, y: position.y }, - zoomLevel: this.root.camera.zoomLevel, - layer: this.root.currentLayer, - }); - - this.sortWaypoints(); - - // Show notification about creation - this.root.hud.signals.notification.dispatch( - T.ingame.waypoints.creationSuccessNotification, - enumNotificationType.success - ); - this.root.signals.achievementCheck.dispatch( - ACHIEVEMENTS.mapMarkers15, - this.waypoints.length - 1 // Disregard HUB - ); - - // Re-render the list and thus add it - this.rerenderWaypointList(); - } - - /** - * Renames a waypoint with the given label - * @param {Waypoint} waypoint - * @param {string} label - */ - renameWaypoint(waypoint, label) { - waypoint.label = label; - - this.sortWaypoints(); - - // Show notification about renamed - this.root.hud.signals.notification.dispatch( - T.ingame.waypoints.creationSuccessNotification, - enumNotificationType.success - ); - - // Re-render the list and thus add it - this.rerenderWaypointList(); - } - - /** - * Called every frame to update stuff - */ - update() { - if (this.domAttach) { - this.domAttach.update(this.root.camera.getIsMapOverlayActive()); - } - } - - /** - * Sort waypoints by name - */ - sortWaypoints() { - this.waypoints.sort((a, b) => { - if (!a.label) { - return -1; - } - if (!b.label) { - return 1; - } - return this.getWaypointLabel(a) - .padEnd(MAX_LABEL_LENGTH, "0") - .localeCompare(this.getWaypointLabel(b).padEnd(MAX_LABEL_LENGTH, "0")); - }); - } - - /** - * Returns the label for a given waypoint - * @param {Waypoint} waypoint - * @returns {string} - */ - getWaypointLabel(waypoint) { - return waypoint.label || T.ingame.waypoints.hub; - } - - /** - * Returns if a waypoint is deletable - * @param {Waypoint} waypoint - * @returns {boolean} - */ - isWaypointDeletable(waypoint) { - return waypoint.label !== null; - } - - /** - * Returns the screen space bounds of the given waypoint or null - * if it couldn't be determined. Also returns wheter its a shape or not - * @param {Waypoint} waypoint - * @return {{ - * screenBounds: Rectangle - * item: BaseItem|null, - * text: string - * }} - */ - getWaypointScreenParams(waypoint) { - if (!this.root.camera.getIsMapOverlayActive()) { - return null; - } - - // Find parameters - const scale = this.getWaypointUiScale(); - const screenPos = this.root.camera.worldToScreen(new Vector(waypoint.center.x, waypoint.center.y)); - - // Distinguish between text and item waypoints -> Figure out parameters - const originalLabel = this.getWaypointLabel(waypoint); - let text, item, textWidth; - - if (ShapeDefinition.isValidShortKey(originalLabel)) { - // If the label is actually a key, render the shape icon - item = this.root.shapeDefinitionMgr.getShapeItemFromShortKey(originalLabel); - textWidth = 40; - } else { - // Otherwise render a regular waypoint - text = originalLabel; - textWidth = this.getTextWidth(text); - } - - return { - screenBounds: new Rectangle( - screenPos.x - 7 * scale, - screenPos.y - 12 * scale, - 15 * scale + textWidth, - 15 * scale - ), - item, - text, - }; - } - - /** - * Finds the currently intersected waypoint on the map overview under - * the cursor. - * - * @returns {Waypoint | null} - */ - findCurrentIntersectedWaypoint() { - const mousePos = this.root.app.mousePosition; - if (!mousePos) { - return; - } - - for (let i = 0; i < this.waypoints.length; ++i) { - const waypoint = this.waypoints[i]; - const params = this.getWaypointScreenParams(waypoint); - if (params && params.screenBounds.containsPoint(mousePos.x, mousePos.y)) { - return waypoint; - } - } - } - - /** - * Mouse-Down handler - * @param {Vector} pos - * @param {enumMouseButton} button - */ - onMouseDown(pos, button) { - const waypoint = this.findCurrentIntersectedWaypoint(); - if (waypoint) { - if (button === enumMouseButton.left) { - this.root.soundProxy.playUiClick(); - this.moveToWaypoint(waypoint); - } else if (button === enumMouseButton.right) { - if (this.isWaypointDeletable(waypoint)) { - this.root.soundProxy.playUiClick(); - this.requestSaveMarker({ waypoint }); - } else { - this.root.soundProxy.playUiError(); - } - } - - return STOP_PROPAGATION; - } else { - // Allow right click to create a marker - if (button === enumMouseButton.right) { - if (this.root.camera.getIsMapOverlayActive()) { - const worldPos = this.root.camera.screenToWorld(pos); - this.requestSaveMarker({ worldPos }); - return STOP_PROPAGATION; - } - } - } - } - - /** - * Rerenders the compass - */ - rerenderWaypointsCompass() { - const dims = 48; - const indicatorSize = 30; - const cameraPos = this.root.camera.center; - - const context = this.compassBuffer.context; - context.clearRect(0, 0, dims, dims); - - const distanceToHub = cameraPos.length(); - const compassVisible = distanceToHub > (10 * globalConfig.tileSize) / this.root.camera.zoomLevel; - const targetCompassAlpha = compassVisible ? 1 : 0; - - // Fade the compas in / out - this.currentCompassOpacity = lerp(this.currentCompassOpacity, targetCompassAlpha, 0.08); - - // Render the compass - if (this.currentCompassOpacity > 0.01) { - context.globalAlpha = this.currentCompassOpacity; - const angle = cameraPos.angle() + Math.radians(45) + Math.PI / 2; - context.translate(dims / 2, dims / 2); - context.rotate(angle); - this.directionIndicatorSprite.drawCentered(context, 0, 0, indicatorSize); - context.rotate(-angle); - context.translate(-dims / 2, -dims / 2); - context.globalAlpha = 1; - } - - // Render the regualr icon - const iconOpacity = 1 - this.currentCompassOpacity; - if (iconOpacity > 0.01) { - context.globalAlpha = iconOpacity; - this.waypointSprites.regular.drawCentered(context, dims / 2, dims / 2, dims * 0.7); - context.globalAlpha = 1; - } - } - - /** - * Draws the waypoints on the map - * @param {DrawParameters} parameters - */ - drawOverlays(parameters) { - const mousePos = this.root.app.mousePosition; - const desiredOpacity = this.root.camera.getIsMapOverlayActive() ? 1 : 0; - this.currentMarkerOpacity = lerp(this.currentMarkerOpacity, desiredOpacity, 0.08); - - this.rerenderWaypointsCompass(); - - // Don't render with low opacity - if (this.currentMarkerOpacity < 0.01) { - return; - } - - // Determine rendering scale - const scale = this.getWaypointUiScale(); - - // Set the font size - const textSize = this.getTextScale(); - parameters.context.font = "bold " + textSize + "px GameFont"; - parameters.context.textBaseline = "middle"; - - // Loop over all waypoints - for (let i = 0; i < this.waypoints.length; ++i) { - const waypoint = this.waypoints[i]; - - const waypointData = this.getWaypointScreenParams(waypoint); - if (!waypointData) { - // Not relevant - continue; - } - - if (!parameters.visibleRect.containsRect(waypointData.screenBounds)) { - // Out of screen - continue; - } - - const bounds = waypointData.screenBounds; - const contentPaddingX = 7 * scale; - const isSelected = mousePos && bounds.containsPoint(mousePos.x, mousePos.y); - - // Render the background rectangle - parameters.context.globalAlpha = this.currentMarkerOpacity * (isSelected ? 1 : 0.7); - parameters.context.fillStyle = "rgba(255, 255, 255, 0.7)"; - parameters.context.beginRoundedRect(bounds.x, bounds.y, bounds.w, bounds.h, 6); - parameters.context.fill(); - - // Render the text - if (waypointData.item) { - const canvas = this.getWaypointCanvas(waypoint); - const itemSize = 14 * scale; - parameters.context.drawImage( - canvas, - bounds.x + contentPaddingX + 6 * scale, - bounds.y + bounds.h / 2 - itemSize / 2, - itemSize, - itemSize - ); - } else if (waypointData.text) { - // Render the text - parameters.context.fillStyle = "#000"; - parameters.context.textBaseline = "middle"; - parameters.context.fillText( - waypointData.text, - bounds.x + contentPaddingX + 6 * scale, - bounds.y + bounds.h / 2 - ); - parameters.context.textBaseline = "alphabetic"; - } else { - assertAlways(false, "Waypoint has no item and text"); - } - - // Render the small icon on the left - this.waypointSprites[waypoint.layer].drawCentered( - parameters.context, - bounds.x + contentPaddingX, - bounds.y + bounds.h / 2, - bounds.h * 0.6 - ); - } - - parameters.context.textBaseline = "alphabetic"; - parameters.context.globalAlpha = 1; - } -} +import { makeOffscreenBuffer } from "../../../core/buffer_utils"; +import { globalConfig, THIRDPARTY_URLS } from "../../../core/config"; +import { DrawParameters } from "../../../core/draw_parameters"; +import { gMetaBuildingRegistry } from "../../../core/global_registries"; +import { Loader } from "../../../core/loader"; +import { DialogWithForm } from "../../../core/modal_dialog_elements"; +import { FormElementInput } from "../../../core/modal_dialog_forms"; +import { Rectangle } from "../../../core/rectangle"; +import { STOP_PROPAGATION } from "../../../core/signal"; +import { + arrayDeleteValue, + fillInLinkIntoTranslation, + lerp, + makeDiv, + removeAllChildren, +} from "../../../core/utils"; +import { Vector } from "../../../core/vector"; +import { T } from "../../../translations"; +import { BaseItem } from "../../base_item"; +import { MetaHubBuilding } from "../../buildings/hub"; +import { enumMouseButton } from "../../camera"; +import { KEYMAPPINGS } from "../../key_action_mapper"; +import { ShapeDefinition } from "../../shape_definition"; +import { BaseHUDPart } from "../base_hud_part"; +import { DynamicDomAttach } from "../dynamic_dom_attach"; +import { enumNotificationType } from "./notifications"; + +/** @typedef {{ + * label: string | null, + * center: { x: number, y: number }, + * zoomLevel: number, + * layer: Layer, + * }} Waypoint */ + +/** + * Used when a shape icon is rendered instead + */ +const MAX_LABEL_LENGTH = 71; + +export class HUDWaypoints extends BaseHUDPart { + /** + * Creates the overview of waypoints + * @param {HTMLElement} parent + */ + createElements(parent) { + // Create the helper box on the lower right when zooming out + if (this.root.app.settings.getAllSettings().offerHints) { + this.hintElement = makeDiv( + parent, + "ingame_HUD_Waypoints_Hint", + [], + ` + ${T.ingame.waypoints.waypoints} + ${T.ingame.waypoints.description.replace( + "", + `${this.root.keyMapper + .getBinding(KEYMAPPINGS.navigation.createMarker) + .getKeyCodeString()}` + )} + ` + ); + } + + // Create the waypoint list on the upper right + this.waypointsListElement = makeDiv(parent, "ingame_HUD_Waypoints", [], "Waypoints"); + } + + /** + * Serializes the waypoints + */ + serialize() { + return { + waypoints: this.waypoints, + }; + } + + /** + * Deserializes the waypoints + * @param {{waypoints: Array}} data + */ + deserialize(data) { + if (!data || !data.waypoints || !Array.isArray(data.waypoints)) { + return "Invalid waypoints data"; + } + this.waypoints = data.waypoints; + this.rerenderWaypointList(); + } + + /** + * Initializes everything + */ + initialize() { + // Cache the sprite for the waypoints + + this.waypointSprites = { + regular: Loader.getSprite("sprites/misc/waypoint.png"), + wires: Loader.getSprite("sprites/misc/waypoint_wires.png"), + }; + + this.directionIndicatorSprite = Loader.getSprite("sprites/misc/hub_direction_indicator.png"); + + /** @type {Array} */ + this.waypoints = []; + this.waypoints.push({ + label: null, + center: { x: 0, y: 0 }, + zoomLevel: 3, + layer: gMetaBuildingRegistry.findByClass(MetaHubBuilding).getLayer(), + }); + + // Create a buffer we can use to measure text + this.dummyBuffer = makeOffscreenBuffer(1, 1, { + reusable: false, + label: "waypoints-measure-canvas", + })[1]; + + // Dynamically attach/detach the lower right hint in the map overview + if (this.hintElement) { + this.domAttach = new DynamicDomAttach(this.root, this.hintElement); + } + + // Catch mouse and key events + this.root.camera.downPreHandler.add(this.onMouseDown, this); + this.root.keyMapper + .getBinding(KEYMAPPINGS.navigation.createMarker) + .add(() => this.requestSaveMarker({})); + + /** + * Stores at how much opacity the markers should be rendered on the map. + * This is interpolated over multiple frames so we have some sort of fade effect + */ + this.currentMarkerOpacity = 1; + this.currentCompassOpacity = 0; + + // Create buffer which is used to indicate the hub direction + const [canvas, context] = makeOffscreenBuffer(48, 48, { + smooth: true, + reusable: false, + label: "waypoints-compass", + }); + this.compassBuffer = { canvas, context }; + + /** + * Stores a cache from a shape short key to its canvas representation + */ + this.cachedKeyToCanvas = {}; + + /** + * Store cached text widths + * @type {Object} + */ + this.cachedTextWidths = {}; + + // Initial render + this.rerenderWaypointList(); + } + + /** + * Returns how long a text will be rendered + * @param {string} text + * @returns {number} + */ + getTextWidth(text) { + if (this.cachedTextWidths[text]) { + return this.cachedTextWidths[text]; + } + + this.dummyBuffer.font = "bold " + this.getTextScale() + "px GameFont"; + return (this.cachedTextWidths[text] = this.dummyBuffer.measureText(text).width); + } + + /** + * Returns how big the text should be rendered + */ + getTextScale() { + return this.getWaypointUiScale() * 12; + } + + /** + * Returns the scale for rendering waypoints + */ + getWaypointUiScale() { + return this.root.app.getEffectiveUiScale(); + } + + /** + * Re-renders the waypoint list to account for changes + */ + rerenderWaypointList() { + removeAllChildren(this.waypointsListElement); + this.cleanupClickDetectors(); + + for (let i = 0; i < this.waypoints.length; ++i) { + const waypoint = this.waypoints[i]; + const label = this.getWaypointLabel(waypoint); + + const element = makeDiv(this.waypointsListElement, null, [ + "waypoint", + "layer--" + waypoint.layer, + ]); + + if (ShapeDefinition.isValidShortKey(label)) { + const canvas = this.getWaypointCanvas(waypoint); + /** + * Create a clone of the cached canvas, as calling appendElement when a canvas is + * already in the document will move the existing canvas to the new position. + */ + const [newCanvas, context] = makeOffscreenBuffer(48, 48, { + smooth: true, + label: label + "-waypoint-" + i, + }); + context.drawImage(canvas, 0, 0); + element.appendChild(newCanvas); + element.classList.add("shapeIcon"); + } else { + element.innerText = label; + } + + if (this.isWaypointDeletable(waypoint)) { + const editButton = makeDiv(element, null, ["editButton"]); + this.trackClicks(editButton, () => this.requestSaveMarker({ waypoint })); + } + + if (!waypoint.label) { + // This must be the hub label + element.classList.add("hub"); + element.insertBefore(this.compassBuffer.canvas, element.childNodes[0]); + } + + this.trackClicks(element, () => this.moveToWaypoint(waypoint), { + targetOnly: true, + }); + } + } + + /** + * Moves the camera to a given waypoint + * @param {Waypoint} waypoint + */ + moveToWaypoint(waypoint) { + this.root.currentLayer = waypoint.layer; + this.root.camera.setDesiredCenter(new Vector(waypoint.center.x, waypoint.center.y)); + this.root.camera.setDesiredZoom(waypoint.zoomLevel); + } + + /** + * Deletes a waypoint from the list + * @param {Waypoint} waypoint + */ + deleteWaypoint(waypoint) { + arrayDeleteValue(this.waypoints, waypoint); + this.rerenderWaypointList(); + } + + /** + * Gets the canvas for a given waypoint + * @param {Waypoint} waypoint + * @returns {HTMLCanvasElement} + */ + getWaypointCanvas(waypoint) { + const key = waypoint.label; + if (this.cachedKeyToCanvas[key]) { + return this.cachedKeyToCanvas[key]; + } + + assert(ShapeDefinition.isValidShortKey(key), "Invalid short key: " + key); + const definition = this.root.shapeDefinitionMgr.getShapeFromShortKey(key); + const preRendered = definition.generateAsCanvas(48); + return (this.cachedKeyToCanvas[key] = preRendered); + } + + /** + * Requests to save a marker at the current camera position. If worldPos is set, + * uses that position instead. + * @param {object} param0 + * @param {Vector=} param0.worldPos Override the world pos, otherwise it is the camera position + * @param {Waypoint=} param0.waypoint Waypoint to be edited. If omitted, create new + */ + requestSaveMarker({ worldPos = null, waypoint = null }) { + // Construct dialog with input field + const markerNameInput = new FormElementInput({ + id: "markerName", + label: null, + placeholder: "", + defaultValue: waypoint ? waypoint.label : "", + validator: val => + val.length > 0 && (val.length < MAX_LABEL_LENGTH || ShapeDefinition.isValidShortKey(val)), + }); + const dialog = new DialogWithForm({ + app: this.root.app, + title: waypoint ? T.dialogs.createMarker.titleEdit : T.dialogs.createMarker.title, + desc: fillInLinkIntoTranslation(T.dialogs.createMarker.desc, THIRDPARTY_URLS.shapeViewer), + formElements: [markerNameInput], + buttons: waypoint ? ["delete:bad", "cancel", "ok:good"] : ["cancel", "ok:good"], + }); + this.root.hud.parts.dialogs.internalShowDialog(dialog); + + // Edit marker + if (waypoint) { + dialog.buttonSignals.ok.add(() => { + // Actually rename the waypoint + this.renameWaypoint(waypoint, markerNameInput.getValue()); + }); + dialog.buttonSignals.delete.add(() => { + // Actually delete the waypoint + this.deleteWaypoint(waypoint); + }); + } else { + // Compute where to create the marker + const center = worldPos || this.root.camera.center; + + dialog.buttonSignals.ok.add(() => { + // Actually create the waypoint + this.addWaypoint(markerNameInput.getValue(), center); + }); + } + } + + /** + * Adds a new waypoint at the given location with the given label + * @param {string} label + * @param {Vector} position + */ + addWaypoint(label, position) { + this.waypoints.push({ + label, + center: { x: position.x, y: position.y }, + zoomLevel: this.root.camera.zoomLevel, + layer: this.root.currentLayer, + }); + + this.sortWaypoints(); + + // Show notification about creation + this.root.hud.signals.notification.dispatch( + T.ingame.waypoints.creationSuccessNotification, + enumNotificationType.success + ); + + // Re-render the list and thus add it + this.rerenderWaypointList(); + } + + /** + * Renames a waypoint with the given label + * @param {Waypoint} waypoint + * @param {string} label + */ + renameWaypoint(waypoint, label) { + waypoint.label = label; + + this.sortWaypoints(); + + // Show notification about renamed + this.root.hud.signals.notification.dispatch( + T.ingame.waypoints.creationSuccessNotification, + enumNotificationType.success + ); + + // Re-render the list and thus add it + this.rerenderWaypointList(); + } + + /** + * Called every frame to update stuff + */ + update() { + if (this.domAttach) { + this.domAttach.update(this.root.camera.getIsMapOverlayActive()); + } + } + + /** + * Sort waypoints by name + */ + sortWaypoints() { + this.waypoints.sort((a, b) => { + if (!a.label) { + return -1; + } + if (!b.label) { + return 1; + } + return this.getWaypointLabel(a) + .padEnd(MAX_LABEL_LENGTH, "0") + .localeCompare(this.getWaypointLabel(b).padEnd(MAX_LABEL_LENGTH, "0")); + }); + } + + /** + * Returns the label for a given waypoint + * @param {Waypoint} waypoint + * @returns {string} + */ + getWaypointLabel(waypoint) { + return waypoint.label || T.ingame.waypoints.hub; + } + + /** + * Returns if a waypoint is deletable + * @param {Waypoint} waypoint + * @returns {boolean} + */ + isWaypointDeletable(waypoint) { + return waypoint.label !== null; + } + + /** + * Returns the screen space bounds of the given waypoint or null + * if it couldn't be determined. Also returns wheter its a shape or not + * @param {Waypoint} waypoint + * @return {{ + * screenBounds: Rectangle + * item: BaseItem|null, + * text: string + * }} + */ + getWaypointScreenParams(waypoint) { + if (!this.root.camera.getIsMapOverlayActive()) { + return null; + } + + // Find parameters + const scale = this.getWaypointUiScale(); + const screenPos = this.root.camera.worldToScreen(new Vector(waypoint.center.x, waypoint.center.y)); + + // Distinguish between text and item waypoints -> Figure out parameters + const originalLabel = this.getWaypointLabel(waypoint); + let text, item, textWidth; + + if (ShapeDefinition.isValidShortKey(originalLabel)) { + // If the label is actually a key, render the shape icon + item = this.root.shapeDefinitionMgr.getShapeItemFromShortKey(originalLabel); + textWidth = 40; + } else { + // Otherwise render a regular waypoint + text = originalLabel; + textWidth = this.getTextWidth(text); + } + + return { + screenBounds: new Rectangle( + screenPos.x - 7 * scale, + screenPos.y - 12 * scale, + 15 * scale + textWidth, + 15 * scale + ), + item, + text, + }; + } + + /** + * Finds the currently intersected waypoint on the map overview under + * the cursor. + * + * @returns {Waypoint | null} + */ + findCurrentIntersectedWaypoint() { + const mousePos = this.root.app.mousePosition; + if (!mousePos) { + return; + } + + for (let i = 0; i < this.waypoints.length; ++i) { + const waypoint = this.waypoints[i]; + const params = this.getWaypointScreenParams(waypoint); + if (params && params.screenBounds.containsPoint(mousePos.x, mousePos.y)) { + return waypoint; + } + } + } + + /** + * Mouse-Down handler + * @param {Vector} pos + * @param {enumMouseButton} button + */ + onMouseDown(pos, button) { + const waypoint = this.findCurrentIntersectedWaypoint(); + if (waypoint) { + if (button === enumMouseButton.left) { + this.root.soundProxy.playUiClick(); + this.moveToWaypoint(waypoint); + } else if (button === enumMouseButton.right) { + if (this.isWaypointDeletable(waypoint)) { + this.root.soundProxy.playUiClick(); + this.requestSaveMarker({ waypoint }); + } else { + this.root.soundProxy.playUiError(); + } + } + + return STOP_PROPAGATION; + } else { + // Allow right click to create a marker + if (button === enumMouseButton.right) { + if (this.root.camera.getIsMapOverlayActive()) { + const worldPos = this.root.camera.screenToWorld(pos); + this.requestSaveMarker({ worldPos }); + return STOP_PROPAGATION; + } + } + } + } + + /** + * Rerenders the compass + */ + rerenderWaypointsCompass() { + const dims = 48; + const indicatorSize = 30; + const cameraPos = this.root.camera.center; + + const context = this.compassBuffer.context; + context.clearRect(0, 0, dims, dims); + + const distanceToHub = cameraPos.length(); + const compassVisible = distanceToHub > (10 * globalConfig.tileSize) / this.root.camera.zoomLevel; + const targetCompassAlpha = compassVisible ? 1 : 0; + + // Fade the compas in / out + this.currentCompassOpacity = lerp(this.currentCompassOpacity, targetCompassAlpha, 0.08); + + // Render the compass + if (this.currentCompassOpacity > 0.01) { + context.globalAlpha = this.currentCompassOpacity; + const angle = cameraPos.angle() + Math.radians(45) + Math.PI / 2; + context.translate(dims / 2, dims / 2); + context.rotate(angle); + this.directionIndicatorSprite.drawCentered(context, 0, 0, indicatorSize); + context.rotate(-angle); + context.translate(-dims / 2, -dims / 2); + context.globalAlpha = 1; + } + + // Render the regualr icon + const iconOpacity = 1 - this.currentCompassOpacity; + if (iconOpacity > 0.01) { + context.globalAlpha = iconOpacity; + this.waypointSprites.regular.drawCentered(context, dims / 2, dims / 2, dims * 0.7); + context.globalAlpha = 1; + } + } + + /** + * Draws the waypoints on the map + * @param {DrawParameters} parameters + */ + drawOverlays(parameters) { + const mousePos = this.root.app.mousePosition; + const desiredOpacity = this.root.camera.getIsMapOverlayActive() ? 1 : 0; + this.currentMarkerOpacity = lerp(this.currentMarkerOpacity, desiredOpacity, 0.08); + + this.rerenderWaypointsCompass(); + + // Don't render with low opacity + if (this.currentMarkerOpacity < 0.01) { + return; + } + + // Determine rendering scale + const scale = this.getWaypointUiScale(); + + // Set the font size + const textSize = this.getTextScale(); + parameters.context.font = "bold " + textSize + "px GameFont"; + parameters.context.textBaseline = "middle"; + + // Loop over all waypoints + for (let i = 0; i < this.waypoints.length; ++i) { + const waypoint = this.waypoints[i]; + + const waypointData = this.getWaypointScreenParams(waypoint); + if (!waypointData) { + // Not relevant + continue; + } + + if (!parameters.visibleRect.containsRect(waypointData.screenBounds)) { + // Out of screen + continue; + } + + const bounds = waypointData.screenBounds; + const contentPaddingX = 7 * scale; + const isSelected = mousePos && bounds.containsPoint(mousePos.x, mousePos.y); + + // Render the background rectangle + parameters.context.globalAlpha = this.currentMarkerOpacity * (isSelected ? 1 : 0.7); + parameters.context.fillStyle = "rgba(255, 255, 255, 0.7)"; + parameters.context.beginPath(); + parameters.context.roundRect(bounds.x, bounds.y, bounds.w, bounds.h, 6); + parameters.context.fill(); + + // Render the text + if (waypointData.item) { + const canvas = this.getWaypointCanvas(waypoint); + const itemSize = 14 * scale; + parameters.context.drawImage( + canvas, + bounds.x + contentPaddingX + 6 * scale, + bounds.y + bounds.h / 2 - itemSize / 2, + itemSize, + itemSize + ); + } else if (waypointData.text) { + // Render the text + parameters.context.fillStyle = "#000"; + parameters.context.textBaseline = "middle"; + parameters.context.fillText( + waypointData.text, + bounds.x + contentPaddingX + 6 * scale, + bounds.y + bounds.h / 2 + ); + parameters.context.textBaseline = "alphabetic"; + } else { + assertAlways(false, "Waypoint has no item and text"); + } + + // Render the small icon on the left + this.waypointSprites[waypoint.layer].drawCentered( + parameters.context, + bounds.x + contentPaddingX, + bounds.y + bounds.h / 2, + bounds.h * 0.6 + ); + } + + parameters.context.textBaseline = "alphabetic"; + parameters.context.globalAlpha = 1; + } +} diff --git a/src/js/game/hud/parts/wires_overlay.js b/src/js/game/hud/parts/wires_overlay.js index 328d6689..1fabb89d 100644 --- a/src/js/game/hud/parts/wires_overlay.js +++ b/src/js/game/hud/parts/wires_overlay.js @@ -1,152 +1,152 @@ -import { makeOffscreenBuffer } from "../../../core/buffer_utils"; -import { globalConfig } from "../../../core/config"; -import { DrawParameters } from "../../../core/draw_parameters"; -import { Loader } from "../../../core/loader"; -import { lerp } from "../../../core/utils"; -import { SOUNDS } from "../../../platform/sound"; -import { KEYMAPPINGS } from "../../key_action_mapper"; -import { enumHubGoalRewards } from "../../tutorial_goals"; -import { BaseHUDPart } from "../base_hud_part"; - -const copy = require("clipboard-copy"); -const wiresBackgroundDpi = 4; - -export class HUDWiresOverlay extends BaseHUDPart { - createElements(parent) {} - - initialize() { - // Probably not the best location, but the one which makes most sense - this.root.keyMapper.getBinding(KEYMAPPINGS.ingame.switchLayers).add(this.switchLayers, this); - this.root.keyMapper.getBinding(KEYMAPPINGS.placement.copyWireValue).add(this.copyWireValue, this); - - this.generateTilePattern(); - - this.currentAlpha = 0.0; - } - - /** - * Switches between layers - */ - switchLayers() { - if (!this.root.gameMode.getSupportsWires()) { - return; - } - if (this.root.currentLayer === "regular") { - if ( - this.root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_wires_painter_and_levers) || - (G_IS_DEV && globalConfig.debug.allBuildingsUnlocked) - ) { - this.root.currentLayer = "wires"; - } - } else { - this.root.currentLayer = "regular"; - } - this.root.signals.editModeChanged.dispatch(this.root.currentLayer); - } - - /** - * Generates the background pattern for the wires overlay - */ - generateTilePattern() { - const overlayTile = Loader.getSprite("sprites/wires/overlay_tile.png"); - const dims = globalConfig.tileSize * wiresBackgroundDpi; - const [canvas, context] = makeOffscreenBuffer(dims, dims, { - smooth: false, - reusable: false, - label: "wires-tile-pattern", - }); - context.clearRect(0, 0, dims, dims); - overlayTile.draw(context, 0, 0, dims, dims); - this.tilePatternCanvas = canvas; - } - - update() { - const desiredAlpha = this.root.currentLayer === "wires" ? 1.0 : 0.0; - - // On low performance, skip the fade - if (this.root.entityMgr.entities.length > 5000 || this.root.dynamicTickrate.averageFps < 50) { - this.currentAlpha = desiredAlpha; - } else { - this.currentAlpha = lerp(this.currentAlpha, desiredAlpha, 0.12); - } - } - - /** - * Copies the wires value below the cursor - */ - copyWireValue() { - if (this.root.currentLayer !== "wires") { - return; - } - - const mousePos = this.root.app.mousePosition; - if (!mousePos) { - return; - } - - const tile = this.root.camera.screenToWorld(mousePos).toTileSpace(); - const contents = this.root.map.getLayerContentXY(tile.x, tile.y, "wires"); - if (!contents) { - return; - } - - let value = null; - if (contents.components.Wire) { - const network = contents.components.Wire.linkedNetwork; - if (network && network.hasValue()) { - value = network.currentValue; - } - } - - if (contents.components.ConstantSignal) { - value = contents.components.ConstantSignal.signal; - } - - if (value) { - copy(value.getAsCopyableKey()); - this.root.soundProxy.playUi(SOUNDS.copy); - } else { - copy(""); - this.root.soundProxy.playUiError(); - } - } - - /** - * - * @param {DrawParameters} parameters - */ - draw(parameters) { - if (this.currentAlpha < 0.02) { - return; - } - - const hasTileGrid = !this.root.app.settings.getAllSettings().disableTileGrid; - if (hasTileGrid && !this.cachedPatternBackground) { - this.cachedPatternBackground = parameters.context.createPattern(this.tilePatternCanvas, "repeat"); - } - - const bounds = parameters.visibleRect; - - parameters.context.globalAlpha = this.currentAlpha; - - const scaleFactor = 1 / wiresBackgroundDpi; - parameters.context.globalCompositeOperation = "overlay"; - parameters.context.fillStyle = "rgba(50, 200, 150, 1)"; - parameters.context.fillRect(bounds.x, bounds.y, bounds.w, bounds.h); - parameters.context.globalCompositeOperation = "source-over"; - - parameters.context.scale(scaleFactor, scaleFactor); - parameters.context.fillStyle = hasTileGrid - ? this.cachedPatternBackground - : "rgba(78, 137, 125, 0.75)"; - parameters.context.fillRect( - bounds.x / scaleFactor, - bounds.y / scaleFactor, - bounds.w / scaleFactor, - bounds.h / scaleFactor - ); - parameters.context.scale(1 / scaleFactor, 1 / scaleFactor); - - parameters.context.globalAlpha = 1; - } -} +import { makeOffscreenBuffer } from "../../../core/buffer_utils"; +import { globalConfig } from "../../../core/config"; +import { DrawParameters } from "../../../core/draw_parameters"; +import { Loader } from "../../../core/loader"; +import { lerp } from "../../../core/utils"; +import { SOUNDS } from "../../../platform/sound"; +import { KEYMAPPINGS } from "../../key_action_mapper"; +import { enumHubGoalRewards } from "../../tutorial_goals"; +import { BaseHUDPart } from "../base_hud_part"; + +import copy from "clipboard-copy"; +const wiresBackgroundDpi = 4; + +export class HUDWiresOverlay extends BaseHUDPart { + createElements(parent) {} + + initialize() { + // Probably not the best location, but the one which makes most sense + this.root.keyMapper.getBinding(KEYMAPPINGS.ingame.switchLayers).add(this.switchLayers, this); + this.root.keyMapper.getBinding(KEYMAPPINGS.placement.copyWireValue).add(this.copyWireValue, this); + + this.generateTilePattern(); + + this.currentAlpha = 0.0; + } + + /** + * Switches between layers + */ + switchLayers() { + if (!this.root.gameMode.getSupportsWires()) { + return; + } + if (this.root.currentLayer === "regular") { + if ( + this.root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_wires_painter_and_levers) || + (G_IS_DEV && globalConfig.debug.allBuildingsUnlocked) + ) { + this.root.currentLayer = "wires"; + } + } else { + this.root.currentLayer = "regular"; + } + this.root.signals.editModeChanged.dispatch(this.root.currentLayer); + } + + /** + * Generates the background pattern for the wires overlay + */ + generateTilePattern() { + const overlayTile = Loader.getSprite("sprites/wires/overlay_tile.png"); + const dims = globalConfig.tileSize * wiresBackgroundDpi; + const [canvas, context] = makeOffscreenBuffer(dims, dims, { + smooth: false, + reusable: false, + label: "wires-tile-pattern", + }); + context.clearRect(0, 0, dims, dims); + overlayTile.draw(context, 0, 0, dims, dims); + this.tilePatternCanvas = canvas; + } + + update() { + const desiredAlpha = this.root.currentLayer === "wires" ? 1.0 : 0.0; + + // On low performance, skip the fade + if (this.root.entityMgr.entities.size > 5000 || this.root.dynamicTickrate.averageFps < 50) { + this.currentAlpha = desiredAlpha; + } else { + this.currentAlpha = lerp(this.currentAlpha, desiredAlpha, 0.12); + } + } + + /** + * Copies the wires value below the cursor + */ + copyWireValue() { + if (this.root.currentLayer !== "wires") { + return; + } + + const mousePos = this.root.app.mousePosition; + if (!mousePos) { + return; + } + + const tile = this.root.camera.screenToWorld(mousePos).toTileSpace(); + const contents = this.root.map.getLayerContentXY(tile.x, tile.y, "wires"); + if (!contents) { + return; + } + + let value = null; + if (contents.components.Wire) { + const network = contents.components.Wire.linkedNetwork; + if (network && network.hasValue()) { + value = network.currentValue; + } + } + + if (contents.components.ConstantSignal) { + value = contents.components.ConstantSignal.signal; + } + + if (value) { + copy(value.getAsCopyableKey()); + this.root.soundProxy.playUi(SOUNDS.copy); + } else { + copy(""); + this.root.soundProxy.playUiError(); + } + } + + /** + * + * @param {DrawParameters} parameters + */ + draw(parameters) { + if (this.currentAlpha < 0.02) { + return; + } + + const hasTileGrid = !this.root.app.settings.getAllSettings().disableTileGrid; + if (hasTileGrid && !this.cachedPatternBackground) { + this.cachedPatternBackground = parameters.context.createPattern(this.tilePatternCanvas, "repeat"); + } + + const bounds = parameters.visibleRect; + + parameters.context.globalAlpha = this.currentAlpha; + + const scaleFactor = 1 / wiresBackgroundDpi; + parameters.context.globalCompositeOperation = "overlay"; + parameters.context.fillStyle = "rgba(50, 200, 150, 1)"; + parameters.context.fillRect(bounds.x, bounds.y, bounds.w, bounds.h); + parameters.context.globalCompositeOperation = "source-over"; + + parameters.context.scale(scaleFactor, scaleFactor); + parameters.context.fillStyle = hasTileGrid + ? this.cachedPatternBackground + : "rgba(78, 137, 125, 0.75)"; + parameters.context.fillRect( + bounds.x / scaleFactor, + bounds.y / scaleFactor, + bounds.w / scaleFactor, + bounds.h / scaleFactor + ); + parameters.context.scale(1 / scaleFactor, 1 / scaleFactor); + + parameters.context.globalAlpha = 1; + } +} diff --git a/src/js/game/hud/parts/wires_toolbar.js b/src/js/game/hud/parts/wires_toolbar.js index 5141bbeb..7a7703eb 100644 --- a/src/js/game/hud/parts/wires_toolbar.js +++ b/src/js/game/hud/parts/wires_toolbar.js @@ -1,42 +1,42 @@ -import { HUDBaseToolbar } from "./base_toolbar"; -import { MetaWireBuilding } from "../../buildings/wire"; -import { MetaConstantSignalBuilding } from "../../buildings/constant_signal"; -import { MetaLogicGateBuilding } from "../../buildings/logic_gate"; -import { MetaLeverBuilding } from "../../buildings/lever"; -import { MetaWireTunnelBuilding } from "../../buildings/wire_tunnel"; -import { MetaVirtualProcessorBuilding } from "../../buildings/virtual_processor"; -import { MetaTransistorBuilding } from "../../buildings/transistor"; -import { MetaAnalyzerBuilding } from "../../buildings/analyzer"; -import { MetaComparatorBuilding } from "../../buildings/comparator"; -import { MetaReaderBuilding } from "../../buildings/reader"; -import { MetaFilterBuilding } from "../../buildings/filter"; -import { MetaDisplayBuilding } from "../../buildings/display"; -import { MetaStorageBuilding } from "../../buildings/storage"; - -export class HUDWiresToolbar extends HUDBaseToolbar { - constructor(root) { - super(root, { - primaryBuildings: [ - MetaWireBuilding, - MetaWireTunnelBuilding, - MetaConstantSignalBuilding, - MetaLogicGateBuilding, - MetaVirtualProcessorBuilding, - MetaAnalyzerBuilding, - MetaComparatorBuilding, - MetaTransistorBuilding, - ], - secondaryBuildings: [ - MetaStorageBuilding, - MetaReaderBuilding, - MetaLeverBuilding, - MetaFilterBuilding, - MetaDisplayBuilding, - ], - visibilityCondition: () => - !this.root.camera.getIsMapOverlayActive() && this.root.currentLayer === "wires", - htmlElementId: "ingame_HUD_wires_toolbar", - layer: "wires", - }); - } -} +import { HUDBaseToolbar } from "./base_toolbar"; +import { MetaWireBuilding } from "../../buildings/wire"; +import { MetaConstantSignalBuilding } from "../../buildings/constant_signal"; +import { MetaLogicGateBuilding } from "../../buildings/logic_gate"; +import { MetaLeverBuilding } from "../../buildings/lever"; +import { MetaWireTunnelBuilding } from "../../buildings/wire_tunnel"; +import { MetaVirtualProcessorBuilding } from "../../buildings/virtual_processor"; +import { MetaTransistorBuilding } from "../../buildings/transistor"; +import { MetaAnalyzerBuilding } from "../../buildings/analyzer"; +import { MetaComparatorBuilding } from "../../buildings/comparator"; +import { MetaReaderBuilding } from "../../buildings/reader"; +import { MetaFilterBuilding } from "../../buildings/filter"; +import { MetaDisplayBuilding } from "../../buildings/display"; +import { MetaStorageBuilding } from "../../buildings/storage"; + +export class HUDWiresToolbar extends HUDBaseToolbar { + constructor(root) { + super(root, { + primaryBuildings: [ + MetaWireBuilding, + MetaWireTunnelBuilding, + MetaConstantSignalBuilding, + MetaLogicGateBuilding, + MetaVirtualProcessorBuilding, + MetaAnalyzerBuilding, + MetaComparatorBuilding, + MetaTransistorBuilding, + ], + secondaryBuildings: [ + MetaStorageBuilding, + MetaReaderBuilding, + MetaLeverBuilding, + MetaFilterBuilding, + MetaDisplayBuilding, + ], + visibilityCondition: () => + !this.root.camera.getIsMapOverlayActive() && this.root.currentLayer === "wires", + htmlElementId: "ingame_HUD_wires_toolbar", + layer: "wires", + }); + } +} diff --git a/src/js/game/hud/trailer_maker.js b/src/js/game/hud/trailer_maker.js deleted file mode 100644 index e9193a93..00000000 --- a/src/js/game/hud/trailer_maker.js +++ /dev/null @@ -1,122 +0,0 @@ -import { GameRoot } from "../root"; -import { globalConfig } from "../../core/config"; -import { Vector, mixVector } from "../../core/vector"; -import { lerp } from "../../core/utils"; - -/* dev:start */ -import trailerPoints from "./trailer_points"; - -const tickrate = 1 / 165; - -export class TrailerMaker { - /** - * - * @param {GameRoot} root - */ - constructor(root) { - this.root = root; - - this.markers = []; - this.playbackMarkers = null; - this.currentPlaybackOrigin = new Vector(); - this.currentPlaybackZoom = 3; - - window.addEventListener("keydown", ev => { - if (ev.key === "j") { - console.log("Record"); - this.markers.push({ - pos: this.root.camera.center.copy(), - zoom: this.root.camera.zoomLevel, - time: 1, - wait: 0, - }); - } else if (ev.key === "k") { - console.log("Export"); - const json = JSON.stringify(this.markers); - const handle = window.open("about:blank"); - handle.document.write(json); - } else if (ev.key === "u") { - if (this.playbackMarkers && this.playbackMarkers.length > 0) { - this.playbackMarkers = []; - return; - } - console.log("Playback"); - this.playbackMarkers = trailerPoints.map(p => Object.assign({}, p)); - this.playbackMarkers.unshift(this.playbackMarkers[0]); - this.currentPlaybackOrigin = Vector.fromSerializedObject(this.playbackMarkers[0].pos); - - this.currentPlaybackZoom = this.playbackMarkers[0].zoom; - this.root.camera.center = this.currentPlaybackOrigin.copy(); - this.root.camera.zoomLevel = this.currentPlaybackZoom; - console.log("STart at", this.currentPlaybackOrigin); - - // this.root.entityMgr.getAllWithComponent(MinerComponent).forEach(miner => { - // miner.components.Miner.itemChainBuffer = []; - // miner.components.Miner.lastMiningTime = this.root.time.now() + 5; - // miner.components.ItemEjector.slots.forEach(slot => (slot.item = null)); - // }); - - // this.root.logic.tryPlaceBuilding({ - // origin: new Vector(-428, -15), - // building: gMetaBuildingRegistry.findByClass(MetaBeltBaseBuilding), - // originalRotation: 0, - // rotation: 0, - // variant: "default", - // rotationVariant: 0, - // }); - - // this.root.logic.tryPlaceBuilding({ - // origin: new Vector(-427, -15), - // building: gMetaBuildingRegistry.findByClass(MetaBeltBaseBuilding), - // originalRotation: 0, - // rotation: 0, - // variant: "default", - // rotationVariant: 0, - // }); - } - }); - } - - update() { - if (this.playbackMarkers && this.playbackMarkers.length > 0) { - const nextMarker = this.playbackMarkers[0]; - - if (!nextMarker.startTime) { - console.log("Starting to approach", nextMarker.pos); - nextMarker.startTime = performance.now() / 1000.0; - } - - const speed = - globalConfig.tileSize * - globalConfig.beltSpeedItemsPerSecond * - globalConfig.itemSpacingOnBelts; - // let time = - // this.currentPlaybackOrigin.distance(Vector.fromSerializedObject(nextMarker.pos)) / speed; - const time = nextMarker.time; - - const progress = (performance.now() / 1000.0 - nextMarker.startTime) / time; - - if (progress > 1.0) { - if (nextMarker.wait > 0) { - nextMarker.wait -= tickrate; - } else { - console.log("Approached"); - this.currentPlaybackOrigin = this.root.camera.center.copy(); - this.currentPlaybackZoom = this.root.camera.zoomLevel; - this.playbackMarkers.shift(); - } - return; - } - - const targetPos = Vector.fromSerializedObject(nextMarker.pos); - const targetZoom = nextMarker.zoom; - - const pos = mixVector(this.currentPlaybackOrigin, targetPos, progress); - const zoom = lerp(this.currentPlaybackZoom, targetZoom, progress); - this.root.camera.zoomLevel = zoom; - this.root.camera.center = pos; - } - } -} - -/* dev:end */ diff --git a/src/js/game/hud/trailer_points.js b/src/js/game/hud/trailer_points.js deleted file mode 100644 index 35a9be91..00000000 --- a/src/js/game/hud/trailer_points.js +++ /dev/null @@ -1,89 +0,0 @@ -export default [ - // // initial - // { pos: { x: -13665, y: -434 }, zoom: 6, time: 1, wait: 8 }, - - // // Go up to first curve - // { pos: { x: -13665, y: -580 }, zoom: 6, time: 1, wait: 0 }, - - // // To balancers - // { pos: { x: -13450, y: -580 }, zoom: 6, time: 1, wait: 0 }, - - // // To cutters - // { pos: { x: -13350, y: -580 }, zoom: 3, time: 1, wait: 2 }, - - // // To initial cutters - // { pos: { x: -12713, y: -580 }, zoom: 3, time: 1, wait: 2.5 }, - - // // To rotaters 3,2,1,0 - // { pos: { x: -12402, y: -580 }, zoom: 3, time: 1, wait: 0 }, - - // // Zoom in further to stackers - // { pos: { x: -12045, y: -580 }, zoom: 6, time: 1, wait: 4 }, - - // // Focus on painter - // { pos: { x: -11700, y: -660 }, zoom: 6, time: 1, wait: 3.5 }, - - // // Zoom in to mixers - // { pos: { x: -11463, y: -520 }, zoom: 6, time: 1, wait: 3.8 }, - - // // Focus to second painter - // { pos: { x: -11290, y: -610 }, zoom: 6, time: 1, wait: 1 }, - - // // Second stacker - // { pos: { x: -11022, y: -610 }, zoom: 6, time: 1, wait: 0 }, - - // // Go right until first curve - // { pos: { x: -10859, y: -650 }, zoom: 6, time: 1, wait: 0 }, - - // // Go up to stacker - // { pos: { x: -10859, y: -1120 }, zoom: 6, time: 1, wait: 0 }, - - // // Go further up - // { pos: { x: -10859, y: -1260 }, zoom: 6, time: 1, wait: 0 }, - - // // Go left - // { pos: { x: -11235, y: -1260 }, zoom: 6, time: 1, wait: 1 }, - - // OWO Savegames - // { pos: { x: -4939.356940622392, y: 71.76431237675517 }, zoom: 5.06640625, time: 1, wait: 1 }, - // { pos: { x: -4275.441641063683, y: 26.3603982512193 }, zoom: 0.45, time: 32, wait: 0 }, - - // Eve - - // { pos: { x: -277.22574043554704, y: 2151.1873666983033 }, zoom: 3.1, time: 0, wait: 2 }, - // { pos: { x: -43.64015426578788, y: 1577.5520572108883 }, zoom: 1.4, time: 16, wait: 0 }, - // { pos: { x: 133.22735227708466, y: 957.2211413984563 }, zoom: 1.4, time: 8, wait: 0 }, - // { pos: { x: 480.20365842184424, y: -313.5485044644265 }, zoom: 1.4, time: 8, wait: 0 }, - // { - // pos: { x: 452.56528647804333, y: -1341.6422407571154 }, - // zoom: 1.4, - // time: 8, - // wait: 0, - // }, - - // D - { pos: { x: -7506.562977380196, y: 1777.6671860680613 }, zoom: 2.3764616075569833, time: 0, wait: 1 }, - { pos: { x: -7506.562977380196, y: 1777.6671860680613 }, zoom: 2.3764616075569833, time: 1, wait: 0 }, - { pos: { x: -6592.471896026158, y: 1841.974816890533 }, zoom: 1.4594444847409322, time: 24, wait: 0 }, - { pos: { x: -7274.384090342281, y: 729.3783696229457 }, zoom: 1.4594444847409322, time: 24, wait: 0 }, - { pos: { x: -6048.006011617565, y: 764.6297752493597 }, zoom: 1.1853320776932916, time: 24, wait: 0 }, - { - pos: { x: -3674.7204249483366, y: 658.6366426023269 }, - zoom: 0.25332031250000003, - time: 24, - wait: 0, - }, - { - pos: { x: -1213.9916574596728, y: -1387.1496772071198 }, - zoom: 0.443058809814453, - time: 24, - wait: 0, - }, - { - pos: { x: 1722.5210292405573, y: -2457.2072755163636 }, - zoom: 0.6313986260996299, - time: 24, - wait: 0, - }, - { pos: { x: 3533.263459106946, y: -1806.6756300805193 }, zoom: 1.551908182277415, time: 24, wait: 0 }, -]; diff --git a/src/js/game/items/boolean_item.js b/src/js/game/items/boolean_item.js index 9ee3e3e5..74a90f88 100644 --- a/src/js/game/items/boolean_item.js +++ b/src/js/game/items/boolean_item.js @@ -1,110 +1,110 @@ -import { DrawParameters } from "../../core/draw_parameters"; -import { Loader } from "../../core/loader"; -import { types } from "../../savegame/serialization"; -import { BaseItem } from "../base_item"; -import { globalConfig } from "../../core/config"; - -export class BooleanItem extends BaseItem { - static getId() { - return "boolean_item"; - } - - static getSchema() { - return types.uint; - } - - serialize() { - return this.value; - } - - deserialize(data) { - this.value = data; - } - - /** @returns {"boolean"} **/ - getItemType() { - return "boolean"; - } - - /** - * @returns {string} - */ - getAsCopyableKey() { - return this.value ? "1" : "0"; - } - - /** - * @param {number} value - */ - constructor(value) { - super(); - this.value = value ? 1 : 0; - } - - /** - * @param {BaseItem} other - */ - equalsImpl(other) { - return this.value === /** @type {BooleanItem} */ (other).value; - } - - /** - * @param {number} x - * @param {number} y - * @param {number} diameter - * @param {DrawParameters} parameters - */ - drawItemCenteredImpl(x, y, parameters, diameter = globalConfig.defaultItemDiameter) { - let sprite; - if (this.value) { - sprite = Loader.getSprite("sprites/wires/boolean_true.png"); - } else { - sprite = Loader.getSprite("sprites/wires/boolean_false.png"); - } - sprite.drawCachedCentered(parameters, x, y, diameter); - } - - /** - * Draws the item to a canvas - * @param {CanvasRenderingContext2D} context - * @param {number} size - */ - drawFullSizeOnCanvas(context, size) { - let sprite; - if (this.value) { - sprite = Loader.getSprite("sprites/wires/boolean_true.png"); - } else { - sprite = Loader.getSprite("sprites/wires/boolean_false.png"); - } - sprite.drawCentered(context, size / 2, size / 2, size); - } -} - -export const BOOL_FALSE_SINGLETON = new BooleanItem(0); -export const BOOL_TRUE_SINGLETON = new BooleanItem(1); - -/** - * Returns whether the item is Boolean and TRUE - * @param {BaseItem} item - * @returns {boolean} - */ -export function isTrueItem(item) { - return item && item.getItemType() === "boolean" && !!(/** @type {BooleanItem} */ (item).value); -} - -/** - * Returns whether the item is truthy - * @param {BaseItem} item - * @returns {boolean} - */ -export function isTruthyItem(item) { - if (!item) { - return false; - } - - if (item.getItemType() === "boolean") { - return !!(/** @type {BooleanItem} */ (item).value); - } - - return true; -} +import { DrawParameters } from "../../core/draw_parameters"; +import { Loader } from "../../core/loader"; +import { types } from "../../savegame/serialization"; +import { BaseItem } from "../base_item"; +import { globalConfig } from "../../core/config"; + +export class BooleanItem extends BaseItem { + static getId() { + return "boolean_item"; + } + + static getSchema() { + return types.uint; + } + + serialize() { + return this.value; + } + + deserialize(data) { + this.value = data; + } + + /** @returns {"boolean"} **/ + getItemType() { + return "boolean"; + } + + /** + * @returns {string} + */ + getAsCopyableKey() { + return this.value ? "1" : "0"; + } + + /** + * @param {number} value + */ + constructor(value) { + super(); + this.value = value ? 1 : 0; + } + + /** + * @param {BaseItem} other + */ + equalsImpl(other) { + return this.value === /** @type {BooleanItem} */ (other).value; + } + + /** + * @param {number} x + * @param {number} y + * @param {number} diameter + * @param {DrawParameters} parameters + */ + drawItemCenteredImpl(x, y, parameters, diameter = globalConfig.defaultItemDiameter) { + let sprite; + if (this.value) { + sprite = Loader.getSprite("sprites/wires/boolean_true.png"); + } else { + sprite = Loader.getSprite("sprites/wires/boolean_false.png"); + } + sprite.drawCachedCentered(parameters, x, y, diameter); + } + + /** + * Draws the item to a canvas + * @param {CanvasRenderingContext2D} context + * @param {number} size + */ + drawFullSizeOnCanvas(context, size) { + let sprite; + if (this.value) { + sprite = Loader.getSprite("sprites/wires/boolean_true.png"); + } else { + sprite = Loader.getSprite("sprites/wires/boolean_false.png"); + } + sprite.drawCentered(context, size / 2, size / 2, size); + } +} + +export const BOOL_FALSE_SINGLETON = new BooleanItem(0); +export const BOOL_TRUE_SINGLETON = new BooleanItem(1); + +/** + * Returns whether the item is Boolean and TRUE + * @param {BaseItem} item + * @returns {boolean} + */ +export function isTrueItem(item) { + return item && item.getItemType() === "boolean" && !!(/** @type {BooleanItem} */ (item).value); +} + +/** + * Returns whether the item is truthy + * @param {BaseItem} item + * @returns {boolean} + */ +export function isTruthyItem(item) { + if (!item) { + return false; + } + + if (item.getItemType() === "boolean") { + return !!(/** @type {BooleanItem} */ (item).value); + } + + return true; +} diff --git a/src/js/game/items/color_item.js b/src/js/game/items/color_item.js index fb7f1701..785a916f 100644 --- a/src/js/game/items/color_item.js +++ b/src/js/game/items/color_item.js @@ -1,92 +1,92 @@ -import { globalConfig } from "../../core/config"; -import { DrawParameters } from "../../core/draw_parameters"; -import { Loader } from "../../core/loader"; -import { types } from "../../savegame/serialization"; -import { BaseItem } from "../base_item"; -import { enumColors } from "../colors"; -import { THEME } from "../theme"; - -export class ColorItem extends BaseItem { - static getId() { - return "color"; - } - - static getSchema() { - return types.enum(enumColors); - } - - serialize() { - return this.color; - } - - deserialize(data) { - this.color = data; - } - - /** @returns {"color"} **/ - getItemType() { - return "color"; - } - - /** - * @returns {string} - */ - getAsCopyableKey() { - return this.color; - } - - /** - * @param {BaseItem} other - */ - equalsImpl(other) { - return this.color === /** @type {ColorItem} */ (other).color; - } - - /** - * @param {enumColors} color - */ - constructor(color) { - super(); - this.color = color; - } - - getBackgroundColorAsResource() { - return THEME.map.resources[this.color]; - } - - /** - * Draws the item to a canvas - * @param {CanvasRenderingContext2D} context - * @param {number} size - */ - drawFullSizeOnCanvas(context, size) { - if (!this.cachedSprite) { - this.cachedSprite = Loader.getSprite("sprites/colors/" + this.color + ".png"); - } - this.cachedSprite.drawCentered(context, size / 2, size / 2, size); - } - - /** - * @param {number} x - * @param {number} y - * @param {number} diameter - * @param {DrawParameters} parameters - */ - drawItemCenteredClipped(x, y, parameters, diameter = globalConfig.defaultItemDiameter) { - const realDiameter = diameter * 0.6; - if (!this.cachedSprite) { - this.cachedSprite = Loader.getSprite("sprites/colors/" + this.color + ".png"); - } - this.cachedSprite.drawCachedCentered(parameters, x, y, realDiameter); - } -} - -/** - * Singleton instances - * @type {Object} - */ -export const COLOR_ITEM_SINGLETONS = {}; - -for (const color in enumColors) { - COLOR_ITEM_SINGLETONS[color] = new ColorItem(color); -} +import { globalConfig } from "../../core/config"; +import { DrawParameters } from "../../core/draw_parameters"; +import { Loader } from "../../core/loader"; +import { types } from "../../savegame/serialization"; +import { BaseItem } from "../base_item"; +import { enumColors } from "../colors"; +import { THEME } from "../theme"; + +export class ColorItem extends BaseItem { + static getId() { + return "color"; + } + + static getSchema() { + return types.enum(enumColors); + } + + serialize() { + return this.color; + } + + deserialize(data) { + this.color = data; + } + + /** @returns {"color"} **/ + getItemType() { + return "color"; + } + + /** + * @returns {string} + */ + getAsCopyableKey() { + return this.color; + } + + /** + * @param {BaseItem} other + */ + equalsImpl(other) { + return this.color === /** @type {ColorItem} */ (other).color; + } + + /** + * @param {enumColors} color + */ + constructor(color) { + super(); + this.color = color; + } + + getBackgroundColorAsResource() { + return THEME.map.resources[this.color]; + } + + /** + * Draws the item to a canvas + * @param {CanvasRenderingContext2D} context + * @param {number} size + */ + drawFullSizeOnCanvas(context, size) { + if (!this.cachedSprite) { + this.cachedSprite = Loader.getSprite("sprites/colors/" + this.color + ".png"); + } + this.cachedSprite.drawCentered(context, size / 2, size / 2, size); + } + + /** + * @param {number} x + * @param {number} y + * @param {number} diameter + * @param {DrawParameters} parameters + */ + drawItemCenteredClipped(x, y, parameters, diameter = globalConfig.defaultItemDiameter) { + const realDiameter = diameter * 0.6; + if (!this.cachedSprite) { + this.cachedSprite = Loader.getSprite("sprites/colors/" + this.color + ".png"); + } + this.cachedSprite.drawCachedCentered(parameters, x, y, realDiameter); + } +} + +/** + * Singleton instances + * @type {Object} + */ +export const COLOR_ITEM_SINGLETONS = {}; + +for (const color in enumColors) { + COLOR_ITEM_SINGLETONS[color] = new ColorItem(color); +} diff --git a/src/js/game/items/shape_item.js b/src/js/game/items/shape_item.js index d61b8f2e..08b4681d 100644 --- a/src/js/game/items/shape_item.js +++ b/src/js/game/items/shape_item.js @@ -1,78 +1,78 @@ -import { DrawParameters } from "../../core/draw_parameters"; -import { types } from "../../savegame/serialization"; -import { BaseItem } from "../base_item"; -import { ShapeDefinition } from "../shape_definition"; -import { THEME } from "../theme"; -import { globalConfig } from "../../core/config"; - -export class ShapeItem extends BaseItem { - static getId() { - return "shape"; - } - - static getSchema() { - return types.string; - } - - serialize() { - return this.definition.getHash(); - } - - deserialize(data) { - this.definition = ShapeDefinition.fromShortKey(data); - } - - /** @returns {"shape"} **/ - getItemType() { - return "shape"; - } - - /** - * @returns {string} - */ - getAsCopyableKey() { - return this.definition.getHash(); - } - - /** - * @param {BaseItem} other - */ - equalsImpl(other) { - return this.definition.getHash() === /** @type {ShapeItem} */ (other).definition.getHash(); - } - - /** - * @param {ShapeDefinition} definition - */ - constructor(definition) { - super(); - - /** - * This property must not be modified on runtime, you have to clone the class in order to change the definition - */ - this.definition = definition; - } - - getBackgroundColorAsResource() { - return THEME.map.resources.shape; - } - - /** - * Draws the item to a canvas - * @param {CanvasRenderingContext2D} context - * @param {number} size - */ - drawFullSizeOnCanvas(context, size) { - this.definition.drawFullSizeOnCanvas(context, size); - } - - /** - * @param {number} x - * @param {number} y - * @param {DrawParameters} parameters - * @param {number=} diameter - */ - drawItemCenteredImpl(x, y, parameters, diameter = globalConfig.defaultItemDiameter) { - this.definition.drawCentered(x, y, parameters, diameter); - } -} +import { DrawParameters } from "../../core/draw_parameters"; +import { types } from "../../savegame/serialization"; +import { BaseItem } from "../base_item"; +import { ShapeDefinition } from "../shape_definition"; +import { THEME } from "../theme"; +import { globalConfig } from "../../core/config"; + +export class ShapeItem extends BaseItem { + static getId() { + return "shape"; + } + + static getSchema() { + return types.string; + } + + serialize() { + return this.definition.getHash(); + } + + deserialize(data) { + this.definition = ShapeDefinition.fromShortKey(data); + } + + /** @returns {"shape"} **/ + getItemType() { + return "shape"; + } + + /** + * @returns {string} + */ + getAsCopyableKey() { + return this.definition.getHash(); + } + + /** + * @param {BaseItem} other + */ + equalsImpl(other) { + return this.definition.getHash() === /** @type {ShapeItem} */ (other).definition.getHash(); + } + + /** + * @param {ShapeDefinition} definition + */ + constructor(definition) { + super(); + + /** + * This property must not be modified on runtime, you have to clone the class in order to change the definition + */ + this.definition = definition; + } + + getBackgroundColorAsResource() { + return THEME.map.resources.shape; + } + + /** + * Draws the item to a canvas + * @param {CanvasRenderingContext2D} context + * @param {number} size + */ + drawFullSizeOnCanvas(context, size) { + this.definition.drawFullSizeOnCanvas(context, size); + } + + /** + * @param {number} x + * @param {number} y + * @param {DrawParameters} parameters + * @param {number=} diameter + */ + drawItemCenteredImpl(x, y, parameters, diameter = globalConfig.defaultItemDiameter) { + this.definition.drawCentered(x, y, parameters, diameter); + } +} diff --git a/src/js/game/key_action_mapper.js b/src/js/game/key_action_mapper.js index 3378368a..7578f948 100644 --- a/src/js/game/key_action_mapper.js +++ b/src/js/game/key_action_mapper.js @@ -1,567 +1,369 @@ -/* typehints:start */ -import { GameRoot } from "./root"; -import { InputReceiver } from "../core/input_receiver"; -import { Application } from "../application"; -/* typehints:end */ - -import { Signal, STOP_PROPAGATION } from "../core/signal"; -import { IS_MOBILE } from "../core/config"; -import { T } from "../translations"; - -export function keyToKeyCode(str) { - return str.toUpperCase().charCodeAt(0); -} - -export const KEYCODES = { - Tab: 9, - Enter: 13, - - Shift: 16, - Ctrl: 17, - Alt: 18, - - Escape: 27, - - Space: 32, - - ArrowLeft: 37, - ArrowUp: 38, - ArrowRight: 39, - ArrowDown: 40, - - Delete: 46, - - F1: 112, - F2: 113, - F3: 114, - F4: 115, - F5: 116, - F6: 117, - F7: 118, - F8: 119, - F9: 120, - F10: 121, - F11: 122, - F12: 123, - - Plus: 187, - Minus: 189, -}; - -export const KEYMAPPINGS = { - // Make sure mods come first so they can override everything - mods: {}, - - general: { - confirm: { keyCode: KEYCODES.Enter }, - back: { keyCode: KEYCODES.Escape, builtin: true }, - }, - - ingame: { - menuOpenShop: { keyCode: keyToKeyCode("F") }, - menuOpenStats: { keyCode: keyToKeyCode("G") }, - menuClose: { keyCode: keyToKeyCode("Q") }, - - toggleHud: { keyCode: KEYCODES.F2 }, - exportScreenshot: { keyCode: KEYCODES.F3 }, - toggleFPSInfo: { keyCode: KEYCODES.F4 }, - - switchLayers: { keyCode: keyToKeyCode("E") }, - - showShapeTooltip: { keyCode: KEYCODES.Alt }, - }, - - navigation: { - mapMoveUp: { keyCode: keyToKeyCode("W") }, - mapMoveRight: { keyCode: keyToKeyCode("D") }, - mapMoveDown: { keyCode: keyToKeyCode("S") }, - mapMoveLeft: { keyCode: keyToKeyCode("A") }, - mapMoveFaster: { keyCode: KEYCODES.Shift }, - - centerMap: { keyCode: KEYCODES.Space }, - mapZoomIn: { keyCode: KEYCODES.Plus, repeated: true }, - mapZoomOut: { keyCode: KEYCODES.Minus, repeated: true }, - createMarker: { keyCode: keyToKeyCode("M") }, - }, - - buildings: { - // Puzzle buildings - constant_producer: { keyCode: keyToKeyCode("H") }, - goal_acceptor: { keyCode: keyToKeyCode("N") }, - block: { keyCode: keyToKeyCode("4") }, - - // Primary Toolbar - belt: { keyCode: keyToKeyCode("1") }, - balancer: { keyCode: keyToKeyCode("2") }, - underground_belt: { keyCode: keyToKeyCode("3") }, - miner: { keyCode: keyToKeyCode("4") }, - cutter: { keyCode: keyToKeyCode("5") }, - rotater: { keyCode: keyToKeyCode("6") }, - stacker: { keyCode: keyToKeyCode("7") }, - mixer: { keyCode: keyToKeyCode("8") }, - painter: { keyCode: keyToKeyCode("9") }, - trash: { keyCode: keyToKeyCode("0") }, - - // Sandbox - item_producer: { keyCode: keyToKeyCode("L") }, - - // Secondary toolbar - storage: { keyCode: keyToKeyCode("Y") }, - reader: { keyCode: keyToKeyCode("U") }, - lever: { keyCode: keyToKeyCode("I") }, - filter: { keyCode: keyToKeyCode("O") }, - display: { keyCode: keyToKeyCode("P") }, - - // Wires toolbar - wire: { keyCode: keyToKeyCode("1") }, - wire_tunnel: { keyCode: keyToKeyCode("2") }, - constant_signal: { keyCode: keyToKeyCode("3") }, - logic_gate: { keyCode: keyToKeyCode("4") }, - virtual_processor: { keyCode: keyToKeyCode("5") }, - analyzer: { keyCode: keyToKeyCode("6") }, - comparator: { keyCode: keyToKeyCode("7") }, - transistor: { keyCode: keyToKeyCode("8") }, - }, - - placement: { - pipette: { keyCode: keyToKeyCode("Q") }, - rotateWhilePlacing: { keyCode: keyToKeyCode("R") }, - rotateInverseModifier: { keyCode: KEYCODES.Shift }, - rotateToUp: { keyCode: KEYCODES.ArrowUp }, - rotateToDown: { keyCode: KEYCODES.ArrowDown }, - rotateToRight: { keyCode: KEYCODES.ArrowRight }, - rotateToLeft: { keyCode: KEYCODES.ArrowLeft }, - cycleBuildingVariants: { keyCode: keyToKeyCode("T") }, - cycleBuildings: { keyCode: KEYCODES.Tab }, - switchDirectionLockSide: { keyCode: keyToKeyCode("R") }, - - copyWireValue: { keyCode: keyToKeyCode("Z") }, - }, - - massSelect: { - massSelectStart: { keyCode: KEYCODES.Ctrl }, - massSelectSelectMultiple: { keyCode: KEYCODES.Shift }, - massSelectCopy: { keyCode: keyToKeyCode("C") }, - massSelectCut: { keyCode: keyToKeyCode("X") }, - massSelectClear: { keyCode: keyToKeyCode("B") }, - confirmMassDelete: { keyCode: KEYCODES.Delete }, - pasteLastBlueprint: { keyCode: keyToKeyCode("V") }, - }, - - placementModifiers: { - lockBeltDirection: { keyCode: KEYCODES.Shift }, - placementDisableAutoOrientation: { keyCode: KEYCODES.Ctrl }, - placeMultiple: { keyCode: KEYCODES.Shift }, - placeInverse: { keyCode: KEYCODES.Alt }, - }, -}; - -// Assign ids -for (const categoryId in KEYMAPPINGS) { - for (const mappingId in KEYMAPPINGS[categoryId]) { - KEYMAPPINGS[categoryId][mappingId].id = mappingId; - } -} - -export const KEYCODE_LMB = 1; -export const KEYCODE_MMB = 2; -export const KEYCODE_RMB = 3; - -/** - * Returns a keycode -> string - * @param {number} code - * @returns {string} - */ -export function getStringForKeyCode(code) { - // @todo: Refactor into dictionary - switch (code) { - case KEYCODE_LMB: - return "LMB"; - case KEYCODE_MMB: - return "MMB"; - case KEYCODE_RMB: - return "RMB"; - case 4: - return "MB4"; - case 5: - return "MB5"; - case 8: - return "⌫"; - case KEYCODES.Tab: - return T.global.keys.tab; - case KEYCODES.Enter: - return "⏎"; - case KEYCODES.Shift: - return "⇪"; - case KEYCODES.Ctrl: - return T.global.keys.control; - case KEYCODES.Alt: - return T.global.keys.alt; - case 19: - return "PAUSE"; - case 20: - return "CAPS"; - case KEYCODES.Escape: - return T.global.keys.escape; - case KEYCODES.Space: - return T.global.keys.space; - case 33: - return "PGUP"; - case 34: - return "PGDOWN"; - case 35: - return "END"; - case 36: - return "HOME"; - case KEYCODES.ArrowLeft: - return "⬅"; - case KEYCODES.ArrowUp: - return "⬆"; - case KEYCODES.ArrowRight: - return "➡"; - case KEYCODES.ArrowDown: - return "⬇"; - case 44: - return "PRNT"; - case 45: - return "INS"; - case 46: - return "DEL"; - case 93: - return "SEL"; - case 96: - return "NUM 0"; - case 97: - return "NUM 1"; - case 98: - return "NUM 2"; - case 99: - return "NUM 3"; - case 100: - return "NUM 4"; - case 101: - return "NUM 5"; - case 102: - return "NUM 6"; - case 103: - return "NUM 7"; - case 104: - return "NUM 8"; - case 105: - return "NUM 9"; - case 106: - return "*"; - case 107: - return "+"; - case 109: - return "-"; - case 110: - return "."; - case 111: - return "/"; - case KEYCODES.F1: - return "F1"; - case KEYCODES.F2: - return "F2"; - case KEYCODES.F3: - return "F3"; - case KEYCODES.F4: - return "F4"; - case KEYCODES.F5: - return "F5"; - case KEYCODES.F6: - return "F6"; - case KEYCODES.F7: - return "F7"; - case KEYCODES.F8: - return "F8"; - case KEYCODES.F9: - return "F9"; - case KEYCODES.F10: - return "F10"; - case KEYCODES.F11: - return "F11"; - case KEYCODES.F12: - return "F12"; - - case 144: - return "NUMLOCK"; - case 145: - return "SCRLOCK"; - case 182: - return "COMP"; - case 183: - return "CALC"; - case 186: - return ";"; - case 187: - return "+"; - case 188: - return ","; - case 189: - return "-"; - case 190: - return "."; - case 191: - return "/"; - case 192: - return "`"; - case 219: - return "["; - case 220: - return "\\"; - case 221: - return "]"; - case 222: - return "'"; - } - - return (48 <= code && code <= 57) || (65 <= code && code <= 90) - ? String.fromCharCode(code) - : "[" + code + "]"; -} - -export class Keybinding { - /** - * - * @param {KeyActionMapper} keyMapper - * @param {Application} app - * @param {object} param0 - * @param {number} param0.keyCode - * @param {boolean=} param0.builtin - * @param {boolean=} param0.repeated - * @param {{ shift?: boolean; alt?: boolean; ctrl?: boolean; }=} param0.modifiers - */ - constructor(keyMapper, app, { keyCode, builtin = false, repeated = false, modifiers = {} }) { - assert(keyCode && Number.isInteger(keyCode), "Invalid key code: " + keyCode); - this.keyMapper = keyMapper; - this.app = app; - this.keyCode = keyCode; - this.builtin = builtin; - this.repeated = repeated; - - this.modifiers = modifiers; - - this.signal = new Signal(); - this.toggled = new Signal(); - } - - /** - * Returns whether this binding is currently pressed - * @returns {boolean} - */ - get pressed() { - // Check if the key is down - if (this.app.inputMgr.keysDown.has(this.keyCode)) { - // Check if it is the top reciever - const reciever = this.keyMapper.inputReceiver; - return this.app.inputMgr.getTopReciever() === reciever; - } - return false; - } - - /** - * Adds an event listener - * @param {function() : void} receiver - * @param {object=} scope - */ - add(receiver, scope = null) { - this.signal.add(receiver, scope); - } - - /** - * Adds an event listener - * @param {function() : void} receiver - * @param {object=} scope - */ - addToTop(receiver, scope = null) { - this.signal.addToTop(receiver, scope); - } - - /** - * @param {Element} elem - * @returns {HTMLElement} the created element, or null if the keybindings are not shown - * */ - appendLabelToElement(elem) { - if (IS_MOBILE) { - return null; - } - const spacer = document.createElement("code"); - spacer.classList.add("keybinding"); - spacer.innerHTML = getStringForKeyCode(this.keyCode); - elem.appendChild(spacer); - return spacer; - } - - /** - * Returns the key code as a nice string - */ - getKeyCodeString() { - return getStringForKeyCode(this.keyCode); - } - - /** - * Remvoes all signal receivers - */ - clearSignalReceivers() { - this.signal.removeAll(); - } -} - -export class KeyActionMapper { - /** - * - * @param {GameRoot} root - * @param {InputReceiver} inputReciever - */ - constructor(root, inputReciever) { - this.root = root; - this.inputReceiver = inputReciever; - - inputReciever.keydown.add(this.handleKeydown, this); - inputReciever.keyup.add(this.handleKeyup, this); - - /** @type {Object.} */ - this.keybindings = {}; - - const overrides = root.app.settings.getKeybindingOverrides(); - - for (const category in KEYMAPPINGS) { - for (const key in KEYMAPPINGS[category]) { - let payload = Object.assign({}, KEYMAPPINGS[category][key]); - if (overrides[key]) { - payload.keyCode = overrides[key]; - } - this.keybindings[key] = new Keybinding(this, this.root.app, payload); - - if (G_IS_DEV) { - // Sanity - if (!T.keybindings.mappings[key]) { - assertAlways(false, "Keybinding " + key + " has no translation!"); - } - } - } - } - - inputReciever.pageBlur.add(this.onPageBlur, this); - inputReciever.destroyed.add(this.cleanup, this); - } - - /** - * Returns all keybindings starting with the given id - * @param {string} pattern - * @returns {Array} - */ - getKeybindingsStartingWith(pattern) { - let result = []; - for (const key in this.keybindings) { - if (key.startsWith(pattern)) { - result.push(this.keybindings[key]); - } - } - return result; - } - - /** - * Forwards the given events to the other mapper (used in tooltips) - * @param {KeyActionMapper} receiver - * @param {Array} bindings - */ - forward(receiver, bindings) { - for (let i = 0; i < bindings.length; ++i) { - const key = bindings[i]; - this.keybindings[key].signal.add((...args) => receiver.keybindings[key].signal.dispatch(...args)); - } - } - - cleanup() { - for (const key in this.keybindings) { - this.keybindings[key].signal.removeAll(); - } - } - - onPageBlur() { - // Reset all down states - // Find mapping - for (const key in this.keybindings) { - /** @type {Keybinding} */ - const binding = this.keybindings[key]; - } - } - - /** - * Internal keydown handler - * @param {object} param0 - * @param {number} param0.keyCode - * @param {boolean} param0.shift - * @param {boolean} param0.alt - * @param {boolean} param0.ctrl - * @param {boolean=} param0.initial - */ - handleKeydown({ keyCode, shift, alt, ctrl, initial }) { - let stop = false; - - // Find mapping - for (const key in this.keybindings) { - /** @type {Keybinding} */ - const binding = this.keybindings[key]; - if (binding.keyCode === keyCode && (initial || binding.repeated)) { - if (binding.modifiers.shift && !shift) { - continue; - } - - if (binding.modifiers.ctrl && !ctrl) { - continue; - } - - if (binding.modifiers.alt && !alt) { - continue; - } - - /** @type {Signal} */ - const signal = this.keybindings[key].signal; - if (signal.dispatch() === STOP_PROPAGATION) { - return; - } - } - } - - if (stop) { - return STOP_PROPAGATION; - } - } - - /** - * Internal keyup handler - * @param {object} param0 - * @param {number} param0.keyCode - * @param {boolean} param0.shift - * @param {boolean} param0.alt - */ - handleKeyup({ keyCode, shift, alt }) { - // Empty - } - - /** - * Returns a given keybinding - * @param {{ keyCode: number }} binding - * @returns {Keybinding} - */ - getBinding(binding) { - // @ts-ignore - const id = binding.id; - assert(id, "Not a valid keybinding: " + JSON.stringify(binding)); - assert(this.keybindings[id], "Keybinding " + id + " not known!"); - return this.keybindings[id]; - } - - /** - * Returns a given keybinding - * @param {string} id - * @returns {Keybinding} - */ - getBindingById(id) { - assert(this.keybindings[id], "Keybinding " + id + " not known!"); - return this.keybindings[id]; - } -} +/* typehints:start */ +import { Application } from "../application"; +import { InputReceiver } from "../core/input_receiver"; +import { GameRoot } from "./root"; +/* typehints:end */ + +import { getStringForKeyCode, KEYCODES, keyToKeyCode } from "@/core/keycodes"; +import { IS_MOBILE } from "../core/config"; +import { Signal, STOP_PROPAGATION } from "../core/signal"; +import { T } from "../translations"; + +export const KEYMAPPINGS = { + // Make sure mods come first so they can override everything + mods: {}, + + general: { + confirm: { keyCode: KEYCODES.Enter }, + back: { keyCode: KEYCODES.Escape, builtin: true }, + }, + + ingame: { + menuOpenShop: { keyCode: keyToKeyCode("F") }, + menuOpenStats: { keyCode: keyToKeyCode("G") }, + menuClose: { keyCode: keyToKeyCode("Q") }, + + toggleHud: { keyCode: KEYCODES.F2 }, + exportScreenshot: { keyCode: KEYCODES.F3 }, + toggleFPSInfo: { keyCode: KEYCODES.F4 }, + + switchLayers: { keyCode: keyToKeyCode("E") }, + + showShapeTooltip: { keyCode: KEYCODES.Alt }, + }, + + navigation: { + mapMoveUp: { keyCode: keyToKeyCode("W") }, + mapMoveRight: { keyCode: keyToKeyCode("D") }, + mapMoveDown: { keyCode: keyToKeyCode("S") }, + mapMoveLeft: { keyCode: keyToKeyCode("A") }, + mapMoveFaster: { keyCode: KEYCODES.Shift }, + + centerMap: { keyCode: KEYCODES.Space }, + mapZoomIn: { keyCode: KEYCODES.Plus, repeated: true }, + mapZoomOut: { keyCode: KEYCODES.Minus, repeated: true }, + createMarker: { keyCode: keyToKeyCode("M") }, + }, + + buildings: { + // Puzzle buildings + constant_producer: { keyCode: keyToKeyCode("H") }, + goal_acceptor: { keyCode: keyToKeyCode("N") }, + block: { keyCode: keyToKeyCode("4") }, + + // Primary Toolbar + belt: { keyCode: keyToKeyCode("1") }, + balancer: { keyCode: keyToKeyCode("2") }, + underground_belt: { keyCode: keyToKeyCode("3") }, + miner: { keyCode: keyToKeyCode("4") }, + cutter: { keyCode: keyToKeyCode("5") }, + rotator: { keyCode: keyToKeyCode("6") }, + stacker: { keyCode: keyToKeyCode("7") }, + mixer: { keyCode: keyToKeyCode("8") }, + painter: { keyCode: keyToKeyCode("9") }, + trash: { keyCode: keyToKeyCode("0") }, + + // Sandbox + item_producer: { keyCode: keyToKeyCode("L") }, + + // Secondary toolbar + storage: { keyCode: keyToKeyCode("Y") }, + reader: { keyCode: keyToKeyCode("U") }, + lever: { keyCode: keyToKeyCode("I") }, + filter: { keyCode: keyToKeyCode("O") }, + display: { keyCode: keyToKeyCode("P") }, + + // Wires toolbar + wire: { keyCode: keyToKeyCode("1") }, + wire_tunnel: { keyCode: keyToKeyCode("2") }, + constant_signal: { keyCode: keyToKeyCode("3") }, + logic_gate: { keyCode: keyToKeyCode("4") }, + virtual_processor: { keyCode: keyToKeyCode("5") }, + analyzer: { keyCode: keyToKeyCode("6") }, + comparator: { keyCode: keyToKeyCode("7") }, + transistor: { keyCode: keyToKeyCode("8") }, + }, + + placement: { + pipette: { keyCode: keyToKeyCode("Q") }, + rotateWhilePlacing: { keyCode: keyToKeyCode("R") }, + rotateInverseModifier: { keyCode: KEYCODES.Shift }, + rotateToUp: { keyCode: KEYCODES.ArrowUp }, + rotateToDown: { keyCode: KEYCODES.ArrowDown }, + rotateToRight: { keyCode: KEYCODES.ArrowRight }, + rotateToLeft: { keyCode: KEYCODES.ArrowLeft }, + cycleBuildingVariants: { keyCode: keyToKeyCode("T") }, + cycleBuildings: { keyCode: KEYCODES.Tab }, + switchDirectionLockSide: { keyCode: keyToKeyCode("R") }, + + copyWireValue: { keyCode: keyToKeyCode("Z") }, + }, + + massSelect: { + massSelectStart: { keyCode: KEYCODES.Ctrl }, + massSelectSelectMultiple: { keyCode: KEYCODES.Shift }, + massSelectCopy: { keyCode: keyToKeyCode("C") }, + massSelectCut: { keyCode: keyToKeyCode("X") }, + massSelectClear: { keyCode: keyToKeyCode("B") }, + confirmMassDelete: { keyCode: KEYCODES.Delete }, + pasteLastBlueprint: { keyCode: keyToKeyCode("V") }, + }, + + placementModifiers: { + lockBeltDirection: { keyCode: KEYCODES.Shift }, + placementDisableAutoOrientation: { keyCode: KEYCODES.Ctrl }, + placeMultiple: { keyCode: KEYCODES.Shift }, + placeInverse: { keyCode: KEYCODES.Alt }, + }, +}; + +// Assign ids +for (const categoryId in KEYMAPPINGS) { + for (const mappingId in KEYMAPPINGS[categoryId]) { + KEYMAPPINGS[categoryId][mappingId].id = mappingId; + } +} + +export class Keybinding { + /** + * + * @param {KeyActionMapper} keyMapper + * @param {Application} app + * @param {object} param0 + * @param {number} param0.keyCode + * @param {boolean=} param0.builtin + * @param {boolean=} param0.repeated + * @param {{ shift?: boolean; alt?: boolean; ctrl?: boolean; }=} param0.modifiers + */ + constructor(keyMapper, app, { keyCode, builtin = false, repeated = false, modifiers = {} }) { + assert(keyCode && Number.isInteger(keyCode), "Invalid key code: " + keyCode); + this.keyMapper = keyMapper; + this.app = app; + this.keyCode = keyCode; + this.builtin = builtin; + this.repeated = repeated; + + this.modifiers = modifiers; + + this.signal = new Signal(); + this.toggled = new Signal(); + } + + /** + * Returns whether this binding is currently pressed + * @returns {boolean} + */ + get pressed() { + // Check if the key is down + if (this.app.inputMgr.keysDown.has(this.keyCode)) { + // Check if it is the top receiver + const receiver = this.keyMapper.inputReceiver; + return this.app.inputMgr.getTopReceiver() === receiver; + } + return false; + } + + /** + * Adds an event listener + * @param {function() : void} receiver + * @param {object=} scope + */ + add(receiver, scope = null) { + this.signal.add(receiver, scope); + } + + /** + * Adds an event listener + * @param {function() : void} receiver + * @param {object=} scope + */ + addToTop(receiver, scope = null) { + this.signal.addToTop(receiver, scope); + } + + /** + * @param {Element} elem + * @returns {HTMLElement} the created element, or null if the keybindings are not shown + * */ + appendLabelToElement(elem) { + if (IS_MOBILE) { + return null; + } + const spacer = document.createElement("kbd"); + spacer.innerHTML = getStringForKeyCode(this.keyCode); + elem.appendChild(spacer); + return spacer; + } + + /** + * Returns the key code as a nice string + */ + getKeyCodeString() { + return getStringForKeyCode(this.keyCode); + } + + /** + * Remvoes all signal receivers + */ + clearSignalReceivers() { + this.signal.removeAll(); + } +} + +export class KeyActionMapper { + /** + * + * @param {GameRoot} root + * @param {InputReceiver} inputReceiver + */ + constructor(root, inputReceiver) { + this.root = root; + this.inputReceiver = inputReceiver; + + inputReceiver.keydown.add(this.handleKeydown, this); + inputReceiver.keyup.add(this.handleKeyup, this); + + /** @type {Object.} */ + this.keybindings = {}; + + const overrides = root.app.settings.getKeybindingOverrides(); + + for (const category in KEYMAPPINGS) { + for (const key in KEYMAPPINGS[category]) { + let payload = Object.assign({}, KEYMAPPINGS[category][key]); + if (overrides[key]) { + payload.keyCode = overrides[key]; + } + this.keybindings[key] = new Keybinding(this, this.root.app, payload); + + if (G_IS_DEV) { + // Sanity + if (!T.keybindings.mappings[key]) { + assertAlways(false, "Keybinding " + key + " has no translation!"); + } + } + } + } + + inputReceiver.pageBlur.add(this.onPageBlur, this); + inputReceiver.destroyed.add(this.cleanup, this); + } + + /** + * Returns all keybindings starting with the given id + * @param {string} pattern + * @returns {Array} + */ + getKeybindingsStartingWith(pattern) { + let result = []; + for (const key in this.keybindings) { + if (key.startsWith(pattern)) { + result.push(this.keybindings[key]); + } + } + return result; + } + + /** + * Forwards the given events to the other mapper (used in tooltips) + * @param {KeyActionMapper} receiver + * @param {Array} bindings + */ + forward(receiver, bindings) { + for (let i = 0; i < bindings.length; ++i) { + const key = bindings[i]; + this.keybindings[key].signal.add((...args) => receiver.keybindings[key].signal.dispatch(...args)); + } + } + + cleanup() { + for (const key in this.keybindings) { + this.keybindings[key].signal.removeAll(); + } + } + + onPageBlur() { + // Reset all down states + // Find mapping + for (const key in this.keybindings) { + /** @type {Keybinding} */ + const binding = this.keybindings[key]; + } + } + + /** + * Internal keydown handler + * @param {object} param0 + * @param {number} param0.keyCode + * @param {boolean} param0.shift + * @param {boolean} param0.alt + * @param {boolean} param0.ctrl + * @param {boolean=} param0.initial + */ + handleKeydown({ keyCode, shift, alt, ctrl, initial }) { + let stop = false; + + // Find mapping + for (const key in this.keybindings) { + /** @type {Keybinding} */ + const binding = this.keybindings[key]; + if (binding.keyCode === keyCode && (initial || binding.repeated)) { + if (binding.modifiers.shift && !shift) { + continue; + } + + if (binding.modifiers.ctrl && !ctrl) { + continue; + } + + if (binding.modifiers.alt && !alt) { + continue; + } + + /** @type {Signal} */ + const signal = this.keybindings[key].signal; + if (signal.dispatch() === STOP_PROPAGATION) { + return; + } + } + } + + if (stop) { + return STOP_PROPAGATION; + } + } + + /** + * Internal keyup handler + * @param {object} param0 + * @param {number} param0.keyCode + * @param {boolean} param0.shift + * @param {boolean} param0.alt + */ + handleKeyup({ keyCode, shift, alt }) { + // Empty + } + + /** + * Returns a given keybinding + * @param {{ keyCode: number }} binding + * @returns {Keybinding} + */ + getBinding(binding) { + // @ts-ignore + const id = binding.id; + assert(id, "Not a valid keybinding: " + JSON.stringify(binding)); + assert(this.keybindings[id], "Keybinding " + id + " not known!"); + return this.keybindings[id]; + } + + /** + * Returns a given keybinding + * @param {string} id + * @returns {Keybinding} + */ + getBindingById(id) { + assert(this.keybindings[id], "Keybinding " + id + " not known!"); + return this.keybindings[id]; + } +} diff --git a/src/js/game/logic.js b/src/js/game/logic.js index 3fdc871e..dd5bec01 100644 --- a/src/js/game/logic.js +++ b/src/js/game/logic.js @@ -4,7 +4,6 @@ import { STOP_PROPAGATION } from "../core/signal"; import { round2Digits } from "../core/utils"; import { enumDirection, enumDirectionToVector, enumInvertedDirections, Vector } from "../core/vector"; import { getBuildingDataFromCode } from "./building_codes"; -import { Component } from "./component"; import { enumWireVariant } from "./components/wire"; import { Entity } from "./entity"; import { CHUNK_OVERLAY_RES } from "./map_chunk_view"; @@ -473,9 +472,9 @@ export class GameLogic { * Clears all belts and items */ clearAllBeltsAndItems() { - for (const entity of this.root.entityMgr.entities) { + for (const entity of this.root.entityMgr.entities.values()) { for (const component of Object.values(entity.components)) { - /** @type {Component} */ (component).clear(); + /** @type {import("./component").Component} */ (component).clear(); } } } diff --git a/src/js/game/map.js b/src/js/game/map.js index 67df7db3..f235e61d 100644 --- a/src/js/game/map.js +++ b/src/js/game/map.js @@ -1,275 +1,275 @@ -import { globalConfig } from "../core/config"; -import { Vector } from "../core/vector"; -import { BasicSerializableObject, types } from "../savegame/serialization"; -import { BaseItem } from "./base_item"; -import { Entity } from "./entity"; -import { MapChunkAggregate } from "./map_chunk_aggregate"; -import { MapChunkView } from "./map_chunk_view"; -import { GameRoot } from "./root"; - -export class BaseMap extends BasicSerializableObject { - static getId() { - return "Map"; - } - - static getSchema() { - return { - seed: types.uint, - }; - } - - /** - * - * @param {GameRoot} root - */ - constructor(root) { - super(); - this.root = root; - - this.seed = 0; - - /** - * Mapping of 'X|Y' to chunk - * @type {Map} */ - this.chunksById = new Map(); - - /** - * Mapping of 'X|Y' to chunk aggregate - * @type {Map} */ - this.aggregatesById = new Map(); - } - - /** - * Returns the given chunk by index - * @param {number} chunkX - * @param {number} chunkY - */ - getChunk(chunkX, chunkY, createIfNotExistent = false) { - const chunkIdentifier = chunkX + "|" + chunkY; - let storedChunk; - - if ((storedChunk = this.chunksById.get(chunkIdentifier))) { - return storedChunk; - } - - if (createIfNotExistent) { - const instance = new MapChunkView(this.root, chunkX, chunkY); - this.chunksById.set(chunkIdentifier, instance); - return instance; - } - - return null; - } - - /** - * Returns the chunk aggregate containing a given chunk - * @param {number} chunkX - * @param {number} chunkY - */ - getAggregateForChunk(chunkX, chunkY, createIfNotExistent = false) { - const aggX = Math.floor(chunkX / globalConfig.chunkAggregateSize); - const aggY = Math.floor(chunkY / globalConfig.chunkAggregateSize); - return this.getAggregate(aggX, aggY, createIfNotExistent); - } - - /** - * Returns the given chunk aggregate by index - * @param {number} aggX - * @param {number} aggY - */ - getAggregate(aggX, aggY, createIfNotExistent = false) { - const aggIdentifier = aggX + "|" + aggY; - let storedAggregate; - - if ((storedAggregate = this.aggregatesById.get(aggIdentifier))) { - return storedAggregate; - } - - if (createIfNotExistent) { - const instance = new MapChunkAggregate(this.root, aggX, aggY); - this.aggregatesById.set(aggIdentifier, instance); - return instance; - } - - return null; - } - - /** - * Gets or creates a new chunk if not existent for the given tile - * @param {number} tileX - * @param {number} tileY - * @returns {MapChunkView} - */ - getOrCreateChunkAtTile(tileX, tileY) { - const chunkX = Math.floor(tileX / globalConfig.mapChunkSize); - const chunkY = Math.floor(tileY / globalConfig.mapChunkSize); - return this.getChunk(chunkX, chunkY, true); - } - - /** - * Gets a chunk if not existent for the given tile - * @param {number} tileX - * @param {number} tileY - * @returns {MapChunkView?} - */ - getChunkAtTileOrNull(tileX, tileY) { - const chunkX = Math.floor(tileX / globalConfig.mapChunkSize); - const chunkY = Math.floor(tileY / globalConfig.mapChunkSize); - return this.getChunk(chunkX, chunkY, false); - } - - /** - * Checks if a given tile is within the map bounds - * @param {Vector} tile - * @returns {boolean} - */ - isValidTile(tile) { - if (G_IS_DEV) { - assert(tile instanceof Vector, "tile is not a vector"); - } - return Number.isInteger(tile.x) && Number.isInteger(tile.y); - } - - /** - * Returns the tile content of a given tile - * @param {Vector} tile - * @param {Layer} layer - * @returns {Entity} Entity or null - */ - getTileContent(tile, layer) { - if (G_IS_DEV) { - this.internalCheckTile(tile); - } - const chunk = this.getChunkAtTileOrNull(tile.x, tile.y); - return chunk && chunk.getLayerContentFromWorldCoords(tile.x, tile.y, layer); - } - - /** - * Returns the lower layers content of the given tile - * @param {number} x - * @param {number} y - * @returns {BaseItem=} - */ - getLowerLayerContentXY(x, y) { - return this.getOrCreateChunkAtTile(x, y).getLowerLayerFromWorldCoords(x, y); - } - - /** - * Returns the tile content of a given tile - * @param {number} x - * @param {number} y - * @param {Layer} layer - * @returns {Entity} Entity or null - */ - getLayerContentXY(x, y, layer) { - const chunk = this.getChunkAtTileOrNull(x, y); - return chunk && chunk.getLayerContentFromWorldCoords(x, y, layer); - } - - /** - * Returns the tile contents of a given tile - * @param {number} x - * @param {number} y - * @returns {Array} Entity or null - */ - getLayersContentsMultipleXY(x, y) { - const chunk = this.getChunkAtTileOrNull(x, y); - if (!chunk) { - return []; - } - return chunk.getLayersContentsMultipleFromWorldCoords(x, y); - } - - /** - * Checks if the tile is used - * @param {Vector} tile - * @param {Layer} layer - * @returns {boolean} - */ - isTileUsed(tile, layer) { - if (G_IS_DEV) { - this.internalCheckTile(tile); - } - const chunk = this.getChunkAtTileOrNull(tile.x, tile.y); - return chunk && chunk.getLayerContentFromWorldCoords(tile.x, tile.y, layer) != null; - } - - /** - * Checks if the tile is used - * @param {number} x - * @param {number} y - * @param {Layer} layer - * @returns {boolean} - */ - isTileUsedXY(x, y, layer) { - const chunk = this.getChunkAtTileOrNull(x, y); - return chunk && chunk.getLayerContentFromWorldCoords(x, y, layer) != null; - } - - /** - * Sets the tiles content - * @param {Vector} tile - * @param {Entity} entity - */ - setTileContent(tile, entity) { - if (G_IS_DEV) { - this.internalCheckTile(tile); - } - - this.getOrCreateChunkAtTile(tile.x, tile.y).setLayerContentFromWorldCords( - tile.x, - tile.y, - entity, - entity.layer - ); - - const staticComponent = entity.components.StaticMapEntity; - assert(staticComponent, "Can only place static map entities in tiles"); - } - - /** - * Places an entity with the StaticMapEntity component - * @param {Entity} entity - */ - placeStaticEntity(entity) { - assert(entity.components.StaticMapEntity, "Entity is not static"); - const staticComp = entity.components.StaticMapEntity; - const rect = staticComp.getTileSpaceBounds(); - for (let dx = 0; dx < rect.w; ++dx) { - for (let dy = 0; dy < rect.h; ++dy) { - const x = rect.x + dx; - const y = rect.y + dy; - this.getOrCreateChunkAtTile(x, y).setLayerContentFromWorldCords(x, y, entity, entity.layer); - } - } - } - - /** - * Removes an entity with the StaticMapEntity component - * @param {Entity} entity - */ - removeStaticEntity(entity) { - assert(entity.components.StaticMapEntity, "Entity is not static"); - const staticComp = entity.components.StaticMapEntity; - const rect = staticComp.getTileSpaceBounds(); - for (let dx = 0; dx < rect.w; ++dx) { - for (let dy = 0; dy < rect.h; ++dy) { - const x = rect.x + dx; - const y = rect.y + dy; - this.getOrCreateChunkAtTile(x, y).setLayerContentFromWorldCords(x, y, null, entity.layer); - } - } - } - - // Internal - - /** - * Checks a given tile for validty - * @param {Vector} tile - */ - internalCheckTile(tile) { - assert(tile instanceof Vector, "tile is not a vector: " + tile); - assert(tile.x % 1 === 0, "Tile X is not a valid integer: " + tile.x); - assert(tile.y % 1 === 0, "Tile Y is not a valid integer: " + tile.y); - } -} +import { globalConfig } from "../core/config"; +import { Vector } from "../core/vector"; +import { BasicSerializableObject, types } from "../savegame/serialization"; +import { BaseItem } from "./base_item"; +import { Entity } from "./entity"; +import { MapChunkAggregate } from "./map_chunk_aggregate"; +import { MapChunkView } from "./map_chunk_view"; +import { GameRoot } from "./root"; + +export class BaseMap extends BasicSerializableObject { + static getId() { + return "Map"; + } + + static getSchema() { + return { + seed: types.uint, + }; + } + + /** + * + * @param {GameRoot} root + */ + constructor(root) { + super(); + this.root = root; + + this.seed = 0; + + /** + * Mapping of 'X|Y' to chunk + * @type {Map} */ + this.chunksById = new Map(); + + /** + * Mapping of 'X|Y' to chunk aggregate + * @type {Map} */ + this.aggregatesById = new Map(); + } + + /** + * Returns the given chunk by index + * @param {number} chunkX + * @param {number} chunkY + */ + getChunk(chunkX, chunkY, createIfNotExistent = false) { + const chunkIdentifier = chunkX + "|" + chunkY; + let storedChunk; + + if ((storedChunk = this.chunksById.get(chunkIdentifier))) { + return storedChunk; + } + + if (createIfNotExistent) { + const instance = new MapChunkView(this.root, chunkX, chunkY); + this.chunksById.set(chunkIdentifier, instance); + return instance; + } + + return null; + } + + /** + * Returns the chunk aggregate containing a given chunk + * @param {number} chunkX + * @param {number} chunkY + */ + getAggregateForChunk(chunkX, chunkY, createIfNotExistent = false) { + const aggX = Math.floor(chunkX / globalConfig.chunkAggregateSize); + const aggY = Math.floor(chunkY / globalConfig.chunkAggregateSize); + return this.getAggregate(aggX, aggY, createIfNotExistent); + } + + /** + * Returns the given chunk aggregate by index + * @param {number} aggX + * @param {number} aggY + */ + getAggregate(aggX, aggY, createIfNotExistent = false) { + const aggIdentifier = aggX + "|" + aggY; + let storedAggregate; + + if ((storedAggregate = this.aggregatesById.get(aggIdentifier))) { + return storedAggregate; + } + + if (createIfNotExistent) { + const instance = new MapChunkAggregate(this.root, aggX, aggY); + this.aggregatesById.set(aggIdentifier, instance); + return instance; + } + + return null; + } + + /** + * Gets or creates a new chunk if not existent for the given tile + * @param {number} tileX + * @param {number} tileY + * @returns {MapChunkView} + */ + getOrCreateChunkAtTile(tileX, tileY) { + const chunkX = Math.floor(tileX / globalConfig.mapChunkSize); + const chunkY = Math.floor(tileY / globalConfig.mapChunkSize); + return this.getChunk(chunkX, chunkY, true); + } + + /** + * Gets a chunk if not existent for the given tile + * @param {number} tileX + * @param {number} tileY + * @returns {MapChunkView?} + */ + getChunkAtTileOrNull(tileX, tileY) { + const chunkX = Math.floor(tileX / globalConfig.mapChunkSize); + const chunkY = Math.floor(tileY / globalConfig.mapChunkSize); + return this.getChunk(chunkX, chunkY, false); + } + + /** + * Checks if a given tile is within the map bounds + * @param {Vector} tile + * @returns {boolean} + */ + isValidTile(tile) { + if (G_IS_DEV) { + assert(tile instanceof Vector, "tile is not a vector"); + } + return Number.isInteger(tile.x) && Number.isInteger(tile.y); + } + + /** + * Returns the tile content of a given tile + * @param {Vector} tile + * @param {Layer} layer + * @returns {Entity} Entity or null + */ + getTileContent(tile, layer) { + if (G_IS_DEV) { + this.internalCheckTile(tile); + } + const chunk = this.getChunkAtTileOrNull(tile.x, tile.y); + return chunk && chunk.getLayerContentFromWorldCoords(tile.x, tile.y, layer); + } + + /** + * Returns the lower layers content of the given tile + * @param {number} x + * @param {number} y + * @returns {BaseItem=} + */ + getLowerLayerContentXY(x, y) { + return this.getOrCreateChunkAtTile(x, y).getLowerLayerFromWorldCoords(x, y); + } + + /** + * Returns the tile content of a given tile + * @param {number} x + * @param {number} y + * @param {Layer} layer + * @returns {Entity} Entity or null + */ + getLayerContentXY(x, y, layer) { + const chunk = this.getChunkAtTileOrNull(x, y); + return chunk && chunk.getLayerContentFromWorldCoords(x, y, layer); + } + + /** + * Returns the tile contents of a given tile + * @param {number} x + * @param {number} y + * @returns {Array} Entity or null + */ + getLayersContentsMultipleXY(x, y) { + const chunk = this.getChunkAtTileOrNull(x, y); + if (!chunk) { + return []; + } + return chunk.getLayersContentsMultipleFromWorldCoords(x, y); + } + + /** + * Checks if the tile is used + * @param {Vector} tile + * @param {Layer} layer + * @returns {boolean} + */ + isTileUsed(tile, layer) { + if (G_IS_DEV) { + this.internalCheckTile(tile); + } + const chunk = this.getChunkAtTileOrNull(tile.x, tile.y); + return chunk && chunk.getLayerContentFromWorldCoords(tile.x, tile.y, layer) != null; + } + + /** + * Checks if the tile is used + * @param {number} x + * @param {number} y + * @param {Layer} layer + * @returns {boolean} + */ + isTileUsedXY(x, y, layer) { + const chunk = this.getChunkAtTileOrNull(x, y); + return chunk && chunk.getLayerContentFromWorldCoords(x, y, layer) != null; + } + + /** + * Sets the tiles content + * @param {Vector} tile + * @param {Entity} entity + */ + setTileContent(tile, entity) { + if (G_IS_DEV) { + this.internalCheckTile(tile); + } + + this.getOrCreateChunkAtTile(tile.x, tile.y).setLayerContentFromWorldCords( + tile.x, + tile.y, + entity, + entity.layer + ); + + const staticComponent = entity.components.StaticMapEntity; + assert(staticComponent, "Can only place static map entities in tiles"); + } + + /** + * Places an entity with the StaticMapEntity component + * @param {Entity} entity + */ + placeStaticEntity(entity) { + assert(entity.components.StaticMapEntity, "Entity is not static"); + const staticComp = entity.components.StaticMapEntity; + const rect = staticComp.getTileSpaceBounds(); + for (let dx = 0; dx < rect.w; ++dx) { + for (let dy = 0; dy < rect.h; ++dy) { + const x = rect.x + dx; + const y = rect.y + dy; + this.getOrCreateChunkAtTile(x, y).setLayerContentFromWorldCords(x, y, entity, entity.layer); + } + } + } + + /** + * Removes an entity with the StaticMapEntity component + * @param {Entity} entity + */ + removeStaticEntity(entity) { + assert(entity.components.StaticMapEntity, "Entity is not static"); + const staticComp = entity.components.StaticMapEntity; + const rect = staticComp.getTileSpaceBounds(); + for (let dx = 0; dx < rect.w; ++dx) { + for (let dy = 0; dy < rect.h; ++dy) { + const x = rect.x + dx; + const y = rect.y + dy; + this.getOrCreateChunkAtTile(x, y).setLayerContentFromWorldCords(x, y, null, entity.layer); + } + } + } + + // Internal + + /** + * Checks a given tile for validty + * @param {Vector} tile + */ + internalCheckTile(tile) { + assert(tile instanceof Vector, "tile is not a vector: " + tile); + assert(tile.x % 1 === 0, "Tile X is not a valid integer: " + tile.x); + assert(tile.y % 1 === 0, "Tile Y is not a valid integer: " + tile.y); + } +} diff --git a/src/js/game/map_chunk_aggregate.js b/src/js/game/map_chunk_aggregate.js index f47ed676..130de77a 100644 --- a/src/js/game/map_chunk_aggregate.js +++ b/src/js/game/map_chunk_aggregate.js @@ -106,19 +106,19 @@ export class MapChunkAggregate { }); const dims = globalConfig.mapChunkWorldSize * globalConfig.chunkAggregateSize; - const extrude = 0.05; // Draw chunk "pixel" art parameters.context.imageSmoothingEnabled = false; drawSpriteClipped({ parameters, sprite, - x: this.x * dims - extrude, - y: this.y * dims - extrude, - w: dims + 2 * extrude, - h: dims + 2 * extrude, + x: this.x * dims, + y: this.y * dims, + w: dims, + h: dims, originalW: aggregateOverlaySize, originalH: aggregateOverlaySize, + pixelAligned: true, }); parameters.context.imageSmoothingEnabled = true; diff --git a/src/js/game/map_chunk_view.js b/src/js/game/map_chunk_view.js index 86c14fb8..2af81ba5 100644 --- a/src/js/game/map_chunk_view.js +++ b/src/js/game/map_chunk_view.js @@ -1,307 +1,307 @@ -import { globalConfig } from "../core/config"; -import { DrawParameters } from "../core/draw_parameters"; -import { getBuildingDataFromCode } from "./building_codes"; -import { Entity } from "./entity"; -import { MapChunk } from "./map_chunk"; -import { GameRoot } from "./root"; -import { THEME } from "./theme"; - -export const CHUNK_OVERLAY_RES = 3; - -export const MOD_CHUNK_DRAW_HOOKS = { - backgroundLayerBefore: [], - backgroundLayerAfter: [], - - foregroundDynamicBefore: [], - foregroundDynamicAfter: [], - - staticBefore: [], - staticAfter: [], -}; - -export class MapChunkView extends MapChunk { - /** - * - * @param {GameRoot} root - * @param {number} x - * @param {number} y - */ - constructor(root, x, y) { - super(root, x, y); - - /** - * Whenever something changes, we increase this number - so we know we need to redraw - */ - this.renderIteration = 0; - - this.markDirty(); - } - - /** - * Marks this chunk as dirty, rerendering all caches - */ - markDirty() { - ++this.renderIteration; - this.renderKey = this.x + "/" + this.y + "@" + this.renderIteration; - this.root.map.getAggregateForChunk(this.x, this.y, true).markDirty(this.x, this.y); - } - - /** - * Draws the background layer - * @param {DrawParameters} parameters - */ - drawBackgroundLayer(parameters) { - const systems = this.root.systemMgr.systems; - - MOD_CHUNK_DRAW_HOOKS.backgroundLayerBefore.forEach(systemId => - systems[systemId].drawChunk(parameters, this) - ); - - if (systems.zone) { - systems.zone.drawChunk(parameters, this); - } - - if (this.root.gameMode.hasResources()) { - systems.mapResources.drawChunk(parameters, this); - } - - systems.beltUnderlays.drawChunk(parameters, this); - systems.belt.drawChunk(parameters, this); - - MOD_CHUNK_DRAW_HOOKS.backgroundLayerAfter.forEach(systemId => - systems[systemId].drawChunk(parameters, this) - ); - } - - /** - * Draws the dynamic foreground layer - * @param {DrawParameters} parameters - */ - drawForegroundDynamicLayer(parameters) { - const systems = this.root.systemMgr.systems; - - MOD_CHUNK_DRAW_HOOKS.foregroundDynamicBefore.forEach(systemId => - systems[systemId].drawChunk(parameters, this) - ); - - systems.itemEjector.drawChunk(parameters, this); - systems.itemAcceptor.drawChunk(parameters, this); - systems.miner.drawChunk(parameters, this); - - MOD_CHUNK_DRAW_HOOKS.foregroundDynamicAfter.forEach(systemId => - systems[systemId].drawChunk(parameters, this) - ); - } - - /** - * Draws the static foreground layer - * @param {DrawParameters} parameters - */ - drawForegroundStaticLayer(parameters) { - const systems = this.root.systemMgr.systems; - - MOD_CHUNK_DRAW_HOOKS.staticBefore.forEach(systemId => systems[systemId].drawChunk(parameters, this)); - - systems.staticMapEntities.drawChunk(parameters, this); - systems.lever.drawChunk(parameters, this); - systems.display.drawChunk(parameters, this); - systems.storage.drawChunk(parameters, this); - systems.constantProducer.drawChunk(parameters, this); - systems.goalAcceptor.drawChunk(parameters, this); - systems.itemProcessorOverlays.drawChunk(parameters, this); - - MOD_CHUNK_DRAW_HOOKS.staticAfter.forEach(systemId => systems[systemId].drawChunk(parameters, this)); - } - - /** - * @param {DrawParameters} parameters - * @param {number} xoffs - * @param {number} yoffs - * @param {number} diameter - */ - drawOverlayPatches(parameters, xoffs, yoffs, diameter) { - for (let i = 0; i < this.patches.length; ++i) { - const patch = this.patches[i]; - if (patch.item.getItemType() === "shape") { - const destX = xoffs + patch.pos.x * globalConfig.tileSize; - const destY = yoffs + patch.pos.y * globalConfig.tileSize; - patch.item.drawItemCenteredClipped(destX, destY, parameters, diameter); - } - } - } - - /** - * - * @param {CanvasRenderingContext2D} context - * @param {number} w - * @param {number} h - * @param {number=} xoffs - * @param {number=} yoffs - */ - generateOverlayBuffer(context, w, h, xoffs, yoffs) { - context.fillStyle = - this.containedEntities.length > 0 - ? THEME.map.chunkOverview.filled - : THEME.map.chunkOverview.empty; - context.fillRect(xoffs, yoffs, w, h); - - if (this.root.app.settings.getAllSettings().displayChunkBorders) { - context.fillStyle = THEME.map.chunkBorders; - context.fillRect(xoffs, yoffs, w, 1); - context.fillRect(xoffs, yoffs + 1, 1, h); - } - - for (let x = 0; x < globalConfig.mapChunkSize; ++x) { - const lowerArray = this.lowerLayer[x]; - const upperArray = this.contents[x]; - for (let y = 0; y < globalConfig.mapChunkSize; ++y) { - const upperContent = upperArray[y]; - if (upperContent) { - const staticComp = upperContent.components.StaticMapEntity; - const data = getBuildingDataFromCode(staticComp.code); - const metaBuilding = data.metaInstance; - - const overlayMatrix = metaBuilding.getSpecialOverlayRenderMatrix( - staticComp.rotation, - data.rotationVariant, - data.variant, - upperContent - ); - - if (overlayMatrix) { - // Draw lower content first since it "shines" through - const lowerContent = lowerArray[y]; - if (lowerContent) { - context.fillStyle = lowerContent.getBackgroundColorAsResource(); - context.fillRect( - xoffs + x * CHUNK_OVERLAY_RES, - yoffs + y * CHUNK_OVERLAY_RES, - CHUNK_OVERLAY_RES, - CHUNK_OVERLAY_RES - ); - } - - context.fillStyle = metaBuilding.getSilhouetteColor( - data.variant, - data.rotationVariant - ); - for (let dx = 0; dx < 3; ++dx) { - for (let dy = 0; dy < 3; ++dy) { - const isFilled = overlayMatrix[dx + dy * 3]; - if (isFilled) { - context.fillRect( - xoffs + x * CHUNK_OVERLAY_RES + dx, - yoffs + y * CHUNK_OVERLAY_RES + dy, - 1, - 1 - ); - } - } - } - - continue; - } else { - context.fillStyle = metaBuilding.getSilhouetteColor( - data.variant, - data.rotationVariant - ); - context.fillRect( - xoffs + x * CHUNK_OVERLAY_RES, - yoffs + y * CHUNK_OVERLAY_RES, - CHUNK_OVERLAY_RES, - CHUNK_OVERLAY_RES - ); - - continue; - } - } - - const lowerContent = lowerArray[y]; - if (lowerContent) { - context.fillStyle = lowerContent.getBackgroundColorAsResource(); - context.fillRect( - xoffs + x * CHUNK_OVERLAY_RES, - yoffs + y * CHUNK_OVERLAY_RES, - CHUNK_OVERLAY_RES, - CHUNK_OVERLAY_RES - ); - } - } - } - - if (this.root.currentLayer === "wires") { - // Draw wires overlay - - context.fillStyle = THEME.map.wires.overlayColor; - context.fillRect(xoffs, yoffs, w, h); - - for (let x = 0; x < globalConfig.mapChunkSize; ++x) { - const wiresArray = this.wireContents[x]; - for (let y = 0; y < globalConfig.mapChunkSize; ++y) { - const content = wiresArray[y]; - if (!content) { - continue; - } - MapChunkView.drawSingleWiresOverviewTile({ - context, - x: xoffs + x * CHUNK_OVERLAY_RES, - y: yoffs + y * CHUNK_OVERLAY_RES, - entity: content, - tileSizePixels: CHUNK_OVERLAY_RES, - }); - } - } - } - } - - /** - * @param {object} param0 - * @param {CanvasRenderingContext2D} param0.context - * @param {number} param0.x - * @param {number} param0.y - * @param {Entity} param0.entity - * @param {number} param0.tileSizePixels - * @param {string=} param0.overrideColor Optionally override the color to be rendered - */ - static drawSingleWiresOverviewTile({ context, x, y, entity, tileSizePixels, overrideColor = null }) { - const staticComp = entity.components.StaticMapEntity; - const data = getBuildingDataFromCode(staticComp.code); - const metaBuilding = data.metaInstance; - const overlayMatrix = metaBuilding.getSpecialOverlayRenderMatrix( - staticComp.rotation, - data.rotationVariant, - data.variant, - entity - ); - context.fillStyle = - overrideColor || metaBuilding.getSilhouetteColor(data.variant, data.rotationVariant); - if (overlayMatrix) { - for (let dx = 0; dx < 3; ++dx) { - for (let dy = 0; dy < 3; ++dy) { - const isFilled = overlayMatrix[dx + dy * 3]; - if (isFilled) { - context.fillRect( - x + (dx * tileSizePixels) / CHUNK_OVERLAY_RES, - y + (dy * tileSizePixels) / CHUNK_OVERLAY_RES, - tileSizePixels / CHUNK_OVERLAY_RES, - tileSizePixels / CHUNK_OVERLAY_RES - ); - } - } - } - } else { - context.fillRect(x, y, tileSizePixels, tileSizePixels); - } - } - - /** - * Draws the wires layer - * @param {DrawParameters} parameters - */ - drawWiresForegroundLayer(parameters) { - const systems = this.root.systemMgr.systems; - systems.wire.drawChunk(parameters, this); - systems.staticMapEntities.drawWiresChunk(parameters, this); - systems.wiredPins.drawChunk(parameters, this); - } -} +import { globalConfig } from "../core/config"; +import { DrawParameters } from "../core/draw_parameters"; +import { getBuildingDataFromCode } from "./building_codes"; +import { Entity } from "./entity"; +import { MapChunk } from "./map_chunk"; +import { GameRoot } from "./root"; +import { THEME } from "./theme"; + +export const CHUNK_OVERLAY_RES = 3; + +export const MOD_CHUNK_DRAW_HOOKS = { + backgroundLayerBefore: [], + backgroundLayerAfter: [], + + foregroundDynamicBefore: [], + foregroundDynamicAfter: [], + + staticBefore: [], + staticAfter: [], +}; + +export class MapChunkView extends MapChunk { + /** + * + * @param {GameRoot} root + * @param {number} x + * @param {number} y + */ + constructor(root, x, y) { + super(root, x, y); + + /** + * Whenever something changes, we increase this number - so we know we need to redraw + */ + this.renderIteration = 0; + + this.markDirty(); + } + + /** + * Marks this chunk as dirty, rerendering all caches + */ + markDirty() { + ++this.renderIteration; + this.renderKey = this.x + "/" + this.y + "@" + this.renderIteration; + this.root.map.getAggregateForChunk(this.x, this.y, true).markDirty(this.x, this.y); + } + + /** + * Draws the background layer + * @param {DrawParameters} parameters + */ + drawBackgroundLayer(parameters) { + const systems = this.root.systemMgr.systems; + + MOD_CHUNK_DRAW_HOOKS.backgroundLayerBefore.forEach(systemId => + systems[systemId].drawChunk(parameters, this) + ); + + if (systems.zone) { + systems.zone.drawChunk(parameters, this); + } + + if (this.root.gameMode.hasResources()) { + systems.mapResources.drawChunk(parameters, this); + } + + systems.beltUnderlays.drawChunk(parameters, this); + systems.belt.drawChunk(parameters, this); + + MOD_CHUNK_DRAW_HOOKS.backgroundLayerAfter.forEach(systemId => + systems[systemId].drawChunk(parameters, this) + ); + } + + /** + * Draws the dynamic foreground layer + * @param {DrawParameters} parameters + */ + drawForegroundDynamicLayer(parameters) { + const systems = this.root.systemMgr.systems; + + MOD_CHUNK_DRAW_HOOKS.foregroundDynamicBefore.forEach(systemId => + systems[systemId].drawChunk(parameters, this) + ); + + systems.itemEjector.drawChunk(parameters, this); + systems.itemAcceptor.drawChunk(parameters, this); + systems.miner.drawChunk(parameters, this); + + MOD_CHUNK_DRAW_HOOKS.foregroundDynamicAfter.forEach(systemId => + systems[systemId].drawChunk(parameters, this) + ); + } + + /** + * Draws the static foreground layer + * @param {DrawParameters} parameters + */ + drawForegroundStaticLayer(parameters) { + const systems = this.root.systemMgr.systems; + + MOD_CHUNK_DRAW_HOOKS.staticBefore.forEach(systemId => systems[systemId].drawChunk(parameters, this)); + + systems.staticMapEntities.drawChunk(parameters, this); + systems.lever.drawChunk(parameters, this); + systems.display.drawChunk(parameters, this); + systems.storage.drawChunk(parameters, this); + systems.constantProducer.drawChunk(parameters, this); + systems.goalAcceptor.drawChunk(parameters, this); + systems.itemProcessorOverlays.drawChunk(parameters, this); + + MOD_CHUNK_DRAW_HOOKS.staticAfter.forEach(systemId => systems[systemId].drawChunk(parameters, this)); + } + + /** + * @param {DrawParameters} parameters + * @param {number} xoffs + * @param {number} yoffs + * @param {number} diameter + */ + drawOverlayPatches(parameters, xoffs, yoffs, diameter) { + for (let i = 0; i < this.patches.length; ++i) { + const patch = this.patches[i]; + if (patch.item.getItemType() === "shape") { + const destX = xoffs + patch.pos.x * globalConfig.tileSize; + const destY = yoffs + patch.pos.y * globalConfig.tileSize; + patch.item.drawItemCenteredClipped(destX, destY, parameters, diameter); + } + } + } + + /** + * + * @param {CanvasRenderingContext2D} context + * @param {number} w + * @param {number} h + * @param {number=} xoffs + * @param {number=} yoffs + */ + generateOverlayBuffer(context, w, h, xoffs, yoffs) { + context.fillStyle = + this.containedEntities.length > 0 + ? THEME.map.chunkOverview.filled + : THEME.map.chunkOverview.empty; + context.fillRect(xoffs, yoffs, w, h); + + if (this.root.app.settings.getAllSettings().displayChunkBorders) { + context.fillStyle = THEME.map.chunkBorders; + context.fillRect(xoffs, yoffs, w, 1); + context.fillRect(xoffs, yoffs + 1, 1, h); + } + + for (let x = 0; x < globalConfig.mapChunkSize; ++x) { + const lowerArray = this.lowerLayer[x]; + const upperArray = this.contents[x]; + for (let y = 0; y < globalConfig.mapChunkSize; ++y) { + const upperContent = upperArray[y]; + if (upperContent) { + const staticComp = upperContent.components.StaticMapEntity; + const data = getBuildingDataFromCode(staticComp.code); + const metaBuilding = data.metaInstance; + + const overlayMatrix = metaBuilding.getSpecialOverlayRenderMatrix( + staticComp.rotation, + data.rotationVariant, + data.variant, + upperContent + ); + + if (overlayMatrix) { + // Draw lower content first since it "shines" through + const lowerContent = lowerArray[y]; + if (lowerContent) { + context.fillStyle = lowerContent.getBackgroundColorAsResource(); + context.fillRect( + xoffs + x * CHUNK_OVERLAY_RES, + yoffs + y * CHUNK_OVERLAY_RES, + CHUNK_OVERLAY_RES, + CHUNK_OVERLAY_RES + ); + } + + context.fillStyle = metaBuilding.getSilhouetteColor( + data.variant, + data.rotationVariant + ); + for (let dx = 0; dx < 3; ++dx) { + for (let dy = 0; dy < 3; ++dy) { + const isFilled = overlayMatrix[dx + dy * 3]; + if (isFilled) { + context.fillRect( + xoffs + x * CHUNK_OVERLAY_RES + dx, + yoffs + y * CHUNK_OVERLAY_RES + dy, + 1, + 1 + ); + } + } + } + + continue; + } else { + context.fillStyle = metaBuilding.getSilhouetteColor( + data.variant, + data.rotationVariant + ); + context.fillRect( + xoffs + x * CHUNK_OVERLAY_RES, + yoffs + y * CHUNK_OVERLAY_RES, + CHUNK_OVERLAY_RES, + CHUNK_OVERLAY_RES + ); + + continue; + } + } + + const lowerContent = lowerArray[y]; + if (lowerContent) { + context.fillStyle = lowerContent.getBackgroundColorAsResource(); + context.fillRect( + xoffs + x * CHUNK_OVERLAY_RES, + yoffs + y * CHUNK_OVERLAY_RES, + CHUNK_OVERLAY_RES, + CHUNK_OVERLAY_RES + ); + } + } + } + + if (this.root.currentLayer === "wires") { + // Draw wires overlay + + context.fillStyle = THEME.map.wires.overlayColor; + context.fillRect(xoffs, yoffs, w, h); + + for (let x = 0; x < globalConfig.mapChunkSize; ++x) { + const wiresArray = this.wireContents[x]; + for (let y = 0; y < globalConfig.mapChunkSize; ++y) { + const content = wiresArray[y]; + if (!content) { + continue; + } + MapChunkView.drawSingleWiresOverviewTile({ + context, + x: xoffs + x * CHUNK_OVERLAY_RES, + y: yoffs + y * CHUNK_OVERLAY_RES, + entity: content, + tileSizePixels: CHUNK_OVERLAY_RES, + }); + } + } + } + } + + /** + * @param {object} param0 + * @param {CanvasRenderingContext2D} param0.context + * @param {number} param0.x + * @param {number} param0.y + * @param {Entity} param0.entity + * @param {number} param0.tileSizePixels + * @param {string=} param0.overrideColor Optionally override the color to be rendered + */ + static drawSingleWiresOverviewTile({ context, x, y, entity, tileSizePixels, overrideColor = null }) { + const staticComp = entity.components.StaticMapEntity; + const data = getBuildingDataFromCode(staticComp.code); + const metaBuilding = data.metaInstance; + const overlayMatrix = metaBuilding.getSpecialOverlayRenderMatrix( + staticComp.rotation, + data.rotationVariant, + data.variant, + entity + ); + context.fillStyle = + overrideColor || metaBuilding.getSilhouetteColor(data.variant, data.rotationVariant); + if (overlayMatrix) { + for (let dx = 0; dx < 3; ++dx) { + for (let dy = 0; dy < 3; ++dy) { + const isFilled = overlayMatrix[dx + dy * 3]; + if (isFilled) { + context.fillRect( + x + (dx * tileSizePixels) / CHUNK_OVERLAY_RES, + y + (dy * tileSizePixels) / CHUNK_OVERLAY_RES, + tileSizePixels / CHUNK_OVERLAY_RES, + tileSizePixels / CHUNK_OVERLAY_RES + ); + } + } + } + } else { + context.fillRect(x, y, tileSizePixels, tileSizePixels); + } + } + + /** + * Draws the wires layer + * @param {DrawParameters} parameters + */ + drawWiresForegroundLayer(parameters) { + const systems = this.root.systemMgr.systems; + systems.wire.drawChunk(parameters, this); + systems.staticMapEntities.drawWiresChunk(parameters, this); + systems.wiredPins.drawChunk(parameters, this); + } +} diff --git a/src/js/game/map_view.js b/src/js/game/map_view.js index a148bfdf..685ec3e6 100644 --- a/src/js/game/map_view.js +++ b/src/js/game/map_view.js @@ -1,264 +1,264 @@ -import { globalConfig } from "../core/config"; -import { DrawParameters } from "../core/draw_parameters"; -import { BaseMap } from "./map"; -import { freeCanvas, makeOffscreenBuffer } from "../core/buffer_utils"; -import { Entity } from "./entity"; -import { THEME } from "./theme"; -import { MapChunkView } from "./map_chunk_view"; -import { MapChunkAggregate } from "./map_chunk_aggregate"; - -/** - * This is the view of the map, it extends the map which is the raw model and allows - * to draw it - */ -export class MapView extends BaseMap { - constructor(root) { - super(root); - - /** - * DPI of the background cache images, required in some places - */ - this.backgroundCacheDPI = 2; - - /** - * The cached background sprite, containing the flat background - * @type {Object} - */ - this.cachedBackgroundCanvases = { - regular: null, - placing: null, - }; - - /** @type {CanvasRenderingContext2D} */ - this.cachedBackgroundContext = null; - this.internalInitializeCachedBackgroundCanvases(); - this.root.signals.aboutToDestruct.add(this.cleanup, this); - - this.root.signals.entityAdded.add(this.onEntityChanged, this); - this.root.signals.entityDestroyed.add(this.onEntityChanged, this); - this.root.signals.entityChanged.add(this.onEntityChanged, this); - } - - cleanup() { - for (const key in this.cachedBackgroundCanvases) { - freeCanvas(this.cachedBackgroundCanvases[key]); - this.cachedBackgroundCanvases[key] = null; - } - } - - /** - * Called when an entity was added, removed or changed - * @param {Entity} entity - */ - onEntityChanged(entity) { - const staticComp = entity.components.StaticMapEntity; - if (staticComp) { - const rect = staticComp.getTileSpaceBounds(); - for (let x = rect.x; x <= rect.right(); ++x) { - for (let y = rect.y; y <= rect.bottom(); ++y) { - this.root.map.getOrCreateChunkAtTile(x, y).markDirty(); - } - } - } - } - - /** - * Draws all static entities like buildings etc. - * @param {DrawParameters} drawParameters - */ - drawStaticEntityDebugOverlays(drawParameters) { - if (G_IS_DEV && (globalConfig.debug.showAcceptorEjectors || globalConfig.debug.showEntityBounds)) { - const cullRange = drawParameters.visibleRect.toTileCullRectangle(); - const top = cullRange.top(); - const right = cullRange.right(); - const bottom = cullRange.bottom(); - const left = cullRange.left(); - - const border = 1; - - const minY = top - border; - const maxY = bottom + border; - const minX = left - border; - const maxX = right + border - 1; - - // Render y from top down for proper blending - for (let y = minY; y <= maxY; ++y) { - for (let x = minX; x <= maxX; ++x) { - // const content = this.tiles[x][y]; - const chunk = this.getChunkAtTileOrNull(x, y); - if (!chunk) { - continue; - } - const content = chunk.getTileContentFromWorldCoords(x, y); - if (content) { - let isBorder = x <= left - 1 || x >= right + 1 || y <= top - 1 || y >= bottom + 1; - if (!isBorder) { - content.drawDebugOverlays(drawParameters); - } - } - } - } - } - } - - /** - * Initializes all canvases used for background rendering - */ - internalInitializeCachedBackgroundCanvases() { - for (const key in this.cachedBackgroundCanvases) { - // Background canvas - const dims = globalConfig.tileSize; - const dpi = this.backgroundCacheDPI; - const [canvas, context] = makeOffscreenBuffer(dims * dpi, dims * dpi, { - smooth: false, - label: "map-cached-bg", - }); - context.scale(dpi, dpi); - - context.fillStyle = THEME.map.background; - context.fillRect(0, 0, dims, dims); - - const borderWidth = THEME.map.gridLineWidth; - context.fillStyle = THEME.map["grid" + key[0].toUpperCase() + key.substring(1)] || "red"; - context.fillRect(0, 0, dims, borderWidth); - context.fillRect(0, borderWidth, borderWidth, dims); - - context.fillRect(dims - borderWidth, borderWidth, borderWidth, dims - 2 * borderWidth); - context.fillRect(borderWidth, dims - borderWidth, dims, borderWidth); - - this.cachedBackgroundCanvases[key] = canvas; - } - } - - /** - * Draws the maps foreground - * @param {DrawParameters} parameters - */ - drawForeground(parameters) { - this.drawVisibleChunks(parameters, MapChunkView.prototype.drawForegroundDynamicLayer); - this.drawVisibleChunks(parameters, MapChunkView.prototype.drawForegroundStaticLayer); - } - - /** - * Calls a given method on all given chunks - * @param {DrawParameters} parameters - * @param {function} method - */ - drawVisibleChunks(parameters, method) { - const cullRange = parameters.visibleRect.allScaled(1 / globalConfig.tileSize); - const top = cullRange.top(); - const right = cullRange.right(); - const bottom = cullRange.bottom(); - const left = cullRange.left(); - - const border = 0; - const minY = top - border; - const maxY = bottom + border; - const minX = left - border; - const maxX = right + border; - - const chunkStartX = Math.floor(minX / globalConfig.mapChunkSize); - const chunkStartY = Math.floor(minY / globalConfig.mapChunkSize); - - const chunkEndX = Math.floor(maxX / globalConfig.mapChunkSize); - const chunkEndY = Math.floor(maxY / globalConfig.mapChunkSize); - - // Render y from top down for proper blending - for (let chunkX = chunkStartX; chunkX <= chunkEndX; ++chunkX) { - for (let chunkY = chunkStartY; chunkY <= chunkEndY; ++chunkY) { - const chunk = this.root.map.getChunk(chunkX, chunkY, true); - method.call(chunk, parameters); - } - } - } - - /** - * Calls a given method on all given chunks - * @param {DrawParameters} parameters - * @param {function} method - */ - drawVisibleAggregates(parameters, method) { - const cullRange = parameters.visibleRect.allScaled(1 / globalConfig.tileSize); - const top = cullRange.top(); - const right = cullRange.right(); - const bottom = cullRange.bottom(); - const left = cullRange.left(); - - const border = 0; - const minY = top - border; - const maxY = bottom + border; - const minX = left - border; - const maxX = right + border; - - const aggregateTiles = globalConfig.chunkAggregateSize * globalConfig.mapChunkSize; - const aggStartX = Math.floor(minX / aggregateTiles); - const aggStartY = Math.floor(minY / aggregateTiles); - - const aggEndX = Math.floor(maxX / aggregateTiles); - const aggEndY = Math.floor(maxY / aggregateTiles); - - // Render y from top down for proper blending - for (let aggX = aggStartX; aggX <= aggEndX; ++aggX) { - for (let aggY = aggStartY; aggY <= aggEndY; ++aggY) { - const aggregate = this.root.map.getAggregate(aggX, aggY, true); - method.call(aggregate, parameters); - } - } - } - - /** - * Draws the wires foreground - * @param {DrawParameters} parameters - */ - drawWiresForegroundLayer(parameters) { - this.drawVisibleChunks(parameters, MapChunkView.prototype.drawWiresForegroundLayer); - } - - /** - * Draws the map overlay - * @param {DrawParameters} parameters - */ - drawOverlay(parameters) { - this.drawVisibleAggregates(parameters, MapChunkAggregate.prototype.drawOverlay); - } - - /** - * Draws the map background - * @param {DrawParameters} parameters - */ - drawBackground(parameters) { - // Render tile grid - if (!this.root.app.settings.getAllSettings().disableTileGrid || !this.root.gameMode.hasResources()) { - const dpi = this.backgroundCacheDPI; - parameters.context.scale(1 / dpi, 1 / dpi); - - let key = "regular"; - - // Disabled rn because it can be really annoying - // eslint-disable-next-line no-constant-condition - if (this.root.hud.parts.buildingPlacer.currentMetaBuilding.get() && false) { - key = "placing"; - } - - // @ts-ignore` - if (this.cachedBackgroundCanvases[key]._contextLost) { - freeCanvas(this.cachedBackgroundCanvases[key]); - this.internalInitializeCachedBackgroundCanvases(); - } - - parameters.context.fillStyle = parameters.context.createPattern( - this.cachedBackgroundCanvases[key], - "repeat" - ); - parameters.context.fillRect( - parameters.visibleRect.x * dpi, - parameters.visibleRect.y * dpi, - parameters.visibleRect.w * dpi, - parameters.visibleRect.h * dpi - ); - parameters.context.scale(dpi, dpi); - } - - this.drawVisibleChunks(parameters, MapChunkView.prototype.drawBackgroundLayer); - } -} +import { freeCanvas, makeOffscreenBuffer } from "../core/buffer_utils"; +import { globalConfig } from "../core/config"; +import { DrawParameters } from "../core/draw_parameters"; +import { Entity } from "./entity"; +import { BaseMap } from "./map"; +import { MapChunkAggregate } from "./map_chunk_aggregate"; +import { MapChunkView } from "./map_chunk_view"; +import { THEME } from "./theme"; + +/** + * This is the view of the map, it extends the map which is the raw model and allows + * to draw it + */ +export class MapView extends BaseMap { + constructor(root) { + super(root); + + /** + * DPI of the background cache images, required in some places + */ + this.backgroundCacheDPI = 2; + + /** + * The cached background sprite, containing the flat background + * @type {Object} + */ + this.cachedBackgroundCanvases = { + regular: null, + placing: null, + }; + + /** @type {CanvasRenderingContext2D} */ + this.cachedBackgroundContext = null; + this.internalInitializeCachedBackgroundCanvases(); + this.root.signals.aboutToDestruct.add(this.cleanup, this); + + this.root.signals.entityAdded.add(this.onEntityChanged, this); + this.root.signals.entityDestroyed.add(this.onEntityChanged, this); + this.root.signals.entityChanged.add(this.onEntityChanged, this); + } + + cleanup() { + for (const key in this.cachedBackgroundCanvases) { + freeCanvas(this.cachedBackgroundCanvases[key]); + this.cachedBackgroundCanvases[key] = null; + } + } + + /** + * Called when an entity was added, removed or changed + * @param {Entity} entity + */ + onEntityChanged(entity) { + if (!this.root.gameInitialized) { + return; + } + + const staticComp = entity.components.StaticMapEntity; + if (staticComp) { + const rect = staticComp.getTileSpaceBounds(); + for (let x = rect.x; x <= rect.right(); ++x) { + for (let y = rect.y; y <= rect.bottom(); ++y) { + this.root.map.getOrCreateChunkAtTile(x, y).markDirty(); + } + } + } + } + + /** + * Draws all static entities like buildings etc. + * @param {DrawParameters} drawParameters + */ + drawStaticEntityDebugOverlays(drawParameters) { + if (G_IS_DEV && (globalConfig.debug.showAcceptorEjectors || globalConfig.debug.showEntityBounds)) { + const cullRange = drawParameters.visibleRect.toTileCullRectangle(); + const top = cullRange.top(); + const right = cullRange.right(); + const bottom = cullRange.bottom(); + const left = cullRange.left(); + + const border = 1; + + const minY = top - border; + const maxY = bottom + border; + const minX = left - border; + const maxX = right + border - 1; + + // Render y from top down for proper blending + for (let y = minY; y <= maxY; ++y) { + for (let x = minX; x <= maxX; ++x) { + // const content = this.tiles[x][y]; + const chunk = this.getChunkAtTileOrNull(x, y); + if (!chunk) { + continue; + } + const content = chunk.getTileContentFromWorldCoords(x, y); + if (content) { + let isBorder = x <= left - 1 || x >= right + 1 || y <= top - 1 || y >= bottom + 1; + if (!isBorder) { + content.drawDebugOverlays(drawParameters); + } + } + } + } + } + } + + /** + * Initializes all canvases used for background rendering + */ + internalInitializeCachedBackgroundCanvases() { + for (const key in this.cachedBackgroundCanvases) { + // Background canvas + const dims = globalConfig.tileSize; + const dpi = this.backgroundCacheDPI; + const [canvas, context] = makeOffscreenBuffer(dims * dpi, dims * dpi, { + smooth: false, + label: "map-cached-bg", + }); + context.scale(dpi, dpi); + + context.fillStyle = THEME.map.background; + context.fillRect(0, 0, dims, dims); + + const borderWidth = THEME.map.gridLineWidth; + context.fillStyle = THEME.map["grid" + key[0].toUpperCase() + key.substring(1)] || "red"; + context.fillRect(0, 0, dims, borderWidth); + context.fillRect(0, borderWidth, borderWidth, dims); + + context.fillRect(dims - borderWidth, borderWidth, borderWidth, dims - 2 * borderWidth); + context.fillRect(borderWidth, dims - borderWidth, dims, borderWidth); + + this.cachedBackgroundCanvases[key] = canvas; + } + } + + /** + * Draws the maps foreground + * @param {DrawParameters} parameters + */ + drawForeground(parameters) { + this.drawVisibleChunks(parameters, MapChunkView.prototype.drawForegroundDynamicLayer); + this.drawVisibleChunks(parameters, MapChunkView.prototype.drawForegroundStaticLayer); + } + + /** + * Calls a given method on all given chunks + * @param {DrawParameters} parameters + * @param {function} method + */ + drawVisibleChunks(parameters, method) { + const cullRange = parameters.visibleRect.allScaled(1 / globalConfig.tileSize); + const top = cullRange.top(); + const right = cullRange.right(); + const bottom = cullRange.bottom(); + const left = cullRange.left(); + + const border = 0; + const minY = top - border; + const maxY = bottom + border; + const minX = left - border; + const maxX = right + border; + + const chunkStartX = Math.floor(minX / globalConfig.mapChunkSize); + const chunkStartY = Math.floor(minY / globalConfig.mapChunkSize); + + const chunkEndX = Math.floor(maxX / globalConfig.mapChunkSize); + const chunkEndY = Math.floor(maxY / globalConfig.mapChunkSize); + + // Render y from top down for proper blending + for (let chunkX = chunkStartX; chunkX <= chunkEndX; ++chunkX) { + for (let chunkY = chunkStartY; chunkY <= chunkEndY; ++chunkY) { + const chunk = this.root.map.getChunk(chunkX, chunkY, true); + method.call(chunk, parameters); + } + } + } + + /** + * Calls a given method on all given chunks + * @param {DrawParameters} parameters + * @param {function} method + */ + drawVisibleAggregates(parameters, method) { + const cullRange = parameters.visibleRect.allScaled(1 / globalConfig.tileSize); + const top = cullRange.top(); + const right = cullRange.right(); + const bottom = cullRange.bottom(); + const left = cullRange.left(); + + const border = 0; + const minY = top - border; + const maxY = bottom + border; + const minX = left - border; + const maxX = right + border; + + const aggregateTiles = globalConfig.chunkAggregateSize * globalConfig.mapChunkSize; + const aggStartX = Math.floor(minX / aggregateTiles); + const aggStartY = Math.floor(minY / aggregateTiles); + + const aggEndX = Math.floor(maxX / aggregateTiles); + const aggEndY = Math.floor(maxY / aggregateTiles); + + // Render y from top down for proper blending + for (let aggX = aggStartX; aggX <= aggEndX; ++aggX) { + for (let aggY = aggStartY; aggY <= aggEndY; ++aggY) { + const aggregate = this.root.map.getAggregate(aggX, aggY, true); + method.call(aggregate, parameters); + } + } + } + + /** + * Draws the wires foreground + * @param {DrawParameters} parameters + */ + drawWiresForegroundLayer(parameters) { + this.drawVisibleChunks(parameters, MapChunkView.prototype.drawWiresForegroundLayer); + } + + /** + * Draws the map overlay + * @param {DrawParameters} parameters + */ + drawOverlay(parameters) { + this.drawVisibleAggregates(parameters, MapChunkAggregate.prototype.drawOverlay); + } + + /** + * Draws the map background + * @param {DrawParameters} parameters + */ + drawBackground(parameters) { + // Render tile grid + if (!this.root.app.settings.getAllSettings().disableTileGrid || !this.root.gameMode.hasResources()) { + let key = "regular"; + + // Disabled rn because it can be really annoying + // eslint-disable-next-line no-constant-condition + if (this.root.hud.parts.buildingPlacer.currentMetaBuilding.get() && false) { + key = "placing"; + } + + parameters.context.fillStyle = parameters.context.createPattern( + this.cachedBackgroundCanvases[key], + "repeat" + ); + } else { + parameters.context.fillStyle = THEME.map.background; + } + + const dpi = this.backgroundCacheDPI; + parameters.context.scale(1 / dpi, 1 / dpi); + parameters.context.fillRect( + parameters.visibleRect.x * dpi, + parameters.visibleRect.y * dpi, + parameters.visibleRect.w * dpi, + parameters.visibleRect.h * dpi + ); + parameters.context.scale(dpi, dpi); + + this.drawVisibleChunks(parameters, MapChunkView.prototype.drawBackgroundLayer); + } +} diff --git a/src/js/game/meta_building.js b/src/js/game/meta_building.js index c661b84f..701d2d97 100644 --- a/src/js/game/meta_building.js +++ b/src/js/game/meta_building.js @@ -1,289 +1,289 @@ -import { Loader } from "../core/loader"; -import { AtlasSprite } from "../core/sprites"; -import { Vector } from "../core/vector"; -import { SOUNDS } from "../platform/sound"; -import { StaticMapEntityComponent } from "./components/static_map_entity"; -import { Entity } from "./entity"; -import { GameRoot } from "./root"; -import { getCodeFromBuildingData } from "./building_codes"; - -export const defaultBuildingVariant = "default"; - -export class MetaBuilding { - /** - * - * @param {string} id Building id - */ - constructor(id) { - this.id = id; - } - - /** - * Should return all possible variants of this building, no matter - * if they are already available or will be unlocked later on - * - * @returns {Array<{ variant: string, rotationVariant?: number, internalId?: number|string }>} - */ - static getAllVariantCombinations() { - throw new Error("implement getAllVariantCombinations"); - } - - /** - * Returns the id of this building - */ - getId() { - return this.id; - } - - /** - * Returns the edit layer of the building - * @returns {Layer} - */ - getLayer() { - return "regular"; - } - - /** - * Should return the dimensions of the building - */ - getDimensions(variant = defaultBuildingVariant) { - return new Vector(1, 1); - } - - /** - * Returns whether the building has the direction lock switch available - * @param {string} variant - */ - getHasDirectionLockAvailable(variant) { - return false; - } - - /** - * Whether to stay in placement mode after having placed a building - */ - getStayInPlacementMode() { - return false; - } - - /** - * Can return a special interlaved 9 elements overlay matrix for rendering - * @param {number} rotation - * @param {number} rotationVariant - * @param {string} variant - * @param {Entity} entity - * @returns {Array|null} - */ - getSpecialOverlayRenderMatrix(rotation, rotationVariant, variant, entity) { - return null; - } - - /** - * Should return additional statistics about this building - * @param {GameRoot} root - * @param {string} variant - * @returns {Array<[string, string]>} - */ - getAdditionalStatistics(root, variant) { - return []; - } - - /** - * Returns whether this building can get replaced - * @param {string} variant - * @param {number} rotationVariant - */ - getIsReplaceable(variant, rotationVariant) { - return false; - } - - /** - * Whether to flip the orientation after a building has been placed - useful - * for tunnels. - */ - getFlipOrientationAfterPlacement() { - return false; - } - - /** - * Whether to show a preview of the wires layer when placing the building - */ - getShowWiresLayerPreview() { - return false; - } - - /** - * Whether to rotate automatically in the dragging direction while placing - * @param {string} variant - */ - getRotateAutomaticallyWhilePlacing(variant) { - return false; - } - - /** - * Returns whether this building is removable - * @param {GameRoot} root - * @returns {boolean} - */ - getIsRemovable(root) { - return true; - } - - /** - * Returns the placement sound - * @returns {string} - */ - getPlacementSound() { - return SOUNDS.placeBuilding; - } - - /** - * @param {GameRoot} root - */ - getAvailableVariants(root) { - return [defaultBuildingVariant]; - } - - /** - * Returns a preview sprite - * @returns {AtlasSprite} - */ - getPreviewSprite(rotationVariant = 0, variant = defaultBuildingVariant) { - return Loader.getSprite( - "sprites/buildings/" + - this.id + - (variant === defaultBuildingVariant ? "" : "-" + variant) + - ".png" - ); - } - - /** - * Returns a sprite for blueprints - * @returns {AtlasSprite} - */ - getBlueprintSprite(rotationVariant = 0, variant = defaultBuildingVariant) { - return Loader.getSprite( - "sprites/blueprints/" + - this.id + - (variant === defaultBuildingVariant ? "" : "-" + variant) + - ".png" - ); - } - - /** - * Returns whether this building is rotateable - * @returns {boolean} - */ - getIsRotateable() { - return true; - } - - /** - * Returns whether this building is unlocked for the given game - * @param {GameRoot} root - */ - getIsUnlocked(root) { - return true; - } - - /** - * Should return a silhouette color for the map overview or null if not set - * @param {string} variant - * @param {number} rotationVariant - */ - getSilhouetteColor(variant, rotationVariant) { - return null; - } - - /** - * Should return false if the pins are already included in the sprite of the building - * @returns {boolean} - */ - getRenderPins() { - return true; - } - - /** - * Creates the entity without placing it - * @param {object} param0 - * @param {GameRoot} param0.root - * @param {Vector} param0.origin Origin tile - * @param {number=} param0.rotation Rotation - * @param {number} param0.originalRotation Original Rotation - * @param {number} param0.rotationVariant Rotation variant - * @param {string} param0.variant - */ - createEntity({ root, origin, rotation, originalRotation, rotationVariant, variant }) { - const entity = new Entity(root); - entity.layer = this.getLayer(); - entity.addComponent( - new StaticMapEntityComponent({ - origin: new Vector(origin.x, origin.y), - rotation, - originalRotation, - tileSize: this.getDimensions(variant).copy(), - code: getCodeFromBuildingData(this, variant, rotationVariant), - }) - ); - this.setupEntityComponents(entity, root); - this.updateVariants(entity, rotationVariant, variant); - return entity; - } - - /** - * Returns the sprite for a given variant - * @param {number} rotationVariant - * @param {string} variant - * @returns {AtlasSprite} - */ - getSprite(rotationVariant, variant) { - return Loader.getSprite( - "sprites/buildings/" + - this.id + - (variant === defaultBuildingVariant ? "" : "-" + variant) + - ".png" - ); - } - - /** - * Should compute the optimal rotation variant on the given tile - * @param {object} param0 - * @param {GameRoot} param0.root - * @param {Vector} param0.tile - * @param {number} param0.rotation - * @param {string} param0.variant - * @param {Layer} param0.layer - * @return {{ rotation: number, rotationVariant: number, connectedEntities?: Array }} - */ - computeOptimalDirectionAndRotationVariantAtTile({ root, tile, rotation, variant, layer }) { - if (!this.getIsRotateable()) { - return { - rotation: 0, - rotationVariant: 0, - }; - } - return { - rotation, - rotationVariant: 0, - }; - } - - /** - * Should update the entity to match the given variants - * @param {Entity} entity - * @param {number} rotationVariant - * @param {string} variant - */ - updateVariants(entity, rotationVariant, variant) {} - - // PRIVATE INTERFACE - - /** - * Should setup the entity components - * @param {Entity} entity - * @param {GameRoot} root - * @abstract - */ - setupEntityComponents(entity, root) { - abstract; - } -} +import { Loader } from "../core/loader"; +import { AtlasSprite } from "../core/sprites"; +import { Vector } from "../core/vector"; +import { SOUNDS } from "../platform/sound"; +import { StaticMapEntityComponent } from "./components/static_map_entity"; +import { Entity } from "./entity"; +import { GameRoot } from "./root"; +import { getCodeFromBuildingData } from "./building_codes"; + +export const defaultBuildingVariant = "default"; + +export class MetaBuilding { + /** + * + * @param {string} id Building id + */ + constructor(id) { + this.id = id; + } + + /** + * Should return all possible variants of this building, no matter + * if they are already available or will be unlocked later on + * + * @returns {Array<{ variant: string, rotationVariant?: number, internalId?: number|string }>} + */ + static getAllVariantCombinations() { + throw new Error("implement getAllVariantCombinations"); + } + + /** + * Returns the id of this building + */ + getId() { + return this.id; + } + + /** + * Returns the edit layer of the building + * @returns {Layer} + */ + getLayer() { + return "regular"; + } + + /** + * Should return the dimensions of the building + */ + getDimensions(variant = defaultBuildingVariant) { + return new Vector(1, 1); + } + + /** + * Returns whether the building has the direction lock switch available + * @param {string} variant + */ + getHasDirectionLockAvailable(variant) { + return false; + } + + /** + * Whether to stay in placement mode after having placed a building + */ + getStayInPlacementMode() { + return false; + } + + /** + * Can return a special interlaved 9 elements overlay matrix for rendering + * @param {number} rotation + * @param {number} rotationVariant + * @param {string} variant + * @param {Entity} entity + * @returns {Array|null} + */ + getSpecialOverlayRenderMatrix(rotation, rotationVariant, variant, entity) { + return null; + } + + /** + * Should return additional statistics about this building + * @param {GameRoot} root + * @param {string} variant + * @returns {Array<[string, string]>} + */ + getAdditionalStatistics(root, variant) { + return []; + } + + /** + * Returns whether this building can get replaced + * @param {string} variant + * @param {number} rotationVariant + */ + getIsReplaceable(variant, rotationVariant) { + return false; + } + + /** + * Whether to flip the orientation after a building has been placed - useful + * for tunnels. + */ + getFlipOrientationAfterPlacement() { + return false; + } + + /** + * Whether to show a preview of the wires layer when placing the building + */ + getShowWiresLayerPreview() { + return false; + } + + /** + * Whether to rotate automatically in the dragging direction while placing + * @param {string} variant + */ + getRotateAutomaticallyWhilePlacing(variant) { + return false; + } + + /** + * Returns whether this building is removable + * @param {GameRoot} root + * @returns {boolean} + */ + getIsRemovable(root) { + return true; + } + + /** + * Returns the placement sound + * @returns {string} + */ + getPlacementSound() { + return SOUNDS.placeBuilding; + } + + /** + * @param {GameRoot} root + */ + getAvailableVariants(root) { + return [defaultBuildingVariant]; + } + + /** + * Returns a preview sprite + * @returns {AtlasSprite} + */ + getPreviewSprite(rotationVariant = 0, variant = defaultBuildingVariant) { + return Loader.getSprite( + "sprites/buildings/" + + this.id + + (variant === defaultBuildingVariant ? "" : "-" + variant) + + ".png" + ); + } + + /** + * Returns a sprite for blueprints + * @returns {AtlasSprite} + */ + getBlueprintSprite(rotationVariant = 0, variant = defaultBuildingVariant) { + return Loader.getSprite( + "sprites/blueprints/" + + this.id + + (variant === defaultBuildingVariant ? "" : "-" + variant) + + ".png" + ); + } + + /** + * Returns whether this building is rotateable + * @returns {boolean} + */ + getIsRotateable() { + return true; + } + + /** + * Returns whether this building is unlocked for the given game + * @param {GameRoot} root + */ + getIsUnlocked(root) { + return true; + } + + /** + * Should return a silhouette color for the map overview or null if not set + * @param {string} variant + * @param {number} rotationVariant + */ + getSilhouetteColor(variant, rotationVariant) { + return null; + } + + /** + * Should return false if the pins are already included in the sprite of the building + * @returns {boolean} + */ + getRenderPins() { + return true; + } + + /** + * Creates the entity without placing it + * @param {object} param0 + * @param {GameRoot} param0.root + * @param {Vector} param0.origin Origin tile + * @param {number=} param0.rotation Rotation + * @param {number} param0.originalRotation Original Rotation + * @param {number} param0.rotationVariant Rotation variant + * @param {string} param0.variant + */ + createEntity({ root, origin, rotation, originalRotation, rotationVariant, variant }) { + const entity = new Entity(root); + entity.layer = this.getLayer(); + entity.addComponent( + new StaticMapEntityComponent({ + origin: new Vector(origin.x, origin.y), + rotation, + originalRotation, + tileSize: this.getDimensions(variant).copy(), + code: getCodeFromBuildingData(this, variant, rotationVariant), + }) + ); + this.setupEntityComponents(entity, root); + this.updateVariants(entity, rotationVariant, variant); + return entity; + } + + /** + * Returns the sprite for a given variant + * @param {number} rotationVariant + * @param {string} variant + * @returns {AtlasSprite} + */ + getSprite(rotationVariant, variant) { + return Loader.getSprite( + "sprites/buildings/" + + this.id + + (variant === defaultBuildingVariant ? "" : "-" + variant) + + ".png" + ); + } + + /** + * Should compute the optimal rotation variant on the given tile + * @param {object} param0 + * @param {GameRoot} param0.root + * @param {Vector} param0.tile + * @param {number} param0.rotation + * @param {string} param0.variant + * @param {Layer} param0.layer + * @return {{ rotation: number, rotationVariant: number, connectedEntities?: Array }} + */ + computeOptimalDirectionAndRotationVariantAtTile({ root, tile, rotation, variant, layer }) { + if (!this.getIsRotateable()) { + return { + rotation: 0, + rotationVariant: 0, + }; + } + return { + rotation, + rotationVariant: 0, + }; + } + + /** + * Should update the entity to match the given variants + * @param {Entity} entity + * @param {number} rotationVariant + * @param {string} variant + */ + updateVariants(entity, rotationVariant, variant) {} + + // PRIVATE INTERFACE + + /** + * Should setup the entity components + * @param {Entity} entity + * @param {GameRoot} root + * @abstract + */ + setupEntityComponents(entity, root) { + abstract; + } +} diff --git a/src/js/game/meta_building_registry.js b/src/js/game/meta_building_registry.js index eba6fd61..bdadb049 100644 --- a/src/js/game/meta_building_registry.js +++ b/src/js/game/meta_building_registry.js @@ -1,134 +1,134 @@ -import { gMetaBuildingRegistry } from "../core/global_registries"; -import { createLogger } from "../core/logging"; -import { T } from "../translations"; -import { MetaAnalyzerBuilding } from "./buildings/analyzer"; -import { MetaBalancerBuilding } from "./buildings/balancer"; -import { MetaBeltBuilding } from "./buildings/belt"; -import { MetaBlockBuilding } from "./buildings/block"; -import { MetaComparatorBuilding } from "./buildings/comparator"; -import { MetaConstantProducerBuilding } from "./buildings/constant_producer"; -import { MetaConstantSignalBuilding } from "./buildings/constant_signal"; -import { MetaCutterBuilding } from "./buildings/cutter"; -import { MetaDisplayBuilding } from "./buildings/display"; -import { MetaFilterBuilding } from "./buildings/filter"; -import { MetaGoalAcceptorBuilding } from "./buildings/goal_acceptor"; -import { MetaHubBuilding } from "./buildings/hub"; -import { MetaItemProducerBuilding } from "./buildings/item_producer"; -import { MetaLeverBuilding } from "./buildings/lever"; -import { MetaLogicGateBuilding } from "./buildings/logic_gate"; -import { MetaMinerBuilding } from "./buildings/miner"; -import { MetaMixerBuilding } from "./buildings/mixer"; -import { MetaPainterBuilding } from "./buildings/painter"; -import { MetaReaderBuilding } from "./buildings/reader"; -import { MetaRotaterBuilding } from "./buildings/rotater"; -import { MetaStackerBuilding } from "./buildings/stacker"; -import { MetaStorageBuilding } from "./buildings/storage"; -import { MetaTransistorBuilding } from "./buildings/transistor"; -import { MetaTrashBuilding } from "./buildings/trash"; -import { MetaUndergroundBeltBuilding } from "./buildings/underground_belt"; -import { MetaVirtualProcessorBuilding } from "./buildings/virtual_processor"; -import { MetaWireBuilding } from "./buildings/wire"; -import { MetaWireTunnelBuilding } from "./buildings/wire_tunnel"; -import { buildBuildingCodeCache, gBuildingVariants, registerBuildingVariant } from "./building_codes"; -import { KEYMAPPINGS } from "./key_action_mapper"; -import { defaultBuildingVariant, MetaBuilding } from "./meta_building"; - -const logger = createLogger("building_registry"); - -/** - * - * @param {typeof MetaBuilding} metaBuilding - */ -export function registerBuildingVariants(metaBuilding) { - gMetaBuildingRegistry.register(metaBuilding); - const combinations = metaBuilding.getAllVariantCombinations(); - combinations.forEach(combination => { - registerBuildingVariant( - combination.internalId, - metaBuilding, - combination.variant || defaultBuildingVariant, - combination.rotationVariant || 0 - ); - }); -} - -export function initMetaBuildingRegistry() { - const buildings = [ - MetaBalancerBuilding, - MetaMinerBuilding, - MetaCutterBuilding, - MetaRotaterBuilding, - MetaStackerBuilding, - MetaMixerBuilding, - MetaPainterBuilding, - MetaTrashBuilding, - MetaStorageBuilding, - MetaBeltBuilding, - MetaUndergroundBeltBuilding, - MetaGoalAcceptorBuilding, - MetaHubBuilding, - MetaWireBuilding, - MetaConstantSignalBuilding, - MetaLogicGateBuilding, - MetaLeverBuilding, - MetaFilterBuilding, - MetaWireTunnelBuilding, - MetaDisplayBuilding, - MetaVirtualProcessorBuilding, - MetaReaderBuilding, - MetaTransistorBuilding, - MetaAnalyzerBuilding, - MetaComparatorBuilding, - MetaItemProducerBuilding, - MetaConstantProducerBuilding, - MetaBlockBuilding, - ]; - - buildings.forEach(registerBuildingVariants); - - // Check for valid keycodes - if (G_IS_DEV) { - gMetaBuildingRegistry.entries.forEach(metaBuilding => { - const id = metaBuilding.getId(); - if (!["hub"].includes(id)) { - if (!KEYMAPPINGS.buildings[id]) { - console.error( - "Building " + id + " has no keybinding assigned! Add it to key_action_mapper.js" - ); - } - - if (!T.buildings[id]) { - console.error("Translation for building " + id + " missing!"); - } else if (!T.buildings[id].default) { - console.error("Translation for building " + id + " missing (default variant)!"); - } - } - }); - } - - logger.log("Registered", gMetaBuildingRegistry.getNumEntries(), "buildings"); - logger.log("Registered", Object.keys(gBuildingVariants).length, "building codes"); -} - -/** - * Once all sprites are loaded, propagates the cache - */ -export function initSpriteCache() { - logger.log("Propagating sprite cache"); - for (const key in gBuildingVariants) { - const variant = gBuildingVariants[key]; - - variant.sprite = variant.metaInstance.getSprite(variant.rotationVariant, variant.variant); - variant.blueprintSprite = variant.metaInstance.getBlueprintSprite( - variant.rotationVariant, - variant.variant - ); - variant.silhouetteColor = variant.metaInstance.getSilhouetteColor( - variant.variant, - variant.rotationVariant - ); - } - - // Update caches - buildBuildingCodeCache(); -} +import { gMetaBuildingRegistry } from "../core/global_registries"; +import { createLogger } from "../core/logging"; +import { T } from "../translations"; +import { buildBuildingCodeCache, gBuildingVariants, registerBuildingVariant } from "./building_codes"; +import { MetaAnalyzerBuilding } from "./buildings/analyzer"; +import { MetaBalancerBuilding } from "./buildings/balancer"; +import { MetaBeltBuilding } from "./buildings/belt"; +import { MetaBlockBuilding } from "./buildings/block"; +import { MetaComparatorBuilding } from "./buildings/comparator"; +import { MetaConstantProducerBuilding } from "./buildings/constant_producer"; +import { MetaConstantSignalBuilding } from "./buildings/constant_signal"; +import { MetaCutterBuilding } from "./buildings/cutter"; +import { MetaDisplayBuilding } from "./buildings/display"; +import { MetaFilterBuilding } from "./buildings/filter"; +import { MetaGoalAcceptorBuilding } from "./buildings/goal_acceptor"; +import { MetaHubBuilding } from "./buildings/hub"; +import { MetaItemProducerBuilding } from "./buildings/item_producer"; +import { MetaLeverBuilding } from "./buildings/lever"; +import { MetaLogicGateBuilding } from "./buildings/logic_gate"; +import { MetaMinerBuilding } from "./buildings/miner"; +import { MetaMixerBuilding } from "./buildings/mixer"; +import { MetaPainterBuilding } from "./buildings/painter"; +import { MetaReaderBuilding } from "./buildings/reader"; +import { MetaRotatorBuilding } from "./buildings/rotator"; +import { MetaStackerBuilding } from "./buildings/stacker"; +import { MetaStorageBuilding } from "./buildings/storage"; +import { MetaTransistorBuilding } from "./buildings/transistor"; +import { MetaTrashBuilding } from "./buildings/trash"; +import { MetaUndergroundBeltBuilding } from "./buildings/underground_belt"; +import { MetaVirtualProcessorBuilding } from "./buildings/virtual_processor"; +import { MetaWireBuilding } from "./buildings/wire"; +import { MetaWireTunnelBuilding } from "./buildings/wire_tunnel"; +import { KEYMAPPINGS } from "./key_action_mapper"; +import { defaultBuildingVariant, MetaBuilding } from "./meta_building"; + +const logger = createLogger("building_registry"); + +/** + * + * @param {typeof MetaBuilding} metaBuilding + */ +export function registerBuildingVariants(metaBuilding) { + gMetaBuildingRegistry.register(metaBuilding); + const combinations = metaBuilding.getAllVariantCombinations(); + combinations.forEach(combination => { + registerBuildingVariant( + combination.internalId, + metaBuilding, + combination.variant || defaultBuildingVariant, + combination.rotationVariant || 0 + ); + }); +} + +export function initMetaBuildingRegistry() { + const buildings = [ + MetaBalancerBuilding, + MetaMinerBuilding, + MetaCutterBuilding, + MetaRotatorBuilding, + MetaStackerBuilding, + MetaMixerBuilding, + MetaPainterBuilding, + MetaTrashBuilding, + MetaStorageBuilding, + MetaBeltBuilding, + MetaUndergroundBeltBuilding, + MetaGoalAcceptorBuilding, + MetaHubBuilding, + MetaWireBuilding, + MetaConstantSignalBuilding, + MetaLogicGateBuilding, + MetaLeverBuilding, + MetaFilterBuilding, + MetaWireTunnelBuilding, + MetaDisplayBuilding, + MetaVirtualProcessorBuilding, + MetaReaderBuilding, + MetaTransistorBuilding, + MetaAnalyzerBuilding, + MetaComparatorBuilding, + MetaItemProducerBuilding, + MetaConstantProducerBuilding, + MetaBlockBuilding, + ]; + + buildings.forEach(registerBuildingVariants); + + // Check for valid keycodes + if (G_IS_DEV) { + gMetaBuildingRegistry.entries.forEach(metaBuilding => { + const id = metaBuilding.getId(); + if (!["hub"].includes(id)) { + if (!KEYMAPPINGS.buildings[id]) { + console.error( + "Building " + id + " has no keybinding assigned! Add it to key_action_mapper.js" + ); + } + + if (!T.buildings[id]) { + console.error("Translation for building " + id + " missing!"); + } else if (!T.buildings[id].default) { + console.error("Translation for building " + id + " missing (default variant)!"); + } + } + }); + } + + logger.log("Registered", gMetaBuildingRegistry.getNumEntries(), "buildings"); + logger.log("Registered", Object.keys(gBuildingVariants).length, "building codes"); +} + +/** + * Once all sprites are loaded, propagates the cache + */ +export function initSpriteCache() { + logger.log("Propagating sprite cache"); + for (const key in gBuildingVariants) { + const variant = gBuildingVariants[key]; + + variant.sprite = variant.metaInstance.getSprite(variant.rotationVariant, variant.variant); + variant.blueprintSprite = variant.metaInstance.getBlueprintSprite( + variant.rotationVariant, + variant.variant + ); + variant.silhouetteColor = variant.metaInstance.getSilhouetteColor( + variant.variant, + variant.rotationVariant + ); + } + + // Update caches + buildBuildingCodeCache(); +} diff --git a/src/js/game/modes/levels.js b/src/js/game/modes/levels.js index d6533448..dc34b101 100644 --- a/src/js/game/modes/levels.js +++ b/src/js/game/modes/levels.js @@ -1,167 +1,8 @@ -/* typehints:start */ -import { Application } from "../../application"; -/* typehints:end */ -import { WEB_STEAM_SSO_AUTHENTICATED } from "../../core/steam_sso"; import { enumHubGoalRewards } from "../tutorial_goals"; export const finalGameShape = "RuCw--Cw:----Ru--"; -const chinaShapes = G_WEGAME_VERSION || G_CHINA_VERSION; -//////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////// - -/** - * - * @param {Application} app - * @returns - */ -const WEB_DEMO_LEVELS = app => { - const levels = [ - // 1 - // Circle - { - shape: "CuCuCuCu", // belts t1 - required: 10, - reward: enumHubGoalRewards.reward_cutter_and_trash, - }, - - // 2 - // Cutter - { - shape: "----CuCu", // - required: 20, - reward: enumHubGoalRewards.no_reward, - }, - - // 3 - // Rectangle - { - shape: "RuRuRuRu", // miners t1 - required: 30, - reward: enumHubGoalRewards.reward_balancer, - }, - - // 4 - { - shape: "RuRu----", // processors t2 - required: 30, - reward: enumHubGoalRewards.reward_rotater, - }, - - // 5 - // Rotater - { - shape: "Cu----Cu", // belts t2 - required: 75, - reward: enumHubGoalRewards.reward_tunnel, - }, - - // 6 - // Painter - { - shape: "Cu------", // miners t2 - required: 50, - reward: enumHubGoalRewards.reward_painter, - }, - - // 7 - { - shape: "CrCrCrCr", // unused - required: 85, - reward: enumHubGoalRewards.reward_rotater_ccw, - }, - - // 8 - { - shape: "RbRb----", // painter t2 - required: 100, - reward: enumHubGoalRewards.reward_mixer, - }, - { - shape: "RpRp----", - required: 0, - reward: enumHubGoalRewards.reward_demo_end, - }, - ]; - - return levels; -}; - -//////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////// - -const STEAM_DEMO_LEVELS = () => [ - // 1 - // Circle - { - shape: "CuCuCuCu", // belts t1 - required: 35, - reward: enumHubGoalRewards.reward_cutter_and_trash, - }, - - // 2 - // Cutter - { - shape: "----CuCu", // - required: 45, - reward: enumHubGoalRewards.no_reward, - }, - - // 3 - // Rectangle - { - shape: "RuRuRuRu", // miners t1 - required: 90, - reward: enumHubGoalRewards.reward_balancer, - }, - - // 4 - { - shape: "RuRu----", // processors t2 - required: 70, - reward: enumHubGoalRewards.reward_rotater, - }, - - // 5 - // Rotater - { - shape: "Cu----Cu", // belts t2 - required: 160, - reward: enumHubGoalRewards.reward_tunnel, - }, - - // 6 - { - shape: "Cu------", // miners t2 - required: 160, - reward: enumHubGoalRewards.reward_painter, - }, - - // 7 - // Painter - { - shape: "CrCrCrCr", // unused - required: 140, - reward: enumHubGoalRewards.reward_rotater_ccw, - }, - // 8 - { - shape: "RbRb----", // painter t2 - required: 225, - reward: enumHubGoalRewards.reward_mixer, - }, - // End of demo - { - shape: "CpCpCpCp", - required: 0, - reward: enumHubGoalRewards.reward_demo_end, - }, -]; - -//////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////// - -const STANDALONE_LEVELS = () => [ +export const REGULAR_MODE_LEVELS = [ // 1 // Circle { @@ -190,11 +31,11 @@ const STANDALONE_LEVELS = () => [ { shape: "RuRu----", // processors t2 required: 70, - reward: enumHubGoalRewards.reward_rotater, + reward: enumHubGoalRewards.reward_rotator, }, // 5 - // Rotater + // Rotator { shape: "Cu----Cu", // belts t2 required: 170, @@ -213,7 +54,7 @@ const STANDALONE_LEVELS = () => [ { shape: "CrCrCrCr", // unused required: 300, - reward: enumHubGoalRewards.reward_rotater_ccw, + reward: enumHubGoalRewards.reward_rotator_ccw, }, // 8 { @@ -255,7 +96,7 @@ const STANDALONE_LEVELS = () => [ // 13 // Tunnel Tier 2 { - shape: chinaShapes ? "CuCuCuCu:CwCwCwCw:Sb--Sr--" : "RpRpRpRp:CwCwCwCw", // painting t3 + shape: "RpRpRpRp:CwCwCwCw", // painting t3 required: 3800, reward: enumHubGoalRewards.reward_underground_belt_tier_2, }, @@ -288,17 +129,17 @@ const STANDALONE_LEVELS = () => [ // 17 // Double painter { - shape: chinaShapes ? "CyCyCyCy:CyCyCyCy:RyRyRyRy:RuRuRuRu" : "CbRbRbCb:CwCwCwCw:WbWbWbWb", // miner t4 (two variants) + shape: "CbRbRbCb:CwCwCwCw:WbWbWbWb", // miner t4 (two variants) required: 20000, reward: enumHubGoalRewards.reward_painter_double, }, // 18 - // Rotater (180deg) + // Rotator (180deg) { shape: "Sg----Sg:CgCgCgCg:--CyCy--", // unused required: 20000, - reward: enumHubGoalRewards.reward_rotater_180, + reward: enumHubGoalRewards.reward_rotator_180, }, // 19 @@ -328,7 +169,7 @@ const STANDALONE_LEVELS = () => [ // 22 // Constant signal { - shape: chinaShapes ? "RrSySrSy:RyCrCwCr:CyCyRyCy" : "Cg----Cr:Cw----Cw:Sy------:Cy----Cy", + shape: "Cg----Cr:Cw----Cw:Sy------:Cy----Cy", required: 25000, reward: enumHubGoalRewards.reward_constant_signal, }, @@ -336,14 +177,14 @@ const STANDALONE_LEVELS = () => [ // 23 // Display { - shape: chinaShapes ? "CrCrCrCr:CwCwCwCw:WwWwWwWw:CrCrCrCr" : "CcSyCcSy:SyCcSyCc:CcSyCcSy", + shape: "CcSyCcSy:SyCcSyCc:CcSyCcSy", required: 25000, reward: enumHubGoalRewards.reward_display, }, // 24 Logic gates { - shape: chinaShapes ? "Su----Su:RwRwRwRw:Cu----Cu:CwCwCwCw" : "CcRcCcRc:RwCwRwCw:Sr--Sw--:CyCyCyCy", + shape: "CcRcCcRc:RwCwRwCw:Sr--Sw--:CyCyCyCy", required: 25000, reward: enumHubGoalRewards.reward_logic_gates, }, @@ -362,15 +203,3 @@ const STANDALONE_LEVELS = () => [ reward: enumHubGoalRewards.reward_freeplay, }, ]; - -/** - * Generates the level definitions - */ -export function generateLevelsForVariant(app) { - if (G_IS_STEAM_DEMO) { - return STEAM_DEMO_LEVELS(); - } else if (G_IS_STANDALONE || WEB_STEAM_SSO_AUTHENTICATED) { - return STANDALONE_LEVELS(); - } - return WEB_DEMO_LEVELS(app); -} diff --git a/src/js/game/modes/puzzle_play.js b/src/js/game/modes/puzzle_play.js index 19957eee..89ea99b2 100644 --- a/src/js/game/modes/puzzle_play.js +++ b/src/js/game/modes/puzzle_play.js @@ -33,7 +33,7 @@ import { gMetaBuildingRegistry } from "../../core/global_registries"; import { HUDPuzzleNextPuzzle } from "../hud/parts/next_puzzle"; const logger = createLogger("puzzle-play"); -const copy = require("clipboard-copy"); +import copy from "clipboard-copy"; export class PuzzlePlayGameMode extends PuzzleGameMode { static getId() { @@ -177,7 +177,7 @@ export class PuzzlePlayGameMode extends PuzzleGameMode { } ); - return new Promise(resolve => { + return new /** @type {typeof Promise} */ (Promise)(resolve => { optionSelected.add(option => { const closeLoading = this.root.hud.parts.dialogs.showLoadingDialog(); diff --git a/src/js/game/modes/regular.js b/src/js/game/modes/regular.js index 8d5d6633..c9e2edc3 100644 --- a/src/js/game/modes/regular.js +++ b/src/js/game/modes/regular.js @@ -1,422 +1,393 @@ -/* typehints:start */ -import { GameRoot } from "../root"; -import { MetaBuilding } from "../meta_building"; -/* typehints:end */ - -import { findNiceIntegerValue } from "../../core/utils"; -import { MetaConstantProducerBuilding } from "../buildings/constant_producer"; -import { MetaGoalAcceptorBuilding } from "../buildings/goal_acceptor"; -import { enumGameModeIds, enumGameModeTypes, GameMode } from "../game_mode"; -import { ShapeDefinition } from "../shape_definition"; -import { enumHubGoalRewards } from "../tutorial_goals"; -import { HUDWiresToolbar } from "../hud/parts/wires_toolbar"; -import { HUDUnlockNotification } from "../hud/parts/unlock_notification"; -import { HUDMassSelector } from "../hud/parts/mass_selector"; -import { HUDShop } from "../hud/parts/shop"; -import { HUDWaypoints } from "../hud/parts/waypoints"; -import { HUDStatistics } from "../hud/parts/statistics"; -import { HUDWireInfo } from "../hud/parts/wire_info"; -import { HUDLeverToggle } from "../hud/parts/lever_toggle"; -import { HUDPinnedShapes } from "../hud/parts/pinned_shapes"; -import { HUDNotifications } from "../hud/parts/notifications"; -import { HUDScreenshotExporter } from "../hud/parts/screenshot_exporter"; -import { HUDWiresOverlay } from "../hud/parts/wires_overlay"; -import { HUDShapeViewer } from "../hud/parts/shape_viewer"; -import { HUDLayerPreview } from "../hud/parts/layer_preview"; -import { HUDTutorialVideoOffer } from "../hud/parts/tutorial_video_offer"; -import { HUDMinerHighlight } from "../hud/parts/miner_highlight"; -import { HUDGameMenu } from "../hud/parts/game_menu"; -import { HUDConstantSignalEdit } from "../hud/parts/constant_signal_edit"; -import { IS_MOBILE } from "../../core/config"; -import { HUDKeybindingOverlay } from "../hud/parts/keybinding_overlay"; -import { HUDWatermark } from "../hud/parts/watermark"; -import { HUDStandaloneAdvantages } from "../hud/parts/standalone_advantages"; -import { HUDPartTutorialHints } from "../hud/parts/tutorial_hints"; -import { HUDInteractiveTutorial } from "../hud/parts/interactive_tutorial"; -import { MetaBlockBuilding } from "../buildings/block"; -import { MetaItemProducerBuilding } from "../buildings/item_producer"; -import { MOD_SIGNALS } from "../../mods/mod_signals"; -import { finalGameShape, generateLevelsForVariant } from "./levels"; -import { WEB_STEAM_SSO_AUTHENTICATED } from "../../core/steam_sso"; - -/** @typedef {{ - * shape: string, - * amount: number - * }} UpgradeRequirement */ - -/** @typedef {{ - * required: Array - * improvement?: number, - * excludePrevious?: boolean - * }} TierRequirement */ - -/** @typedef {Array} UpgradeTiers */ - -/** @typedef {{ - * shape: string, - * required: number, - * reward: enumHubGoalRewards, - * throughputOnly?: boolean - * }} LevelDefinition */ - -export const rocketShape = "CbCuCbCu:Sr------:--CrSrCr:CwCwCwCw"; -const preparementShape = "CpRpCp--:SwSwSwSw"; - -// Tiers need % of the previous tier as requirement too -const tierGrowth = 2.5; - -const chinaShapes = G_WEGAME_VERSION || G_CHINA_VERSION; - -const upgradesCache = {}; - -/** - * Generates all upgrades - * @returns {Object} */ -function generateUpgrades(limitedVersion = false, difficulty = 1) { - if (upgradesCache[limitedVersion]) { - return upgradesCache[limitedVersion]; - } - - const fixedImprovements = [0.5, 0.5, 1, 1, 2, 1, 1]; - const numEndgameUpgrades = limitedVersion ? 0 : 1000 - fixedImprovements.length - 1; - - function generateInfiniteUnlocks() { - return new Array(numEndgameUpgrades).fill(null).map((_, i) => ({ - required: [ - { shape: preparementShape, amount: 30000 + i * 10000 }, - { shape: finalGameShape, amount: 20000 + i * 5000 }, - { shape: rocketShape, amount: 20000 + i * 5000 }, - ], - excludePrevious: true, - })); - } - - // Fill in endgame upgrades - for (let i = 0; i < numEndgameUpgrades; ++i) { - if (i < 20) { - fixedImprovements.push(0.1); - } else if (i < 50) { - fixedImprovements.push(0.05); - } else if (i < 100) { - fixedImprovements.push(0.025); - } else { - fixedImprovements.push(0.0125); - } - } - - const upgrades = { - belt: [ - { - required: [{ shape: "CuCuCuCu", amount: 30 }], - }, - { - required: [{ shape: "--CuCu--", amount: 500 }], - }, - { - required: [{ shape: "CpCpCpCp", amount: 1000 }], - }, - { - required: [{ shape: "SrSrSrSr:CyCyCyCy", amount: 6000 }], - }, - { - required: [{ shape: "SrSrSrSr:CyCyCyCy:SwSwSwSw", amount: 25000 }], - }, - { - required: [{ shape: preparementShape, amount: 25000 }], - excludePrevious: true, - }, - { - required: [ - { shape: preparementShape, amount: 25000 }, - { shape: finalGameShape, amount: 50000 }, - ], - excludePrevious: true, - }, - ...generateInfiniteUnlocks(), - ], - - miner: [ - { - required: [{ shape: "RuRuRuRu", amount: 300 }], - }, - { - required: [{ shape: "Cu------", amount: 800 }], - }, - { - required: [{ shape: "ScScScSc", amount: 3500 }], - }, - { - required: [{ shape: "CwCwCwCw:WbWbWbWb", amount: 23000 }], - }, - { - required: [ - { - shape: chinaShapes - ? "CyCyCyCy:CyCyCyCy:RyRyRyRy:RuRuRuRu" - : "CbRbRbCb:CwCwCwCw:WbWbWbWb", - amount: 50000, - }, - ], - }, - { - required: [{ shape: preparementShape, amount: 25000 }], - excludePrevious: true, - }, - { - required: [ - { shape: preparementShape, amount: 25000 }, - { shape: finalGameShape, amount: 50000 }, - ], - excludePrevious: true, - }, - ...generateInfiniteUnlocks(), - ], - - processors: [ - { - required: [{ shape: "SuSuSuSu", amount: 500 }], - }, - { - required: [{ shape: "RuRu----", amount: 600 }], - }, - { - required: [{ shape: "CgScScCg", amount: 3500 }], - }, - { - required: [{ shape: "CwCrCwCr:SgSgSgSg", amount: 25000 }], - }, - { - required: [{ shape: "WrRgWrRg:CwCrCwCr:SgSgSgSg", amount: 50000 }], - }, - { - required: [{ shape: preparementShape, amount: 25000 }], - excludePrevious: true, - }, - { - required: [ - { shape: preparementShape, amount: 25000 }, - { shape: finalGameShape, amount: 50000 }, - ], - excludePrevious: true, - }, - ...generateInfiniteUnlocks(), - ], - - painting: [ - { - required: [{ shape: "RbRb----", amount: 600 }], - }, - { - required: [{ shape: "WrWrWrWr", amount: 3800 }], - }, - { - required: [ - { - shape: chinaShapes ? "CuCuCuCu:CwCwCwCw:Sb--Sr--" : "RpRpRpRp:CwCwCwCw", - amount: 6500, - }, - ], - }, - { - required: [{ shape: "WpWpWpWp:CwCwCwCw:WpWpWpWp", amount: 25000 }], - }, - { - required: [{ shape: "WpWpWpWp:CwCwCwCw:WpWpWpWp:CwCwCwCw", amount: 50000 }], - }, - { - required: [{ shape: preparementShape, amount: 25000 }], - excludePrevious: true, - }, - { - required: [ - { shape: preparementShape, amount: 25000 }, - { shape: finalGameShape, amount: 50000 }, - ], - excludePrevious: true, - }, - ...generateInfiniteUnlocks(), - ], - }; - - // Automatically generate tier levels - for (const upgradeId in upgrades) { - const upgradeTiers = upgrades[upgradeId]; - - let currentTierRequirements = []; - for (let i = 0; i < upgradeTiers.length; ++i) { - const tierHandle = upgradeTiers[i]; - tierHandle.improvement = fixedImprovements[i]; - - tierHandle.required.forEach(required => { - required.amount = Math.round(required.amount * difficulty); - }); - const originalRequired = tierHandle.required.slice(); - - for (let k = currentTierRequirements.length - 1; k >= 0; --k) { - const oldTierRequirement = currentTierRequirements[k]; - if (!tierHandle.excludePrevious) { - tierHandle.required.unshift({ - shape: oldTierRequirement.shape, - amount: oldTierRequirement.amount, - }); - } - } - currentTierRequirements.push( - ...originalRequired.map(req => ({ - amount: req.amount, - shape: req.shape, - })) - ); - currentTierRequirements.forEach(tier => { - tier.amount = findNiceIntegerValue(tier.amount * tierGrowth); - }); - } - } - - MOD_SIGNALS.modifyUpgrades.dispatch(upgrades); - - // VALIDATE - if (G_IS_DEV) { - for (const upgradeId in upgrades) { - upgrades[upgradeId].forEach(tier => { - tier.required.forEach(({ shape }) => { - try { - ShapeDefinition.fromShortKey(shape); - } catch (ex) { - throw new Error("Invalid upgrade goal: '" + ex + "' for shape" + shape); - } - }); - }); - } - } - - upgradesCache[limitedVersion] = upgrades; - return upgrades; -} - -let levelDefinitionsCache = null; - -/** - * Generates the level definitions - */ -export function generateLevelDefinitions(app) { - if (levelDefinitionsCache) { - return levelDefinitionsCache; - } - const levelDefinitions = generateLevelsForVariant(app); - MOD_SIGNALS.modifyLevelDefinitions.dispatch(levelDefinitions); - if (G_IS_DEV) { - levelDefinitions.forEach(({ shape }) => { - try { - ShapeDefinition.fromShortKey(shape); - } catch (ex) { - throw new Error("Invalid tutorial goal: '" + ex + "' for shape" + shape); - } - }); - } - levelDefinitionsCache = levelDefinitions; - return levelDefinitions; -} - -export class RegularGameMode extends GameMode { - static getId() { - return enumGameModeIds.regular; - } - - static getType() { - return enumGameModeTypes.default; - } - - /** @param {GameRoot} root */ - constructor(root) { - super(root); - - this.additionalHudParts = { - wiresToolbar: HUDWiresToolbar, - unlockNotification: HUDUnlockNotification, - massSelector: HUDMassSelector, - shop: HUDShop, - statistics: HUDStatistics, - waypoints: HUDWaypoints, - wireInfo: HUDWireInfo, - leverToggle: HUDLeverToggle, - pinnedShapes: HUDPinnedShapes, - notifications: HUDNotifications, - screenshotExporter: HUDScreenshotExporter, - wiresOverlay: HUDWiresOverlay, - shapeViewer: HUDShapeViewer, - layerPreview: HUDLayerPreview, - minerHighlight: HUDMinerHighlight, - tutorialVideoOffer: HUDTutorialVideoOffer, - gameMenu: HUDGameMenu, - constantSignalEdit: HUDConstantSignalEdit, - }; - - if (!IS_MOBILE) { - this.additionalHudParts.keybindingOverlay = HUDKeybindingOverlay; - } - - if (this.root.app.restrictionMgr.getIsStandaloneMarketingActive()) { - this.additionalHudParts.watermark = HUDWatermark; - this.additionalHudParts.standaloneAdvantages = HUDStandaloneAdvantages; - } - - if (this.root.app.settings.getAllSettings().offerHints) { - if (!G_WEGAME_VERSION) { - this.additionalHudParts.tutorialHints = HUDPartTutorialHints; - } - this.additionalHudParts.interactiveTutorial = HUDInteractiveTutorial; - } - - /** @type {(typeof MetaBuilding)[]} */ - this.hiddenBuildings = [ - MetaConstantProducerBuilding, - MetaGoalAcceptorBuilding, - MetaBlockBuilding, - MetaItemProducerBuilding, - ]; - } - - get difficultyMultiplicator() { - if (G_IS_STANDALONE || WEB_STEAM_SSO_AUTHENTICATED) { - if (G_IS_STEAM_DEMO) { - return 0.75; - } - return 1; - } - return 0.5; - } - - /** - * Should return all available upgrades - * @returns {Object} - */ - getUpgrades() { - return generateUpgrades( - !this.root.app.restrictionMgr.getHasExtendedUpgrades(), - this.difficultyMultiplicator - ); - } - - /** - * Returns the goals for all levels including their reward - * @returns {Array} - */ - getLevelDefinitions() { - return generateLevelDefinitions(this.root.app); - } - - /** - * Should return whether free play is available or if the game stops - * after the predefined levels - * @returns {boolean} - */ - getIsFreeplayAvailable() { - return this.root.app.restrictionMgr.getHasExtendedLevelsAndFreeplay(); - } - - /** @returns {boolean} */ - hasAchievements() { - return true; - } -} +/* typehints:start */ +import { MetaBuilding } from "../meta_building"; +import { GameRoot } from "../root"; +/* typehints:end */ + +import { IS_MOBILE } from "../../core/config"; +import { findNiceIntegerValue } from "../../core/utils"; +import { MOD_SIGNALS } from "../../mods/mod_signals"; +import { MetaBlockBuilding } from "../buildings/block"; +import { MetaConstantProducerBuilding } from "../buildings/constant_producer"; +import { MetaGoalAcceptorBuilding } from "../buildings/goal_acceptor"; +import { MetaItemProducerBuilding } from "../buildings/item_producer"; +import { enumGameModeIds, enumGameModeTypes, GameMode } from "../game_mode"; +import { HUDConstantSignalEdit } from "../hud/parts/constant_signal_edit"; +import { HUDGameMenu } from "../hud/parts/game_menu"; +import { HUDInteractiveTutorial } from "../hud/parts/interactive_tutorial"; +import { HUDKeybindingOverlay } from "../hud/parts/keybinding_overlay"; +import { HUDLayerPreview } from "../hud/parts/layer_preview"; +import { HUDLeverToggle } from "../hud/parts/lever_toggle"; +import { HUDMassSelector } from "../hud/parts/mass_selector"; +import { HUDMinerHighlight } from "../hud/parts/miner_highlight"; +import { HUDNotifications } from "../hud/parts/notifications"; +import { HUDPinnedShapes } from "../hud/parts/pinned_shapes"; +import { HUDScreenshotExporter } from "../hud/parts/screenshot_exporter"; +import { HUDShapeViewer } from "../hud/parts/shape_viewer"; +import { HUDShop } from "../hud/parts/shop"; +import { HUDStatistics } from "../hud/parts/statistics"; +import { HUDPartTutorialHints } from "../hud/parts/tutorial_hints"; +import { HUDTutorialVideoOffer } from "../hud/parts/tutorial_video_offer"; +import { HUDUnlockNotification } from "../hud/parts/unlock_notification"; +import { HUDWaypoints } from "../hud/parts/waypoints"; +import { HUDWireInfo } from "../hud/parts/wire_info"; +import { HUDWiresOverlay } from "../hud/parts/wires_overlay"; +import { HUDWiresToolbar } from "../hud/parts/wires_toolbar"; +import { ShapeDefinition } from "../shape_definition"; +import { enumHubGoalRewards } from "../tutorial_goals"; +import { finalGameShape, REGULAR_MODE_LEVELS } from "./levels"; + +/** @typedef {{ + * shape: string, + * amount: number + * }} UpgradeRequirement */ + +/** @typedef {{ + * required: Array + * improvement?: number, + * excludePrevious?: boolean + * }} TierRequirement */ + +/** @typedef {Array} UpgradeTiers */ + +/** @typedef {{ + * shape: string, + * required: number, + * reward: enumHubGoalRewards, + * throughputOnly?: boolean + * }} LevelDefinition */ + +export const rocketShape = "CbCuCbCu:Sr------:--CrSrCr:CwCwCwCw"; +const preparementShape = "CpRpCp--:SwSwSwSw"; + +// Tiers need % of the previous tier as requirement too +const tierGrowth = 2.5; + +// TODO: Convert this file to TS and fix types. Maybe split the levels and upgrades as well +let upgradesCache = null; + +/** + * Generates all upgrades + * @returns {Object} + */ +function generateUpgrades() { + if (upgradesCache) { + return upgradesCache; + } + + const fixedImprovements = [0.5, 0.5, 1, 1, 2, 1, 1]; + const numEndgameUpgrades = 1000 - fixedImprovements.length - 1; + + function generateInfiniteUnlocks() { + return new Array(numEndgameUpgrades).fill(null).map((_, i) => ({ + required: [ + { shape: preparementShape, amount: 30000 + i * 10000 }, + { shape: finalGameShape, amount: 20000 + i * 5000 }, + { shape: rocketShape, amount: 20000 + i * 5000 }, + ], + excludePrevious: true, + })); + } + + // Fill in endgame upgrades + for (let i = 0; i < numEndgameUpgrades; ++i) { + if (i < 20) { + fixedImprovements.push(0.1); + } else if (i < 50) { + fixedImprovements.push(0.05); + } else if (i < 100) { + fixedImprovements.push(0.025); + } else { + fixedImprovements.push(0.0125); + } + } + + const upgrades = { + belt: [ + { + required: [{ shape: "CuCuCuCu", amount: 30 }], + }, + { + required: [{ shape: "--CuCu--", amount: 500 }], + }, + { + required: [{ shape: "CpCpCpCp", amount: 1000 }], + }, + { + required: [{ shape: "SrSrSrSr:CyCyCyCy", amount: 6000 }], + }, + { + required: [{ shape: "SrSrSrSr:CyCyCyCy:SwSwSwSw", amount: 25000 }], + }, + { + required: [{ shape: preparementShape, amount: 25000 }], + excludePrevious: true, + }, + { + required: [ + { shape: preparementShape, amount: 25000 }, + { shape: finalGameShape, amount: 50000 }, + ], + excludePrevious: true, + }, + ...generateInfiniteUnlocks(), + ], + + miner: [ + { + required: [{ shape: "RuRuRuRu", amount: 300 }], + }, + { + required: [{ shape: "Cu------", amount: 800 }], + }, + { + required: [{ shape: "ScScScSc", amount: 3500 }], + }, + { + required: [{ shape: "CwCwCwCw:WbWbWbWb", amount: 23000 }], + }, + { + required: [ + { + shape: "CbRbRbCb:CwCwCwCw:WbWbWbWb", + amount: 50000, + }, + ], + }, + { + required: [{ shape: preparementShape, amount: 25000 }], + excludePrevious: true, + }, + { + required: [ + { shape: preparementShape, amount: 25000 }, + { shape: finalGameShape, amount: 50000 }, + ], + excludePrevious: true, + }, + ...generateInfiniteUnlocks(), + ], + + processors: [ + { + required: [{ shape: "SuSuSuSu", amount: 500 }], + }, + { + required: [{ shape: "RuRu----", amount: 600 }], + }, + { + required: [{ shape: "CgScScCg", amount: 3500 }], + }, + { + required: [{ shape: "CwCrCwCr:SgSgSgSg", amount: 25000 }], + }, + { + required: [{ shape: "WrRgWrRg:CwCrCwCr:SgSgSgSg", amount: 50000 }], + }, + { + required: [{ shape: preparementShape, amount: 25000 }], + excludePrevious: true, + }, + { + required: [ + { shape: preparementShape, amount: 25000 }, + { shape: finalGameShape, amount: 50000 }, + ], + excludePrevious: true, + }, + ...generateInfiniteUnlocks(), + ], + + painting: [ + { + required: [{ shape: "RbRb----", amount: 600 }], + }, + { + required: [{ shape: "WrWrWrWr", amount: 3800 }], + }, + { + required: [ + { + shape: "RpRpRpRp:CwCwCwCw", + amount: 6500, + }, + ], + }, + { + required: [{ shape: "WpWpWpWp:CwCwCwCw:WpWpWpWp", amount: 25000 }], + }, + { + required: [{ shape: "WpWpWpWp:CwCwCwCw:WpWpWpWp:CwCwCwCw", amount: 50000 }], + }, + { + required: [{ shape: preparementShape, amount: 25000 }], + excludePrevious: true, + }, + { + required: [ + { shape: preparementShape, amount: 25000 }, + { shape: finalGameShape, amount: 50000 }, + ], + excludePrevious: true, + }, + ...generateInfiniteUnlocks(), + ], + }; + + // Automatically generate tier levels + for (const upgradeId in upgrades) { + const upgradeTiers = upgrades[upgradeId]; + + let currentTierRequirements = []; + for (let i = 0; i < upgradeTiers.length; ++i) { + const tierHandle = upgradeTiers[i]; + tierHandle.improvement = fixedImprovements[i]; + + const originalRequired = tierHandle.required.slice(); + + for (let k = currentTierRequirements.length - 1; k >= 0; --k) { + const oldTierRequirement = currentTierRequirements[k]; + if (!tierHandle.excludePrevious) { + tierHandle.required.unshift({ + shape: oldTierRequirement.shape, + amount: oldTierRequirement.amount, + }); + } + } + currentTierRequirements.push( + ...originalRequired.map(req => ({ + amount: req.amount, + shape: req.shape, + })) + ); + currentTierRequirements.forEach(tier => { + tier.amount = findNiceIntegerValue(tier.amount * tierGrowth); + }); + } + } + + MOD_SIGNALS.modifyUpgrades.dispatch(upgrades); + + // VALIDATE + if (G_IS_DEV) { + for (const upgradeId in upgrades) { + upgrades[upgradeId].forEach(tier => { + tier.required.forEach(({ shape }) => { + try { + ShapeDefinition.fromShortKey(shape); + } catch (ex) { + throw new Error("Invalid upgrade goal for shape " + shape, { cause: ex }); + } + }); + }); + } + } + + upgradesCache = upgrades; + return upgrades; +} + +let levelDefinitionsCache = null; + +/** + * Generates the level definitions + */ +export function generateLevelDefinitions() { + // NOTE: This cache is useless in production, but is there because of the G_IS_DEV validation + if (levelDefinitionsCache) { + return levelDefinitionsCache; + } + + const levelDefinitions = REGULAR_MODE_LEVELS; + MOD_SIGNALS.modifyLevelDefinitions.dispatch(levelDefinitions); + + if (G_IS_DEV) { + levelDefinitions.forEach(({ shape }) => { + try { + ShapeDefinition.fromShortKey(shape); + } catch (ex) { + throw new Error("Invalid tutorial goal for shape " + shape, { cause: ex }); + } + }); + } + + levelDefinitionsCache = levelDefinitions; + return levelDefinitions; +} + +export class RegularGameMode extends GameMode { + static getId() { + return enumGameModeIds.regular; + } + + static getType() { + return enumGameModeTypes.default; + } + + /** @param {GameRoot} root */ + constructor(root) { + super(root); + + this.additionalHudParts = { + wiresToolbar: HUDWiresToolbar, + unlockNotification: HUDUnlockNotification, + massSelector: HUDMassSelector, + shop: HUDShop, + statistics: HUDStatistics, + waypoints: HUDWaypoints, + wireInfo: HUDWireInfo, + leverToggle: HUDLeverToggle, + pinnedShapes: HUDPinnedShapes, + notifications: HUDNotifications, + screenshotExporter: HUDScreenshotExporter, + wiresOverlay: HUDWiresOverlay, + shapeViewer: HUDShapeViewer, + layerPreview: HUDLayerPreview, + minerHighlight: HUDMinerHighlight, + tutorialVideoOffer: HUDTutorialVideoOffer, + gameMenu: HUDGameMenu, + constantSignalEdit: HUDConstantSignalEdit, + }; + + if (!IS_MOBILE) { + this.additionalHudParts.keybindingOverlay = HUDKeybindingOverlay; + } + + if (this.root.app.settings.getAllSettings().offerHints) { + this.additionalHudParts.tutorialHints = HUDPartTutorialHints; + this.additionalHudParts.interactiveTutorial = HUDInteractiveTutorial; + } + + /** @type {(typeof MetaBuilding)[]} */ + this.hiddenBuildings = [ + MetaConstantProducerBuilding, + MetaGoalAcceptorBuilding, + MetaBlockBuilding, + MetaItemProducerBuilding, + ]; + } + + /** + * Should return all available upgrades + * @returns {Object} + */ + getUpgrades() { + return generateUpgrades(); + } + + /** + * Returns the goals for all levels including their reward + * @returns {Array} + */ + getLevelDefinitions() { + return generateLevelDefinitions(); + } + + /** + * Should return whether free play is available or if the game stops + * after the predefined levels + * @returns {boolean} + */ + getIsFreeplayAvailable() { + return true; + } +} diff --git a/src/js/game/root.js b/src/js/game/root.js index 64004e9d..b41d76c4 100644 --- a/src/js/game/root.js +++ b/src/js/game/root.js @@ -1,242 +1,233 @@ -/* eslint-disable no-unused-vars */ -import { Signal } from "../core/signal"; -import { RandomNumberGenerator } from "../core/rng"; -import { createLogger } from "../core/logging"; - -// Type hints -/* typehints:start */ -import { GameTime } from "./time/game_time"; -import { EntityManager } from "./entity_manager"; -import { GameSystemManager } from "./game_system_manager"; -import { AchievementProxy } from "./achievement_proxy"; -import { GameHUD } from "./hud/hud"; -import { MapView } from "./map_view"; -import { Camera } from "./camera"; -import { InGameState } from "../states/ingame"; -import { AutomaticSave } from "./automatic_save"; -import { Application } from "../application"; -import { SoundProxy } from "./sound_proxy"; -import { Savegame } from "../savegame/savegame"; -import { GameLogic } from "./logic"; -import { ShapeDefinitionManager } from "./shape_definition_manager"; -import { HubGoals } from "./hub_goals"; -import { BufferMaintainer } from "../core/buffer_maintainer"; -import { ProductionAnalytics } from "./production_analytics"; -import { Entity } from "./entity"; -import { ShapeDefinition } from "./shape_definition"; -import { BaseItem } from "./base_item"; -import { DynamicTickrate } from "./dynamic_tickrate"; -import { KeyActionMapper } from "./key_action_mapper"; -import { Vector } from "../core/vector"; -import { GameMode } from "./game_mode"; -/* typehints:end */ - -const logger = createLogger("game/root"); - -/** @type {Array} */ -export const layers = ["regular", "wires"]; - -/** - * The game root is basically the whole game state at a given point, - * combining all important classes. We don't have globals, but this - * class is passed to almost all game classes. - */ -export class GameRoot { - /** - * Constructs a new game root - * @param {Application} app - */ - constructor(app) { - this.app = app; - - /** @type {Savegame} */ - this.savegame = null; - - /** @type {InGameState} */ - this.gameState = null; - - /** @type {KeyActionMapper} */ - this.keyMapper = null; - - // Store game dimensions - this.gameWidth = 500; - this.gameHeight = 500; - - // Stores whether the current session is a fresh game (true), or was continued (false) - /** @type {boolean} */ - this.gameIsFresh = true; - - // Stores whether the logic is already initialized - /** @type {boolean} */ - this.logicInitialized = false; - - // Stores whether the game is already initialized, that is, all systems etc have been created - /** @type {boolean} */ - this.gameInitialized = false; - - /** - * Whether a bulk operation is running - */ - this.bulkOperationRunning = false; - - /** - * Whether a immutable operation is running - */ - this.immutableOperationRunning = false; - - //////// Other properties /////// - - /** @type {Camera} */ - this.camera = null; - - /** @type {HTMLCanvasElement} */ - this.canvas = null; - - /** @type {CanvasRenderingContext2D} */ - this.context = null; - - /** @type {MapView} */ - this.map = null; - - /** @type {GameLogic} */ - this.logic = null; - - /** @type {EntityManager} */ - this.entityMgr = null; - - /** @type {GameHUD} */ - this.hud = null; - - /** @type {GameSystemManager} */ - this.systemMgr = null; - - /** @type {GameTime} */ - this.time = null; - - /** @type {HubGoals} */ - this.hubGoals = null; - - /** @type {BufferMaintainer} */ - this.buffers = null; - - /** @type {AutomaticSave} */ - this.automaticSave = null; - - /** @type {SoundProxy} */ - this.soundProxy = null; - - /** @type {AchievementProxy} */ - this.achievementProxy = null; - - /** @type {ShapeDefinitionManager} */ - this.shapeDefinitionMgr = null; - - /** @type {ProductionAnalytics} */ - this.productionAnalytics = null; - - /** @type {DynamicTickrate} */ - this.dynamicTickrate = null; - - /** @type {Layer} */ - this.currentLayer = "regular"; - - /** @type {GameMode} */ - this.gameMode = null; - - this.signals = { - // Entities - entityManuallyPlaced: /** @type {TypedSignal<[Entity]>} */ (new Signal()), - entityAdded: /** @type {TypedSignal<[Entity]>} */ (new Signal()), - entityChanged: /** @type {TypedSignal<[Entity]>} */ (new Signal()), - entityGotNewComponent: /** @type {TypedSignal<[Entity]>} */ (new Signal()), - entityComponentRemoved: /** @type {TypedSignal<[Entity]>} */ (new Signal()), - entityQueuedForDestroy: /** @type {TypedSignal<[Entity]>} */ (new Signal()), - entityDestroyed: /** @type {TypedSignal<[Entity]>} */ (new Signal()), - - // Global - resized: /** @type {TypedSignal<[number, number]>} */ (new Signal()), - readyToRender: /** @type {TypedSignal<[]>} */ (new Signal()), - aboutToDestruct: /** @type {TypedSignal<[]>} */ new Signal(), - - // Game Hooks - gameSaved: /** @type {TypedSignal<[]>} */ (new Signal()), // Game got saved - gameRestored: /** @type {TypedSignal<[]>} */ (new Signal()), // Game got restored - - gameFrameStarted: /** @type {TypedSignal<[]>} */ (new Signal()), // New frame - - storyGoalCompleted: /** @type {TypedSignal<[number, string]>} */ (new Signal()), - upgradePurchased: /** @type {TypedSignal<[string]>} */ (new Signal()), - - // Called right after game is initialized - postLoadHook: /** @type {TypedSignal<[]>} */ (new Signal()), - - shapeDelivered: /** @type {TypedSignal<[ShapeDefinition]>} */ (new Signal()), - itemProduced: /** @type {TypedSignal<[BaseItem]>} */ (new Signal()), - - bulkOperationFinished: /** @type {TypedSignal<[]>} */ (new Signal()), - immutableOperationFinished: /** @type {TypedSignal<[]>} */ (new Signal()), - - editModeChanged: /** @type {TypedSignal<[Layer]>} */ (new Signal()), - - // Called to check if an entity can be placed, second parameter is an additional offset. - // Use to introduce additional placement checks - prePlacementCheck: /** @type {TypedSignal<[Entity, Vector]>} */ (new Signal()), - - // Called before actually placing an entity, use to perform additional logic - // for freeing space before actually placing. - freeEntityAreaBeforeBuild: /** @type {TypedSignal<[Entity]>} */ (new Signal()), - - // Called with an achievement key and necessary args to validate it can be unlocked. - achievementCheck: /** @type {TypedSignal<[string, any]>} */ (new Signal()), - bulkAchievementCheck: /** @type {TypedSignal<(string|any)[]>} */ (new Signal()), - - // Puzzle mode - puzzleComplete: /** @type {TypedSignal<[]>} */ (new Signal()), - }; - - // RNG's - /** @type {Object.>} */ - this.rngs = {}; - - // Work queue - this.queue = { - requireRedraw: false, - }; - } - - /** - * Destructs the game root - */ - destruct() { - logger.log("destructing root"); - this.signals.aboutToDestruct.dispatch(); - - this.reset(); - } - - /** - * Resets the whole root and removes all properties - */ - reset() { - if (this.signals) { - // Destruct all signals - for (let i = 0; i < this.signals.length; ++i) { - this.signals[i].removeAll(); - } - } - - if (this.hud) { - this.hud.cleanup(); - } - if (this.camera) { - this.camera.cleanup(); - } - - // Finally free all properties - for (let prop in this) { - if (this.hasOwnProperty(prop)) { - delete this[prop]; - } - } - } -} +import { createLogger } from "../core/logging"; +import { RandomNumberGenerator } from "../core/rng"; +import { Signal } from "../core/signal"; + +// Type hints +/* typehints:start */ +import { Application } from "../application"; +import { BufferMaintainer } from "../core/buffer_maintainer"; +import { Vector } from "../core/vector"; +import { Savegame } from "../savegame/savegame"; +import { InGameState } from "../states/ingame"; +import { AutomaticSave } from "./automatic_save"; +import { BaseItem } from "./base_item"; +import { Camera } from "./camera"; +import { DynamicTickrate } from "./dynamic_tickrate"; +import { Entity } from "./entity"; +import { EntityManager } from "./entity_manager"; +import { GameMode } from "./game_mode"; +import { GameSystemManager } from "./game_system_manager"; +import { HubGoals } from "./hub_goals"; +import { GameHUD } from "./hud/hud"; +import { KeyActionMapper } from "./key_action_mapper"; +import { GameLogic } from "./logic"; +import { MapView } from "./map_view"; +import { ProductionAnalytics } from "./production_analytics"; +import { ShapeDefinition } from "./shape_definition"; +import { ShapeDefinitionManager } from "./shape_definition_manager"; +import { SoundProxy } from "./sound_proxy"; +import { GameTime } from "./time/game_time"; +/* typehints:end */ + +const logger = createLogger("game/root"); + +/** @type {Array} */ +export const layers = ["regular", "wires"]; + +/** + * The game root is basically the whole game state at a given point, + * combining all important classes. We don't have globals, but this + * class is passed to almost all game classes. + */ +export class GameRoot { + /** + * Constructs a new game root + * @param {Application} app + */ + constructor(app) { + this.app = app; + + /** @type {Savegame} */ + this.savegame = null; + + /** @type {InGameState} */ + this.gameState = null; + + /** @type {KeyActionMapper} */ + this.keyMapper = null; + + // Store game dimensions + this.gameWidth = 500; + this.gameHeight = 500; + + // Stores whether the current session is a fresh game (true), or was continued (false) + /** @type {boolean} */ + this.gameIsFresh = true; + + // Stores whether the logic is already initialized + /** @type {boolean} */ + this.logicInitialized = false; + + // Stores whether the game is already initialized, that is, all systems etc have been created + /** @type {boolean} */ + this.gameInitialized = false; + + /** + * Whether a bulk operation is running + */ + this.bulkOperationRunning = false; + + /** + * Whether a immutable operation is running + */ + this.immutableOperationRunning = false; + + //////// Other properties /////// + + /** @type {Camera} */ + this.camera = null; + + /** @type {HTMLCanvasElement} */ + this.canvas = null; + + /** @type {CanvasRenderingContext2D} */ + this.context = null; + + /** @type {MapView} */ + this.map = null; + + /** @type {GameLogic} */ + this.logic = null; + + /** @type {EntityManager} */ + this.entityMgr = null; + + /** @type {GameHUD} */ + this.hud = null; + + /** @type {GameSystemManager} */ + this.systemMgr = null; + + /** @type {GameTime} */ + this.time = null; + + /** @type {HubGoals} */ + this.hubGoals = null; + + /** @type {BufferMaintainer} */ + this.buffers = null; + + /** @type {AutomaticSave} */ + this.automaticSave = null; + + /** @type {SoundProxy} */ + this.soundProxy = null; + + /** @type {ShapeDefinitionManager} */ + this.shapeDefinitionMgr = null; + + /** @type {ProductionAnalytics} */ + this.productionAnalytics = null; + + /** @type {DynamicTickrate} */ + this.dynamicTickrate = null; + + /** @type {Layer} */ + this.currentLayer = "regular"; + + /** @type {GameMode} */ + this.gameMode = null; + + this.signals = { + // Entities + entityManuallyPlaced: /** @type {Signal<[Entity]>} */ (new Signal()), + entityAdded: /** @type {Signal<[Entity]>} */ (new Signal()), + entityChanged: /** @type {Signal<[Entity]>} */ (new Signal()), + entityGotNewComponent: /** @type {Signal<[Entity]>} */ (new Signal()), + entityComponentRemoved: /** @type {Signal<[Entity]>} */ (new Signal()), + entityQueuedForDestroy: /** @type {Signal<[Entity]>} */ (new Signal()), + entityDestroyed: /** @type {Signal<[Entity]>} */ (new Signal()), + + // Global + resized: /** @type {Signal<[number, number]>} */ (new Signal()), + readyToRender: /** @type {Signal<[]>} */ (new Signal()), + aboutToDestruct: /** @type {Signal<[]>} */ new Signal(), + + // Game Hooks + gameSaved: /** @type {Signal<[]>} */ (new Signal()), // Game got saved + gameRestored: /** @type {Signal<[]>} */ (new Signal()), // Game got restored + + gameFrameStarted: /** @type {Signal<[]>} */ (new Signal()), // New frame + + storyGoalCompleted: /** @type {Signal<[number, string]>} */ (new Signal()), + upgradePurchased: /** @type {Signal<[string]>} */ (new Signal()), + + // Called right after game is initialized + postLoadHook: /** @type {Signal<[]>} */ (new Signal()), + + shapeDelivered: /** @type {Signal<[ShapeDefinition]>} */ (new Signal()), + itemProduced: /** @type {Signal<[BaseItem]>} */ (new Signal()), + + bulkOperationFinished: /** @type {Signal<[]>} */ (new Signal()), + immutableOperationFinished: /** @type {Signal<[]>} */ (new Signal()), + + editModeChanged: /** @type {Signal<[Layer]>} */ (new Signal()), + + // Called to check if an entity can be placed, second parameter is an additional offset. + // Use to introduce additional placement checks + prePlacementCheck: /** @type {Signal<[Entity, Vector]>} */ (new Signal()), + + // Called before actually placing an entity, use to perform additional logic + // for freeing space before actually placing. + freeEntityAreaBeforeBuild: /** @type {Signal<[Entity]>} */ (new Signal()), + + // Puzzle mode + puzzleComplete: /** @type {Signal<[]>} */ (new Signal()), + }; + + // RNG's + /** @type {Object.>} */ + this.rngs = {}; + + // Work queue + this.queue = { + requireRedraw: false, + }; + } + + /** + * Destructs the game root + */ + destruct() { + logger.log("destructing root"); + this.signals.aboutToDestruct.dispatch(); + + this.reset(); + } + + /** + * Resets the whole root and removes all properties + */ + reset() { + if (this.signals) { + // Destruct all signals + for (let i = 0; i < this.signals.length; ++i) { + this.signals[i].removeAll(); + } + } + + if (this.hud) { + this.hud.cleanup(); + } + if (this.camera) { + this.camera.cleanup(); + } + + // Finally free all properties + for (const prop in this) { + if (Object.hasOwn(this, prop)) { + delete this[prop]; + } + } + } +} diff --git a/src/js/game/shape_definition_manager.js b/src/js/game/shape_definition_manager.js index 89203f1e..30641d98 100644 --- a/src/js/game/shape_definition_manager.js +++ b/src/js/game/shape_definition_manager.js @@ -1,270 +1,264 @@ -import { createLogger } from "../core/logging"; -import { BasicSerializableObject } from "../savegame/serialization"; -import { enumColors } from "./colors"; -import { ShapeItem } from "./items/shape_item"; -import { GameRoot } from "./root"; -import { enumSubShape, ShapeDefinition } from "./shape_definition"; -import { ACHIEVEMENTS } from "../platform/achievement_provider"; - -const logger = createLogger("shape_definition_manager"); - -export class ShapeDefinitionManager extends BasicSerializableObject { - static getId() { - return "ShapeDefinitionManager"; - } - - /** - * - * @param {GameRoot} root - */ - constructor(root) { - super(); - this.root = root; - - /** - * Store a cache from key -> definition - * @type {Object} - */ - this.shapeKeyToDefinition = {}; - - /** - * Store a cache from key -> item - */ - this.shapeKeyToItem = {}; - - // Caches operations in the form of 'operation/def1[/def2]' - /** @type {Object.|ShapeDefinition>} */ - this.operationCache = {}; - } - - /** - * Returns a shape instance from a given short key - * @param {string} hash - * @returns {ShapeDefinition} - */ - getShapeFromShortKey(hash) { - const cached = this.shapeKeyToDefinition[hash]; - if (cached) { - return cached; - } - return (this.shapeKeyToDefinition[hash] = ShapeDefinition.fromShortKey(hash)); - } - - /** - * Returns a item instance from a given short key - * @param {string} hash - * @returns {ShapeItem} - */ - getShapeItemFromShortKey(hash) { - const cached = this.shapeKeyToItem[hash]; - if (cached) { - return cached; - } - const definition = this.getShapeFromShortKey(hash); - return (this.shapeKeyToItem[hash] = new ShapeItem(definition)); - } - - /** - * Returns a shape item for a given definition - * @param {ShapeDefinition} definition - * @returns {ShapeItem} - */ - getShapeItemFromDefinition(definition) { - return this.getShapeItemFromShortKey(definition.getHash()); - } - - /** - * Registers a new shape definition - * @param {ShapeDefinition} definition - */ - registerShapeDefinition(definition) { - const id = definition.getHash(); - assert(!this.shapeKeyToDefinition[id], "Shape Definition " + id + " already exists"); - this.shapeKeyToDefinition[id] = definition; - // logger.log("Registered shape with key", id); - } - - /** - * Generates a definition for splitting a shape definition in two halfs - * @param {ShapeDefinition} definition - * @returns {[ShapeDefinition, ShapeDefinition]} - */ - shapeActionCutHalf(definition) { - const key = "cut/" + definition.getHash(); - if (this.operationCache[key]) { - return /** @type {[ShapeDefinition, ShapeDefinition]} */ (this.operationCache[key]); - } - const rightSide = definition.cloneFilteredByQuadrants([2, 3]); - const leftSide = definition.cloneFilteredByQuadrants([0, 1]); - - this.root.signals.achievementCheck.dispatch(ACHIEVEMENTS.cutShape, null); - - return /** @type {[ShapeDefinition, ShapeDefinition]} */ (this.operationCache[key] = [ - this.registerOrReturnHandle(rightSide), - this.registerOrReturnHandle(leftSide), - ]); - } - - /** - * Generates a definition for splitting a shape definition in four quads - * @param {ShapeDefinition} definition - * @returns {[ShapeDefinition, ShapeDefinition, ShapeDefinition, ShapeDefinition]} - */ - shapeActionCutQuad(definition) { - const key = "cut-quad/" + definition.getHash(); - if (this.operationCache[key]) { - return /** @type {[ShapeDefinition, ShapeDefinition, ShapeDefinition, ShapeDefinition]} */ (this - .operationCache[key]); - } - - return /** @type {[ShapeDefinition, ShapeDefinition, ShapeDefinition, ShapeDefinition]} */ (this.operationCache[ - key - ] = [ - this.registerOrReturnHandle(definition.cloneFilteredByQuadrants([0])), - this.registerOrReturnHandle(definition.cloneFilteredByQuadrants([1])), - this.registerOrReturnHandle(definition.cloneFilteredByQuadrants([2])), - this.registerOrReturnHandle(definition.cloneFilteredByQuadrants([3])), - ]); - } - - /** - * Generates a definition for rotating a shape clockwise - * @param {ShapeDefinition} definition - * @returns {ShapeDefinition} - */ - shapeActionRotateCW(definition) { - const key = "rotate-cw/" + definition.getHash(); - if (this.operationCache[key]) { - return /** @type {ShapeDefinition} */ (this.operationCache[key]); - } - - const rotated = definition.cloneRotateCW(); - - this.root.signals.achievementCheck.dispatch(ACHIEVEMENTS.rotateShape, null); - - return /** @type {ShapeDefinition} */ (this.operationCache[key] = this.registerOrReturnHandle( - rotated - )); - } - - /** - * Generates a definition for rotating a shape counter clockwise - * @param {ShapeDefinition} definition - * @returns {ShapeDefinition} - */ - shapeActionRotateCCW(definition) { - const key = "rotate-ccw/" + definition.getHash(); - if (this.operationCache[key]) { - return /** @type {ShapeDefinition} */ (this.operationCache[key]); - } - - const rotated = definition.cloneRotateCCW(); - - return /** @type {ShapeDefinition} */ (this.operationCache[key] = this.registerOrReturnHandle( - rotated - )); - } - - /** - * Generates a definition for rotating a shape FL - * @param {ShapeDefinition} definition - * @returns {ShapeDefinition} - */ - shapeActionRotate180(definition) { - const key = "rotate-fl/" + definition.getHash(); - if (this.operationCache[key]) { - return /** @type {ShapeDefinition} */ (this.operationCache[key]); - } - - const rotated = definition.cloneRotate180(); - - return /** @type {ShapeDefinition} */ (this.operationCache[key] = this.registerOrReturnHandle( - rotated - )); - } - - /** - * Generates a definition for stacking the upper definition onto the lower one - * @param {ShapeDefinition} lowerDefinition - * @param {ShapeDefinition} upperDefinition - * @returns {ShapeDefinition} - */ - shapeActionStack(lowerDefinition, upperDefinition) { - const key = "stack/" + lowerDefinition.getHash() + "/" + upperDefinition.getHash(); - if (this.operationCache[key]) { - return /** @type {ShapeDefinition} */ (this.operationCache[key]); - } - - this.root.signals.achievementCheck.dispatch(ACHIEVEMENTS.stackShape, null); - - const stacked = lowerDefinition.cloneAndStackWith(upperDefinition); - return /** @type {ShapeDefinition} */ (this.operationCache[key] = this.registerOrReturnHandle( - stacked - )); - } - - /** - * Generates a definition for painting it with the given color - * @param {ShapeDefinition} definition - * @param {enumColors} color - * @returns {ShapeDefinition} - */ - shapeActionPaintWith(definition, color) { - const key = "paint/" + definition.getHash() + "/" + color; - if (this.operationCache[key]) { - return /** @type {ShapeDefinition} */ (this.operationCache[key]); - } - - this.root.signals.achievementCheck.dispatch(ACHIEVEMENTS.paintShape, null); - - const colorized = definition.cloneAndPaintWith(color); - return /** @type {ShapeDefinition} */ (this.operationCache[key] = this.registerOrReturnHandle( - colorized - )); - } - - /** - * Generates a definition for painting it with the 4 colors - * @param {ShapeDefinition} definition - * @param {[enumColors, enumColors, enumColors, enumColors]} colors - * @returns {ShapeDefinition} - */ - shapeActionPaintWith4Colors(definition, colors) { - const key = "paint4/" + definition.getHash() + "/" + colors.join(","); - if (this.operationCache[key]) { - return /** @type {ShapeDefinition} */ (this.operationCache[key]); - } - const colorized = definition.cloneAndPaintWith4Colors(colors); - return /** @type {ShapeDefinition} */ (this.operationCache[key] = this.registerOrReturnHandle( - colorized - )); - } - - /** - * Checks if we already have cached this definition, and if so throws it away and returns the already - * cached variant - * @param {ShapeDefinition} definition - */ - registerOrReturnHandle(definition) { - const id = definition.getHash(); - if (this.shapeKeyToDefinition[id]) { - return this.shapeKeyToDefinition[id]; - } - this.shapeKeyToDefinition[id] = definition; - // logger.log("Registered shape with key (2)", id); - return definition; - } - - /** - * - * @param {[enumSubShape, enumSubShape, enumSubShape, enumSubShape]} subShapes - * @returns {ShapeDefinition} - */ - getDefinitionFromSimpleShapes(subShapes, color = enumColors.uncolored) { - const shapeLayer = /** @type {import("./shape_definition").ShapeLayer} */ (subShapes.map( - subShape => ({ subShape, color }) - )); - - return this.registerOrReturnHandle(new ShapeDefinition({ layers: [shapeLayer] })); - } -} +import { createLogger } from "../core/logging"; +import { BasicSerializableObject } from "../savegame/serialization"; +import { enumColors } from "./colors"; +import { ShapeItem } from "./items/shape_item"; +import { GameRoot } from "./root"; +import { enumSubShape, ShapeDefinition } from "./shape_definition"; + +const logger = createLogger("shape_definition_manager"); + +export class ShapeDefinitionManager extends BasicSerializableObject { + static getId() { + return "ShapeDefinitionManager"; + } + + /** + * + * @param {GameRoot} root + */ + constructor(root) { + super(); + this.root = root; + + /** + * Store a cache from key -> definition + * @type {Object} + */ + this.shapeKeyToDefinition = {}; + + /** + * Store a cache from key -> item + */ + this.shapeKeyToItem = {}; + + // Caches operations in the form of 'operation/def1[/def2]' + /** @type {Object.|ShapeDefinition>} */ + this.operationCache = {}; + } + + /** + * Returns a shape instance from a given short key + * @param {string} hash + * @returns {ShapeDefinition} + */ + getShapeFromShortKey(hash) { + const cached = this.shapeKeyToDefinition[hash]; + if (cached) { + return cached; + } + return (this.shapeKeyToDefinition[hash] = ShapeDefinition.fromShortKey(hash)); + } + + /** + * Returns a item instance from a given short key + * @param {string} hash + * @returns {ShapeItem} + */ + getShapeItemFromShortKey(hash) { + const cached = this.shapeKeyToItem[hash]; + if (cached) { + return cached; + } + const definition = this.getShapeFromShortKey(hash); + return (this.shapeKeyToItem[hash] = new ShapeItem(definition)); + } + + /** + * Returns a shape item for a given definition + * @param {ShapeDefinition} definition + * @returns {ShapeItem} + */ + getShapeItemFromDefinition(definition) { + return this.getShapeItemFromShortKey(definition.getHash()); + } + + /** + * Registers a new shape definition + * @param {ShapeDefinition} definition + */ + registerShapeDefinition(definition) { + const id = definition.getHash(); + assert(!this.shapeKeyToDefinition[id], "Shape Definition " + id + " already exists"); + this.shapeKeyToDefinition[id] = definition; + // logger.log("Registered shape with key", id); + } + + /** + * Generates a definition for splitting a shape definition in two halfs + * @param {ShapeDefinition} definition + * @returns {[ShapeDefinition, ShapeDefinition]} + */ + shapeActionCutHalf(definition) { + const key = "cut/" + definition.getHash(); + if (this.operationCache[key]) { + return /** @type {[ShapeDefinition, ShapeDefinition]} */ (this.operationCache[key]); + } + const rightSide = definition.cloneFilteredByQuadrants([2, 3]); + const leftSide = definition.cloneFilteredByQuadrants([0, 1]); + + return /** @type {[ShapeDefinition, ShapeDefinition]} */ ( + this.operationCache[key] = [ + this.registerOrReturnHandle(rightSide), + this.registerOrReturnHandle(leftSide), + ] + ); + } + + /** + * Generates a definition for splitting a shape definition in four quads + * @param {ShapeDefinition} definition + * @returns {[ShapeDefinition, ShapeDefinition, ShapeDefinition, ShapeDefinition]} + */ + shapeActionCutQuad(definition) { + const key = "cut-quad/" + definition.getHash(); + if (this.operationCache[key]) { + return /** @type {[ShapeDefinition, ShapeDefinition, ShapeDefinition, ShapeDefinition]} */ ( + this.operationCache[key] + ); + } + + return /** @type {[ShapeDefinition, ShapeDefinition, ShapeDefinition, ShapeDefinition]} */ ( + this.operationCache[key] = [ + this.registerOrReturnHandle(definition.cloneFilteredByQuadrants([0])), + this.registerOrReturnHandle(definition.cloneFilteredByQuadrants([1])), + this.registerOrReturnHandle(definition.cloneFilteredByQuadrants([2])), + this.registerOrReturnHandle(definition.cloneFilteredByQuadrants([3])), + ] + ); + } + + /** + * Generates a definition for rotating a shape clockwise + * @param {ShapeDefinition} definition + * @returns {ShapeDefinition} + */ + shapeActionRotateCW(definition) { + const key = "rotate-cw/" + definition.getHash(); + if (this.operationCache[key]) { + return /** @type {ShapeDefinition} */ (this.operationCache[key]); + } + + const rotated = definition.cloneRotateCW(); + + return /** @type {ShapeDefinition} */ ( + this.operationCache[key] = this.registerOrReturnHandle(rotated) + ); + } + + /** + * Generates a definition for rotating a shape counter clockwise + * @param {ShapeDefinition} definition + * @returns {ShapeDefinition} + */ + shapeActionRotateCCW(definition) { + const key = "rotate-ccw/" + definition.getHash(); + if (this.operationCache[key]) { + return /** @type {ShapeDefinition} */ (this.operationCache[key]); + } + + const rotated = definition.cloneRotateCCW(); + + return /** @type {ShapeDefinition} */ ( + this.operationCache[key] = this.registerOrReturnHandle(rotated) + ); + } + + /** + * Generates a definition for rotating a shape FL + * @param {ShapeDefinition} definition + * @returns {ShapeDefinition} + */ + shapeActionRotate180(definition) { + const key = "rotate-fl/" + definition.getHash(); + if (this.operationCache[key]) { + return /** @type {ShapeDefinition} */ (this.operationCache[key]); + } + + const rotated = definition.cloneRotate180(); + + return /** @type {ShapeDefinition} */ ( + this.operationCache[key] = this.registerOrReturnHandle(rotated) + ); + } + + /** + * Generates a definition for stacking the upper definition onto the lower one + * @param {ShapeDefinition} lowerDefinition + * @param {ShapeDefinition} upperDefinition + * @returns {ShapeDefinition} + */ + shapeActionStack(lowerDefinition, upperDefinition) { + const key = "stack/" + lowerDefinition.getHash() + "/" + upperDefinition.getHash(); + if (this.operationCache[key]) { + return /** @type {ShapeDefinition} */ (this.operationCache[key]); + } + + const stacked = lowerDefinition.cloneAndStackWith(upperDefinition); + return /** @type {ShapeDefinition} */ ( + this.operationCache[key] = this.registerOrReturnHandle(stacked) + ); + } + + /** + * Generates a definition for painting it with the given color + * @param {ShapeDefinition} definition + * @param {enumColors} color + * @returns {ShapeDefinition} + */ + shapeActionPaintWith(definition, color) { + const key = "paint/" + definition.getHash() + "/" + color; + if (this.operationCache[key]) { + return /** @type {ShapeDefinition} */ (this.operationCache[key]); + } + + const colorized = definition.cloneAndPaintWith(color); + return /** @type {ShapeDefinition} */ ( + this.operationCache[key] = this.registerOrReturnHandle(colorized) + ); + } + + /** + * Generates a definition for painting it with the 4 colors + * @param {ShapeDefinition} definition + * @param {[enumColors, enumColors, enumColors, enumColors]} colors + * @returns {ShapeDefinition} + */ + shapeActionPaintWith4Colors(definition, colors) { + const key = "paint4/" + definition.getHash() + "/" + colors.join(","); + if (this.operationCache[key]) { + return /** @type {ShapeDefinition} */ (this.operationCache[key]); + } + const colorized = definition.cloneAndPaintWith4Colors(colors); + return /** @type {ShapeDefinition} */ ( + this.operationCache[key] = this.registerOrReturnHandle(colorized) + ); + } + + /** + * Checks if we already have cached this definition, and if so throws it away and returns the already + * cached variant + * @param {ShapeDefinition} definition + */ + registerOrReturnHandle(definition) { + const id = definition.getHash(); + if (this.shapeKeyToDefinition[id]) { + return this.shapeKeyToDefinition[id]; + } + this.shapeKeyToDefinition[id] = definition; + // logger.log("Registered shape with key (2)", id); + return definition; + } + + /** + * + * @param {[enumSubShape, enumSubShape, enumSubShape, enumSubShape]} subShapes + * @returns {ShapeDefinition} + */ + getDefinitionFromSimpleShapes(subShapes, color = enumColors.uncolored) { + const shapeLayer = /** @type {import("./shape_definition").ShapeLayer} */ ( + subShapes.map(subShape => ({ subShape, color })) + ); + + return this.registerOrReturnHandle(new ShapeDefinition({ layers: [shapeLayer] })); + } +} diff --git a/src/js/game/systems/belt.js b/src/js/game/systems/belt.js index 38ac9b0b..6893e546 100644 --- a/src/js/game/systems/belt.js +++ b/src/js/game/systems/belt.js @@ -1,564 +1,572 @@ -import { globalConfig } from "../../core/config"; -import { DrawParameters } from "../../core/draw_parameters"; -import { gMetaBuildingRegistry } from "../../core/global_registries"; -import { Loader } from "../../core/loader"; -import { createLogger } from "../../core/logging"; -import { AtlasSprite } from "../../core/sprites"; -import { fastArrayDeleteValue } from "../../core/utils"; -import { enumDirection, enumDirectionToVector, enumInvertedDirections, Vector } from "../../core/vector"; -import { BeltPath } from "../belt_path"; -import { arrayBeltVariantToRotation, MetaBeltBuilding } from "../buildings/belt"; -import { getCodeFromBuildingData } from "../building_codes"; -import { BeltComponent } from "../components/belt"; -import { Entity } from "../entity"; -import { GameSystem } from "../game_system"; -import { GameSystemWithFilter } from "../game_system_with_filter"; -import { MapChunkView } from "../map_chunk_view"; -import { defaultBuildingVariant } from "../meta_building"; - -export const BELT_ANIM_COUNT = 14; - -const logger = createLogger("belt"); - -/** - * Manages all belts - */ -export class BeltSystem extends GameSystem { - constructor(root) { - super(root); - /** - * @type {Object.>} - */ - this.beltSprites = { - [enumDirection.top]: Loader.getSprite("sprites/belt/built/forward_0.png"), - [enumDirection.left]: Loader.getSprite("sprites/belt/built/left_0.png"), - [enumDirection.right]: Loader.getSprite("sprites/belt/built/right_0.png"), - }; - - /** - * @type {Object.>} - */ - this.beltAnimations = { - [enumDirection.top]: [], - [enumDirection.left]: [], - [enumDirection.right]: [], - }; - - for (let i = 0; i < BELT_ANIM_COUNT; ++i) { - this.beltAnimations[enumDirection.top].push( - Loader.getSprite("sprites/belt/built/forward_" + i + ".png") - ); - this.beltAnimations[enumDirection.left].push( - Loader.getSprite("sprites/belt/built/left_" + i + ".png") - ); - this.beltAnimations[enumDirection.right].push( - Loader.getSprite("sprites/belt/built/right_" + i + ".png") - ); - } - - this.root.signals.entityDestroyed.add(this.onEntityDestroyed, this); - this.root.signals.entityDestroyed.add(this.updateSurroundingBeltPlacement, this); - - // Notice: These must come *after* the entity destroyed signals - this.root.signals.entityAdded.add(this.onEntityAdded, this); - this.root.signals.entityAdded.add(this.updateSurroundingBeltPlacement, this); - - /** @type {Array} */ - this.beltPaths = []; - } - - /** - * Serializes all belt paths - * @returns {Array} - */ - serializePaths() { - let data = []; - for (let i = 0; i < this.beltPaths.length; ++i) { - data.push(this.beltPaths[i].serialize()); - } - return data; - } - - /** - * Deserializes all belt paths - * @param {Array} data - */ - deserializePaths(data) { - if (!Array.isArray(data)) { - return "Belt paths are not an array: " + typeof data; - } - - for (let i = 0; i < data.length; ++i) { - const path = BeltPath.fromSerialized(this.root, data[i]); - // If path is a string, that means its an error - if (!(path instanceof BeltPath)) { - return "Failed to create path from belt data: " + path; - } - this.beltPaths.push(path); - } - - if (this.beltPaths.length === 0) { - // Old savegames might not have paths yet - logger.warn("Recomputing belt paths (most likely the savegame is old or empty)"); - this.recomputeAllBeltPaths(); - } else { - logger.warn("Restored", this.beltPaths.length, "belt paths"); - } - - if (G_IS_DEV && globalConfig.debug.checkBeltPaths) { - this.debug_verifyBeltPaths(); - } - } - - /** - * Updates the belt placement after an entity has been added / deleted - * @param {Entity} entity - */ - updateSurroundingBeltPlacement(entity) { - if (!this.root.gameInitialized) { - return; - } - - const staticComp = entity.components.StaticMapEntity; - if (!staticComp) { - return; - } - - const metaBelt = gMetaBuildingRegistry.findByClass(MetaBeltBuilding); - // Compute affected area - const originalRect = staticComp.getTileSpaceBounds(); - const affectedArea = originalRect.expandedInAllDirections(1); - - /** @type {Set} */ - const changedPaths = new Set(); - - for (let x = affectedArea.x; x < affectedArea.right(); ++x) { - for (let y = affectedArea.y; y < affectedArea.bottom(); ++y) { - if (originalRect.containsPoint(x, y)) { - // Make sure we don't update the original entity - continue; - } - - const targetEntities = this.root.map.getLayersContentsMultipleXY(x, y); - for (let i = 0; i < targetEntities.length; ++i) { - const targetEntity = targetEntities[i]; - - const targetBeltComp = targetEntity.components.Belt; - const targetStaticComp = targetEntity.components.StaticMapEntity; - - if (!targetBeltComp) { - // Not a belt - continue; - } - - const { - rotation, - rotationVariant, - } = metaBelt.computeOptimalDirectionAndRotationVariantAtTile({ - root: this.root, - tile: new Vector(x, y), - rotation: targetStaticComp.originalRotation, - variant: defaultBuildingVariant, - layer: targetEntity.layer, - }); - - // Compute delta to see if anything changed - const newDirection = arrayBeltVariantToRotation[rotationVariant]; - - if ( - !this.root.immutableOperationRunning && - (targetStaticComp.rotation !== rotation || newDirection !== targetBeltComp.direction) - ) { - const originalPath = targetBeltComp.assignedPath; - - // Ok, first remove it from its current path - this.deleteEntityFromPath(targetBeltComp.assignedPath, targetEntity); - - // Change stuff - targetStaticComp.rotation = rotation; - metaBelt.updateVariants(targetEntity, rotationVariant, defaultBuildingVariant); - - // Update code as well - targetStaticComp.code = getCodeFromBuildingData( - metaBelt, - defaultBuildingVariant, - rotationVariant - ); - - // Update the original path since it might have picked up the entit1y - originalPath.onPathChanged(); - - // Now add it again - this.addEntityToPaths(targetEntity); - - // Sanity - if (G_IS_DEV && globalConfig.debug.checkBeltPaths) { - this.debug_verifyBeltPaths(); - } - - // Make sure the chunks know about the update - this.root.signals.entityChanged.dispatch(targetEntity); - } - - if (targetBeltComp.assignedPath) { - changedPaths.add(targetBeltComp.assignedPath); - } - } - } - } - - // notify all paths *afterwards* to avoid multi-updates - changedPaths.forEach(path => path.onSurroundingsChanged()); - - if (G_IS_DEV && globalConfig.debug.checkBeltPaths) { - this.debug_verifyBeltPaths(); - } - } - - /** - * Called when an entity got destroyed - * @param {Entity} entity - */ - onEntityDestroyed(entity) { - if (!this.root.gameInitialized) { - return; - } - - if (!entity.components.Belt) { - return; - } - - const assignedPath = entity.components.Belt.assignedPath; - assert(assignedPath, "Entity has no belt path assigned"); - this.deleteEntityFromPath(assignedPath, entity); - if (G_IS_DEV && globalConfig.debug.checkBeltPaths) { - this.debug_verifyBeltPaths(); - } - } - - /** - * Attempts to delete the belt from its current path - * @param {BeltPath} path - * @param {Entity} entity - */ - deleteEntityFromPath(path, entity) { - if (path.entityPath.length === 1) { - // This is a single entity path, easy to do, simply erase whole path - fastArrayDeleteValue(this.beltPaths, path); - return; - } - - // Notice: Since there might be circular references, it is important to check - // which role the entity has - if (path.isStartEntity(entity)) { - // We tried to delete the start - path.deleteEntityOnStart(entity); - } else if (path.isEndEntity(entity)) { - // We tried to delete the end - path.deleteEntityOnEnd(entity); - } else { - // We tried to delete something inbetween - const newPath = path.deleteEntityOnPathSplitIntoTwo(entity); - this.beltPaths.push(newPath); - } - - // Sanity - entity.components.Belt.assignedPath = null; - } - - /** - * Adds the given entity to the appropriate paths - * @param {Entity} entity - */ - addEntityToPaths(entity) { - const fromEntity = this.findSupplyingEntity(entity); - const toEntity = this.findFollowUpEntity(entity); - - // Check if we can add the entity to the previous path - if (fromEntity) { - const fromPath = fromEntity.components.Belt.assignedPath; - fromPath.extendOnEnd(entity); - - // Check if we now can extend the current path by the next path - if (toEntity) { - const toPath = toEntity.components.Belt.assignedPath; - - if (fromPath === toPath) { - // This is a circular dependency -> Ignore - } else { - fromPath.extendByPath(toPath); - - // Delete now obsolete path - fastArrayDeleteValue(this.beltPaths, toPath); - } - } - } else { - if (toEntity) { - // Prepend it to the other path - const toPath = toEntity.components.Belt.assignedPath; - toPath.extendOnBeginning(entity); - } else { - // This is an empty belt path - const path = new BeltPath(this.root, [entity]); - this.beltPaths.push(path); - } - } - } - - /** - * Called when an entity got added - * @param {Entity} entity - */ - onEntityAdded(entity) { - if (!this.root.gameInitialized) { - return; - } - - if (!entity.components.Belt) { - return; - } - - this.addEntityToPaths(entity); - if (G_IS_DEV && globalConfig.debug.checkBeltPaths) { - this.debug_verifyBeltPaths(); - } - } - - /** - * Draws all belt paths - * @param {DrawParameters} parameters - */ - drawBeltItems(parameters) { - for (let i = 0; i < this.beltPaths.length; ++i) { - this.beltPaths[i].draw(parameters); - } - } - - /** - * Verifies all belt paths - */ - debug_verifyBeltPaths() { - for (let i = 0; i < this.beltPaths.length; ++i) { - this.beltPaths[i].debug_checkIntegrity("general-verify"); - } - - const belts = this.root.entityMgr.getAllWithComponent(BeltComponent); - for (let i = 0; i < belts.length; ++i) { - const path = belts[i].components.Belt.assignedPath; - if (!path) { - throw new Error("Belt has no path: " + belts[i].uid); - } - if (this.beltPaths.indexOf(path) < 0) { - throw new Error("Path of entity not contained: " + belts[i].uid); - } - } - } - - /** - * Finds the follow up entity for a given belt. Used for building the dependencies - * @param {Entity} entity - * @returns {Entity|null} - */ - findFollowUpEntity(entity) { - const staticComp = entity.components.StaticMapEntity; - const beltComp = entity.components.Belt; - - const followUpDirection = staticComp.localDirectionToWorld(beltComp.direction); - const followUpVector = enumDirectionToVector[followUpDirection]; - - const followUpTile = staticComp.origin.add(followUpVector); - const followUpEntity = this.root.map.getLayerContentXY(followUpTile.x, followUpTile.y, entity.layer); - - // Check if there's a belt at the tile we point to - if (followUpEntity) { - const followUpBeltComp = followUpEntity.components.Belt; - if (followUpBeltComp) { - const followUpStatic = followUpEntity.components.StaticMapEntity; - - const acceptedDirection = followUpStatic.localDirectionToWorld(enumDirection.top); - if (acceptedDirection === followUpDirection) { - return followUpEntity; - } - } - } - - return null; - } - - /** - * Finds the supplying belt for a given belt. Used for building the dependencies - * @param {Entity} entity - * @returns {Entity|null} - */ - findSupplyingEntity(entity) { - const staticComp = entity.components.StaticMapEntity; - - const supplyDirection = staticComp.localDirectionToWorld(enumDirection.bottom); - const supplyVector = enumDirectionToVector[supplyDirection]; - - const supplyTile = staticComp.origin.add(supplyVector); - const supplyEntity = this.root.map.getLayerContentXY(supplyTile.x, supplyTile.y, entity.layer); - - // Check if there's a belt at the tile we point to - if (supplyEntity) { - const supplyBeltComp = supplyEntity.components.Belt; - if (supplyBeltComp) { - const supplyStatic = supplyEntity.components.StaticMapEntity; - const otherDirection = supplyStatic.localDirectionToWorld( - enumInvertedDirections[supplyBeltComp.direction] - ); - - if (otherDirection === supplyDirection) { - return supplyEntity; - } - } - } - - return null; - } - - /** - * Recomputes the belt path network. Only required for old savegames - */ - recomputeAllBeltPaths() { - logger.warn("Recomputing all belt paths"); - const visitedUids = new Set(); - - const result = []; - - const beltEntities = this.root.entityMgr.getAllWithComponent(BeltComponent); - - for (let i = 0; i < beltEntities.length; ++i) { - const entity = beltEntities[i]; - if (visitedUids.has(entity.uid)) { - continue; - } - - // Mark entity as visited - visitedUids.add(entity.uid); - - // Compute path, start with entity and find precedors / successors - const path = [entity]; - - // Prevent infinite loops - let maxIter = 99999; - - // Find precedors - let prevEntity = this.findSupplyingEntity(entity); - while (prevEntity && --maxIter > 0) { - if (visitedUids.has(prevEntity.uid)) { - break; - } - path.unshift(prevEntity); - visitedUids.add(prevEntity.uid); - prevEntity = this.findSupplyingEntity(prevEntity); - } - - // Find succedors - let nextEntity = this.findFollowUpEntity(entity); - while (nextEntity && --maxIter > 0) { - if (visitedUids.has(nextEntity.uid)) { - break; - } - - path.push(nextEntity); - visitedUids.add(nextEntity.uid); - nextEntity = this.findFollowUpEntity(nextEntity); - } - - assert(maxIter > 1, "Ran out of iterations"); - result.push(new BeltPath(this.root, path)); - } - - logger.log("Found", this.beltPaths.length, "belt paths"); - this.beltPaths = result; - } - - /** - * Updates all belts - */ - update() { - if (G_IS_DEV && globalConfig.debug.checkBeltPaths) { - this.debug_verifyBeltPaths(); - } - - for (let i = 0; i < this.beltPaths.length; ++i) { - this.beltPaths[i].update(); - } - - if (G_IS_DEV && globalConfig.debug.checkBeltPaths) { - this.debug_verifyBeltPaths(); - } - } - - /** - * Draws a given chunk - * @param {DrawParameters} parameters - * @param {MapChunkView} chunk - */ - drawChunk(parameters, chunk) { - if (G_IS_DEV && globalConfig.debug.doNotRenderStatics) { - return; - } - - // Limit speed to avoid belts going backwards - const speedMultiplier = Math.min(this.root.hubGoals.getBeltBaseSpeed(), 10); - - // SYNC with systems/item_acceptor.js:drawEntityUnderlays! - // 126 / 42 is the exact animation speed of the png animation - const animationIndex = Math.floor( - ((this.root.time.realtimeNow() * speedMultiplier * BELT_ANIM_COUNT * 126) / 42) * - globalConfig.itemSpacingOnBelts - ); - const contents = chunk.containedEntitiesByLayer.regular; - - if (this.root.app.settings.getAllSettings().simplifiedBelts) { - // POTATO Mode: Only show items when belt is hovered - let hoveredBeltPath = null; - const mousePos = this.root.app.mousePosition; - if (mousePos && this.root.currentLayer === "regular") { - const tile = this.root.camera.screenToWorld(mousePos).toTileSpace(); - const contents = this.root.map.getLayerContentXY(tile.x, tile.y, "regular"); - if (contents && contents.components.Belt) { - hoveredBeltPath = contents.components.Belt.assignedPath; - } - } - - for (let i = 0; i < contents.length; ++i) { - const entity = contents[i]; - if (entity.components.Belt) { - const direction = entity.components.Belt.direction; - let sprite = this.beltAnimations[direction][0]; - - if (entity.components.Belt.assignedPath === hoveredBeltPath) { - sprite = this.beltAnimations[direction][animationIndex % BELT_ANIM_COUNT]; - } - - // Culling happens within the static map entity component - entity.components.StaticMapEntity.drawSpriteOnBoundsClipped(parameters, sprite, 0); - } - } - } else { - for (let i = 0; i < contents.length; ++i) { - const entity = contents[i]; - if (entity.components.Belt) { - const direction = entity.components.Belt.direction; - const sprite = this.beltAnimations[direction][animationIndex % BELT_ANIM_COUNT]; - - // Culling happens within the static map entity component - entity.components.StaticMapEntity.drawSpriteOnBoundsClipped(parameters, sprite, 0); - } - } - } - } - - /** - * Draws the belt path debug overlays - * @param {DrawParameters} parameters - */ - drawBeltPathDebug(parameters) { - for (let i = 0; i < this.beltPaths.length; ++i) { - this.beltPaths[i].drawDebug(parameters); - } - } -} +import { globalConfig } from "../../core/config"; +import { DrawParameters } from "../../core/draw_parameters"; +import { gMetaBuildingRegistry } from "../../core/global_registries"; +import { Loader } from "../../core/loader"; +import { createLogger } from "../../core/logging"; +import { AtlasSprite } from "../../core/sprites"; +import { fastArrayDeleteValue } from "../../core/utils"; +import { enumDirection, enumDirectionToVector, enumInvertedDirections, Vector } from "../../core/vector"; +import { BeltPath } from "../belt_path"; +import { getCodeFromBuildingData } from "../building_codes"; +import { arrayBeltVariantToRotation, MetaBeltBuilding } from "../buildings/belt"; +import { BeltComponent } from "../components/belt"; +import { Entity } from "../entity"; +import { GameSystem } from "../game_system"; +import { MapChunkView } from "../map_chunk_view"; +import { defaultBuildingVariant } from "../meta_building"; + +export const BELT_ANIM_COUNT = 14; + +const logger = createLogger("belt"); + +/** + * Manages all belts + */ +export class BeltSystem extends GameSystem { + constructor(root) { + super(root); + /** + * @type {Object.>} + */ + this.beltSprites = { + [enumDirection.top]: Loader.getSprite("sprites/belt/built/forward_0.png"), + [enumDirection.left]: Loader.getSprite("sprites/belt/built/left_0.png"), + [enumDirection.right]: Loader.getSprite("sprites/belt/built/right_0.png"), + }; + + /** + * @type {Object.>} + */ + this.beltAnimations = { + [enumDirection.top]: [], + [enumDirection.left]: [], + [enumDirection.right]: [], + }; + + for (let i = 0; i < BELT_ANIM_COUNT; ++i) { + this.beltAnimations[enumDirection.top].push( + Loader.getSprite("sprites/belt/built/forward_" + i + ".png") + ); + this.beltAnimations[enumDirection.left].push( + Loader.getSprite("sprites/belt/built/left_" + i + ".png") + ); + this.beltAnimations[enumDirection.right].push( + Loader.getSprite("sprites/belt/built/right_" + i + ".png") + ); + } + + this.root.signals.entityDestroyed.add(this.onEntityDestroyed, this); + this.root.signals.entityDestroyed.add(this.updateSurroundingBeltPlacement, this); + + // Notice: These must come *after* the entity destroyed signals + this.root.signals.entityAdded.add(this.onEntityAdded, this); + this.root.signals.entityAdded.add(this.updateSurroundingBeltPlacement, this); + + /** @type {Array} */ + this.beltPaths = []; + } + + /** + * Serializes all belt paths + * @returns {Array} + */ + serializePaths() { + let data = []; + for (let i = 0; i < this.beltPaths.length; ++i) { + data.push(this.beltPaths[i].serialize()); + } + return data; + } + + /** + * Deserializes all belt paths + * @param {Array} data + */ + deserializePaths(data) { + if (!Array.isArray(data)) { + return "Belt paths are not an array: " + typeof data; + } + + for (let i = 0; i < data.length; ++i) { + const path = BeltPath.fromSerialized(this.root, data[i]); + // If path is a string, that means its an error + if (!(path instanceof BeltPath)) { + return "Failed to create path from belt data: " + path; + } + this.beltPaths.push(path); + } + + if (this.beltPaths.length === 0) { + // Old savegames might not have paths yet + logger.warn("Recomputing belt paths (most likely the savegame is old or empty)"); + this.recomputeAllBeltPaths(); + } else { + logger.warn("Restored", this.beltPaths.length, "belt paths"); + } + + if (G_IS_DEV && globalConfig.debug.checkBeltPaths) { + this.debug_verifyBeltPaths(); + } + } + + /** + * Updates the belt placement after an entity has been added / deleted + * @param {Entity} entity + */ + updateSurroundingBeltPlacement(entity) { + if (!this.root.gameInitialized) { + return; + } + + const staticComp = entity.components.StaticMapEntity; + if (!staticComp) { + return; + } + + const metaBelt = gMetaBuildingRegistry.findByClass(MetaBeltBuilding); + // Compute affected area + const originalRect = staticComp.getTileSpaceBounds(); + const affectedArea = originalRect.expandedInAllDirections(1); + + /** @type {Set} */ + const changedPaths = new Set(); + + for (let x = affectedArea.x; x < affectedArea.right(); ++x) { + for (let y = affectedArea.y; y < affectedArea.bottom(); ++y) { + if (originalRect.containsPoint(x, y)) { + // Make sure we don't update the original entity + continue; + } + + const targetEntities = this.root.map.getLayersContentsMultipleXY(x, y); + for (let i = 0; i < targetEntities.length; ++i) { + const targetEntity = targetEntities[i]; + + const targetBeltComp = targetEntity.components.Belt; + const targetStaticComp = targetEntity.components.StaticMapEntity; + + if (!targetBeltComp) { + // Not a belt + continue; + } + + const { rotation, rotationVariant } = + metaBelt.computeOptimalDirectionAndRotationVariantAtTile({ + root: this.root, + tile: new Vector(x, y), + rotation: targetStaticComp.originalRotation, + variant: defaultBuildingVariant, + layer: targetEntity.layer, + }); + + // Compute delta to see if anything changed + const newDirection = arrayBeltVariantToRotation[rotationVariant]; + + if ( + !this.root.immutableOperationRunning && + (targetStaticComp.rotation !== rotation || newDirection !== targetBeltComp.direction) + ) { + const originalPath = targetBeltComp.assignedPath; + + // Ok, first remove it from its current path + this.deleteEntityFromPath(targetBeltComp.assignedPath, targetEntity); + + // Change stuff + targetStaticComp.rotation = rotation; + metaBelt.updateVariants(targetEntity, rotationVariant, defaultBuildingVariant); + + // Update code as well + targetStaticComp.code = getCodeFromBuildingData( + metaBelt, + defaultBuildingVariant, + rotationVariant + ); + + // Update the original path since it might have picked up the entit1y + originalPath.onPathChanged(); + + // Now add it again + this.addEntityToPaths(targetEntity); + + // Sanity + if (G_IS_DEV && globalConfig.debug.checkBeltPaths) { + this.debug_verifyBeltPaths(); + } + + // Make sure the chunks know about the update + this.root.signals.entityChanged.dispatch(targetEntity); + } + + if (targetBeltComp.assignedPath) { + changedPaths.add(targetBeltComp.assignedPath); + } + } + } + } + + // notify all paths *afterwards* to avoid multi-updates + changedPaths.forEach(path => path.onSurroundingsChanged()); + + if (G_IS_DEV && globalConfig.debug.checkBeltPaths) { + this.debug_verifyBeltPaths(); + } + } + + /** + * Called when an entity got destroyed + * @param {Entity} entity + */ + onEntityDestroyed(entity) { + if (!this.root.gameInitialized) { + return; + } + + if (!entity.components.Belt) { + return; + } + + const assignedPath = entity.components.Belt.assignedPath; + assert(assignedPath, "Entity has no belt path assigned"); + this.deleteEntityFromPath(assignedPath, entity); + if (G_IS_DEV && globalConfig.debug.checkBeltPaths) { + this.debug_verifyBeltPaths(); + } + } + + /** + * Attempts to delete the belt from its current path + * @param {BeltPath} path + * @param {Entity} entity + */ + deleteEntityFromPath(path, entity) { + if (path.entityPath.length === 1) { + // This is a single entity path, easy to do, simply erase whole path + fastArrayDeleteValue(this.beltPaths, path); + return; + } + + // Notice: Since there might be circular references, it is important to check + // which role the entity has + if (path.isStartEntity(entity)) { + // We tried to delete the start + path.deleteEntityOnStart(entity); + } else if (path.isEndEntity(entity)) { + // We tried to delete the end + path.deleteEntityOnEnd(entity); + } else { + // We tried to delete something inbetween + const newPath = path.deleteEntityOnPathSplitIntoTwo(entity); + this.beltPaths.push(newPath); + } + + // Sanity + entity.components.Belt.assignedPath = null; + } + + /** + * Adds the given entity to the appropriate paths + * @param {Entity} entity + */ + addEntityToPaths(entity) { + const fromEntity = this.findSupplyingEntity(entity); + const toEntity = this.findFollowUpEntity(entity); + + // Check if we can add the entity to the previous path + if (fromEntity) { + const fromPath = fromEntity.components.Belt.assignedPath; + fromPath.extendOnEnd(entity); + + // Check if we now can extend the current path by the next path + if (toEntity) { + const toPath = toEntity.components.Belt.assignedPath; + + if (fromPath === toPath) { + // This is a circular dependency -> Ignore + } else { + fromPath.extendByPath(toPath); + + // Delete now obsolete path + fastArrayDeleteValue(this.beltPaths, toPath); + } + } + } else { + if (toEntity) { + // Prepend it to the other path + const toPath = toEntity.components.Belt.assignedPath; + toPath.extendOnBeginning(entity); + } else { + // This is an empty belt path + const path = new BeltPath(this.root, [entity]); + this.beltPaths.push(path); + } + } + } + + /** + * Called when an entity got added + * @param {Entity} entity + */ + onEntityAdded(entity) { + if (!this.root.gameInitialized) { + return; + } + + if (!entity.components.Belt) { + return; + } + + this.addEntityToPaths(entity); + if (G_IS_DEV && globalConfig.debug.checkBeltPaths) { + this.debug_verifyBeltPaths(); + } + } + + /** + * Draws all belt paths + * @param {DrawParameters} parameters + */ + drawBeltItems(parameters) { + for (let i = 0; i < this.beltPaths.length; ++i) { + this.beltPaths[i].draw(parameters); + } + } + + /** + * Verifies all belt paths + */ + debug_verifyBeltPaths() { + for (let i = 0; i < this.beltPaths.length; ++i) { + this.beltPaths[i].debug_checkIntegrity("general-verify"); + } + + const belts = this.root.entityMgr.getAllWithComponent(BeltComponent); + for (let i = 0; i < belts.length; ++i) { + const path = belts[i].components.Belt.assignedPath; + if (!path) { + throw new Error("Belt has no path: " + belts[i].uid); + } + if (this.beltPaths.indexOf(path) < 0) { + throw new Error("Path of entity not contained: " + belts[i].uid); + } + } + } + + /** + * Finds the follow up entity for a given belt. Used for building the dependencies + * @param {Entity} entity + * @returns {Entity|null} + */ + findFollowUpEntity(entity) { + const staticComp = entity.components.StaticMapEntity; + const beltComp = entity.components.Belt; + + const followUpDirection = staticComp.localDirectionToWorld(beltComp.direction); + const followUpVector = enumDirectionToVector[followUpDirection]; + + const followUpTile = staticComp.origin.add(followUpVector); + const followUpEntity = this.root.map.getLayerContentXY(followUpTile.x, followUpTile.y, entity.layer); + + // Check if there's a belt at the tile we point to + if (followUpEntity) { + const followUpBeltComp = followUpEntity.components.Belt; + if (followUpBeltComp) { + const followUpStatic = followUpEntity.components.StaticMapEntity; + + const acceptedDirection = followUpStatic.localDirectionToWorld(enumDirection.top); + if (acceptedDirection === followUpDirection) { + return followUpEntity; + } + } + } + + return null; + } + + /** + * Finds the supplying belt for a given belt. Used for building the dependencies + * @param {Entity} entity + * @returns {Entity|null} + */ + findSupplyingEntity(entity) { + const staticComp = entity.components.StaticMapEntity; + + const supplyDirection = staticComp.localDirectionToWorld(enumDirection.bottom); + const supplyVector = enumDirectionToVector[supplyDirection]; + + const supplyTile = staticComp.origin.add(supplyVector); + const supplyEntity = this.root.map.getLayerContentXY(supplyTile.x, supplyTile.y, entity.layer); + + // Check if there's a belt at the tile we point to + if (supplyEntity) { + const supplyBeltComp = supplyEntity.components.Belt; + if (supplyBeltComp) { + const supplyStatic = supplyEntity.components.StaticMapEntity; + const otherDirection = supplyStatic.localDirectionToWorld( + enumInvertedDirections[supplyBeltComp.direction] + ); + + if (otherDirection === supplyDirection) { + return supplyEntity; + } + } + } + + return null; + } + + /** + * Recomputes the belt path network. Only required for old savegames + */ + recomputeAllBeltPaths() { + logger.warn("Recomputing all belt paths"); + const visitedUids = new Set(); + + const result = []; + + const beltEntities = this.root.entityMgr.getEntitiesWithComponent(BeltComponent); + + for (const entity of beltEntities) { + if (visitedUids.has(entity.uid)) { + continue; + } + + // Mark entity as visited + visitedUids.add(entity.uid); + + // Compute path, start with entity and find precedors / successors + const path = [entity]; + + // Prevent infinite loops + let maxIter = 99999; + + // Find precedors + let prevEntity = this.findSupplyingEntity(entity); + while (prevEntity && --maxIter > 0) { + if (visitedUids.has(prevEntity.uid)) { + break; + } + path.unshift(prevEntity); + visitedUids.add(prevEntity.uid); + prevEntity = this.findSupplyingEntity(prevEntity); + } + + // Find succedors + let nextEntity = this.findFollowUpEntity(entity); + while (nextEntity && --maxIter > 0) { + if (visitedUids.has(nextEntity.uid)) { + break; + } + + path.push(nextEntity); + visitedUids.add(nextEntity.uid); + nextEntity = this.findFollowUpEntity(nextEntity); + } + + assert(maxIter > 1, "Ran out of iterations"); + result.push(new BeltPath(this.root, path)); + } + + logger.log("Found", this.beltPaths.length, "belt paths"); + this.beltPaths = result; + } + + /** + * Updates all belts + */ + update() { + if (G_IS_DEV && globalConfig.debug.checkBeltPaths) { + this.debug_verifyBeltPaths(); + } + + for (let i = 0; i < this.beltPaths.length; ++i) { + this.beltPaths[i].update(); + } + + if (G_IS_DEV && globalConfig.debug.checkBeltPaths) { + this.debug_verifyBeltPaths(); + } + } + + /** + * Draws a given chunk + * @param {DrawParameters} parameters + * @param {MapChunkView} chunk + */ + drawChunk(parameters, chunk) { + if (G_IS_DEV && globalConfig.debug.doNotRenderStatics) { + return; + } + + // Limit speed to avoid belts going backwards + const speedMultiplier = Math.min(this.root.hubGoals.getBeltBaseSpeed(), 10); + + // SYNC with systems/item_acceptor.js:drawEntityUnderlays! + // 126 / 42 is the exact animation speed of the png animation + const animationIndex = Math.floor( + ((this.root.time.realtimeNow() * speedMultiplier * BELT_ANIM_COUNT * 126) / 42) * + globalConfig.itemSpacingOnBelts + ); + const contents = chunk.containedEntitiesByLayer.regular; + + if (this.root.app.settings.getAllSettings().simplifiedBelts) { + // POTATO Mode: Only show items when belt is hovered + let hoveredBeltPath = null; + const mousePos = this.root.app.mousePosition; + if (mousePos && this.root.currentLayer === "regular") { + const tile = this.root.camera.screenToWorld(mousePos).toTileSpace(); + const contents = this.root.map.getLayerContentXY(tile.x, tile.y, "regular"); + if (contents && contents.components.Belt) { + hoveredBeltPath = contents.components.Belt.assignedPath; + } + } + + for (let i = 0; i < contents.length; ++i) { + const entity = contents[i]; + if (entity.components.Belt) { + const direction = entity.components.Belt.direction; + let sprite = this.beltAnimations[direction][0]; + + if (entity.components.Belt.assignedPath === hoveredBeltPath) { + sprite = this.beltAnimations[direction][animationIndex % BELT_ANIM_COUNT]; + } + + // Culling happens within the static map entity component + entity.components.StaticMapEntity.drawSpriteOnBoundsClipped( + parameters, + sprite, + 0, + null, + true + ); + } + } + } else { + for (let i = 0; i < contents.length; ++i) { + const entity = contents[i]; + if (entity.components.Belt) { + const direction = entity.components.Belt.direction; + const sprite = this.beltAnimations[direction][animationIndex % BELT_ANIM_COUNT]; + + // Culling happens within the static map entity component + entity.components.StaticMapEntity.drawSpriteOnBoundsClipped( + parameters, + sprite, + 0, + null, + true + ); + } + } + } + } + + /** + * Draws the belt path debug overlays + * @param {DrawParameters} parameters + */ + drawBeltPathDebug(parameters) { + for (let i = 0; i < this.beltPaths.length; ++i) { + this.beltPaths[i].drawDebug(parameters); + } + } +} diff --git a/src/js/game/systems/belt_reader.js b/src/js/game/systems/belt_reader.js index 41211c05..1e29992f 100644 --- a/src/js/game/systems/belt_reader.js +++ b/src/js/game/systems/belt_reader.js @@ -1,56 +1,56 @@ -import { GameSystemWithFilter } from "../game_system_with_filter"; -import { BeltReaderComponent } from "../components/belt_reader"; -import { globalConfig } from "../../core/config"; -import { BOOL_TRUE_SINGLETON, BOOL_FALSE_SINGLETON } from "../items/boolean_item"; - -export class BeltReaderSystem extends GameSystemWithFilter { - constructor(root) { - super(root, [BeltReaderComponent]); - } - - update() { - const now = this.root.time.now(); - const minimumTime = now - globalConfig.readerAnalyzeIntervalSeconds; - const minimumTimeForThroughput = now - 1; - for (let i = 0; i < this.allEntities.length; ++i) { - const entity = this.allEntities[i]; - const readerComp = entity.components.BeltReader; - const pinsComp = entity.components.WiredPins; - - // Remove outdated items - while (readerComp.lastItemTimes[0] < minimumTime) { - readerComp.lastItemTimes.shift(); - } - - if (pinsComp) { - pinsComp.slots[1].value = readerComp.lastItem; - pinsComp.slots[0].value = - (readerComp.lastItemTimes[readerComp.lastItemTimes.length - 1] || 0) > - minimumTimeForThroughput - ? BOOL_TRUE_SINGLETON - : BOOL_FALSE_SINGLETON; - } - - if (now - readerComp.lastThroughputComputation > 0.5) { - // Compute throughput - readerComp.lastThroughputComputation = now; - - let throughput = 0; - if (readerComp.lastItemTimes.length < 2) { - throughput = 0; - } else { - let averageSpacing = 0; - let averageSpacingNum = 0; - for (let i = 0; i < readerComp.lastItemTimes.length - 1; ++i) { - averageSpacing += readerComp.lastItemTimes[i + 1] - readerComp.lastItemTimes[i]; - ++averageSpacingNum; - } - - throughput = 1 / (averageSpacing / averageSpacingNum); - } - - readerComp.lastThroughput = Math.min(globalConfig.beltSpeedItemsPerSecond * 23.9, throughput); - } - } - } -} +import { GameSystemWithFilter } from "../game_system_with_filter"; +import { BeltReaderComponent } from "../components/belt_reader"; +import { globalConfig } from "../../core/config"; +import { BOOL_TRUE_SINGLETON, BOOL_FALSE_SINGLETON } from "../items/boolean_item"; + +export class BeltReaderSystem extends GameSystemWithFilter { + constructor(root) { + super(root, [BeltReaderComponent]); + } + + update() { + const now = this.root.time.now(); + const minimumTime = now - globalConfig.readerAnalyzeIntervalSeconds; + const minimumTimeForThroughput = now - 1; + for (let i = 0; i < this.allEntities.length; ++i) { + const entity = this.allEntities[i]; + const readerComp = entity.components.BeltReader; + const pinsComp = entity.components.WiredPins; + + // Remove outdated items + while (readerComp.lastItemTimes[0] < minimumTime) { + readerComp.lastItemTimes.shift(); + } + + if (pinsComp) { + pinsComp.slots[1].value = readerComp.lastItem; + pinsComp.slots[0].value = + (readerComp.lastItemTimes[readerComp.lastItemTimes.length - 1] || 0) > + minimumTimeForThroughput + ? BOOL_TRUE_SINGLETON + : BOOL_FALSE_SINGLETON; + } + + if (now - readerComp.lastThroughputComputation > 0.5) { + // Compute throughput + readerComp.lastThroughputComputation = now; + + let throughput = 0; + if (readerComp.lastItemTimes.length < 2) { + throughput = 0; + } else { + let averageSpacing = 0; + let averageSpacingNum = 0; + for (let i = 0; i < readerComp.lastItemTimes.length - 1; ++i) { + averageSpacing += readerComp.lastItemTimes[i + 1] - readerComp.lastItemTimes[i]; + ++averageSpacingNum; + } + + throughput = 1 / (averageSpacing / averageSpacingNum); + } + + readerComp.lastThroughput = Math.min(globalConfig.beltSpeedItemsPerSecond * 23.9, throughput); + } + } + } +} diff --git a/src/js/game/systems/belt_underlays.js b/src/js/game/systems/belt_underlays.js index ddbe051a..5b5a9390 100644 --- a/src/js/game/systems/belt_underlays.js +++ b/src/js/game/systems/belt_underlays.js @@ -1,298 +1,314 @@ -import { globalConfig } from "../../core/config"; -import { DrawParameters } from "../../core/draw_parameters"; -import { Loader } from "../../core/loader"; -import { Rectangle } from "../../core/rectangle"; -import { FULL_CLIP_RECT } from "../../core/sprites"; -import { StaleAreaDetector } from "../../core/stale_area_detector"; -import { - enumDirection, - enumDirectionToAngle, - enumDirectionToVector, - enumInvertedDirections, - Vector, -} from "../../core/vector"; -import { BeltComponent } from "../components/belt"; -import { BeltUnderlaysComponent, enumClippedBeltUnderlayType } from "../components/belt_underlays"; -import { ItemAcceptorComponent } from "../components/item_acceptor"; -import { ItemEjectorComponent } from "../components/item_ejector"; -import { Entity } from "../entity"; -import { GameSystem } from "../game_system"; -import { MapChunkView } from "../map_chunk_view"; -import { BELT_ANIM_COUNT } from "./belt"; - -/** - * Mapping from underlay type to clip rect - * @type {Object} - */ -const enumUnderlayTypeToClipRect = { - [enumClippedBeltUnderlayType.none]: null, - [enumClippedBeltUnderlayType.full]: FULL_CLIP_RECT, - [enumClippedBeltUnderlayType.topOnly]: new Rectangle(0, 0, 1, 0.5), - [enumClippedBeltUnderlayType.bottomOnly]: new Rectangle(0, 0.5, 1, 0.5), -}; - -export class BeltUnderlaysSystem extends GameSystem { - constructor(root) { - super(root); - - this.underlayBeltSprites = []; - - for (let i = 0; i < BELT_ANIM_COUNT; ++i) { - this.underlayBeltSprites.push(Loader.getSprite("sprites/belt/built/forward_" + i + ".png")); - } - - // Automatically recompute areas - this.staleArea = new StaleAreaDetector({ - root, - name: "belt-underlay", - recomputeMethod: this.recomputeStaleArea.bind(this), - }); - - this.staleArea.recomputeOnComponentsChanged( - [BeltUnderlaysComponent, BeltComponent, ItemAcceptorComponent, ItemEjectorComponent], - 1 - ); - } - - update() { - this.staleArea.update(); - } - - /** - * Called when an area changed - Resets all caches in the given area - * @param {Rectangle} area - */ - recomputeStaleArea(area) { - for (let x = 0; x < area.w; ++x) { - for (let y = 0; y < area.h; ++y) { - const tileX = area.x + x; - const tileY = area.y + y; - const entity = this.root.map.getLayerContentXY(tileX, tileY, "regular"); - if (entity) { - const underlayComp = entity.components.BeltUnderlays; - if (underlayComp) { - for (let i = 0; i < underlayComp.underlays.length; ++i) { - underlayComp.underlays[i].cachedType = null; - } - } - } - } - } - } - - /** - * Checks if a given tile is connected and has an acceptor - * @param {Vector} tile - * @param {enumDirection} fromDirection - * @returns {boolean} - */ - checkIsAcceptorConnected(tile, fromDirection) { - const contents = this.root.map.getLayerContentXY(tile.x, tile.y, "regular"); - if (!contents) { - return false; - } - - const staticComp = contents.components.StaticMapEntity; - - // Check if its a belt, since then its simple - const beltComp = contents.components.Belt; - if (beltComp) { - return staticComp.localDirectionToWorld(enumDirection.bottom) === fromDirection; - } - - // Check if there's an item acceptor - const acceptorComp = contents.components.ItemAcceptor; - if (acceptorComp) { - // Check each slot to see if its connected - for (let i = 0; i < acceptorComp.slots.length; ++i) { - const slot = acceptorComp.slots[i]; - const slotTile = staticComp.localTileToWorld(slot.pos); - - // Step 1: Check if the tile matches - if (!slotTile.equals(tile)) { - continue; - } - - // Step 2: Check if the direction matches - const slotDirection = staticComp.localDirectionToWorld(slot.direction); - if (slotDirection === fromDirection) { - return true; - } - } - } - - return false; - } - - /** - * Checks if a given tile is connected and has an ejector - * @param {Vector} tile - * @param {enumDirection} toDirection - * @returns {boolean} - */ - checkIsEjectorConnected(tile, toDirection) { - const contents = this.root.map.getLayerContentXY(tile.x, tile.y, "regular"); - if (!contents) { - return false; - } - - const staticComp = contents.components.StaticMapEntity; - - // Check if its a belt, since then its simple - const beltComp = contents.components.Belt; - if (beltComp) { - return staticComp.localDirectionToWorld(beltComp.direction) === toDirection; - } - - // Check for an ejector - const ejectorComp = contents.components.ItemEjector; - if (ejectorComp) { - // Check each slot to see if its connected - for (let i = 0; i < ejectorComp.slots.length; ++i) { - const slot = ejectorComp.slots[i]; - const slotTile = staticComp.localTileToWorld(slot.pos); - - // Step 1: Check if the tile matches - if (!slotTile.equals(tile)) { - continue; - } - - // Step 2: Check if the direction matches - const slotDirection = staticComp.localDirectionToWorld(slot.direction); - if (slotDirection === toDirection) { - return true; - } - } - } - - return false; - } - - /** - * Computes the flag for a given tile - * @param {Entity} entity - * @param {import("../components/belt_underlays").BeltUnderlayTile} underlayTile - * @returns {enumClippedBeltUnderlayType} The type of the underlay - */ - computeBeltUnderlayType(entity, underlayTile) { - if (underlayTile.cachedType) { - return underlayTile.cachedType; - } - - const staticComp = entity.components.StaticMapEntity; - - const transformedPos = staticComp.localTileToWorld(underlayTile.pos); - const destX = transformedPos.x * globalConfig.tileSize; - const destY = transformedPos.y * globalConfig.tileSize; - - // Extract direction and angle - const worldDirection = staticComp.localDirectionToWorld(underlayTile.direction); - const worldDirectionVector = enumDirectionToVector[worldDirection]; - - // Figure out if there is anything connected at the top - const connectedTop = this.checkIsAcceptorConnected( - transformedPos.add(worldDirectionVector), - enumInvertedDirections[worldDirection] - ); - - // Figure out if there is anything connected at the bottom - const connectedBottom = this.checkIsEjectorConnected( - transformedPos.sub(worldDirectionVector), - worldDirection - ); - - let flag = enumClippedBeltUnderlayType.none; - - if (connectedTop && connectedBottom) { - flag = enumClippedBeltUnderlayType.full; - } else if (connectedTop) { - flag = enumClippedBeltUnderlayType.topOnly; - } else if (connectedBottom) { - flag = enumClippedBeltUnderlayType.bottomOnly; - } - - return (underlayTile.cachedType = flag); - } - - /** - * Draws a given chunk - * @param {DrawParameters} parameters - * @param {MapChunkView} chunk - */ - drawChunk(parameters, chunk) { - // Limit speed to avoid belts going backwards - const speedMultiplier = Math.min(this.root.hubGoals.getBeltBaseSpeed(), 10); - - const contents = chunk.containedEntitiesByLayer.regular; - for (let i = 0; i < contents.length; ++i) { - const entity = contents[i]; - const underlayComp = entity.components.BeltUnderlays; - if (!underlayComp) { - continue; - } - - const staticComp = entity.components.StaticMapEntity; - const underlays = underlayComp.underlays; - for (let i = 0; i < underlays.length; ++i) { - // Extract underlay parameters - const { pos, direction } = underlays[i]; - const transformedPos = staticComp.localTileToWorld(pos); - const destX = transformedPos.x * globalConfig.tileSize; - const destY = transformedPos.y * globalConfig.tileSize; - - // Culling, Part 1: Check if the chunk contains the tile - if (!chunk.tileSpaceRectangle.containsPoint(transformedPos.x, transformedPos.y)) { - continue; - } - - // Culling, Part 2: Check if the overlay is visible - if ( - !parameters.visibleRect.containsRect4Params( - destX, - destY, - globalConfig.tileSize, - globalConfig.tileSize - ) - ) { - continue; - } - - // Extract direction and angle - const worldDirection = staticComp.localDirectionToWorld(direction); - const angle = enumDirectionToAngle[worldDirection]; - - const underlayType = this.computeBeltUnderlayType(entity, underlays[i]); - const clipRect = enumUnderlayTypeToClipRect[underlayType]; - if (!clipRect) { - // Empty - continue; - } - - // Actually draw the sprite - const x = destX + globalConfig.halfTileSize; - const y = destY + globalConfig.halfTileSize; - const angleRadians = Math.radians(angle); - - // SYNC with systems/belt.js:drawSingleEntity! - const animationIndex = Math.floor( - ((this.root.time.realtimeNow() * speedMultiplier * BELT_ANIM_COUNT * 126) / 42) * - globalConfig.itemSpacingOnBelts - ); - parameters.context.translate(x, y); - parameters.context.rotate(angleRadians); - this.underlayBeltSprites[ - animationIndex % this.underlayBeltSprites.length - ].drawCachedWithClipRect( - parameters, - -globalConfig.halfTileSize, - -globalConfig.halfTileSize, - globalConfig.tileSize, - globalConfig.tileSize, - clipRect - ); - parameters.context.rotate(-angleRadians); - parameters.context.translate(-x, -y); - } - } - } -} +import { globalConfig } from "../../core/config"; +import { DrawParameters } from "../../core/draw_parameters"; +import { Loader } from "../../core/loader"; +import { Rectangle } from "../../core/rectangle"; +import { FULL_CLIP_RECT } from "../../core/sprites"; +import { StaleAreaDetector } from "../../core/stale_area_detector"; +import { + enumDirection, + enumDirectionToAngle, + enumDirectionToVector, + enumInvertedDirections, + Vector, +} from "../../core/vector"; +import { BeltComponent } from "../components/belt"; +import { BeltUnderlaysComponent, enumClippedBeltUnderlayType } from "../components/belt_underlays"; +import { ItemAcceptorComponent } from "../components/item_acceptor"; +import { ItemEjectorComponent } from "../components/item_ejector"; +import { Entity } from "../entity"; +import { GameSystem } from "../game_system"; +import { MapChunkView } from "../map_chunk_view"; +import { BELT_ANIM_COUNT } from "./belt"; + +/** + * Mapping from underlay type to clip rect + * @type {Object} + */ +const enumUnderlayTypeToClipRect = { + [enumClippedBeltUnderlayType.none]: null, + [enumClippedBeltUnderlayType.full]: FULL_CLIP_RECT, + [enumClippedBeltUnderlayType.topOnly]: new Rectangle(0, 0, 1, 0.5), + [enumClippedBeltUnderlayType.bottomOnly]: new Rectangle(0, 0.5, 1, 0.5), +}; + +export class BeltUnderlaysSystem extends GameSystem { + constructor(root) { + super(root); + + this.underlayBeltSprites = []; + + for (let i = 0; i < BELT_ANIM_COUNT; ++i) { + this.underlayBeltSprites.push(Loader.getSprite("sprites/belt/built/forward_" + i + ".png")); + } + + // Automatically recompute areas + this.staleArea = new StaleAreaDetector({ + root, + name: "belt-underlay", + recomputeMethod: this.recomputeStaleArea.bind(this), + }); + + this.staleArea.recomputeOnComponentsChanged( + [BeltUnderlaysComponent, BeltComponent, ItemAcceptorComponent, ItemEjectorComponent], + 1 + ); + } + + update() { + this.staleArea.update(); + } + + /** + * Called when an area changed - Resets all caches in the given area + * @param {Rectangle} area + */ + recomputeStaleArea(area) { + for (let x = 0; x < area.w; ++x) { + for (let y = 0; y < area.h; ++y) { + const tileX = area.x + x; + const tileY = area.y + y; + const entity = this.root.map.getLayerContentXY(tileX, tileY, "regular"); + if (entity) { + const underlayComp = entity.components.BeltUnderlays; + if (underlayComp) { + for (let i = 0; i < underlayComp.underlays.length; ++i) { + underlayComp.underlays[i].cachedType = null; + } + } + } + } + } + } + + /** + * Checks if a given tile is connected and has an acceptor + * @param {Vector} tile + * @param {enumDirection} fromDirection + * @returns {boolean} + */ + checkIsAcceptorConnected(tile, fromDirection) { + const contents = this.root.map.getLayerContentXY(tile.x, tile.y, "regular"); + if (!contents) { + return false; + } + + const staticComp = contents.components.StaticMapEntity; + + // Check if its a belt, since then its simple + const beltComp = contents.components.Belt; + if (beltComp) { + return staticComp.localDirectionToWorld(enumDirection.bottom) === fromDirection; + } + + // Check if there's an item acceptor + const acceptorComp = contents.components.ItemAcceptor; + if (acceptorComp) { + // Check each slot to see if its connected + for (let i = 0; i < acceptorComp.slots.length; ++i) { + const slot = acceptorComp.slots[i]; + const slotTile = staticComp.localTileToWorld(slot.pos); + + // Step 1: Check if the tile matches + if (!slotTile.equals(tile)) { + continue; + } + + // Step 2: Check if the direction matches + const slotDirection = staticComp.localDirectionToWorld(slot.direction); + if (slotDirection === fromDirection) { + return true; + } + } + } + + return false; + } + + /** + * Checks if a given tile is connected and has an ejector + * @param {Vector} tile + * @param {enumDirection} toDirection + * @returns {boolean} + */ + checkIsEjectorConnected(tile, toDirection) { + const contents = this.root.map.getLayerContentXY(tile.x, tile.y, "regular"); + if (!contents) { + return false; + } + + const staticComp = contents.components.StaticMapEntity; + + // Check if its a belt, since then its simple + const beltComp = contents.components.Belt; + if (beltComp) { + return staticComp.localDirectionToWorld(beltComp.direction) === toDirection; + } + + // Check for an ejector + const ejectorComp = contents.components.ItemEjector; + if (ejectorComp) { + // Check each slot to see if its connected + for (let i = 0; i < ejectorComp.slots.length; ++i) { + const slot = ejectorComp.slots[i]; + const slotTile = staticComp.localTileToWorld(slot.pos); + + // Step 1: Check if the tile matches + if (!slotTile.equals(tile)) { + continue; + } + + // Step 2: Check if the direction matches + const slotDirection = staticComp.localDirectionToWorld(slot.direction); + if (slotDirection === toDirection) { + return true; + } + } + } + + return false; + } + + /** + * Computes the flag for a given tile + * @param {Entity} entity + * @param {import("../components/belt_underlays").BeltUnderlayTile} underlayTile + * @returns {enumClippedBeltUnderlayType} The type of the underlay + */ + computeBeltUnderlayType(entity, underlayTile) { + if (underlayTile.cachedType) { + return underlayTile.cachedType; + } + + const staticComp = entity.components.StaticMapEntity; + + const transformedPos = staticComp.localTileToWorld(underlayTile.pos); + const destX = transformedPos.x * globalConfig.tileSize; + const destY = transformedPos.y * globalConfig.tileSize; + + // Extract direction and angle + const worldDirection = staticComp.localDirectionToWorld(underlayTile.direction); + const worldDirectionVector = enumDirectionToVector[worldDirection]; + + // Figure out if there is anything connected at the top + const connectedTop = this.checkIsAcceptorConnected( + transformedPos.add(worldDirectionVector), + enumInvertedDirections[worldDirection] + ); + + // Figure out if there is anything connected at the bottom + const connectedBottom = this.checkIsEjectorConnected( + transformedPos.sub(worldDirectionVector), + worldDirection + ); + + let flag = enumClippedBeltUnderlayType.none; + + if (connectedTop && connectedBottom) { + flag = enumClippedBeltUnderlayType.full; + } else if (connectedTop) { + flag = enumClippedBeltUnderlayType.topOnly; + } else if (connectedBottom) { + flag = enumClippedBeltUnderlayType.bottomOnly; + } + + return (underlayTile.cachedType = flag); + } + + /** + * Draws a given chunk + * @param {DrawParameters} parameters + * @param {MapChunkView} chunk + */ + drawChunk(parameters, chunk) { + // Limit speed to avoid belts going backwards + const speedMultiplier = Math.min(this.root.hubGoals.getBeltBaseSpeed(), 10); + + const contents = chunk.containedEntitiesByLayer.regular; + for (let i = 0; i < contents.length; ++i) { + const entity = contents[i]; + const underlayComp = entity.components.BeltUnderlays; + if (!underlayComp) { + continue; + } + + const staticComp = entity.components.StaticMapEntity; + const underlays = underlayComp.underlays; + for (let i = 0; i < underlays.length; ++i) { + // Extract underlay parameters + const { pos, direction } = underlays[i]; + const transformedPos = staticComp.localTileToWorld(pos); + const destX = transformedPos.x * globalConfig.tileSize; + const destY = transformedPos.y * globalConfig.tileSize; + + // Culling, Part 1: Check if the chunk contains the tile + if (!chunk.tileSpaceRectangle.containsPoint(transformedPos.x, transformedPos.y)) { + continue; + } + + // Culling, Part 2: Check if the overlay is visible + if ( + !parameters.visibleRect.containsRect4Params( + destX, + destY, + globalConfig.tileSize, + globalConfig.tileSize + ) + ) { + continue; + } + + // Extract direction and angle + const worldDirection = staticComp.localDirectionToWorld(direction); + const angle = enumDirectionToAngle[worldDirection]; + + const underlayType = this.computeBeltUnderlayType(entity, underlays[i]); + const clipRect = enumUnderlayTypeToClipRect[underlayType]; + if (!clipRect) { + // Empty + continue; + } + + // Actually draw the sprite + const x = destX + globalConfig.halfTileSize; + const y = destY + globalConfig.halfTileSize; + const angleRadians = Math.radians(angle); + + // SYNC with systems/belt.js:drawSingleEntity! + const animationIndex = Math.floor( + ((this.root.time.realtimeNow() * speedMultiplier * BELT_ANIM_COUNT * 126) / 42) * + globalConfig.itemSpacingOnBelts + ); + + // See components/static_map_entity.js:drawSpriteOnBoundsClipped + const transform = parameters.context.getTransform(); + const matrix = new DOMMatrix().rotate(0, 0, -angle).multiplySelf(transform); + let { x: x1, y: y1 } = matrix.transformPoint( + new DOMPoint(x - globalConfig.halfTileSize, y - globalConfig.halfTileSize) + ); + let { x: x2, y: y2 } = matrix.transformPoint( + new DOMPoint(x + globalConfig.halfTileSize, y + globalConfig.halfTileSize) + ); + if (x1 > x2) { + [x1, x2] = [x2, x1]; + } + if (y1 > y2) { + [y1, y2] = [y2, y1]; + } + x1 = Math.round(x1); + y1 = Math.round(y1); + x2 = Math.round(x2); + y2 = Math.round(y2); + if (x2 - x1 == 0 || y2 - y1 == 0) { + continue; + } + + parameters.context.resetTransform(); + parameters.context.rotate(angleRadians); + this.underlayBeltSprites[ + animationIndex % this.underlayBeltSprites.length + ].drawCachedWithClipRect(parameters, x1, y1, x2 - x1, y2 - y1, clipRect); + parameters.context.setTransform(transform); + } + } + } +} diff --git a/src/js/game/systems/constant_producer.js b/src/js/game/systems/constant_producer.js index a95efdb0..c936d496 100644 --- a/src/js/game/systems/constant_producer.js +++ b/src/js/game/systems/constant_producer.js @@ -1,60 +1,60 @@ -import { globalConfig } from "../../core/config"; -import { DrawParameters } from "../../core/draw_parameters"; -import { Vector } from "../../core/vector"; -import { ConstantSignalComponent } from "../components/constant_signal"; -import { ItemProducerComponent } from "../components/item_producer"; -import { GameSystemWithFilter } from "../game_system_with_filter"; -import { MapChunk } from "../map_chunk"; - -export class ConstantProducerSystem extends GameSystemWithFilter { - constructor(root) { - super(root, [ConstantSignalComponent, ItemProducerComponent]); - } - - update() { - for (let i = 0; i < this.allEntities.length; ++i) { - const entity = this.allEntities[i]; - const signalComp = entity.components.ConstantSignal; - const ejectorComp = entity.components.ItemEjector; - if (!ejectorComp) { - continue; - } - ejectorComp.tryEject(0, signalComp.signal); - } - } - - /** - * - * @param {DrawParameters} parameters - * @param {MapChunk} chunk - * @returns - */ - drawChunk(parameters, chunk) { - const contents = chunk.containedEntitiesByLayer.regular; - for (let i = 0; i < contents.length; ++i) { - const producerComp = contents[i].components.ItemProducer; - const signalComp = contents[i].components.ConstantSignal; - - if (!producerComp || !signalComp) { - continue; - } - - const staticComp = contents[i].components.StaticMapEntity; - const item = signalComp.signal; - - if (!item) { - continue; - } - - const center = staticComp.getTileSpaceBounds().getCenter().toWorldSpace(); - - const localOffset = new Vector(0, 1).rotateFastMultipleOf90(staticComp.rotation); - item.drawItemCenteredClipped( - center.x + localOffset.x, - center.y + localOffset.y, - parameters, - globalConfig.tileSize * 0.65 - ); - } - } -} +import { globalConfig } from "../../core/config"; +import { DrawParameters } from "../../core/draw_parameters"; +import { Vector } from "../../core/vector"; +import { ConstantSignalComponent } from "../components/constant_signal"; +import { ItemProducerComponent } from "../components/item_producer"; +import { GameSystemWithFilter } from "../game_system_with_filter"; +import { MapChunk } from "../map_chunk"; + +export class ConstantProducerSystem extends GameSystemWithFilter { + constructor(root) { + super(root, [ConstantSignalComponent, ItemProducerComponent]); + } + + update() { + for (let i = 0; i < this.allEntities.length; ++i) { + const entity = this.allEntities[i]; + const signalComp = entity.components.ConstantSignal; + const ejectorComp = entity.components.ItemEjector; + if (!ejectorComp) { + continue; + } + ejectorComp.tryEject(0, signalComp.signal); + } + } + + /** + * + * @param {DrawParameters} parameters + * @param {MapChunk} chunk + * @returns + */ + drawChunk(parameters, chunk) { + const contents = chunk.containedEntitiesByLayer.regular; + for (let i = 0; i < contents.length; ++i) { + const producerComp = contents[i].components.ItemProducer; + const signalComp = contents[i].components.ConstantSignal; + + if (!producerComp || !signalComp) { + continue; + } + + const staticComp = contents[i].components.StaticMapEntity; + const item = signalComp.signal; + + if (!item) { + continue; + } + + const center = staticComp.getTileSpaceBounds().getCenter().toWorldSpace(); + + const localOffset = new Vector(0, 1).rotateFastMultipleOf90(staticComp.rotation); + item.drawItemCenteredClipped( + center.x + localOffset.x, + center.y + localOffset.y, + parameters, + globalConfig.tileSize * 0.65 + ); + } + } +} diff --git a/src/js/game/systems/constant_signal.js b/src/js/game/systems/constant_signal.js index 75a4dbdd..af0e3c4b 100644 --- a/src/js/game/systems/constant_signal.js +++ b/src/js/game/systems/constant_signal.js @@ -1,28 +1,28 @@ -import { ConstantSignalComponent } from "../components/constant_signal"; -import { GameSystemWithFilter } from "../game_system_with_filter"; - -export class ConstantSignalSystem extends GameSystemWithFilter { - constructor(root) { - super(root, [ConstantSignalComponent]); - - this.root.signals.entityManuallyPlaced.add(entity => { - const editorHud = this.root.hud.parts.constantSignalEdit; - if (editorHud) { - editorHud.editConstantSignal(entity, { deleteOnCancel: true }); - } - }); - } - - update() { - // Set signals - for (let i = 0; i < this.allEntities.length; ++i) { - const entity = this.allEntities[i]; - const signalComp = entity.components.ConstantSignal; - const pinsComp = entity.components.WiredPins; - - if (pinsComp) { - pinsComp.slots[0].value = signalComp.signal; - } - } - } -} +import { ConstantSignalComponent } from "../components/constant_signal"; +import { GameSystemWithFilter } from "../game_system_with_filter"; + +export class ConstantSignalSystem extends GameSystemWithFilter { + constructor(root) { + super(root, [ConstantSignalComponent]); + + this.root.signals.entityManuallyPlaced.add(entity => { + const editorHud = this.root.hud.parts.constantSignalEdit; + if (editorHud) { + editorHud.editConstantSignal(entity, { deleteOnCancel: true }); + } + }); + } + + update() { + // Set signals + for (let i = 0; i < this.allEntities.length; ++i) { + const entity = this.allEntities[i]; + const signalComp = entity.components.ConstantSignal; + const pinsComp = entity.components.WiredPins; + + if (pinsComp) { + pinsComp.slots[0].value = signalComp.signal; + } + } + } +} diff --git a/src/js/game/systems/display.js b/src/js/game/systems/display.js index a54c7a10..63aa4fb0 100644 --- a/src/js/game/systems/display.js +++ b/src/js/game/systems/display.js @@ -1,117 +1,117 @@ -import { globalConfig } from "../../core/config"; -import { Loader } from "../../core/loader"; -import { BaseItem } from "../base_item"; -import { enumColors } from "../colors"; -import { GameSystem } from "../game_system"; -import { isTrueItem } from "../items/boolean_item"; -import { ColorItem, COLOR_ITEM_SINGLETONS } from "../items/color_item"; -import { MapChunkView } from "../map_chunk_view"; - -/** @type {{ - * [x: string]: (item: BaseItem) => BaseItem - * }} */ -export const MODS_ADDITIONAL_DISPLAY_ITEM_RESOLVER = {}; - -/** @type {{ - * [x: string]: (parameters: import("../../core/draw_parameters").DrawParameters, entity: import("../entity").Entity, item: BaseItem) => BaseItem - * }} */ -export const MODS_ADDITIONAL_DISPLAY_ITEM_DRAW = {}; -export class DisplaySystem extends GameSystem { - constructor(root) { - super(root); - - /** @type {Object} */ - this.displaySprites = {}; - - for (const colorId in enumColors) { - if (colorId === enumColors.uncolored) { - continue; - } - this.displaySprites[colorId] = Loader.getSprite("sprites/wires/display/" + colorId + ".png"); - } - } - - /** - * Returns the color / value a display should show - * @param {BaseItem} value - * @returns {BaseItem} - */ - getDisplayItem(value) { - if (!value) { - return null; - } - - if (MODS_ADDITIONAL_DISPLAY_ITEM_RESOLVER[value.getItemType()]) { - return MODS_ADDITIONAL_DISPLAY_ITEM_RESOLVER[value.getItemType()].apply(this, [value]); - } - - switch (value.getItemType()) { - case "boolean": { - return isTrueItem(value) ? COLOR_ITEM_SINGLETONS[enumColors.white] : null; - } - - case "color": { - const item = /**@type {ColorItem} */ (value); - return item.color === enumColors.uncolored ? null : item; - } - - case "shape": { - return value; - } - - default: - assertAlways(false, "Unknown item type: " + value.getItemType()); - } - } - - /** - * Draws a given chunk - * @param {import("../../core/draw_utils").DrawParameters} parameters - * @param {MapChunkView} chunk - */ - drawChunk(parameters, chunk) { - const contents = chunk.containedEntitiesByLayer.regular; - for (let i = 0; i < contents.length; ++i) { - const entity = contents[i]; - if (entity && entity.components.Display) { - const pinsComp = entity.components.WiredPins; - const network = pinsComp.slots[0].linkedNetwork; - - if (!network || !network.hasValue()) { - continue; - } - - const value = this.getDisplayItem(network.currentValue); - - if (!value) { - continue; - } - - if (MODS_ADDITIONAL_DISPLAY_ITEM_DRAW[value.getItemType()]) { - return MODS_ADDITIONAL_DISPLAY_ITEM_DRAW[value.getItemType()].apply(this, [ - parameters, - entity, - value, - ]); - } - - const origin = entity.components.StaticMapEntity.origin; - if (value.getItemType() === "color") { - this.displaySprites[/** @type {ColorItem} */ (value).color].drawCachedCentered( - parameters, - (origin.x + 0.5) * globalConfig.tileSize, - (origin.y + 0.5) * globalConfig.tileSize, - globalConfig.tileSize - ); - } else if (value.getItemType() === "shape") { - value.drawItemCenteredClipped( - (origin.x + 0.5) * globalConfig.tileSize, - (origin.y + 0.5) * globalConfig.tileSize, - parameters, - 30 - ); - } - } - } - } -} +import { globalConfig } from "../../core/config"; +import { Loader } from "../../core/loader"; +import { BaseItem } from "../base_item"; +import { enumColors } from "../colors"; +import { GameSystem } from "../game_system"; +import { isTrueItem } from "../items/boolean_item"; +import { ColorItem, COLOR_ITEM_SINGLETONS } from "../items/color_item"; +import { MapChunkView } from "../map_chunk_view"; + +/** @type {{ + * [x: string]: (item: BaseItem) => BaseItem + * }} */ +export const MODS_ADDITIONAL_DISPLAY_ITEM_RESOLVER = {}; + +/** @type {{ + * [x: string]: (parameters: import("../../core/draw_parameters").DrawParameters, entity: import("../entity").Entity, item: BaseItem) => BaseItem + * }} */ +export const MODS_ADDITIONAL_DISPLAY_ITEM_DRAW = {}; +export class DisplaySystem extends GameSystem { + constructor(root) { + super(root); + + /** @type {Object} */ + this.displaySprites = {}; + + for (const colorId in enumColors) { + if (colorId === enumColors.uncolored) { + continue; + } + this.displaySprites[colorId] = Loader.getSprite("sprites/wires/display/" + colorId + ".png"); + } + } + + /** + * Returns the color / value a display should show + * @param {BaseItem} value + * @returns {BaseItem} + */ + getDisplayItem(value) { + if (!value) { + return null; + } + + if (MODS_ADDITIONAL_DISPLAY_ITEM_RESOLVER[value.getItemType()]) { + return MODS_ADDITIONAL_DISPLAY_ITEM_RESOLVER[value.getItemType()].apply(this, [value]); + } + + switch (value.getItemType()) { + case "boolean": { + return isTrueItem(value) ? COLOR_ITEM_SINGLETONS[enumColors.white] : null; + } + + case "color": { + const item = /**@type {ColorItem} */ (value); + return item.color === enumColors.uncolored ? null : item; + } + + case "shape": { + return value; + } + + default: + assertAlways(false, "Unknown item type: " + value.getItemType()); + } + } + + /** + * Draws a given chunk + * @param {import("../../core/draw_utils").DrawParameters} parameters + * @param {MapChunkView} chunk + */ + drawChunk(parameters, chunk) { + const contents = chunk.containedEntitiesByLayer.regular; + for (let i = 0; i < contents.length; ++i) { + const entity = contents[i]; + if (entity && entity.components.Display) { + const pinsComp = entity.components.WiredPins; + const network = pinsComp.slots[0].linkedNetwork; + + if (!network || !network.hasValue()) { + continue; + } + + const value = this.getDisplayItem(network.currentValue); + + if (!value) { + continue; + } + + if (MODS_ADDITIONAL_DISPLAY_ITEM_DRAW[value.getItemType()]) { + return MODS_ADDITIONAL_DISPLAY_ITEM_DRAW[value.getItemType()].apply(this, [ + parameters, + entity, + value, + ]); + } + + const origin = entity.components.StaticMapEntity.origin; + if (value.getItemType() === "color") { + this.displaySprites[/** @type {ColorItem} */ (value).color].drawCachedCentered( + parameters, + (origin.x + 0.5) * globalConfig.tileSize, + (origin.y + 0.5) * globalConfig.tileSize, + globalConfig.tileSize + ); + } else if (value.getItemType() === "shape") { + value.drawItemCenteredClipped( + (origin.x + 0.5) * globalConfig.tileSize, + (origin.y + 0.5) * globalConfig.tileSize, + parameters, + 30 + ); + } + } + } + } +} diff --git a/src/js/game/systems/filter.js b/src/js/game/systems/filter.js index a6442b41..473092cf 100644 --- a/src/js/game/systems/filter.js +++ b/src/js/game/systems/filter.js @@ -1,85 +1,85 @@ -import { globalConfig } from "../../core/config"; -import { BaseItem } from "../base_item"; -import { FilterComponent } from "../components/filter"; -import { Entity } from "../entity"; -import { GameSystemWithFilter } from "../game_system_with_filter"; -import { BOOL_TRUE_SINGLETON } from "../items/boolean_item"; - -const MAX_ITEMS_IN_QUEUE = 2; - -export class FilterSystem extends GameSystemWithFilter { - constructor(root) { - super(root, [FilterComponent]); - } - - update() { - const progress = - this.root.dynamicTickrate.deltaSeconds * - this.root.hubGoals.getBeltBaseSpeed() * - globalConfig.itemSpacingOnBelts; - - const requiredProgress = 1 - progress; - - for (let i = 0; i < this.allEntities.length; ++i) { - const entity = this.allEntities[i]; - const filterComp = entity.components.Filter; - const ejectorComp = entity.components.ItemEjector; - - // Process payloads - const slotsAndLists = [filterComp.pendingItemsToLeaveThrough, filterComp.pendingItemsToReject]; - for (let slotIndex = 0; slotIndex < slotsAndLists.length; ++slotIndex) { - const pendingItems = slotsAndLists[slotIndex]; - - for (let j = 0; j < pendingItems.length; ++j) { - const nextItem = pendingItems[j]; - // Advance next item - nextItem.progress = Math.min(requiredProgress, nextItem.progress + progress); - // Check if it's ready to eject - if (nextItem.progress >= requiredProgress - 1e-5) { - if (ejectorComp.tryEject(slotIndex, nextItem.item)) { - pendingItems.shift(); - } - } - } - } - } - } - - /** - * - * @param {Entity} entity - * @param {number} slot - * @param {BaseItem} item - */ - tryAcceptItem(entity, slot, item) { - const network = entity.components.WiredPins.slots[0].linkedNetwork; - if (!network || !network.hasValue()) { - // Filter is not connected - return false; - } - - const value = network.currentValue; - const filterComp = entity.components.Filter; - assert(filterComp, "entity is no filter"); - - // Figure out which list we have to check - let listToCheck; - if (value.equals(BOOL_TRUE_SINGLETON) || value.equals(item)) { - listToCheck = filterComp.pendingItemsToLeaveThrough; - } else { - listToCheck = filterComp.pendingItemsToReject; - } - - if (listToCheck.length >= MAX_ITEMS_IN_QUEUE) { - // Busy - return false; - } - - // Actually accept item - listToCheck.push({ - item, - progress: 0.0, - }); - return true; - } -} +import { globalConfig } from "../../core/config"; +import { BaseItem } from "../base_item"; +import { FilterComponent } from "../components/filter"; +import { Entity } from "../entity"; +import { GameSystemWithFilter } from "../game_system_with_filter"; +import { BOOL_TRUE_SINGLETON } from "../items/boolean_item"; + +const MAX_ITEMS_IN_QUEUE = 2; + +export class FilterSystem extends GameSystemWithFilter { + constructor(root) { + super(root, [FilterComponent]); + } + + update() { + const progress = + this.root.dynamicTickrate.deltaSeconds * + this.root.hubGoals.getBeltBaseSpeed() * + globalConfig.itemSpacingOnBelts; + + const requiredProgress = 1 - progress; + + for (let i = 0; i < this.allEntities.length; ++i) { + const entity = this.allEntities[i]; + const filterComp = entity.components.Filter; + const ejectorComp = entity.components.ItemEjector; + + // Process payloads + const slotsAndLists = [filterComp.pendingItemsToLeaveThrough, filterComp.pendingItemsToReject]; + for (let slotIndex = 0; slotIndex < slotsAndLists.length; ++slotIndex) { + const pendingItems = slotsAndLists[slotIndex]; + + for (let j = 0; j < pendingItems.length; ++j) { + const nextItem = pendingItems[j]; + // Advance next item + nextItem.progress = Math.min(requiredProgress, nextItem.progress + progress); + // Check if it's ready to eject + if (nextItem.progress >= requiredProgress - 1e-5) { + if (ejectorComp.tryEject(slotIndex, nextItem.item)) { + pendingItems.shift(); + } + } + } + } + } + } + + /** + * + * @param {Entity} entity + * @param {number} slot + * @param {BaseItem} item + */ + tryAcceptItem(entity, slot, item) { + const network = entity.components.WiredPins.slots[0].linkedNetwork; + if (!network || !network.hasValue()) { + // Filter is not connected + return false; + } + + const value = network.currentValue; + const filterComp = entity.components.Filter; + assert(filterComp, "entity is no filter"); + + // Figure out which list we have to check + let listToCheck; + if (value.equals(BOOL_TRUE_SINGLETON) || value.equals(item)) { + listToCheck = filterComp.pendingItemsToLeaveThrough; + } else { + listToCheck = filterComp.pendingItemsToReject; + } + + if (listToCheck.length >= MAX_ITEMS_IN_QUEUE) { + // Busy + return false; + } + + // Actually accept item + listToCheck.push({ + item, + progress: 0.0, + }); + return true; + } +} diff --git a/src/js/game/systems/goal_acceptor.js b/src/js/game/systems/goal_acceptor.js index 2ffc3b52..497a4472 100644 --- a/src/js/game/systems/goal_acceptor.js +++ b/src/js/game/systems/goal_acceptor.js @@ -1,134 +1,134 @@ -import { globalConfig } from "../../core/config"; -import { DrawParameters } from "../../core/draw_parameters"; -import { clamp, lerp } from "../../core/utils"; -import { Vector } from "../../core/vector"; -import { GoalAcceptorComponent } from "../components/goal_acceptor"; -import { GameSystemWithFilter } from "../game_system_with_filter"; -import { MapChunk } from "../map_chunk"; - -export class GoalAcceptorSystem extends GameSystemWithFilter { - constructor(root) { - super(root, [GoalAcceptorComponent]); - - this.puzzleCompleted = false; - } - - update() { - const now = this.root.time.now(); - - let allAccepted = true; - - for (let i = 0; i < this.allEntities.length; ++i) { - const entity = this.allEntities[i]; - const goalComp = entity.components.GoalAcceptor; - - if (!goalComp.lastDelivery) { - allAccepted = false; - continue; - } - - if (now - goalComp.lastDelivery.time > goalComp.getRequiredSecondsPerItem()) { - goalComp.clearItems(); - } - - if (goalComp.currentDeliveredItems < globalConfig.goalAcceptorItemsRequired) { - allAccepted = false; - } - } - - if ( - !this.puzzleCompleted && - this.root.gameInitialized && - allAccepted && - !this.root.gameMode.getIsEditor() - ) { - this.root.signals.puzzleComplete.dispatch(); - this.puzzleCompleted = true; - } - } - - /** - * - * @param {DrawParameters} parameters - * @param {MapChunk} chunk - * @returns - */ - drawChunk(parameters, chunk) { - const contents = chunk.containedEntitiesByLayer.regular; - for (let i = 0; i < contents.length; ++i) { - const goalComp = contents[i].components.GoalAcceptor; - - if (!goalComp) { - continue; - } - - const staticComp = contents[i].components.StaticMapEntity; - const item = goalComp.item; - - const requiredItems = globalConfig.goalAcceptorItemsRequired; - - const fillPercentage = clamp(goalComp.currentDeliveredItems / requiredItems, 0, 1); - - const center = staticComp.getTileSpaceBounds().getCenter().toWorldSpace(); - if (item) { - const localOffset = new Vector(0, -1.8).rotateFastMultipleOf90(staticComp.rotation); - item.drawItemCenteredClipped( - center.x + localOffset.x, - center.y + localOffset.y, - parameters, - globalConfig.tileSize * 0.65 - ); - } - - const isValid = item && goalComp.currentDeliveredItems >= requiredItems; - - parameters.context.translate(center.x, center.y); - parameters.context.rotate((staticComp.rotation / 180) * Math.PI); - - parameters.context.lineWidth = 1; - parameters.context.fillStyle = "#8de255"; - parameters.context.strokeStyle = "#64666e"; - parameters.context.lineCap = "round"; - - // progress arc - - goalComp.displayPercentage = lerp(goalComp.displayPercentage, fillPercentage, 0.2); - - const startAngle = Math.PI * 0.595; - const maxAngle = Math.PI * 1.82; - parameters.context.beginPath(); - parameters.context.arc( - 0.25, - -1.5, - 11.6, - startAngle, - startAngle + goalComp.displayPercentage * maxAngle, - false - ); - parameters.context.arc( - 0.25, - -1.5, - 15.5, - startAngle + goalComp.displayPercentage * maxAngle, - startAngle, - true - ); - parameters.context.closePath(); - parameters.context.fill(); - parameters.context.stroke(); - parameters.context.lineCap = "butt"; - - // LED indicator - - parameters.context.lineWidth = 1.2; - parameters.context.strokeStyle = "#64666e"; - parameters.context.fillStyle = isValid ? "#8de255" : "#ff666a"; - parameters.context.beginCircle(10, 11.8, 5); - parameters.context.fill(); - parameters.context.stroke(); - - parameters.context.rotate((-staticComp.rotation / 180) * Math.PI); - parameters.context.translate(-center.x, -center.y); - } - } -} +import { globalConfig } from "../../core/config"; +import { DrawParameters } from "../../core/draw_parameters"; +import { clamp, lerp } from "../../core/utils"; +import { Vector } from "../../core/vector"; +import { GoalAcceptorComponent } from "../components/goal_acceptor"; +import { GameSystemWithFilter } from "../game_system_with_filter"; +import { MapChunk } from "../map_chunk"; + +export class GoalAcceptorSystem extends GameSystemWithFilter { + constructor(root) { + super(root, [GoalAcceptorComponent]); + + this.puzzleCompleted = false; + } + + update() { + const now = this.root.time.now(); + + let allAccepted = true; + + for (let i = 0; i < this.allEntities.length; ++i) { + const entity = this.allEntities[i]; + const goalComp = entity.components.GoalAcceptor; + + if (!goalComp.lastDelivery) { + allAccepted = false; + continue; + } + + if (now - goalComp.lastDelivery.time > goalComp.getRequiredSecondsPerItem()) { + goalComp.clearItems(); + } + + if (goalComp.currentDeliveredItems < globalConfig.goalAcceptorItemsRequired) { + allAccepted = false; + } + } + + if ( + !this.puzzleCompleted && + this.root.gameInitialized && + allAccepted && + !this.root.gameMode.getIsEditor() + ) { + this.root.signals.puzzleComplete.dispatch(); + this.puzzleCompleted = true; + } + } + + /** + * + * @param {DrawParameters} parameters + * @param {MapChunk} chunk + * @returns + */ + drawChunk(parameters, chunk) { + const contents = chunk.containedEntitiesByLayer.regular; + for (let i = 0; i < contents.length; ++i) { + const goalComp = contents[i].components.GoalAcceptor; + + if (!goalComp) { + continue; + } + + const staticComp = contents[i].components.StaticMapEntity; + const item = goalComp.item; + + const requiredItems = globalConfig.goalAcceptorItemsRequired; + + const fillPercentage = clamp(goalComp.currentDeliveredItems / requiredItems, 0, 1); + + const center = staticComp.getTileSpaceBounds().getCenter().toWorldSpace(); + if (item) { + const localOffset = new Vector(0, -1.8).rotateFastMultipleOf90(staticComp.rotation); + item.drawItemCenteredClipped( + center.x + localOffset.x, + center.y + localOffset.y, + parameters, + globalConfig.tileSize * 0.65 + ); + } + + const isValid = item && goalComp.currentDeliveredItems >= requiredItems; + + parameters.context.translate(center.x, center.y); + parameters.context.rotate((staticComp.rotation / 180) * Math.PI); + + parameters.context.lineWidth = 1; + parameters.context.fillStyle = "#8de255"; + parameters.context.strokeStyle = "#64666e"; + parameters.context.lineCap = "round"; + + // progress arc + + goalComp.displayPercentage = lerp(goalComp.displayPercentage, fillPercentage, 0.2); + + const startAngle = Math.PI * 0.595; + const maxAngle = Math.PI * 1.82; + parameters.context.beginPath(); + parameters.context.arc( + 0.25, + -1.5, + 11.6, + startAngle, + startAngle + goalComp.displayPercentage * maxAngle, + false + ); + parameters.context.arc( + 0.25, + -1.5, + 15.5, + startAngle + goalComp.displayPercentage * maxAngle, + startAngle, + true + ); + parameters.context.closePath(); + parameters.context.fill(); + parameters.context.stroke(); + parameters.context.lineCap = "butt"; + + // LED indicator + + parameters.context.lineWidth = 1.2; + parameters.context.strokeStyle = "#64666e"; + parameters.context.fillStyle = isValid ? "#8de255" : "#ff666a"; + parameters.context.beginCircle(10, 11.8, 5); + parameters.context.fill(); + parameters.context.stroke(); + + parameters.context.rotate((-staticComp.rotation / 180) * Math.PI); + parameters.context.translate(-center.x, -center.y); + } + } +} diff --git a/src/js/game/systems/hub.js b/src/js/game/systems/hub.js index 2002b66e..700e5f5f 100644 --- a/src/js/game/systems/hub.js +++ b/src/js/game/systems/hub.js @@ -1,196 +1,192 @@ -import { globalConfig } from "../../core/config"; -import { smoothenDpi } from "../../core/dpi_manager"; -import { DrawParameters } from "../../core/draw_parameters"; -import { drawSpriteClipped } from "../../core/draw_utils"; -import { Loader } from "../../core/loader"; -import { Rectangle } from "../../core/rectangle"; -import { ORIGINAL_SPRITE_SCALE } from "../../core/sprites"; -import { formatBigNumber } from "../../core/utils"; -import { T } from "../../translations"; -import { HubComponent } from "../components/hub"; -import { Entity } from "../entity"; -import { GameSystemWithFilter } from "../game_system_with_filter"; - -const HUB_SIZE_TILES = 4; -const HUB_SIZE_PIXELS = HUB_SIZE_TILES * globalConfig.tileSize; - -export class HubSystem extends GameSystemWithFilter { - constructor(root) { - super(root, [HubComponent]); - - this.hubSprite = Loader.getSprite("sprites/buildings/hub.png"); - } - - /** - * @param {DrawParameters} parameters - */ - draw(parameters) { - for (let i = 0; i < this.allEntities.length; ++i) { - this.drawEntity(parameters, this.allEntities[i]); - } - } - - update() { - for (let i = 0; i < this.allEntities.length; ++i) { - // Set hub goal - const entity = this.allEntities[i]; - const pinsComp = entity.components.WiredPins; - pinsComp.slots[0].value = this.root.shapeDefinitionMgr.getShapeItemFromDefinition( - this.root.hubGoals.currentGoal.definition - ); - } - } - /** - * - * @param {HTMLCanvasElement} canvas - * @param {CanvasRenderingContext2D} context - * @param {number} w - * @param {number} h - * @param {number} dpi - */ - redrawHubBaseTexture(canvas, context, w, h, dpi) { - // This method is quite ugly, please ignore it! - - context.scale(dpi, dpi); - - const parameters = new DrawParameters({ - context, - visibleRect: new Rectangle(0, 0, w, h), - desiredAtlasScale: ORIGINAL_SPRITE_SCALE, - zoomLevel: dpi * 0.75, - root: this.root, - }); - - context.clearRect(0, 0, w, h); - - this.hubSprite.draw(context, 0, 0, w, h); - - if (this.root.hubGoals.isEndOfDemoReached()) { - // End of demo - context.font = "bold 12px GameFont"; - context.fillStyle = "#fd0752"; - context.textAlign = "center"; - context.fillText(T.buildings.hub.endOfDemo.toUpperCase(), w / 2, h / 2 + 6); - context.textAlign = "left"; - - return; - } - - const definition = this.root.hubGoals.currentGoal.definition; - definition.drawCentered(45, 58, parameters, 36); - - const goals = this.root.hubGoals.currentGoal; - - const textOffsetX = 70; - const textOffsetY = 61; - - if (goals.throughputOnly) { - // Throughput - const deliveredText = T.ingame.statistics.shapesDisplayUnits.second.replace( - "", - formatBigNumber(goals.required) - ); - - context.font = "bold 12px GameFont"; - context.fillStyle = "#64666e"; - context.textAlign = "left"; - context.fillText(deliveredText, textOffsetX, textOffsetY); - } else { - // Deliver count - const delivered = this.root.hubGoals.getCurrentGoalDelivered(); - const deliveredText = "" + formatBigNumber(delivered); - - if (delivered > 9999) { - context.font = "bold 16px GameFont"; - } else if (delivered > 999) { - context.font = "bold 20px GameFont"; - } else { - context.font = "bold 25px GameFont"; - } - context.fillStyle = "#64666e"; - context.textAlign = "left"; - context.fillText(deliveredText, textOffsetX, textOffsetY); - - // Required - context.font = "13px GameFont"; - context.fillStyle = "#a4a6b0"; - context.fillText("/ " + formatBigNumber(goals.required), textOffsetX, textOffsetY + 13); - } - - // Reward - const rewardText = T.storyRewards[goals.reward].title.toUpperCase(); - if (rewardText.length > 12) { - context.font = "bold 8px GameFont"; - } else { - context.font = "bold 10px GameFont"; - } - context.fillStyle = "#fd0752"; - context.textAlign = "center"; - - context.fillText(rewardText, HUB_SIZE_PIXELS / 2, 105); - - // Level "8" - context.font = "bold 10px GameFont"; - context.fillStyle = "#fff"; - context.fillText("" + this.root.hubGoals.level, 27, 32); - - // "LVL" - context.textAlign = "center"; - context.fillStyle = "#fff"; - context.font = "bold 6px GameFont"; - context.fillText(T.buildings.hub.levelShortcut, 27, 22); - - // "Deliver" - context.fillStyle = "#64666e"; - context.font = "bold 10px GameFont"; - context.fillText(T.buildings.hub.deliver.toUpperCase(), HUB_SIZE_PIXELS / 2, 30); - - // "To unlock" - const unlockText = T.buildings.hub.toUnlock.toUpperCase(); - if (unlockText.length > 15) { - context.font = "bold 8px GameFont"; - } else { - context.font = "bold 10px GameFont"; - } - context.fillText(T.buildings.hub.toUnlock.toUpperCase(), HUB_SIZE_PIXELS / 2, 92); - - context.textAlign = "left"; - } - - /** - * @param {DrawParameters} parameters - * @param {Entity} entity - */ - drawEntity(parameters, entity) { - const staticComp = entity.components.StaticMapEntity; - if (!staticComp.shouldBeDrawn(parameters)) { - return; - } - - // Deliver count - const delivered = this.root.hubGoals.getCurrentGoalDelivered(); - const deliveredText = "" + formatBigNumber(delivered); - - const dpi = smoothenDpi(globalConfig.shapesSharpness * parameters.zoomLevel); - const canvas = parameters.root.buffers.getForKey({ - key: "hub", - subKey: dpi + "/" + this.root.hubGoals.level + "/" + deliveredText, - w: globalConfig.tileSize * 4, - h: globalConfig.tileSize * 4, - dpi, - redrawMethod: this.redrawHubBaseTexture.bind(this), - }); - - const extrude = 8; - drawSpriteClipped({ - parameters, - sprite: canvas, - x: staticComp.origin.x * globalConfig.tileSize - extrude, - y: staticComp.origin.y * globalConfig.tileSize - extrude, - w: HUB_SIZE_PIXELS + 2 * extrude, - h: HUB_SIZE_PIXELS + 2 * extrude, - originalW: HUB_SIZE_PIXELS * dpi, - originalH: HUB_SIZE_PIXELS * dpi, - }); - } -} +import { globalConfig } from "../../core/config"; +import { smoothenDpi } from "../../core/dpi_manager"; +import { DrawParameters } from "../../core/draw_parameters"; +import { drawSpriteClipped } from "../../core/draw_utils"; +import { Loader } from "../../core/loader"; +import { Rectangle } from "../../core/rectangle"; +import { ORIGINAL_SPRITE_SCALE } from "../../core/sprites"; +import { formatBigNumber } from "../../core/utils"; +import { T } from "../../translations"; +import { HubComponent } from "../components/hub"; +import { Entity } from "../entity"; +import { GameSystemWithFilter } from "../game_system_with_filter"; + +const HUB_SIZE_TILES = 4; +const HUB_SIZE_PIXELS = HUB_SIZE_TILES * globalConfig.tileSize; + +export class HubSystem extends GameSystemWithFilter { + constructor(root) { + super(root, [HubComponent]); + } + + /** + * @param {DrawParameters} parameters + */ + draw(parameters) { + for (let i = 0; i < this.allEntities.length; ++i) { + this.drawEntity(parameters, this.allEntities[i]); + } + } + + update() { + for (let i = 0; i < this.allEntities.length; ++i) { + // Set hub goal + const entity = this.allEntities[i]; + const pinsComp = entity.components.WiredPins; + pinsComp.slots[0].value = this.root.shapeDefinitionMgr.getShapeItemFromDefinition( + this.root.hubGoals.currentGoal.definition + ); + } + } + /** + * + * @param {HTMLCanvasElement} canvas + * @param {CanvasRenderingContext2D} context + * @param {number} w + * @param {number} h + * @param {number} dpi + */ + redrawHubBaseTexture(canvas, context, w, h, dpi) { + // This method is quite ugly, please ignore it! + + context.scale(dpi, dpi); + + const parameters = new DrawParameters({ + context, + visibleRect: new Rectangle(0, 0, w, h), + desiredAtlasScale: ORIGINAL_SPRITE_SCALE, + zoomLevel: dpi * 0.75, + root: this.root, + }); + + context.clearRect(0, 0, w, h); + + if (this.root.hubGoals.isEndOfDemoReached()) { + // End of demo + context.font = "bold 12px GameFont"; + context.fillStyle = "#fd0752"; + context.textAlign = "center"; + context.fillText(T.buildings.hub.endOfDemo.toUpperCase(), w / 2, h / 2 + 6); + context.textAlign = "left"; + + return; + } + + const definition = this.root.hubGoals.currentGoal.definition; + definition.drawCentered(45, 58, parameters, 36); + + const goals = this.root.hubGoals.currentGoal; + + const textOffsetX = 70; + const textOffsetY = 61; + + if (goals.throughputOnly) { + // Throughput + const deliveredText = T.ingame.statistics.shapesDisplayUnits.second.replace( + "", + formatBigNumber(goals.required) + ); + + context.font = "bold 12px GameFont"; + context.fillStyle = "#64666e"; + context.textAlign = "left"; + context.fillText(deliveredText, textOffsetX, textOffsetY); + } else { + // Deliver count + const delivered = this.root.hubGoals.getCurrentGoalDelivered(); + const deliveredText = "" + formatBigNumber(delivered); + + if (delivered > 9999) { + context.font = "bold 16px GameFont"; + } else if (delivered > 999) { + context.font = "bold 20px GameFont"; + } else { + context.font = "bold 25px GameFont"; + } + context.fillStyle = "#64666e"; + context.textAlign = "left"; + context.fillText(deliveredText, textOffsetX, textOffsetY); + + // Required + context.font = "13px GameFont"; + context.fillStyle = "#a4a6b0"; + context.fillText("/ " + formatBigNumber(goals.required), textOffsetX, textOffsetY + 13); + } + + // Reward + const rewardText = T.storyRewards[goals.reward].title.toUpperCase(); + if (rewardText.length > 12) { + context.font = "bold 8px GameFont"; + } else { + context.font = "bold 10px GameFont"; + } + context.fillStyle = "#fd0752"; + context.textAlign = "center"; + + context.fillText(rewardText, HUB_SIZE_PIXELS / 2, 105); + + // Level "8" + context.font = "bold 10px GameFont"; + context.fillStyle = "#fff"; + context.fillText("" + this.root.hubGoals.level, 27, 32); + + // "LVL" + context.textAlign = "center"; + context.fillStyle = "#fff"; + context.font = "bold 6px GameFont"; + context.fillText(T.buildings.hub.levelShortcut, 27, 22); + + // "Deliver" + context.fillStyle = "#64666e"; + context.font = "bold 10px GameFont"; + context.fillText(T.buildings.hub.deliver.toUpperCase(), HUB_SIZE_PIXELS / 2, 30); + + // "To unlock" + const unlockText = T.buildings.hub.toUnlock.toUpperCase(); + if (unlockText.length > 15) { + context.font = "bold 8px GameFont"; + } else { + context.font = "bold 10px GameFont"; + } + context.fillText(T.buildings.hub.toUnlock.toUpperCase(), HUB_SIZE_PIXELS / 2, 92); + + context.textAlign = "left"; + } + + /** + * @param {DrawParameters} parameters + * @param {Entity} entity + */ + drawEntity(parameters, entity) { + const staticComp = entity.components.StaticMapEntity; + if (!staticComp.shouldBeDrawn(parameters)) { + return; + } + + // Deliver count + const delivered = this.root.hubGoals.getCurrentGoalDelivered(); + const deliveredText = "" + formatBigNumber(delivered); + + const dpi = smoothenDpi(globalConfig.shapesSharpness * parameters.zoomLevel); + const canvas = parameters.root.buffers.getForKey({ + key: "hub", + subKey: dpi + "/" + this.root.hubGoals.level + "/" + deliveredText, + w: globalConfig.tileSize * 4, + h: globalConfig.tileSize * 4, + dpi, + redrawMethod: this.redrawHubBaseTexture.bind(this), + }); + + const extrude = 2 * 4; + drawSpriteClipped({ + parameters, + sprite: canvas, + x: staticComp.origin.x * globalConfig.tileSize - extrude, + y: staticComp.origin.y * globalConfig.tileSize - extrude, + w: HUB_SIZE_PIXELS + 2 * extrude, + h: HUB_SIZE_PIXELS + 2 * extrude, + originalW: HUB_SIZE_PIXELS * dpi, + originalH: HUB_SIZE_PIXELS * dpi, + }); + } +} diff --git a/src/js/game/systems/item_acceptor.js b/src/js/game/systems/item_acceptor.js index 780b4abd..491c821a 100644 --- a/src/js/game/systems/item_acceptor.js +++ b/src/js/game/systems/item_acceptor.js @@ -1,106 +1,105 @@ -import { globalConfig } from "../../core/config"; -import { DrawParameters } from "../../core/draw_parameters"; -import { fastArrayDelete } from "../../core/utils"; -import { enumDirectionToVector } from "../../core/vector"; -import { ItemAcceptorComponent } from "../components/item_acceptor"; -import { GameSystemWithFilter } from "../game_system_with_filter"; -import { MapChunkView } from "../map_chunk_view"; - -export class ItemAcceptorSystem extends GameSystemWithFilter { - constructor(root) { - super(root, [ItemAcceptorComponent]); - - // Well ... it's better to be verbose I guess? - this.accumulatedTicksWhileInMapOverview = 0; - } - - update() { - if (this.root.app.settings.getAllSettings().simplifiedBelts) { - // Disabled in potato mode - return; - } - - // This system doesn't render anything while in map overview, - // so simply accumulate ticks - if (this.root.camera.getIsMapOverlayActive()) { - ++this.accumulatedTicksWhileInMapOverview; - return; - } - - // Compute how much ticks we missed - const numTicks = 1 + this.accumulatedTicksWhileInMapOverview; - const progress = - this.root.dynamicTickrate.deltaSeconds * - 2 * - this.root.hubGoals.getBeltBaseSpeed() * - globalConfig.itemSpacingOnBelts * // * 2 because its only a half tile - numTicks; - - // Reset accumulated ticks - this.accumulatedTicksWhileInMapOverview = 0; - - for (let i = 0; i < this.allEntities.length; ++i) { - const entity = this.allEntities[i]; - const aceptorComp = entity.components.ItemAcceptor; - const animations = aceptorComp.itemConsumptionAnimations; - - // Process item consumption animations to avoid items popping from the belts - for (let animIndex = 0; animIndex < animations.length; ++animIndex) { - const anim = animations[animIndex]; - anim.animProgress += progress; - if (anim.animProgress > 1) { - fastArrayDelete(animations, animIndex); - animIndex -= 1; - } - } - } - } - - /** - * @param {DrawParameters} parameters - * @param {MapChunkView} chunk - */ - drawChunk(parameters, chunk) { - if (this.root.app.settings.getAllSettings().simplifiedBelts) { - // Disabled in potato mode - return; - } - - const contents = chunk.containedEntitiesByLayer.regular; - for (let i = 0; i < contents.length; ++i) { - const entity = contents[i]; - const acceptorComp = entity.components.ItemAcceptor; - if (!acceptorComp) { - continue; - } - - const staticComp = entity.components.StaticMapEntity; - for (let animIndex = 0; animIndex < acceptorComp.itemConsumptionAnimations.length; ++animIndex) { - const { item, slotIndex, animProgress, direction } = acceptorComp.itemConsumptionAnimations[ - animIndex - ]; - - const slotData = acceptorComp.slots[slotIndex]; - const realSlotPos = staticComp.localTileToWorld(slotData.pos); - - if (!chunk.tileSpaceRectangle.containsPoint(realSlotPos.x, realSlotPos.y)) { - // Not within this chunk - continue; - } - - const fadeOutDirection = enumDirectionToVector[staticComp.localDirectionToWorld(direction)]; - const finalTile = realSlotPos.subScalars( - fadeOutDirection.x * (animProgress / 2 - 0.5), - fadeOutDirection.y * (animProgress / 2 - 0.5) - ); - - item.drawItemCenteredClipped( - (finalTile.x + 0.5) * globalConfig.tileSize, - (finalTile.y + 0.5) * globalConfig.tileSize, - parameters, - globalConfig.defaultItemDiameter - ); - } - } - } -} +import { globalConfig } from "../../core/config"; +import { DrawParameters } from "../../core/draw_parameters"; +import { fastArrayDelete } from "../../core/utils"; +import { enumDirectionToVector } from "../../core/vector"; +import { ItemAcceptorComponent } from "../components/item_acceptor"; +import { GameSystemWithFilter } from "../game_system_with_filter"; +import { MapChunkView } from "../map_chunk_view"; + +export class ItemAcceptorSystem extends GameSystemWithFilter { + constructor(root) { + super(root, [ItemAcceptorComponent]); + + // Well ... it's better to be verbose I guess? + this.accumulatedTicksWhileInMapOverview = 0; + } + + update() { + if (this.root.app.settings.getAllSettings().simplifiedBelts) { + // Disabled in potato mode + return; + } + + // This system doesn't render anything while in map overview, + // so simply accumulate ticks + if (this.root.camera.getIsMapOverlayActive()) { + ++this.accumulatedTicksWhileInMapOverview; + return; + } + + // Compute how much ticks we missed + const numTicks = 1 + this.accumulatedTicksWhileInMapOverview; + const progress = + this.root.dynamicTickrate.deltaSeconds * + 2 * + this.root.hubGoals.getBeltBaseSpeed() * + globalConfig.itemSpacingOnBelts * // * 2 because its only a half tile + numTicks; + + // Reset accumulated ticks + this.accumulatedTicksWhileInMapOverview = 0; + + for (let i = 0; i < this.allEntities.length; ++i) { + const entity = this.allEntities[i]; + const aceptorComp = entity.components.ItemAcceptor; + const animations = aceptorComp.itemConsumptionAnimations; + + // Process item consumption animations to avoid items popping from the belts + for (let animIndex = 0; animIndex < animations.length; ++animIndex) { + const anim = animations[animIndex]; + anim.animProgress += progress; + if (anim.animProgress > 1) { + fastArrayDelete(animations, animIndex); + animIndex -= 1; + } + } + } + } + + /** + * @param {DrawParameters} parameters + * @param {MapChunkView} chunk + */ + drawChunk(parameters, chunk) { + if (this.root.app.settings.getAllSettings().simplifiedBelts) { + // Disabled in potato mode + return; + } + + const contents = chunk.containedEntitiesByLayer.regular; + for (let i = 0; i < contents.length; ++i) { + const entity = contents[i]; + const acceptorComp = entity.components.ItemAcceptor; + if (!acceptorComp) { + continue; + } + + const staticComp = entity.components.StaticMapEntity; + for (let animIndex = 0; animIndex < acceptorComp.itemConsumptionAnimations.length; ++animIndex) { + const { item, slotIndex, animProgress, direction } = + acceptorComp.itemConsumptionAnimations[animIndex]; + + const slotData = acceptorComp.slots[slotIndex]; + const realSlotPos = staticComp.localTileToWorld(slotData.pos); + + if (!chunk.tileSpaceRectangle.containsPoint(realSlotPos.x, realSlotPos.y)) { + // Not within this chunk + continue; + } + + const fadeOutDirection = enumDirectionToVector[staticComp.localDirectionToWorld(direction)]; + const finalTile = realSlotPos.subScalars( + fadeOutDirection.x * (animProgress / 2 - 0.5), + fadeOutDirection.y * (animProgress / 2 - 0.5) + ); + + item.drawItemCenteredClipped( + (finalTile.x + 0.5) * globalConfig.tileSize, + (finalTile.y + 0.5) * globalConfig.tileSize, + parameters, + globalConfig.defaultItemDiameter + ); + } + } + } +} diff --git a/src/js/game/systems/item_ejector.js b/src/js/game/systems/item_ejector.js index 8c7468ad..499326fe 100644 --- a/src/js/game/systems/item_ejector.js +++ b/src/js/game/systems/item_ejector.js @@ -1,417 +1,417 @@ -import { globalConfig } from "../../core/config"; -import { DrawParameters } from "../../core/draw_parameters"; -import { createLogger } from "../../core/logging"; -import { Rectangle } from "../../core/rectangle"; -import { StaleAreaDetector } from "../../core/stale_area_detector"; -import { enumDirection, enumDirectionToVector } from "../../core/vector"; -import { BaseItem } from "../base_item"; -import { BeltComponent } from "../components/belt"; -import { ItemAcceptorComponent } from "../components/item_acceptor"; -import { ItemEjectorComponent } from "../components/item_ejector"; -import { Entity } from "../entity"; -import { GameSystemWithFilter } from "../game_system_with_filter"; -import { MapChunkView } from "../map_chunk_view"; - -const logger = createLogger("systems/ejector"); - -export class ItemEjectorSystem extends GameSystemWithFilter { - constructor(root) { - super(root, [ItemEjectorComponent]); - - this.staleAreaDetector = new StaleAreaDetector({ - root: this.root, - name: "item-ejector", - recomputeMethod: this.recomputeArea.bind(this), - }); - - this.staleAreaDetector.recomputeOnComponentsChanged( - [ItemEjectorComponent, ItemAcceptorComponent, BeltComponent], - 1 - ); - - this.root.signals.postLoadHook.add(this.recomputeCacheFull, this); - } - - /** - * Recomputes an area after it changed - * @param {Rectangle} area - */ - recomputeArea(area) { - /** @type {Set} */ - const seenUids = new Set(); - for (let x = 0; x < area.w; ++x) { - for (let y = 0; y < area.h; ++y) { - const tileX = area.x + x; - const tileY = area.y + y; - // @NOTICE: Item ejector currently only supports regular layer - const contents = this.root.map.getLayerContentXY(tileX, tileY, "regular"); - if (contents && contents.components.ItemEjector) { - if (!seenUids.has(contents.uid)) { - seenUids.add(contents.uid); - this.recomputeSingleEntityCache(contents); - } - } - } - } - } - - /** - * Recomputes the whole cache after the game has loaded - */ - recomputeCacheFull() { - logger.log("Full cache recompute in post load hook"); - for (let i = 0; i < this.allEntities.length; ++i) { - const entity = this.allEntities[i]; - this.recomputeSingleEntityCache(entity); - } - } - - /** - * @param {Entity} entity - */ - recomputeSingleEntityCache(entity) { - const ejectorComp = entity.components.ItemEjector; - const staticComp = entity.components.StaticMapEntity; - - for (let slotIndex = 0; slotIndex < ejectorComp.slots.length; ++slotIndex) { - const ejectorSlot = ejectorComp.slots[slotIndex]; - - // Clear the old cache. - ejectorSlot.cachedDestSlot = null; - ejectorSlot.cachedTargetEntity = null; - ejectorSlot.cachedBeltPath = null; - - // Figure out where and into which direction we eject items - const ejectSlotWsTile = staticComp.localTileToWorld(ejectorSlot.pos); - const ejectSlotWsDirection = staticComp.localDirectionToWorld(ejectorSlot.direction); - const ejectSlotWsDirectionVector = enumDirectionToVector[ejectSlotWsDirection]; - const ejectSlotTargetWsTile = ejectSlotWsTile.add(ejectSlotWsDirectionVector); - - // Try to find the given acceptor component to take the item - // Since there can be cross layer dependencies, check on all layers - const targetEntities = this.root.map.getLayersContentsMultipleXY( - ejectSlotTargetWsTile.x, - ejectSlotTargetWsTile.y - ); - - for (let i = 0; i < targetEntities.length; ++i) { - const targetEntity = targetEntities[i]; - - const targetStaticComp = targetEntity.components.StaticMapEntity; - const targetBeltComp = targetEntity.components.Belt; - - // Check for belts (special case) - if (targetBeltComp) { - const beltAcceptingDirection = targetStaticComp.localDirectionToWorld(enumDirection.top); - if (ejectSlotWsDirection === beltAcceptingDirection) { - ejectorSlot.cachedTargetEntity = targetEntity; - ejectorSlot.cachedBeltPath = targetBeltComp.assignedPath; - break; - } - } - - // Check for item acceptors - const targetAcceptorComp = targetEntity.components.ItemAcceptor; - if (!targetAcceptorComp) { - // Entity doesn't accept items - continue; - } - - const matchingSlot = targetAcceptorComp.findMatchingSlot( - targetStaticComp.worldToLocalTile(ejectSlotTargetWsTile), - targetStaticComp.worldDirectionToLocal(ejectSlotWsDirection) - ); - - if (!matchingSlot) { - // No matching slot found - continue; - } - - // A slot can always be connected to one other slot only - ejectorSlot.cachedTargetEntity = targetEntity; - ejectorSlot.cachedDestSlot = matchingSlot; - break; - } - } - } - - update() { - this.staleAreaDetector.update(); - - // Precompute effective belt speed - let progressGrowth = 2 * this.root.dynamicTickrate.deltaSeconds; - - if (G_IS_DEV && globalConfig.debug.instantBelts) { - progressGrowth = 1; - } - - // Go over all cache entries - for (let i = 0; i < this.allEntities.length; ++i) { - const sourceEntity = this.allEntities[i]; - const sourceEjectorComp = sourceEntity.components.ItemEjector; - - const slots = sourceEjectorComp.slots; - for (let j = 0; j < slots.length; ++j) { - const sourceSlot = slots[j]; - const item = sourceSlot.item; - if (!item) { - // No item available to be ejected - continue; - } - - // Advance items on the slot - sourceSlot.progress = Math.min( - 1, - sourceSlot.progress + - progressGrowth * - this.root.hubGoals.getBeltBaseSpeed() * - globalConfig.itemSpacingOnBelts - ); - - if (G_IS_DEV && globalConfig.debug.disableEjectorProcessing) { - sourceSlot.progress = 1.0; - } - - // Check if we are still in the process of ejecting, can't proceed then - if (sourceSlot.progress < 1.0) { - continue; - } - - // Check if we are ejecting to a belt path - const destPath = sourceSlot.cachedBeltPath; - if (destPath) { - // Try passing the item over - if (destPath.tryAcceptItem(item)) { - sourceSlot.item = null; - } - - // Always stop here, since there can *either* be a belt path *or* - // a slot - continue; - } - - // Check if the target acceptor can actually accept this item - const destEntity = sourceSlot.cachedTargetEntity; - const destSlot = sourceSlot.cachedDestSlot; - if (destSlot) { - const targetAcceptorComp = destEntity.components.ItemAcceptor; - if (!targetAcceptorComp.canAcceptItem(destSlot.index, item)) { - continue; - } - - // Try to hand over the item - if (this.tryPassOverItem(item, destEntity, destSlot.index)) { - // Handover successful, clear slot - if (!this.root.app.settings.getAllSettings().simplifiedBelts) { - targetAcceptorComp.onItemAccepted(destSlot.index, destSlot.slot.direction, item); - } - sourceSlot.item = null; - continue; - } - } - } - } - } - - /** - * - * @param {BaseItem} item - * @param {Entity} receiver - * @param {number} slotIndex - */ - tryPassOverItem(item, receiver, slotIndex) { - // Try figuring out how what to do with the item - // @TODO: Kinda hacky. How to solve this properly? Don't want to go through inheritance hell. - - const beltComp = receiver.components.Belt; - if (beltComp) { - const path = beltComp.assignedPath; - assert(path, "belt has no path"); - if (path.tryAcceptItem(item)) { - return true; - } - // Belt can have nothing else - return false; - } - - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - // - // NOTICE ! THIS CODE IS DUPLICATED IN THE BELT PATH FOR PERFORMANCE REASONS - // - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - - const itemProcessorComp = receiver.components.ItemProcessor; - if (itemProcessorComp) { - // Check for potential filters - if (!this.root.systemMgr.systems.itemProcessor.checkRequirements(receiver, item, slotIndex)) { - return false; - } - - // Its an item processor .. - if (itemProcessorComp.tryTakeItem(item, slotIndex)) { - return true; - } - // Item processor can have nothing else - return false; - } - - const undergroundBeltComp = receiver.components.UndergroundBelt; - if (undergroundBeltComp) { - // Its an underground belt. yay. - if ( - undergroundBeltComp.tryAcceptExternalItem( - item, - this.root.hubGoals.getUndergroundBeltBaseSpeed() - ) - ) { - return true; - } - - // Underground belt can have nothing else - return false; - } - - const storageComp = receiver.components.Storage; - if (storageComp) { - // It's a storage - if (storageComp.canAcceptItem(item)) { - storageComp.takeItem(item); - return true; - } - - // Storage can't have anything else - return false; - } - - const filterComp = receiver.components.Filter; - if (filterComp) { - // It's a filter! Unfortunately the filter has to know a lot about it's - // surrounding state and components, so it can't be within the component itself. - if (this.root.systemMgr.systems.filter.tryAcceptItem(receiver, slotIndex, item)) { - return true; - } - } - - return false; - } - - /** - * @param {DrawParameters} parameters - * @param {MapChunkView} chunk - */ - drawChunk(parameters, chunk) { - if (this.root.app.settings.getAllSettings().simplifiedBelts) { - // Disabled in potato mode - return; - } - - const contents = chunk.containedEntitiesByLayer.regular; - - for (let i = 0; i < contents.length; ++i) { - const entity = contents[i]; - const ejectorComp = entity.components.ItemEjector; - if (!ejectorComp) { - continue; - } - - const staticComp = entity.components.StaticMapEntity; - - for (let i = 0; i < ejectorComp.slots.length; ++i) { - const slot = ejectorComp.slots[i]; - const ejectedItem = slot.item; - - if (!ejectedItem) { - // No item - continue; - } - - if (!ejectorComp.renderFloatingItems && !slot.cachedTargetEntity) { - // Not connected to any building - continue; - } - - // Limit the progress to the maximum available space on the next belt (also see #1000) - let progress = slot.progress; - const nextBeltPath = slot.cachedBeltPath; - if (nextBeltPath) { - /* - If you imagine the track between the center of the building and the center of the first belt as - a range from 0 to 1: - - Building Belt - | X | X | - | 0...................1 | - - And for example the first item on belt has a distance of 0.4 to the beginning of the belt: - - Building Belt - | X | X | - | 0...................1 | - ^ item - - Then the space towards this first item is always 0.5 (the distance from the center of the building to the beginning of the belt) - PLUS the spacing to the item, so in this case 0.5 + 0.4 = 0.9: - - Building Belt - | X | X | - | 0...................1 | - ^ item @ 0.9 - - Since items must not get clashed, we need to substract some spacing (lets assume it is 0.6, exact value see globalConfig.itemSpacingOnBelts), - So we do 0.9 - globalConfig.itemSpacingOnBelts = 0.3 - - Building Belt - | X | X | - | 0...................1 | - ^ ^ item @ 0.9 - ^ max progress = 0.3 - - Because now our range actually only goes to the end of the building, and not towards the center of the building, we need to multiply - all values by 2: - - Building Belt - | X | X | - | 0.........1.........2 | - ^ ^ item @ 1.8 - ^ max progress = 0.6 - - And that's it! If you summarize the calculations from above into a formula, you get the one below. - */ - - const maxProgress = - (0.5 + nextBeltPath.spacingToFirstItem - globalConfig.itemSpacingOnBelts) * 2; - progress = Math.min(maxProgress, progress); - } - - // Skip if the item would barely be visible - if (progress < 0.05) { - continue; - } - - const realPosition = staticComp.localTileToWorld(slot.pos); - if (!chunk.tileSpaceRectangle.containsPoint(realPosition.x, realPosition.y)) { - // Not within this chunk - continue; - } - - const realDirection = staticComp.localDirectionToWorld(slot.direction); - const realDirectionVector = enumDirectionToVector[realDirection]; - - const tileX = realPosition.x + 0.5 + realDirectionVector.x * 0.5 * progress; - const tileY = realPosition.y + 0.5 + realDirectionVector.y * 0.5 * progress; - - const worldX = tileX * globalConfig.tileSize; - const worldY = tileY * globalConfig.tileSize; - - ejectedItem.drawItemCenteredClipped( - worldX, - worldY, - parameters, - globalConfig.defaultItemDiameter - ); - } - } - } -} +import { globalConfig } from "../../core/config"; +import { DrawParameters } from "../../core/draw_parameters"; +import { createLogger } from "../../core/logging"; +import { Rectangle } from "../../core/rectangle"; +import { StaleAreaDetector } from "../../core/stale_area_detector"; +import { enumDirection, enumDirectionToVector } from "../../core/vector"; +import { BaseItem } from "../base_item"; +import { BeltComponent } from "../components/belt"; +import { ItemAcceptorComponent } from "../components/item_acceptor"; +import { ItemEjectorComponent } from "../components/item_ejector"; +import { Entity } from "../entity"; +import { GameSystemWithFilter } from "../game_system_with_filter"; +import { MapChunkView } from "../map_chunk_view"; + +const logger = createLogger("systems/ejector"); + +export class ItemEjectorSystem extends GameSystemWithFilter { + constructor(root) { + super(root, [ItemEjectorComponent]); + + this.staleAreaDetector = new StaleAreaDetector({ + root: this.root, + name: "item-ejector", + recomputeMethod: this.recomputeArea.bind(this), + }); + + this.staleAreaDetector.recomputeOnComponentsChanged( + [ItemEjectorComponent, ItemAcceptorComponent, BeltComponent], + 1 + ); + + this.root.signals.postLoadHook.add(this.recomputeCacheFull, this); + } + + /** + * Recomputes an area after it changed + * @param {Rectangle} area + */ + recomputeArea(area) { + /** @type {Set} */ + const seenUids = new Set(); + for (let x = 0; x < area.w; ++x) { + for (let y = 0; y < area.h; ++y) { + const tileX = area.x + x; + const tileY = area.y + y; + // @NOTICE: Item ejector currently only supports regular layer + const contents = this.root.map.getLayerContentXY(tileX, tileY, "regular"); + if (contents && contents.components.ItemEjector) { + if (!seenUids.has(contents.uid)) { + seenUids.add(contents.uid); + this.recomputeSingleEntityCache(contents); + } + } + } + } + } + + /** + * Recomputes the whole cache after the game has loaded + */ + recomputeCacheFull() { + logger.log("Full cache recompute in post load hook"); + for (let i = 0; i < this.allEntities.length; ++i) { + const entity = this.allEntities[i]; + this.recomputeSingleEntityCache(entity); + } + } + + /** + * @param {Entity} entity + */ + recomputeSingleEntityCache(entity) { + const ejectorComp = entity.components.ItemEjector; + const staticComp = entity.components.StaticMapEntity; + + for (let slotIndex = 0; slotIndex < ejectorComp.slots.length; ++slotIndex) { + const ejectorSlot = ejectorComp.slots[slotIndex]; + + // Clear the old cache. + ejectorSlot.cachedDestSlot = null; + ejectorSlot.cachedTargetEntity = null; + ejectorSlot.cachedBeltPath = null; + + // Figure out where and into which direction we eject items + const ejectSlotWsTile = staticComp.localTileToWorld(ejectorSlot.pos); + const ejectSlotWsDirection = staticComp.localDirectionToWorld(ejectorSlot.direction); + const ejectSlotWsDirectionVector = enumDirectionToVector[ejectSlotWsDirection]; + const ejectSlotTargetWsTile = ejectSlotWsTile.add(ejectSlotWsDirectionVector); + + // Try to find the given acceptor component to take the item + // Since there can be cross layer dependencies, check on all layers + const targetEntities = this.root.map.getLayersContentsMultipleXY( + ejectSlotTargetWsTile.x, + ejectSlotTargetWsTile.y + ); + + for (let i = 0; i < targetEntities.length; ++i) { + const targetEntity = targetEntities[i]; + + const targetStaticComp = targetEntity.components.StaticMapEntity; + const targetBeltComp = targetEntity.components.Belt; + + // Check for belts (special case) + if (targetBeltComp) { + const beltAcceptingDirection = targetStaticComp.localDirectionToWorld(enumDirection.top); + if (ejectSlotWsDirection === beltAcceptingDirection) { + ejectorSlot.cachedTargetEntity = targetEntity; + ejectorSlot.cachedBeltPath = targetBeltComp.assignedPath; + break; + } + } + + // Check for item acceptors + const targetAcceptorComp = targetEntity.components.ItemAcceptor; + if (!targetAcceptorComp) { + // Entity doesn't accept items + continue; + } + + const matchingSlot = targetAcceptorComp.findMatchingSlot( + targetStaticComp.worldToLocalTile(ejectSlotTargetWsTile), + targetStaticComp.worldDirectionToLocal(ejectSlotWsDirection) + ); + + if (!matchingSlot) { + // No matching slot found + continue; + } + + // A slot can always be connected to one other slot only + ejectorSlot.cachedTargetEntity = targetEntity; + ejectorSlot.cachedDestSlot = matchingSlot; + break; + } + } + } + + update() { + this.staleAreaDetector.update(); + + // Precompute effective belt speed + let progressGrowth = 2 * this.root.dynamicTickrate.deltaSeconds; + + if (G_IS_DEV && globalConfig.debug.instantBelts) { + progressGrowth = 1; + } + + // Go over all cache entries + for (let i = 0; i < this.allEntities.length; ++i) { + const sourceEntity = this.allEntities[i]; + const sourceEjectorComp = sourceEntity.components.ItemEjector; + + const slots = sourceEjectorComp.slots; + for (let j = 0; j < slots.length; ++j) { + const sourceSlot = slots[j]; + const item = sourceSlot.item; + if (!item) { + // No item available to be ejected + continue; + } + + // Advance items on the slot + sourceSlot.progress = Math.min( + 1, + sourceSlot.progress + + progressGrowth * + this.root.hubGoals.getBeltBaseSpeed() * + globalConfig.itemSpacingOnBelts + ); + + if (G_IS_DEV && globalConfig.debug.disableEjectorProcessing) { + sourceSlot.progress = 1.0; + } + + // Check if we are still in the process of ejecting, can't proceed then + if (sourceSlot.progress < 1.0) { + continue; + } + + // Check if we are ejecting to a belt path + const destPath = sourceSlot.cachedBeltPath; + if (destPath) { + // Try passing the item over + if (destPath.tryAcceptItem(item)) { + sourceSlot.item = null; + } + + // Always stop here, since there can *either* be a belt path *or* + // a slot + continue; + } + + // Check if the target acceptor can actually accept this item + const destEntity = sourceSlot.cachedTargetEntity; + const destSlot = sourceSlot.cachedDestSlot; + if (destSlot) { + const targetAcceptorComp = destEntity.components.ItemAcceptor; + if (!targetAcceptorComp.canAcceptItem(destSlot.index, item)) { + continue; + } + + // Try to hand over the item + if (this.tryPassOverItem(item, destEntity, destSlot.index)) { + // Handover successful, clear slot + if (!this.root.app.settings.getAllSettings().simplifiedBelts) { + targetAcceptorComp.onItemAccepted(destSlot.index, destSlot.slot.direction, item); + } + sourceSlot.item = null; + continue; + } + } + } + } + } + + /** + * + * @param {BaseItem} item + * @param {Entity} receiver + * @param {number} slotIndex + */ + tryPassOverItem(item, receiver, slotIndex) { + // Try figuring out how what to do with the item + // @TODO: Kinda hacky. How to solve this properly? Don't want to go through inheritance hell. + + const beltComp = receiver.components.Belt; + if (beltComp) { + const path = beltComp.assignedPath; + assert(path, "belt has no path"); + if (path.tryAcceptItem(item)) { + return true; + } + // Belt can have nothing else + return false; + } + + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + // + // NOTICE ! THIS CODE IS DUPLICATED IN THE BELT PATH FOR PERFORMANCE REASONS + // + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + + const itemProcessorComp = receiver.components.ItemProcessor; + if (itemProcessorComp) { + // Check for potential filters + if (!this.root.systemMgr.systems.itemProcessor.checkRequirements(receiver, item, slotIndex)) { + return false; + } + + // Its an item processor .. + if (itemProcessorComp.tryTakeItem(item, slotIndex)) { + return true; + } + // Item processor can have nothing else + return false; + } + + const undergroundBeltComp = receiver.components.UndergroundBelt; + if (undergroundBeltComp) { + // Its an underground belt. yay. + if ( + undergroundBeltComp.tryAcceptExternalItem( + item, + this.root.hubGoals.getUndergroundBeltBaseSpeed() + ) + ) { + return true; + } + + // Underground belt can have nothing else + return false; + } + + const storageComp = receiver.components.Storage; + if (storageComp) { + // It's a storage + if (storageComp.canAcceptItem(item)) { + storageComp.takeItem(item); + return true; + } + + // Storage can't have anything else + return false; + } + + const filterComp = receiver.components.Filter; + if (filterComp) { + // It's a filter! Unfortunately the filter has to know a lot about it's + // surrounding state and components, so it can't be within the component itself. + if (this.root.systemMgr.systems.filter.tryAcceptItem(receiver, slotIndex, item)) { + return true; + } + } + + return false; + } + + /** + * @param {DrawParameters} parameters + * @param {MapChunkView} chunk + */ + drawChunk(parameters, chunk) { + if (this.root.app.settings.getAllSettings().simplifiedBelts) { + // Disabled in potato mode + return; + } + + const contents = chunk.containedEntitiesByLayer.regular; + + for (let i = 0; i < contents.length; ++i) { + const entity = contents[i]; + const ejectorComp = entity.components.ItemEjector; + if (!ejectorComp) { + continue; + } + + const staticComp = entity.components.StaticMapEntity; + + for (let i = 0; i < ejectorComp.slots.length; ++i) { + const slot = ejectorComp.slots[i]; + const ejectedItem = slot.item; + + if (!ejectedItem) { + // No item + continue; + } + + if (!ejectorComp.renderFloatingItems && !slot.cachedTargetEntity) { + // Not connected to any building + continue; + } + + // Limit the progress to the maximum available space on the next belt (also see #1000) + let progress = slot.progress; + const nextBeltPath = slot.cachedBeltPath; + if (nextBeltPath) { + /* + If you imagine the track between the center of the building and the center of the first belt as + a range from 0 to 1: + + Building Belt + | X | X | + | 0...................1 | + + And for example the first item on belt has a distance of 0.4 to the beginning of the belt: + + Building Belt + | X | X | + | 0...................1 | + ^ item + + Then the space towards this first item is always 0.5 (the distance from the center of the building to the beginning of the belt) + PLUS the spacing to the item, so in this case 0.5 + 0.4 = 0.9: + + Building Belt + | X | X | + | 0...................1 | + ^ item @ 0.9 + + Since items must not get clashed, we need to substract some spacing (lets assume it is 0.6, exact value see globalConfig.itemSpacingOnBelts), + So we do 0.9 - globalConfig.itemSpacingOnBelts = 0.3 + + Building Belt + | X | X | + | 0...................1 | + ^ ^ item @ 0.9 + ^ max progress = 0.3 + + Because now our range actually only goes to the end of the building, and not towards the center of the building, we need to multiply + all values by 2: + + Building Belt + | X | X | + | 0.........1.........2 | + ^ ^ item @ 1.8 + ^ max progress = 0.6 + + And that's it! If you summarize the calculations from above into a formula, you get the one below. + */ + + const maxProgress = + (0.5 + nextBeltPath.spacingToFirstItem - globalConfig.itemSpacingOnBelts) * 2; + progress = Math.min(maxProgress, progress); + } + + // Skip if the item would barely be visible + if (progress < 0.05) { + continue; + } + + const realPosition = staticComp.localTileToWorld(slot.pos); + if (!chunk.tileSpaceRectangle.containsPoint(realPosition.x, realPosition.y)) { + // Not within this chunk + continue; + } + + const realDirection = staticComp.localDirectionToWorld(slot.direction); + const realDirectionVector = enumDirectionToVector[realDirection]; + + const tileX = realPosition.x + 0.5 + realDirectionVector.x * 0.5 * progress; + const tileY = realPosition.y + 0.5 + realDirectionVector.y * 0.5 * progress; + + const worldX = tileX * globalConfig.tileSize; + const worldY = tileY * globalConfig.tileSize; + + ejectedItem.drawItemCenteredClipped( + worldX, + worldY, + parameters, + globalConfig.defaultItemDiameter + ); + } + } + } +} diff --git a/src/js/game/systems/item_processor.js b/src/js/game/systems/item_processor.js index 50e3e9ba..77f6fb0f 100644 --- a/src/js/game/systems/item_processor.js +++ b/src/js/game/systems/item_processor.js @@ -9,7 +9,7 @@ import { import { Entity } from "../entity"; import { GameSystemWithFilter } from "../game_system_with_filter"; import { isTruthyItem } from "../items/boolean_item"; -import { ColorItem, COLOR_ITEM_SINGLETONS } from "../items/color_item"; +import { COLOR_ITEM_SINGLETONS, ColorItem } from "../items/color_item"; import { ShapeItem } from "../items/shape_item"; /** @@ -48,17 +48,17 @@ const MAX_QUEUED_CHARGES = 2; */ /** - * @type {Object void>} + * @type {Object void>} */ export const MOD_ITEM_PROCESSOR_HANDLERS = {}; /** - * @type {Object boolean>} + * @type {Object boolean>} */ export const MODS_PROCESSING_REQUIREMENTS = {}; /** - * @type {Object boolean>} + * @type {Object boolean>} */ export const MODS_CAN_PROCESS = {}; @@ -67,15 +67,15 @@ export class ItemProcessorSystem extends GameSystemWithFilter { super(root, [ItemProcessorComponent]); /** - * @type {Object} + * @type {Object void>} */ this.handlers = { [enumItemProcessorTypes.balancer]: this.process_BALANCER, [enumItemProcessorTypes.cutter]: this.process_CUTTER, [enumItemProcessorTypes.cutterQuad]: this.process_CUTTER_QUAD, - [enumItemProcessorTypes.rotater]: this.process_ROTATER, - [enumItemProcessorTypes.rotaterCCW]: this.process_ROTATER_CCW, - [enumItemProcessorTypes.rotater180]: this.process_ROTATER_180, + [enumItemProcessorTypes.rotator]: this.process_ROTATOR, + [enumItemProcessorTypes.rotatorCCW]: this.process_ROTATOR_CCW, + [enumItemProcessorTypes.rotator180]: this.process_ROTATOR_180, [enumItemProcessorTypes.stacker]: this.process_STACKER, [enumItemProcessorTypes.trash]: this.process_TRASH, [enumItemProcessorTypes.mixer]: this.process_MIXER, @@ -416,7 +416,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter { /** * @param {ProcessorImplementationPayload} payload */ - process_ROTATER(payload) { + process_ROTATOR(payload) { const inputItem = /** @type {ShapeItem} */ (payload.items.get(0)); assert(inputItem instanceof ShapeItem, "Input for rotation is not a shape"); const inputDefinition = inputItem.definition; @@ -430,7 +430,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter { /** * @param {ProcessorImplementationPayload} payload */ - process_ROTATER_CCW(payload) { + process_ROTATOR_CCW(payload) { const inputItem = /** @type {ShapeItem} */ (payload.items.get(0)); assert(inputItem instanceof ShapeItem, "Input for rotation is not a shape"); const inputDefinition = inputItem.definition; @@ -444,7 +444,7 @@ export class ItemProcessorSystem extends GameSystemWithFilter { /** * @param {ProcessorImplementationPayload} payload */ - process_ROTATER_180(payload) { + process_ROTATOR_180(payload) { const inputItem = /** @type {ShapeItem} */ (payload.items.get(0)); assert(inputItem instanceof ShapeItem, "Input for rotation is not a shape"); const inputDefinition = inputItem.definition; diff --git a/src/js/game/systems/item_processor_overlays.js b/src/js/game/systems/item_processor_overlays.js index 0377f779..ff55c0b4 100644 --- a/src/js/game/systems/item_processor_overlays.js +++ b/src/js/game/systems/item_processor_overlays.js @@ -1,142 +1,142 @@ -import { globalConfig } from "../../core/config"; -import { Loader } from "../../core/loader"; -import { round1DigitLocalized, smoothPulse } from "../../core/utils"; -import { enumItemProcessorRequirements, enumItemProcessorTypes } from "../components/item_processor"; -import { Entity } from "../entity"; -import { GameSystem } from "../game_system"; -import { isTruthyItem } from "../items/boolean_item"; -import { MapChunkView } from "../map_chunk_view"; - -export class ItemProcessorOverlaysSystem extends GameSystem { - constructor(root) { - super(root); - - this.spriteDisabled = Loader.getSprite("sprites/misc/processor_disabled.png"); - this.spriteDisconnected = Loader.getSprite("sprites/misc/processor_disconnected.png"); - - this.readerOverlaySprite = Loader.getSprite("sprites/misc/reader_overlay.png"); - - this.drawnUids = new Set(); - this.root.signals.gameFrameStarted.add(this.clearDrawnUids, this); - } - - clearDrawnUids() { - this.drawnUids.clear(); - } - - /** - * - * @param {import("../../core/draw_utils").DrawParameters} parameters - * @param {MapChunkView} chunk - */ - drawChunk(parameters, chunk) { - const contents = chunk.containedEntitiesByLayer.regular; - for (let i = 0; i < contents.length; ++i) { - const entity = contents[i]; - const processorComp = entity.components.ItemProcessor; - const filterComp = entity.components.Filter; - - // Draw processor overlays - if (processorComp) { - const requirement = processorComp.processingRequirement; - if (!requirement && processorComp.type !== enumItemProcessorTypes.reader) { - continue; - } - - if (this.drawnUids.has(entity.uid)) { - continue; - } - this.drawnUids.add(entity.uid); - - switch (requirement) { - case enumItemProcessorRequirements.painterQuad: { - this.drawConnectedSlotRequirement(parameters, entity, { drawIfFalse: true }); - break; - } - } - - if (processorComp.type === enumItemProcessorTypes.reader) { - this.drawReaderOverlays(parameters, entity); - } - } - - // Draw filter overlays - else if (filterComp) { - if (this.drawnUids.has(entity.uid)) { - continue; - } - this.drawnUids.add(entity.uid); - - this.drawConnectedSlotRequirement(parameters, entity, { drawIfFalse: false }); - } - } - } - - /** - * - * @param {import("../../core/draw_utils").DrawParameters} parameters - * @param {Entity} entity - */ - drawReaderOverlays(parameters, entity) { - const staticComp = entity.components.StaticMapEntity; - const readerComp = entity.components.BeltReader; - - this.readerOverlaySprite.drawCachedCentered( - parameters, - (staticComp.origin.x + 0.5) * globalConfig.tileSize, - (staticComp.origin.y + 0.5) * globalConfig.tileSize, - globalConfig.tileSize - ); - - parameters.context.fillStyle = "#333439"; - parameters.context.textAlign = "center"; - parameters.context.font = "bold 10px GameFont"; - parameters.context.fillText( - round1DigitLocalized(readerComp.lastThroughput), - (staticComp.origin.x + 0.5) * globalConfig.tileSize, - (staticComp.origin.y + 0.62) * globalConfig.tileSize - ); - - parameters.context.textAlign = "left"; - } - - /** - * - * @param {import("../../core/draw_utils").DrawParameters} parameters - * @param {Entity} entity - * @param {object} param0 - * @param {boolean=} param0.drawIfFalse - */ - drawConnectedSlotRequirement(parameters, entity, { drawIfFalse = true }) { - const staticComp = entity.components.StaticMapEntity; - const pinsComp = entity.components.WiredPins; - - let anySlotConnected = false; - - // Check if any slot has a value - for (let i = 0; i < pinsComp.slots.length; ++i) { - const slot = pinsComp.slots[i]; - const network = slot.linkedNetwork; - if (network && network.hasValue()) { - anySlotConnected = true; - - if (isTruthyItem(network.currentValue) || !drawIfFalse) { - // No need to draw anything - return; - } - } - } - - const pulse = smoothPulse(this.root.time.now()); - parameters.context.globalAlpha = 0.6 + 0.4 * pulse; - const sprite = anySlotConnected ? this.spriteDisabled : this.spriteDisconnected; - sprite.drawCachedCentered( - parameters, - (staticComp.origin.x + 0.5) * globalConfig.tileSize, - (staticComp.origin.y + 0.5) * globalConfig.tileSize, - globalConfig.tileSize * (0.7 + 0.2 * pulse) - ); - - parameters.context.globalAlpha = 1; - } -} +import { globalConfig } from "../../core/config"; +import { Loader } from "../../core/loader"; +import { round1DigitLocalized, smoothPulse } from "../../core/utils"; +import { enumItemProcessorRequirements, enumItemProcessorTypes } from "../components/item_processor"; +import { Entity } from "../entity"; +import { GameSystem } from "../game_system"; +import { isTruthyItem } from "../items/boolean_item"; +import { MapChunkView } from "../map_chunk_view"; + +export class ItemProcessorOverlaysSystem extends GameSystem { + constructor(root) { + super(root); + + this.spriteDisabled = Loader.getSprite("sprites/misc/processor_disabled.png"); + this.spriteDisconnected = Loader.getSprite("sprites/misc/processor_disconnected.png"); + + this.readerOverlaySprite = Loader.getSprite("sprites/misc/reader_overlay.png"); + + this.drawnUids = new Set(); + this.root.signals.gameFrameStarted.add(this.clearDrawnUids, this); + } + + clearDrawnUids() { + this.drawnUids.clear(); + } + + /** + * + * @param {import("../../core/draw_utils").DrawParameters} parameters + * @param {MapChunkView} chunk + */ + drawChunk(parameters, chunk) { + const contents = chunk.containedEntitiesByLayer.regular; + for (let i = 0; i < contents.length; ++i) { + const entity = contents[i]; + const processorComp = entity.components.ItemProcessor; + const filterComp = entity.components.Filter; + + // Draw processor overlays + if (processorComp) { + const requirement = processorComp.processingRequirement; + if (!requirement && processorComp.type !== enumItemProcessorTypes.reader) { + continue; + } + + if (this.drawnUids.has(entity.uid)) { + continue; + } + this.drawnUids.add(entity.uid); + + switch (requirement) { + case enumItemProcessorRequirements.painterQuad: { + this.drawConnectedSlotRequirement(parameters, entity, { drawIfFalse: true }); + break; + } + } + + if (processorComp.type === enumItemProcessorTypes.reader) { + this.drawReaderOverlays(parameters, entity); + } + } + + // Draw filter overlays + else if (filterComp) { + if (this.drawnUids.has(entity.uid)) { + continue; + } + this.drawnUids.add(entity.uid); + + this.drawConnectedSlotRequirement(parameters, entity, { drawIfFalse: false }); + } + } + } + + /** + * + * @param {import("../../core/draw_utils").DrawParameters} parameters + * @param {Entity} entity + */ + drawReaderOverlays(parameters, entity) { + const staticComp = entity.components.StaticMapEntity; + const readerComp = entity.components.BeltReader; + + this.readerOverlaySprite.drawCachedCentered( + parameters, + (staticComp.origin.x + 0.5) * globalConfig.tileSize, + (staticComp.origin.y + 0.5) * globalConfig.tileSize, + globalConfig.tileSize + ); + + parameters.context.fillStyle = "#333439"; + parameters.context.textAlign = "center"; + parameters.context.font = "bold 10px GameFont"; + parameters.context.fillText( + round1DigitLocalized(readerComp.lastThroughput), + (staticComp.origin.x + 0.5) * globalConfig.tileSize, + (staticComp.origin.y + 0.62) * globalConfig.tileSize + ); + + parameters.context.textAlign = "left"; + } + + /** + * + * @param {import("../../core/draw_utils").DrawParameters} parameters + * @param {Entity} entity + * @param {object} param0 + * @param {boolean=} param0.drawIfFalse + */ + drawConnectedSlotRequirement(parameters, entity, { drawIfFalse = true }) { + const staticComp = entity.components.StaticMapEntity; + const pinsComp = entity.components.WiredPins; + + let anySlotConnected = false; + + // Check if any slot has a value + for (let i = 0; i < pinsComp.slots.length; ++i) { + const slot = pinsComp.slots[i]; + const network = slot.linkedNetwork; + if (network && network.hasValue()) { + anySlotConnected = true; + + if (isTruthyItem(network.currentValue) || !drawIfFalse) { + // No need to draw anything + return; + } + } + } + + const pulse = smoothPulse(this.root.time.now()); + parameters.context.globalAlpha = 0.6 + 0.4 * pulse; + const sprite = anySlotConnected ? this.spriteDisabled : this.spriteDisconnected; + sprite.drawCachedCentered( + parameters, + (staticComp.origin.x + 0.5) * globalConfig.tileSize, + (staticComp.origin.y + 0.5) * globalConfig.tileSize, + globalConfig.tileSize * (0.7 + 0.2 * pulse) + ); + + parameters.context.globalAlpha = 1; + } +} diff --git a/src/js/game/systems/item_producer.js b/src/js/game/systems/item_producer.js index 8ca29ae1..50c4cf66 100644 --- a/src/js/game/systems/item_producer.js +++ b/src/js/game/systems/item_producer.js @@ -1,30 +1,30 @@ -import { ItemProducerComponent } from "../components/item_producer"; -import { GameSystemWithFilter } from "../game_system_with_filter"; - -export class ItemProducerSystem extends GameSystemWithFilter { - constructor(root) { - super(root, [ItemProducerComponent]); - this.item = null; - } - - update() { - for (let i = 0; i < this.allEntities.length; ++i) { - const entity = this.allEntities[i]; - const ejectorComp = entity.components.ItemEjector; - const pinsComp = entity.components.WiredPins; - if (!pinsComp) { - continue; - } - - const pin = pinsComp.slots[0]; - const network = pin.linkedNetwork; - - if (!network || !network.hasValue()) { - continue; - } - - this.item = network.currentValue; - ejectorComp.tryEject(0, this.item); - } - } -} +import { ItemProducerComponent } from "../components/item_producer"; +import { GameSystemWithFilter } from "../game_system_with_filter"; + +export class ItemProducerSystem extends GameSystemWithFilter { + constructor(root) { + super(root, [ItemProducerComponent]); + this.item = null; + } + + update() { + for (let i = 0; i < this.allEntities.length; ++i) { + const entity = this.allEntities[i]; + const ejectorComp = entity.components.ItemEjector; + const pinsComp = entity.components.WiredPins; + if (!pinsComp) { + continue; + } + + const pin = pinsComp.slots[0]; + const network = pin.linkedNetwork; + + if (!network || !network.hasValue()) { + continue; + } + + this.item = network.currentValue; + ejectorComp.tryEject(0, this.item); + } + } +} diff --git a/src/js/game/systems/lever.js b/src/js/game/systems/lever.js index 343894ae..24744f1f 100644 --- a/src/js/game/systems/lever.js +++ b/src/js/game/systems/lever.js @@ -1,43 +1,43 @@ -import { Loader } from "../../core/loader"; -import { LeverComponent } from "../components/lever"; -import { GameSystemWithFilter } from "../game_system_with_filter"; -import { BOOL_FALSE_SINGLETON, BOOL_TRUE_SINGLETON } from "../items/boolean_item"; -import { MapChunkView } from "../map_chunk_view"; - -export class LeverSystem extends GameSystemWithFilter { - constructor(root) { - super(root, [LeverComponent]); - - this.spriteOn = Loader.getSprite("sprites/wires/lever_on.png"); - this.spriteOff = Loader.getSprite("sprites/buildings/lever.png"); - } - - update() { - for (let i = 0; i < this.allEntities.length; ++i) { - const entity = this.allEntities[i]; - - const leverComp = entity.components.Lever; - const pinsComp = entity.components.WiredPins; - - // Simply sync the status to the first slot - pinsComp.slots[0].value = leverComp.toggled ? BOOL_TRUE_SINGLETON : BOOL_FALSE_SINGLETON; - } - } - - /** - * Draws a given chunk - * @param {import("../../core/draw_utils").DrawParameters} parameters - * @param {MapChunkView} chunk - */ - drawChunk(parameters, chunk) { - const contents = chunk.containedEntitiesByLayer.regular; - for (let i = 0; i < contents.length; ++i) { - const entity = contents[i]; - const leverComp = entity.components.Lever; - if (leverComp) { - const sprite = leverComp.toggled ? this.spriteOn : this.spriteOff; - entity.components.StaticMapEntity.drawSpriteOnBoundsClipped(parameters, sprite); - } - } - } -} +import { Loader } from "../../core/loader"; +import { LeverComponent } from "../components/lever"; +import { GameSystemWithFilter } from "../game_system_with_filter"; +import { BOOL_FALSE_SINGLETON, BOOL_TRUE_SINGLETON } from "../items/boolean_item"; +import { MapChunkView } from "../map_chunk_view"; + +export class LeverSystem extends GameSystemWithFilter { + constructor(root) { + super(root, [LeverComponent]); + + this.spriteOn = Loader.getSprite("sprites/wires/lever_on.png"); + this.spriteOff = Loader.getSprite("sprites/buildings/lever.png"); + } + + update() { + for (let i = 0; i < this.allEntities.length; ++i) { + const entity = this.allEntities[i]; + + const leverComp = entity.components.Lever; + const pinsComp = entity.components.WiredPins; + + // Simply sync the status to the first slot + pinsComp.slots[0].value = leverComp.toggled ? BOOL_TRUE_SINGLETON : BOOL_FALSE_SINGLETON; + } + } + + /** + * Draws a given chunk + * @param {import("../../core/draw_utils").DrawParameters} parameters + * @param {MapChunkView} chunk + */ + drawChunk(parameters, chunk) { + const contents = chunk.containedEntitiesByLayer.regular; + for (let i = 0; i < contents.length; ++i) { + const entity = contents[i]; + const leverComp = entity.components.Lever; + if (leverComp) { + const sprite = leverComp.toggled ? this.spriteOn : this.spriteOff; + entity.components.StaticMapEntity.drawSpriteOnBoundsClipped(parameters, sprite); + } + } + } +} diff --git a/src/js/game/systems/logic_gate.js b/src/js/game/systems/logic_gate.js index 4545a331..d83c9669 100644 --- a/src/js/game/systems/logic_gate.js +++ b/src/js/game/systems/logic_gate.js @@ -1,358 +1,357 @@ -import { BaseItem } from "../base_item"; -import { enumColors } from "../colors"; -import { enumLogicGateType, LogicGateComponent } from "../components/logic_gate"; -import { enumPinSlotType } from "../components/wired_pins"; -import { GameSystemWithFilter } from "../game_system_with_filter"; -import { BOOL_FALSE_SINGLETON, BOOL_TRUE_SINGLETON, BooleanItem, isTruthyItem } from "../items/boolean_item"; -import { ColorItem, COLOR_ITEM_SINGLETONS } from "../items/color_item"; -import { ShapeItem } from "../items/shape_item"; -import { ShapeDefinition } from "../shape_definition"; - -export class LogicGateSystem extends GameSystemWithFilter { - constructor(root) { - super(root, [LogicGateComponent]); - - this.boundOperations = { - [enumLogicGateType.and]: this.compute_AND.bind(this), - [enumLogicGateType.not]: this.compute_NOT.bind(this), - [enumLogicGateType.xor]: this.compute_XOR.bind(this), - [enumLogicGateType.or]: this.compute_OR.bind(this), - [enumLogicGateType.transistor]: this.compute_IF.bind(this), - - [enumLogicGateType.rotater]: this.compute_ROTATE.bind(this), - [enumLogicGateType.analyzer]: this.compute_ANALYZE.bind(this), - [enumLogicGateType.cutter]: this.compute_CUT.bind(this), - [enumLogicGateType.unstacker]: this.compute_UNSTACK.bind(this), - [enumLogicGateType.compare]: this.compute_COMPARE.bind(this), - [enumLogicGateType.stacker]: this.compute_STACKER.bind(this), - [enumLogicGateType.painter]: this.compute_PAINTER.bind(this), - }; - } - - update() { - for (let i = 0; i < this.allEntities.length; ++i) { - const entity = this.allEntities[i]; - const logicComp = entity.components.LogicGate; - const slotComp = entity.components.WiredPins; - - const slotValues = []; - - // Store if any conflict was found - let anyConflict = false; - - // Gather inputs from all connected networks - for (let i = 0; i < slotComp.slots.length; ++i) { - const slot = slotComp.slots[i]; - if (slot.type !== enumPinSlotType.logicalAcceptor) { - continue; - } - const network = slot.linkedNetwork; - if (network) { - if (network.valueConflict) { - anyConflict = true; - break; - } - slotValues.push(network.currentValue); - } else { - slotValues.push(null); - } - } - - // Handle conflicts - if (anyConflict) { - for (let i = 0; i < slotComp.slots.length; ++i) { - const slot = slotComp.slots[i]; - if (slot.type !== enumPinSlotType.logicalEjector) { - continue; - } - slot.value = null; - } - continue; - } - - // Compute actual result - const result = this.boundOperations[logicComp.type](slotValues); - - if (Array.isArray(result)) { - let resultIndex = 0; - for (let i = 0; i < slotComp.slots.length; ++i) { - const slot = slotComp.slots[i]; - if (slot.type !== enumPinSlotType.logicalEjector) { - continue; - } - slot.value = result[resultIndex++]; - } - } else { - // @TODO: For now we hardcode the value to always be slot 0 - assert( - slotValues.length === slotComp.slots.length - 1, - "Bad slot config, should have N acceptor slots and 1 ejector" - ); - assert(slotComp.slots[0].type === enumPinSlotType.logicalEjector, "Slot 0 should be ejector"); - slotComp.slots[0].value = result; - } - } - } - - /** - * @param {Array} parameters - * @returns {BaseItem} - */ - compute_AND(parameters) { - assert(parameters.length === 2, "bad parameter count for AND"); - return isTruthyItem(parameters[0]) && isTruthyItem(parameters[1]) - ? BOOL_TRUE_SINGLETON - : BOOL_FALSE_SINGLETON; - } - - /** - * @param {Array} parameters - * @returns {BaseItem} - */ - compute_NOT(parameters) { - return isTruthyItem(parameters[0]) ? BOOL_FALSE_SINGLETON : BOOL_TRUE_SINGLETON; - } - - /** - * @param {Array} parameters - * @returns {BaseItem} - */ - compute_XOR(parameters) { - assert(parameters.length === 2, "bad parameter count for XOR"); - return isTruthyItem(parameters[0]) !== isTruthyItem(parameters[1]) - ? BOOL_TRUE_SINGLETON - : BOOL_FALSE_SINGLETON; - } - - /** - * @param {Array} parameters - * @returns {BaseItem} - */ - compute_OR(parameters) { - assert(parameters.length === 2, "bad parameter count for OR"); - return isTruthyItem(parameters[0]) || isTruthyItem(parameters[1]) - ? BOOL_TRUE_SINGLETON - : BOOL_FALSE_SINGLETON; - } - - /** - * @param {Array} parameters - * @returns {BaseItem} - */ - compute_IF(parameters) { - assert(parameters.length === 2, "bad parameter count for IF"); - const flag = parameters[0]; - const value = parameters[1]; - - // pass through item - if (isTruthyItem(flag)) { - return value; - } - - return null; - } - - /** - * @param {Array} parameters - * @returns {BaseItem} - */ - compute_ROTATE(parameters) { - const item = parameters[0]; - if (!item || item.getItemType() !== "shape") { - // Not a shape - return null; - } - - const definition = /** @type {ShapeItem} */ (item).definition; - const rotatedDefinitionCW = this.root.shapeDefinitionMgr.shapeActionRotateCW(definition); - return this.root.shapeDefinitionMgr.getShapeItemFromDefinition(rotatedDefinitionCW); - } - - /** - * @param {Array} parameters - * @returns {[BaseItem, BaseItem]} - */ - compute_ANALYZE(parameters) { - const item = parameters[0]; - if (!item || item.getItemType() !== "shape") { - // Not a shape - return [null, null]; - } - - const definition = /** @type {ShapeItem} */ (item).definition; - const lowerLayer = /** @type {import("../shape_definition").ShapeLayer} */ (definition.layers[0]); - if (!lowerLayer) { - return [null, null]; - } - - const topRightContent = lowerLayer[0]; - - if (!topRightContent || topRightContent.subShape === null) { - return [null, null]; - } - - const newDefinition = new ShapeDefinition({ - layers: [ - [ - { subShape: topRightContent.subShape, color: enumColors.uncolored }, - { subShape: topRightContent.subShape, color: enumColors.uncolored }, - { subShape: topRightContent.subShape, color: enumColors.uncolored }, - { subShape: topRightContent.subShape, color: enumColors.uncolored }, - ], - ], - }); - - return [ - COLOR_ITEM_SINGLETONS[topRightContent.color], - this.root.shapeDefinitionMgr.getShapeItemFromDefinition(newDefinition), - ]; - } - - /** - * @param {Array} parameters - * @returns {[BaseItem, BaseItem]} - */ - compute_CUT(parameters) { - const item = parameters[0]; - if (!item || item.getItemType() !== "shape") { - // Not a shape - return [null, null]; - } - - const definition = /** @type {ShapeItem} */ (item).definition; - const result = this.root.shapeDefinitionMgr.shapeActionCutHalf(definition); - return [ - result[0].isEntirelyEmpty() - ? null - : this.root.shapeDefinitionMgr.getShapeItemFromDefinition(result[0]), - result[1].isEntirelyEmpty() - ? null - : this.root.shapeDefinitionMgr.getShapeItemFromDefinition(result[1]), - ]; - } - - /** - * @param {Array} parameters - * @returns {[BaseItem, BaseItem]} - */ - compute_UNSTACK(parameters) { - const item = parameters[0]; - if (!item || item.getItemType() !== "shape") { - // Not a shape - return [null, null]; - } - - const definition = /** @type {ShapeItem} */ (item).definition; - const layers = /** @type {Array} */ (definition.layers); - - const upperLayerDefinition = new ShapeDefinition({ - layers: [layers[layers.length - 1]], - }); - - const lowerLayers = layers.slice(0, layers.length - 1); - const lowerLayerDefinition = - lowerLayers.length > 0 ? new ShapeDefinition({ layers: lowerLayers }) : null; - - return [ - lowerLayerDefinition - ? this.root.shapeDefinitionMgr.getShapeItemFromDefinition(lowerLayerDefinition) - : null, - this.root.shapeDefinitionMgr.getShapeItemFromDefinition(upperLayerDefinition), - ]; - } - - /** - * @param {Array} parameters - * @returns {BaseItem} - */ - compute_STACKER(parameters) { - const lowerItem = parameters[0]; - const upperItem = parameters[1]; - - if (!lowerItem || !upperItem) { - // Empty - return null; - } - - if (lowerItem.getItemType() !== "shape" || upperItem.getItemType() !== "shape") { - // Bad type - return null; - } - - const stackedShape = this.root.shapeDefinitionMgr.shapeActionStack( - /** @type {ShapeItem} */ (lowerItem).definition, - /** @type {ShapeItem} */ (upperItem).definition - ); - - return this.root.shapeDefinitionMgr.getShapeItemFromDefinition(stackedShape); - } - - /** - * @param {Array} parameters - * @returns {BaseItem} - */ - compute_PAINTER(parameters) { - const shape = parameters[0]; - const color = parameters[1]; - - if (!shape || !color) { - // Empty - return null; - } - - if (shape.getItemType() !== "shape" || color.getItemType() !== "color") { - // Bad type - return null; - } - - const coloredShape = this.root.shapeDefinitionMgr.shapeActionPaintWith( - /** @type {ShapeItem} */ (shape).definition, - /** @type {ColorItem} */ (color).color - ); - - return this.root.shapeDefinitionMgr.getShapeItemFromDefinition(coloredShape); - } - - /** - * @param {Array} parameters - * @returns {BaseItem} - */ - compute_COMPARE(parameters) { - const itemA = parameters[0]; - const itemB = parameters[1]; - - if (!itemA || !itemB) { - // Empty - return null; - } - - if (itemA.getItemType() !== itemB.getItemType()) { - // Not the same type - return BOOL_FALSE_SINGLETON; - } - - switch (itemA.getItemType()) { - case "shape": { - return /** @type {ShapeItem} */ (itemA).definition.getHash() === - /** @type {ShapeItem} */ (itemB).definition.getHash() - ? BOOL_TRUE_SINGLETON - : BOOL_FALSE_SINGLETON; - } - case "color": { - return /** @type {ColorItem} */ (itemA).color === /** @type {ColorItem} */ (itemB).color - ? BOOL_TRUE_SINGLETON - : BOOL_FALSE_SINGLETON; - } - - case "boolean": { - return /** @type {BooleanItem} */ (itemA).value === /** @type {BooleanItem} */ (itemB).value - ? BOOL_TRUE_SINGLETON - : BOOL_FALSE_SINGLETON; - } - - default: { - assertAlways(false, "Bad item type: " + itemA.getItemType()); - } - } - } -} +import { BaseItem } from "../base_item"; +import { enumColors } from "../colors"; +import { enumLogicGateType, LogicGateComponent } from "../components/logic_gate"; +import { enumPinSlotType } from "../components/wired_pins"; +import { GameSystemWithFilter } from "../game_system_with_filter"; +import { BOOL_FALSE_SINGLETON, BOOL_TRUE_SINGLETON, isTruthyItem } from "../items/boolean_item"; +import { COLOR_ITEM_SINGLETONS } from "../items/color_item"; +import { ShapeDefinition } from "../shape_definition"; + +export class LogicGateSystem extends GameSystemWithFilter { + constructor(root) { + super(root, [LogicGateComponent]); + + this.boundOperations = { + [enumLogicGateType.and]: this.compute_AND.bind(this), + [enumLogicGateType.not]: this.compute_NOT.bind(this), + [enumLogicGateType.xor]: this.compute_XOR.bind(this), + [enumLogicGateType.or]: this.compute_OR.bind(this), + [enumLogicGateType.transistor]: this.compute_IF.bind(this), + + [enumLogicGateType.rotator]: this.compute_ROTATE.bind(this), + [enumLogicGateType.analyzer]: this.compute_ANALYZE.bind(this), + [enumLogicGateType.cutter]: this.compute_CUT.bind(this), + [enumLogicGateType.unstacker]: this.compute_UNSTACK.bind(this), + [enumLogicGateType.compare]: this.compute_COMPARE.bind(this), + [enumLogicGateType.stacker]: this.compute_STACKER.bind(this), + [enumLogicGateType.painter]: this.compute_PAINTER.bind(this), + }; + } + + update() { + for (let i = 0; i < this.allEntities.length; ++i) { + const entity = this.allEntities[i]; + const logicComp = entity.components.LogicGate; + const slotComp = entity.components.WiredPins; + + const slotValues = []; + + // Store if any conflict was found + let anyConflict = false; + + // Gather inputs from all connected networks + for (let i = 0; i < slotComp.slots.length; ++i) { + const slot = slotComp.slots[i]; + if (slot.type !== enumPinSlotType.logicalAcceptor) { + continue; + } + const network = slot.linkedNetwork; + if (network) { + if (network.valueConflict) { + anyConflict = true; + break; + } + slotValues.push(network.currentValue); + } else { + slotValues.push(null); + } + } + + // Handle conflicts + if (anyConflict) { + for (let i = 0; i < slotComp.slots.length; ++i) { + const slot = slotComp.slots[i]; + if (slot.type !== enumPinSlotType.logicalEjector) { + continue; + } + slot.value = null; + } + continue; + } + + // Compute actual result + const result = this.boundOperations[logicComp.type](slotValues); + + if (Array.isArray(result)) { + let resultIndex = 0; + for (let i = 0; i < slotComp.slots.length; ++i) { + const slot = slotComp.slots[i]; + if (slot.type !== enumPinSlotType.logicalEjector) { + continue; + } + slot.value = result[resultIndex++]; + } + } else { + // @TODO: For now we hardcode the value to always be slot 0 + assert( + slotValues.length === slotComp.slots.length - 1, + "Bad slot config, should have N acceptor slots and 1 ejector" + ); + assert(slotComp.slots[0].type === enumPinSlotType.logicalEjector, "Slot 0 should be ejector"); + slotComp.slots[0].value = result; + } + } + } + + /** + * @param {Array} parameters + * @returns {BaseItem} + */ + compute_AND(parameters) { + assert(parameters.length === 2, "bad parameter count for AND"); + return isTruthyItem(parameters[0]) && isTruthyItem(parameters[1]) + ? BOOL_TRUE_SINGLETON + : BOOL_FALSE_SINGLETON; + } + + /** + * @param {Array} parameters + * @returns {BaseItem} + */ + compute_NOT(parameters) { + return isTruthyItem(parameters[0]) ? BOOL_FALSE_SINGLETON : BOOL_TRUE_SINGLETON; + } + + /** + * @param {Array} parameters + * @returns {BaseItem} + */ + compute_XOR(parameters) { + assert(parameters.length === 2, "bad parameter count for XOR"); + return isTruthyItem(parameters[0]) !== isTruthyItem(parameters[1]) + ? BOOL_TRUE_SINGLETON + : BOOL_FALSE_SINGLETON; + } + + /** + * @param {Array} parameters + * @returns {BaseItem} + */ + compute_OR(parameters) { + assert(parameters.length === 2, "bad parameter count for OR"); + return isTruthyItem(parameters[0]) || isTruthyItem(parameters[1]) + ? BOOL_TRUE_SINGLETON + : BOOL_FALSE_SINGLETON; + } + + /** + * @param {Array} parameters + * @returns {BaseItem} + */ + compute_IF(parameters) { + assert(parameters.length === 2, "bad parameter count for IF"); + const flag = parameters[0]; + const value = parameters[1]; + + // pass through item + if (isTruthyItem(flag)) { + return value; + } + + return null; + } + + /** + * @param {Array} parameters + * @returns {BaseItem} + */ + compute_ROTATE(parameters) { + const item = parameters[0]; + if (!item || item.getItemType() !== "shape") { + // Not a shape + return null; + } + + const definition = /** @type {ShapeItem} */ (item).definition; + const rotatedDefinitionCW = this.root.shapeDefinitionMgr.shapeActionRotateCW(definition); + return this.root.shapeDefinitionMgr.getShapeItemFromDefinition(rotatedDefinitionCW); + } + + /** + * @param {Array} parameters + * @returns {[BaseItem, BaseItem]} + */ + compute_ANALYZE(parameters) { + const item = parameters[0]; + if (!item || item.getItemType() !== "shape") { + // Not a shape + return [null, null]; + } + + const definition = /** @type {ShapeItem} */ (item).definition; + const lowerLayer = /** @type {import("../shape_definition").ShapeLayer} */ (definition.layers[0]); + if (!lowerLayer) { + return [null, null]; + } + + const topRightContent = lowerLayer[0]; + + if (!topRightContent || topRightContent.subShape === null) { + return [null, null]; + } + + const newDefinition = new ShapeDefinition({ + layers: [ + [ + { subShape: topRightContent.subShape, color: enumColors.uncolored }, + { subShape: topRightContent.subShape, color: enumColors.uncolored }, + { subShape: topRightContent.subShape, color: enumColors.uncolored }, + { subShape: topRightContent.subShape, color: enumColors.uncolored }, + ], + ], + }); + + return [ + COLOR_ITEM_SINGLETONS[topRightContent.color], + this.root.shapeDefinitionMgr.getShapeItemFromDefinition(newDefinition), + ]; + } + + /** + * @param {Array} parameters + * @returns {[BaseItem, BaseItem]} + */ + compute_CUT(parameters) { + const item = parameters[0]; + if (!item || item.getItemType() !== "shape") { + // Not a shape + return [null, null]; + } + + const definition = /** @type {ShapeItem} */ (item).definition; + const result = this.root.shapeDefinitionMgr.shapeActionCutHalf(definition); + return [ + result[0].isEntirelyEmpty() + ? null + : this.root.shapeDefinitionMgr.getShapeItemFromDefinition(result[0]), + result[1].isEntirelyEmpty() + ? null + : this.root.shapeDefinitionMgr.getShapeItemFromDefinition(result[1]), + ]; + } + + /** + * @param {Array} parameters + * @returns {[BaseItem, BaseItem]} + */ + compute_UNSTACK(parameters) { + const item = parameters[0]; + if (!item || item.getItemType() !== "shape") { + // Not a shape + return [null, null]; + } + + const definition = /** @type {ShapeItem} */ (item).definition; + const layers = /** @type {Array} */ (definition.layers); + + const upperLayerDefinition = new ShapeDefinition({ + layers: [layers[layers.length - 1]], + }); + + const lowerLayers = layers.slice(0, layers.length - 1); + const lowerLayerDefinition = + lowerLayers.length > 0 ? new ShapeDefinition({ layers: lowerLayers }) : null; + + return [ + lowerLayerDefinition + ? this.root.shapeDefinitionMgr.getShapeItemFromDefinition(lowerLayerDefinition) + : null, + this.root.shapeDefinitionMgr.getShapeItemFromDefinition(upperLayerDefinition), + ]; + } + + /** + * @param {Array} parameters + * @returns {BaseItem} + */ + compute_STACKER(parameters) { + const lowerItem = parameters[0]; + const upperItem = parameters[1]; + + if (!lowerItem || !upperItem) { + // Empty + return null; + } + + if (lowerItem.getItemType() !== "shape" || upperItem.getItemType() !== "shape") { + // Bad type + return null; + } + + const stackedShape = this.root.shapeDefinitionMgr.shapeActionStack( + /** @type {ShapeItem} */ (lowerItem).definition, + /** @type {ShapeItem} */ (upperItem).definition + ); + + return this.root.shapeDefinitionMgr.getShapeItemFromDefinition(stackedShape); + } + + /** + * @param {Array} parameters + * @returns {BaseItem} + */ + compute_PAINTER(parameters) { + const shape = parameters[0]; + const color = parameters[1]; + + if (!shape || !color) { + // Empty + return null; + } + + if (shape.getItemType() !== "shape" || color.getItemType() !== "color") { + // Bad type + return null; + } + + const coloredShape = this.root.shapeDefinitionMgr.shapeActionPaintWith( + /** @type {ShapeItem} */ (shape).definition, + /** @type {ColorItem} */ (color).color + ); + + return this.root.shapeDefinitionMgr.getShapeItemFromDefinition(coloredShape); + } + + /** + * @param {Array} parameters + * @returns {BaseItem} + */ + compute_COMPARE(parameters) { + const itemA = parameters[0]; + const itemB = parameters[1]; + + if (!itemA || !itemB) { + // Empty + return null; + } + + if (itemA.getItemType() !== itemB.getItemType()) { + // Not the same type + return BOOL_FALSE_SINGLETON; + } + + switch (itemA.getItemType()) { + case "shape": { + return /** @type {ShapeItem} */ (itemA).definition.getHash() === + /** @type {ShapeItem} */ (itemB).definition.getHash() + ? BOOL_TRUE_SINGLETON + : BOOL_FALSE_SINGLETON; + } + case "color": { + return /** @type {ColorItem} */ (itemA).color === /** @type {ColorItem} */ (itemB).color + ? BOOL_TRUE_SINGLETON + : BOOL_FALSE_SINGLETON; + } + + case "boolean": { + return /** @type {BooleanItem} */ (itemA).value === /** @type {BooleanItem} */ (itemB).value + ? BOOL_TRUE_SINGLETON + : BOOL_FALSE_SINGLETON; + } + + default: { + assertAlways(false, "Bad item type: " + itemA.getItemType()); + } + } + } +} diff --git a/src/js/game/systems/map_resources.js b/src/js/game/systems/map_resources.js index 807afb36..a723b319 100644 --- a/src/js/game/systems/map_resources.js +++ b/src/js/game/systems/map_resources.js @@ -1,122 +1,117 @@ -import { globalConfig } from "../../core/config"; -import { DrawParameters } from "../../core/draw_parameters"; -import { GameSystem } from "../game_system"; -import { MapChunkView } from "../map_chunk_view"; -import { THEME } from "../theme"; -import { drawSpriteClipped } from "../../core/draw_utils"; - -export class MapResourcesSystem extends GameSystem { - /** - * Draws the map resources - * @param {DrawParameters} parameters - * @param {MapChunkView} chunk - */ - drawChunk(parameters, chunk) { - const basicChunkBackground = this.root.buffers.getForKey({ - key: "mapresourcebg", - subKey: chunk.renderKey, - w: globalConfig.mapChunkSize, - h: globalConfig.mapChunkSize, - dpi: 1, - redrawMethod: this.generateChunkBackground.bind(this, chunk), - }); - - parameters.context.imageSmoothingEnabled = false; - drawSpriteClipped({ - parameters, - sprite: basicChunkBackground, - x: chunk.tileX * globalConfig.tileSize, - y: chunk.tileY * globalConfig.tileSize, - w: globalConfig.mapChunkWorldSize, - h: globalConfig.mapChunkWorldSize, - originalW: globalConfig.mapChunkSize, - originalH: globalConfig.mapChunkSize, - }); - parameters.context.imageSmoothingEnabled = true; - - parameters.context.globalAlpha = 0.5; - - if (this.root.app.settings.getAllSettings().lowQualityMapResources) { - // LOW QUALITY: Draw patch items only - for (let i = 0; i < chunk.patches.length; ++i) { - const patch = chunk.patches[i]; - const destX = chunk.x * globalConfig.mapChunkWorldSize + patch.pos.x * globalConfig.tileSize; - const destY = chunk.y * globalConfig.mapChunkWorldSize + patch.pos.y * globalConfig.tileSize; - const diameter = Math.min(80, 40 / parameters.zoomLevel); - - patch.item.drawItemCenteredClipped(destX, destY, parameters, diameter); - } - } else { - // HIGH QUALITY: Draw all items - const layer = chunk.lowerLayer; - const layerEntities = chunk.contents; - for (let x = 0; x < globalConfig.mapChunkSize; ++x) { - const row = layer[x]; - const rowEntities = layerEntities[x]; - const worldX = (chunk.tileX + x) * globalConfig.tileSize; - for (let y = 0; y < globalConfig.mapChunkSize; ++y) { - const lowerItem = row[y]; - - const entity = rowEntities[y]; - if (entity) { - // Don't draw if there is an entity above - continue; - } - - if (lowerItem) { - const worldY = (chunk.tileY + y) * globalConfig.tileSize; - - const destX = worldX + globalConfig.halfTileSize; - const destY = worldY + globalConfig.halfTileSize; - - lowerItem.drawItemCenteredClipped( - destX, - destY, - parameters, - globalConfig.defaultItemDiameter - ); - } - } - } - } - parameters.context.globalAlpha = 1; - } - - /** - * - * @param {MapChunkView} chunk - * @param {HTMLCanvasElement} canvas - * @param {CanvasRenderingContext2D} context - * @param {number} w - * @param {number} h - * @param {number} dpi - */ - generateChunkBackground(chunk, canvas, context, w, h, dpi) { - if (this.root.app.settings.getAllSettings().disableTileGrid) { - // The map doesn't draw a background, so we have to - context.fillStyle = THEME.map.background; - context.fillRect(0, 0, w, h); - } else { - context.clearRect(0, 0, w, h); - } - - context.globalAlpha = 0.5; - const layer = chunk.lowerLayer; - for (let x = 0; x < globalConfig.mapChunkSize; ++x) { - const row = layer[x]; - for (let y = 0; y < globalConfig.mapChunkSize; ++y) { - const item = row[y]; - if (item) { - context.fillStyle = item.getBackgroundColorAsResource(); - context.fillRect(x, y, 1, 1); - } - } - } - - if (this.root.app.settings.getAllSettings().displayChunkBorders) { - context.fillStyle = THEME.map.chunkBorders; - context.fillRect(0, 0, w, 1); - context.fillRect(0, 1, 1, h); - } - } -} +import { globalConfig } from "../../core/config"; +import { DrawParameters } from "../../core/draw_parameters"; +import { GameSystem } from "../game_system"; +import { MapChunkView } from "../map_chunk_view"; +import { THEME } from "../theme"; +import { drawSpriteClipped } from "../../core/draw_utils"; + +export class MapResourcesSystem extends GameSystem { + /** + * Draws the map resources + * @param {DrawParameters} parameters + * @param {MapChunkView} chunk + */ + drawChunk(parameters, chunk) { + const basicChunkBackground = this.root.buffers.getForKey({ + key: "mapresourcebg", + subKey: chunk.renderKey, + w: globalConfig.mapChunkSize, + h: globalConfig.mapChunkSize, + dpi: 1, + redrawMethod: this.generateChunkBackground.bind(this, chunk), + }); + + parameters.context.imageSmoothingEnabled = false; + drawSpriteClipped({ + parameters, + sprite: basicChunkBackground, + x: chunk.tileX * globalConfig.tileSize, + y: chunk.tileY * globalConfig.tileSize, + w: globalConfig.mapChunkWorldSize, + h: globalConfig.mapChunkWorldSize, + originalW: globalConfig.mapChunkSize, + originalH: globalConfig.mapChunkSize, + pixelAligned: true, + }); + parameters.context.imageSmoothingEnabled = true; + + parameters.context.globalAlpha = 0.5; + + if (this.root.app.settings.getAllSettings().lowQualityMapResources) { + // LOW QUALITY: Draw patch items only + for (let i = 0; i < chunk.patches.length; ++i) { + const patch = chunk.patches[i]; + const destX = chunk.x * globalConfig.mapChunkWorldSize + patch.pos.x * globalConfig.tileSize; + const destY = chunk.y * globalConfig.mapChunkWorldSize + patch.pos.y * globalConfig.tileSize; + const diameter = Math.min(80, 40 / parameters.zoomLevel); + + patch.item.drawItemCenteredClipped(destX, destY, parameters, diameter); + } + } else { + // HIGH QUALITY: Draw all items + const layer = chunk.lowerLayer; + const layerEntities = chunk.contents; + for (let x = 0; x < globalConfig.mapChunkSize; ++x) { + const row = layer[x]; + const rowEntities = layerEntities[x]; + const worldX = (chunk.tileX + x) * globalConfig.tileSize; + for (let y = 0; y < globalConfig.mapChunkSize; ++y) { + const lowerItem = row[y]; + + const entity = rowEntities[y]; + if (entity) { + // Don't draw if there is an entity above + continue; + } + + if (lowerItem) { + const worldY = (chunk.tileY + y) * globalConfig.tileSize; + + const destX = worldX + globalConfig.halfTileSize; + const destY = worldY + globalConfig.halfTileSize; + + lowerItem.drawItemCenteredClipped( + destX, + destY, + parameters, + globalConfig.defaultItemDiameter + ); + } + } + } + } + parameters.context.globalAlpha = 1; + } + + /** + * + * @param {MapChunkView} chunk + * @param {HTMLCanvasElement} canvas + * @param {CanvasRenderingContext2D} context + * @param {number} w + * @param {number} h + * @param {number} dpi + */ + generateChunkBackground(chunk, canvas, context, w, h, dpi) { + context.clearRect(0, 0, w, h); + + context.globalAlpha = 0.5; + const layer = chunk.lowerLayer; + for (let x = 0; x < globalConfig.mapChunkSize; ++x) { + const row = layer[x]; + for (let y = 0; y < globalConfig.mapChunkSize; ++y) { + const item = row[y]; + if (item) { + context.fillStyle = item.getBackgroundColorAsResource(); + context.fillRect(x, y, 1, 1); + } + } + } + + if (this.root.app.settings.getAllSettings().displayChunkBorders) { + context.fillStyle = THEME.map.chunkBorders; + context.fillRect(0, 0, w, 1); + context.fillRect(0, 1, 1, h); + } + } +} diff --git a/src/js/game/systems/miner.js b/src/js/game/systems/miner.js index cd478be3..f5fe99c9 100644 --- a/src/js/game/systems/miner.js +++ b/src/js/game/systems/miner.js @@ -1,201 +1,201 @@ -import { globalConfig } from "../../core/config"; -import { DrawParameters } from "../../core/draw_parameters"; -import { enumDirectionToVector } from "../../core/vector"; -import { BaseItem } from "../base_item"; -import { MinerComponent } from "../components/miner"; -import { Entity } from "../entity"; -import { GameSystemWithFilter } from "../game_system_with_filter"; -import { MapChunkView } from "../map_chunk_view"; - -export class MinerSystem extends GameSystemWithFilter { - constructor(root) { - super(root, [MinerComponent]); - - this.needsRecompute = true; - - this.root.signals.entityAdded.add(this.onEntityChanged, this); - this.root.signals.entityChanged.add(this.onEntityChanged, this); - this.root.signals.entityDestroyed.add(this.onEntityChanged, this); - } - - /** - * Called whenever an entity got changed - * @param {Entity} entity - */ - onEntityChanged(entity) { - const minerComp = entity.components.Miner; - if (minerComp && minerComp.chainable) { - // Miner component, need to recompute - this.needsRecompute = true; - } - } - - update() { - let miningSpeed = this.root.hubGoals.getMinerBaseSpeed(); - if (G_IS_DEV && globalConfig.debug.instantMiners) { - miningSpeed *= 100; - } - - for (let i = 0; i < this.allEntities.length; ++i) { - const entity = this.allEntities[i]; - const minerComp = entity.components.Miner; - - // Reset everything on recompute - if (this.needsRecompute) { - minerComp.cachedChainedMiner = null; - } - - // Check if miner is above an actual tile - if (!minerComp.cachedMinedItem) { - const staticComp = entity.components.StaticMapEntity; - const tileBelow = this.root.map.getLowerLayerContentXY( - staticComp.origin.x, - staticComp.origin.y - ); - if (!tileBelow) { - continue; - } - minerComp.cachedMinedItem = tileBelow; - } - - // First, try to get rid of chained items - if (minerComp.itemChainBuffer.length > 0) { - if (this.tryPerformMinerEject(entity, minerComp.itemChainBuffer[0])) { - minerComp.itemChainBuffer.shift(); - continue; - } - } - - const mineDuration = 1 / miningSpeed; - const timeSinceMine = this.root.time.now() - minerComp.lastMiningTime; - if (timeSinceMine > mineDuration) { - // Store how much we overflowed - const buffer = Math.min(timeSinceMine - mineDuration, this.root.dynamicTickrate.deltaSeconds); - - if (this.tryPerformMinerEject(entity, minerComp.cachedMinedItem)) { - // Analytics hook - this.root.signals.itemProduced.dispatch(minerComp.cachedMinedItem); - // Store mining time - minerComp.lastMiningTime = this.root.time.now() - buffer; - } - } - } - - // After this frame we are done - this.needsRecompute = false; - } - - /** - * Finds the target chained miner for a given entity - * @param {Entity} entity - * @returns {Entity|false} The chained entity or null if not found - */ - findChainedMiner(entity) { - const ejectComp = entity.components.ItemEjector; - const staticComp = entity.components.StaticMapEntity; - const contentsBelow = this.root.map.getLowerLayerContentXY(staticComp.origin.x, staticComp.origin.y); - if (!contentsBelow) { - // This miner has no contents - return null; - } - - const ejectingSlot = ejectComp.slots[0]; - const ejectingPos = staticComp.localTileToWorld(ejectingSlot.pos); - const ejectingDirection = staticComp.localDirectionToWorld(ejectingSlot.direction); - - const targetTile = ejectingPos.add(enumDirectionToVector[ejectingDirection]); - const targetContents = this.root.map.getTileContent(targetTile, "regular"); - - // Check if we are connected to another miner and thus do not eject directly - if (targetContents) { - const targetMinerComp = targetContents.components.Miner; - if (targetMinerComp && targetMinerComp.chainable) { - const targetLowerLayer = this.root.map.getLowerLayerContentXY(targetTile.x, targetTile.y); - if (targetLowerLayer) { - return targetContents; - } - } - } - - return false; - } - - /** - * - * @param {Entity} entity - * @param {BaseItem} item - */ - tryPerformMinerEject(entity, item) { - const minerComp = entity.components.Miner; - const ejectComp = entity.components.ItemEjector; - - // Check if we are a chained miner - if (minerComp.chainable) { - const targetEntity = minerComp.cachedChainedMiner; - - // Check if the cache has to get recomputed - if (targetEntity === null) { - minerComp.cachedChainedMiner = this.findChainedMiner(entity); - } - - // Check if we now have a target - if (targetEntity) { - const targetMinerComp = targetEntity.components.Miner; - if (targetMinerComp.tryAcceptChainedItem(item)) { - return true; - } else { - return false; - } - } - } - - // Seems we are a regular miner or at the end of a row, try actually ejecting - if (ejectComp.tryEject(0, item)) { - return true; - } - - return false; - } - - /** - * - * @param {DrawParameters} parameters - * @param {MapChunkView} chunk - */ - drawChunk(parameters, chunk) { - const contents = chunk.containedEntitiesByLayer.regular; - - for (let i = 0; i < contents.length; ++i) { - const entity = contents[i]; - const minerComp = entity.components.Miner; - if (!minerComp) { - continue; - } - - const staticComp = entity.components.StaticMapEntity; - if (!minerComp.cachedMinedItem) { - continue; - } - - // Draw the item background - this is to hide the ejected item animation from - // the item ejector - - const padding = 3; - const destX = staticComp.origin.x * globalConfig.tileSize + padding; - const destY = staticComp.origin.y * globalConfig.tileSize + padding; - const dimensions = globalConfig.tileSize - 2 * padding; - - if (parameters.visibleRect.containsRect4Params(destX, destY, dimensions, dimensions)) { - parameters.context.fillStyle = minerComp.cachedMinedItem.getBackgroundColorAsResource(); - parameters.context.fillRect(destX, destY, dimensions, dimensions); - } - - minerComp.cachedMinedItem.drawItemCenteredClipped( - (0.5 + staticComp.origin.x) * globalConfig.tileSize, - (0.5 + staticComp.origin.y) * globalConfig.tileSize, - parameters, - globalConfig.defaultItemDiameter - ); - } - } -} +import { globalConfig } from "../../core/config"; +import { DrawParameters } from "../../core/draw_parameters"; +import { enumDirectionToVector } from "../../core/vector"; +import { BaseItem } from "../base_item"; +import { MinerComponent } from "../components/miner"; +import { Entity } from "../entity"; +import { GameSystemWithFilter } from "../game_system_with_filter"; +import { MapChunkView } from "../map_chunk_view"; + +export class MinerSystem extends GameSystemWithFilter { + constructor(root) { + super(root, [MinerComponent]); + + this.needsRecompute = true; + + this.root.signals.entityAdded.add(this.onEntityChanged, this); + this.root.signals.entityChanged.add(this.onEntityChanged, this); + this.root.signals.entityDestroyed.add(this.onEntityChanged, this); + } + + /** + * Called whenever an entity got changed + * @param {Entity} entity + */ + onEntityChanged(entity) { + const minerComp = entity.components.Miner; + if (minerComp && minerComp.chainable) { + // Miner component, need to recompute + this.needsRecompute = true; + } + } + + update() { + let miningSpeed = this.root.hubGoals.getMinerBaseSpeed(); + if (G_IS_DEV && globalConfig.debug.instantMiners) { + miningSpeed *= 100; + } + + for (let i = 0; i < this.allEntities.length; ++i) { + const entity = this.allEntities[i]; + const minerComp = entity.components.Miner; + + // Reset everything on recompute + if (this.needsRecompute) { + minerComp.cachedChainedMiner = null; + } + + // Check if miner is above an actual tile + if (!minerComp.cachedMinedItem) { + const staticComp = entity.components.StaticMapEntity; + const tileBelow = this.root.map.getLowerLayerContentXY( + staticComp.origin.x, + staticComp.origin.y + ); + if (!tileBelow) { + continue; + } + minerComp.cachedMinedItem = tileBelow; + } + + // First, try to get rid of chained items + if (minerComp.itemChainBuffer.length > 0) { + if (this.tryPerformMinerEject(entity, minerComp.itemChainBuffer[0])) { + minerComp.itemChainBuffer.shift(); + continue; + } + } + + const mineDuration = 1 / miningSpeed; + const timeSinceMine = this.root.time.now() - minerComp.lastMiningTime; + if (timeSinceMine > mineDuration) { + // Store how much we overflowed + const buffer = Math.min(timeSinceMine - mineDuration, this.root.dynamicTickrate.deltaSeconds); + + if (this.tryPerformMinerEject(entity, minerComp.cachedMinedItem)) { + // Analytics hook + this.root.signals.itemProduced.dispatch(minerComp.cachedMinedItem); + // Store mining time + minerComp.lastMiningTime = this.root.time.now() - buffer; + } + } + } + + // After this frame we are done + this.needsRecompute = false; + } + + /** + * Finds the target chained miner for a given entity + * @param {Entity} entity + * @returns {Entity|false} The chained entity or null if not found + */ + findChainedMiner(entity) { + const ejectComp = entity.components.ItemEjector; + const staticComp = entity.components.StaticMapEntity; + const contentsBelow = this.root.map.getLowerLayerContentXY(staticComp.origin.x, staticComp.origin.y); + if (!contentsBelow) { + // This miner has no contents + return null; + } + + const ejectingSlot = ejectComp.slots[0]; + const ejectingPos = staticComp.localTileToWorld(ejectingSlot.pos); + const ejectingDirection = staticComp.localDirectionToWorld(ejectingSlot.direction); + + const targetTile = ejectingPos.add(enumDirectionToVector[ejectingDirection]); + const targetContents = this.root.map.getTileContent(targetTile, "regular"); + + // Check if we are connected to another miner and thus do not eject directly + if (targetContents) { + const targetMinerComp = targetContents.components.Miner; + if (targetMinerComp && targetMinerComp.chainable) { + const targetLowerLayer = this.root.map.getLowerLayerContentXY(targetTile.x, targetTile.y); + if (targetLowerLayer) { + return targetContents; + } + } + } + + return false; + } + + /** + * + * @param {Entity} entity + * @param {BaseItem} item + */ + tryPerformMinerEject(entity, item) { + const minerComp = entity.components.Miner; + const ejectComp = entity.components.ItemEjector; + + // Check if we are a chained miner + if (minerComp.chainable) { + const targetEntity = minerComp.cachedChainedMiner; + + // Check if the cache has to get recomputed + if (targetEntity === null) { + minerComp.cachedChainedMiner = this.findChainedMiner(entity); + } + + // Check if we now have a target + if (targetEntity) { + const targetMinerComp = targetEntity.components.Miner; + if (targetMinerComp.tryAcceptChainedItem(item)) { + return true; + } else { + return false; + } + } + } + + // Seems we are a regular miner or at the end of a row, try actually ejecting + if (ejectComp.tryEject(0, item)) { + return true; + } + + return false; + } + + /** + * + * @param {DrawParameters} parameters + * @param {MapChunkView} chunk + */ + drawChunk(parameters, chunk) { + const contents = chunk.containedEntitiesByLayer.regular; + + for (let i = 0; i < contents.length; ++i) { + const entity = contents[i]; + const minerComp = entity.components.Miner; + if (!minerComp) { + continue; + } + + const staticComp = entity.components.StaticMapEntity; + if (!minerComp.cachedMinedItem) { + continue; + } + + // Draw the item background - this is to hide the ejected item animation from + // the item ejector + + const padding = 3; + const destX = staticComp.origin.x * globalConfig.tileSize + padding; + const destY = staticComp.origin.y * globalConfig.tileSize + padding; + const dimensions = globalConfig.tileSize - 2 * padding; + + if (parameters.visibleRect.containsRect4Params(destX, destY, dimensions, dimensions)) { + parameters.context.fillStyle = minerComp.cachedMinedItem.getBackgroundColorAsResource(); + parameters.context.fillRect(destX, destY, dimensions, dimensions); + } + + minerComp.cachedMinedItem.drawItemCenteredClipped( + (0.5 + staticComp.origin.x) * globalConfig.tileSize, + (0.5 + staticComp.origin.y) * globalConfig.tileSize, + parameters, + globalConfig.defaultItemDiameter + ); + } + } +} diff --git a/src/js/game/systems/static_map_entity.js b/src/js/game/systems/static_map_entity.js index 3e891f7b..da6575a5 100644 --- a/src/js/game/systems/static_map_entity.js +++ b/src/js/game/systems/static_map_entity.js @@ -1,82 +1,82 @@ -import { globalConfig } from "../../core/config"; -import { DrawParameters } from "../../core/draw_parameters"; -import { GameSystem } from "../game_system"; -import { MapChunkView } from "../map_chunk_view"; - -export class StaticMapEntitySystem extends GameSystem { - constructor(root) { - super(root); - - /** @type {Set} */ - this.drawnUids = new Set(); - - this.root.signals.gameFrameStarted.add(this.clearUidList, this); - } - - /** - * Clears the uid list when a new frame started - */ - clearUidList() { - this.drawnUids.clear(); - } - - /** - * Draws the static entities - * @param {DrawParameters} parameters - * @param {MapChunkView} chunk - */ - drawChunk(parameters, chunk) { - if (G_IS_DEV && globalConfig.debug.doNotRenderStatics) { - return; - } - - const contents = chunk.containedEntitiesByLayer.regular; - for (let i = 0; i < contents.length; ++i) { - const entity = contents[i]; - - const staticComp = entity.components.StaticMapEntity; - const sprite = staticComp.getSprite(); - if (sprite) { - // Avoid drawing an entity twice which has been drawn for - // another chunk already - if (this.drawnUids.has(entity.uid)) { - continue; - } - - this.drawnUids.add(entity.uid); - staticComp.drawSpriteOnBoundsClipped(parameters, sprite, 2); - } - } - } - - /** - * Draws the static wire entities - * @param {DrawParameters} parameters - * @param {MapChunkView} chunk - */ - drawWiresChunk(parameters, chunk) { - if (G_IS_DEV && globalConfig.debug.doNotRenderStatics) { - return; - } - - const drawnUids = new Set(); - const contents = chunk.wireContents; - for (let y = 0; y < globalConfig.mapChunkSize; ++y) { - for (let x = 0; x < globalConfig.mapChunkSize; ++x) { - const entity = contents[x][y]; - if (entity) { - if (drawnUids.has(entity.uid)) { - continue; - } - drawnUids.add(entity.uid); - const staticComp = entity.components.StaticMapEntity; - - const sprite = staticComp.getSprite(); - if (sprite) { - staticComp.drawSpriteOnBoundsClipped(parameters, sprite, 2); - } - } - } - } - } -} +import { globalConfig } from "../../core/config"; +import { DrawParameters } from "../../core/draw_parameters"; +import { GameSystem } from "../game_system"; +import { MapChunkView } from "../map_chunk_view"; + +export class StaticMapEntitySystem extends GameSystem { + constructor(root) { + super(root); + + /** @type {Set} */ + this.drawnUids = new Set(); + + this.root.signals.gameFrameStarted.add(this.clearUidList, this); + } + + /** + * Clears the uid list when a new frame started + */ + clearUidList() { + this.drawnUids.clear(); + } + + /** + * Draws the static entities + * @param {DrawParameters} parameters + * @param {MapChunkView} chunk + */ + drawChunk(parameters, chunk) { + if (G_IS_DEV && globalConfig.debug.doNotRenderStatics) { + return; + } + + const contents = chunk.containedEntitiesByLayer.regular; + for (let i = 0; i < contents.length; ++i) { + const entity = contents[i]; + + const staticComp = entity.components.StaticMapEntity; + const sprite = staticComp.getSprite(); + if (sprite) { + // Avoid drawing an entity twice which has been drawn for + // another chunk already + if (this.drawnUids.has(entity.uid)) { + continue; + } + + this.drawnUids.add(entity.uid); + staticComp.drawSpriteOnBoundsClipped(parameters, sprite, 2); + } + } + } + + /** + * Draws the static wire entities + * @param {DrawParameters} parameters + * @param {MapChunkView} chunk + */ + drawWiresChunk(parameters, chunk) { + if (G_IS_DEV && globalConfig.debug.doNotRenderStatics) { + return; + } + + const drawnUids = new Set(); + const contents = chunk.wireContents; + for (let y = 0; y < globalConfig.mapChunkSize; ++y) { + for (let x = 0; x < globalConfig.mapChunkSize; ++x) { + const entity = contents[x][y]; + if (entity) { + if (drawnUids.has(entity.uid)) { + continue; + } + drawnUids.add(entity.uid); + const staticComp = entity.components.StaticMapEntity; + + const sprite = staticComp.getSprite(); + if (sprite) { + staticComp.drawSpriteOnBoundsClipped(parameters, sprite, 2); + } + } + } + } + } +} diff --git a/src/js/game/systems/storage.js b/src/js/game/systems/storage.js index 20204a89..ca3229bc 100644 --- a/src/js/game/systems/storage.js +++ b/src/js/game/systems/storage.js @@ -1,106 +1,106 @@ -import { DrawParameters } from "../../core/draw_parameters"; -import { Loader } from "../../core/loader"; -import { formatBigNumber, lerp } from "../../core/utils"; -import { StorageComponent } from "../components/storage"; -import { GameSystemWithFilter } from "../game_system_with_filter"; -import { BOOL_FALSE_SINGLETON, BOOL_TRUE_SINGLETON } from "../items/boolean_item"; -import { MapChunkView } from "../map_chunk_view"; - -export class StorageSystem extends GameSystemWithFilter { - constructor(root) { - super(root, [StorageComponent]); - - this.storageOverlaySprite = Loader.getSprite("sprites/misc/storage_overlay.png"); - - /** - * Stores which uids were already drawn to avoid drawing entities twice - * @type {Set} - */ - this.drawnUids = new Set(); - - this.root.signals.gameFrameStarted.add(this.clearDrawnUids, this); - } - - clearDrawnUids() { - this.drawnUids.clear(); - } - - update() { - for (let i = 0; i < this.allEntities.length; ++i) { - const entity = this.allEntities[i]; - const storageComp = entity.components.Storage; - const pinsComp = entity.components.WiredPins; - - // Eject from storage - if (storageComp.storedItem && storageComp.storedCount > 0) { - const ejectorComp = entity.components.ItemEjector; - - const nextSlot = ejectorComp.getFirstFreeSlot(); - if (nextSlot !== null) { - if (ejectorComp.tryEject(nextSlot, storageComp.storedItem)) { - storageComp.storedCount--; - - if (storageComp.storedCount === 0) { - storageComp.storedItem = null; - } - } - } - } - - let targetAlpha = storageComp.storedCount > 0 ? 1 : 0; - storageComp.overlayOpacity = lerp(storageComp.overlayOpacity, targetAlpha, 0.05); - - // a wired pins component is not guaranteed, but if its there, set the value - if (pinsComp) { - pinsComp.slots[0].value = storageComp.storedItem; - pinsComp.slots[1].value = storageComp.getIsFull() - ? BOOL_TRUE_SINGLETON - : BOOL_FALSE_SINGLETON; - } - } - } - - /** - * @param {DrawParameters} parameters - * @param {MapChunkView} chunk - */ - drawChunk(parameters, chunk) { - const contents = chunk.containedEntitiesByLayer.regular; - for (let i = 0; i < contents.length; ++i) { - const entity = contents[i]; - const storageComp = entity.components.Storage; - if (!storageComp) { - continue; - } - - const storedItem = storageComp.storedItem; - if (!storedItem) { - continue; - } - - if (this.drawnUids.has(entity.uid)) { - continue; - } - - this.drawnUids.add(entity.uid); - - const staticComp = entity.components.StaticMapEntity; - - const context = parameters.context; - context.globalAlpha = storageComp.overlayOpacity; - const center = staticComp.getTileSpaceBounds().getCenter().toWorldSpace(); - storedItem.drawItemCenteredClipped(center.x, center.y, parameters, 30); - - this.storageOverlaySprite.drawCached(parameters, center.x - 15, center.y + 15, 30, 15); - - if (parameters.visibleRect.containsCircle(center.x, center.y + 25, 20)) { - context.font = "bold 10px GameFont"; - context.textAlign = "center"; - context.fillStyle = "#64666e"; - context.fillText(formatBigNumber(storageComp.storedCount), center.x, center.y + 25.5); - context.textAlign = "left"; - } - context.globalAlpha = 1; - } - } -} +import { DrawParameters } from "../../core/draw_parameters"; +import { Loader } from "../../core/loader"; +import { formatBigNumber, lerp } from "../../core/utils"; +import { StorageComponent } from "../components/storage"; +import { GameSystemWithFilter } from "../game_system_with_filter"; +import { BOOL_FALSE_SINGLETON, BOOL_TRUE_SINGLETON } from "../items/boolean_item"; +import { MapChunkView } from "../map_chunk_view"; + +export class StorageSystem extends GameSystemWithFilter { + constructor(root) { + super(root, [StorageComponent]); + + this.storageOverlaySprite = Loader.getSprite("sprites/misc/storage_overlay.png"); + + /** + * Stores which uids were already drawn to avoid drawing entities twice + * @type {Set} + */ + this.drawnUids = new Set(); + + this.root.signals.gameFrameStarted.add(this.clearDrawnUids, this); + } + + clearDrawnUids() { + this.drawnUids.clear(); + } + + update() { + for (let i = 0; i < this.allEntities.length; ++i) { + const entity = this.allEntities[i]; + const storageComp = entity.components.Storage; + const pinsComp = entity.components.WiredPins; + + // Eject from storage + if (storageComp.storedItem && storageComp.storedCount > 0) { + const ejectorComp = entity.components.ItemEjector; + + const nextSlot = ejectorComp.getFirstFreeSlot(); + if (nextSlot !== null) { + if (ejectorComp.tryEject(nextSlot, storageComp.storedItem)) { + storageComp.storedCount--; + + if (storageComp.storedCount === 0) { + storageComp.storedItem = null; + } + } + } + } + + let targetAlpha = storageComp.storedCount > 0 ? 1 : 0; + storageComp.overlayOpacity = lerp(storageComp.overlayOpacity, targetAlpha, 0.05); + + // a wired pins component is not guaranteed, but if its there, set the value + if (pinsComp) { + pinsComp.slots[0].value = storageComp.storedItem; + pinsComp.slots[1].value = storageComp.getIsFull() + ? BOOL_TRUE_SINGLETON + : BOOL_FALSE_SINGLETON; + } + } + } + + /** + * @param {DrawParameters} parameters + * @param {MapChunkView} chunk + */ + drawChunk(parameters, chunk) { + const contents = chunk.containedEntitiesByLayer.regular; + for (let i = 0; i < contents.length; ++i) { + const entity = contents[i]; + const storageComp = entity.components.Storage; + if (!storageComp) { + continue; + } + + const storedItem = storageComp.storedItem; + if (!storedItem) { + continue; + } + + if (this.drawnUids.has(entity.uid)) { + continue; + } + + this.drawnUids.add(entity.uid); + + const staticComp = entity.components.StaticMapEntity; + + const context = parameters.context; + context.globalAlpha = storageComp.overlayOpacity; + const center = staticComp.getTileSpaceBounds().getCenter().toWorldSpace(); + storedItem.drawItemCenteredClipped(center.x, center.y, parameters, 30); + + this.storageOverlaySprite.drawCached(parameters, center.x - 15, center.y + 15, 30, 15); + + if (parameters.visibleRect.containsCircle(center.x, center.y + 25, 20)) { + context.font = "bold 10px GameFont"; + context.textAlign = "center"; + context.fillStyle = "#64666e"; + context.fillText(formatBigNumber(storageComp.storedCount), center.x, center.y + 25.5); + context.textAlign = "left"; + } + context.globalAlpha = 1; + } + } +} diff --git a/src/js/game/systems/underground_belt.js b/src/js/game/systems/underground_belt.js index 9b31eec1..38a204fd 100644 --- a/src/js/game/systems/underground_belt.js +++ b/src/js/game/systems/underground_belt.js @@ -1,353 +1,353 @@ -import { globalConfig } from "../../core/config"; -import { Loader } from "../../core/loader"; -import { createLogger } from "../../core/logging"; -import { Rectangle } from "../../core/rectangle"; -import { StaleAreaDetector } from "../../core/stale_area_detector"; -import { fastArrayDelete } from "../../core/utils"; -import { - enumAngleToDirection, - enumDirection, - enumDirectionToAngle, - enumDirectionToVector, - enumInvertedDirections, -} from "../../core/vector"; -import { enumUndergroundBeltMode, UndergroundBeltComponent } from "../components/underground_belt"; -import { Entity } from "../entity"; -import { GameSystemWithFilter } from "../game_system_with_filter"; - -const logger = createLogger("tunnels"); - -export class UndergroundBeltSystem extends GameSystemWithFilter { - constructor(root) { - super(root, [UndergroundBeltComponent]); - - this.beltSprites = { - [enumUndergroundBeltMode.sender]: Loader.getSprite( - "sprites/buildings/underground_belt_entry.png" - ), - [enumUndergroundBeltMode.receiver]: Loader.getSprite( - "sprites/buildings/underground_belt_exit.png" - ), - }; - - this.staleAreaWatcher = new StaleAreaDetector({ - root: this.root, - name: "underground-belt", - recomputeMethod: this.recomputeArea.bind(this), - }); - - this.root.signals.entityManuallyPlaced.add(this.onEntityManuallyPlaced, this); - - // NOTICE: Once we remove a tunnel, we need to update the whole area to - // clear outdated handles - this.staleAreaWatcher.recomputeOnComponentsChanged( - [UndergroundBeltComponent], - globalConfig.undergroundBeltMaxTilesByTier[globalConfig.undergroundBeltMaxTilesByTier.length - 1] - ); - } - - /** - * Callback when an entity got placed, used to remove belts between underground belts - * @param {Entity} entity - */ - onEntityManuallyPlaced(entity) { - if (!this.root.app.settings.getAllSettings().enableTunnelSmartplace) { - // Smart-place disabled - return; - } - - const undergroundComp = entity.components.UndergroundBelt; - if (undergroundComp && undergroundComp.mode === enumUndergroundBeltMode.receiver) { - const staticComp = entity.components.StaticMapEntity; - const tile = staticComp.origin; - - const direction = enumAngleToDirection[staticComp.rotation]; - const inverseDirection = enumInvertedDirections[direction]; - const offset = enumDirectionToVector[inverseDirection]; - - let currentPos = tile.copy(); - - const tier = undergroundComp.tier; - const range = globalConfig.undergroundBeltMaxTilesByTier[tier]; - - // FIND ENTRANCE - // Search for the entrance which is farthest apart (this is why we can't reuse logic here) - let matchingEntrance = null; - for (let i = 0; i < range; ++i) { - currentPos.addInplace(offset); - const contents = this.root.map.getTileContent(currentPos, entity.layer); - if (!contents) { - continue; - } - - const contentsUndergroundComp = contents.components.UndergroundBelt; - const contentsStaticComp = contents.components.StaticMapEntity; - if ( - contentsUndergroundComp && - contentsUndergroundComp.tier === undergroundComp.tier && - contentsUndergroundComp.mode === enumUndergroundBeltMode.sender && - enumAngleToDirection[contentsStaticComp.rotation] === direction - ) { - matchingEntrance = { - entity: contents, - range: i, - }; - } - } - - if (!matchingEntrance) { - // Nothing found - return; - } - - // DETECT OBSOLETE BELTS BETWEEN - // Remove any belts between entrance and exit which have the same direction, - // but only if they *all* have the right direction - currentPos = tile.copy(); - let allBeltsMatch = true; - for (let i = 0; i < matchingEntrance.range; ++i) { - currentPos.addInplace(offset); - - const contents = this.root.map.getTileContent(currentPos, entity.layer); - if (!contents) { - allBeltsMatch = false; - break; - } - - const contentsStaticComp = contents.components.StaticMapEntity; - const contentsBeltComp = contents.components.Belt; - if (!contentsBeltComp) { - allBeltsMatch = false; - break; - } - - // It's a belt - if ( - contentsBeltComp.direction !== enumDirection.top || - enumAngleToDirection[contentsStaticComp.rotation] !== direction - ) { - allBeltsMatch = false; - break; - } - } - - currentPos = tile.copy(); - if (allBeltsMatch) { - // All belts between this are obsolete, so drop them - for (let i = 0; i < matchingEntrance.range; ++i) { - currentPos.addInplace(offset); - const contents = this.root.map.getTileContent(currentPos, entity.layer); - assert(contents, "Invalid smart underground belt logic"); - this.root.logic.tryDeleteBuilding(contents); - } - } - - // REMOVE OBSOLETE TUNNELS - // Remove any double tunnels, by checking the tile plus the tile above - currentPos = tile.copy().add(offset); - for (let i = 0; i < matchingEntrance.range - 1; ++i) { - const posBefore = currentPos.copy(); - currentPos.addInplace(offset); - - const entityBefore = this.root.map.getTileContent(posBefore, entity.layer); - const entityAfter = this.root.map.getTileContent(currentPos, entity.layer); - - if (!entityBefore || !entityAfter) { - continue; - } - - const undergroundBefore = entityBefore.components.UndergroundBelt; - const undergroundAfter = entityAfter.components.UndergroundBelt; - - if (!undergroundBefore || !undergroundAfter) { - // Not an underground belt - continue; - } - - if ( - // Both same tier - undergroundBefore.tier !== undergroundAfter.tier || - // And same tier as our original entity - undergroundBefore.tier !== undergroundComp.tier - ) { - // Mismatching tier - continue; - } - - if ( - undergroundBefore.mode !== enumUndergroundBeltMode.sender || - undergroundAfter.mode !== enumUndergroundBeltMode.receiver - ) { - // Not the right mode - continue; - } - - // Check rotations - const staticBefore = entityBefore.components.StaticMapEntity; - const staticAfter = entityAfter.components.StaticMapEntity; - - if ( - enumAngleToDirection[staticBefore.rotation] !== direction || - enumAngleToDirection[staticAfter.rotation] !== direction - ) { - // Wrong rotation - continue; - } - - // All good, can remove - this.root.logic.tryDeleteBuilding(entityBefore); - this.root.logic.tryDeleteBuilding(entityAfter); - } - } - } - - /** - * Recomputes the cache in the given area, invalidating all entries there - * @param {Rectangle} area - */ - recomputeArea(area) { - for (let x = area.x; x < area.right(); ++x) { - for (let y = area.y; y < area.bottom(); ++y) { - const entities = this.root.map.getLayersContentsMultipleXY(x, y); - for (let i = 0; i < entities.length; ++i) { - const entity = entities[i]; - const undergroundComp = entity.components.UndergroundBelt; - if (!undergroundComp) { - continue; - } - undergroundComp.cachedLinkedEntity = null; - } - } - } - } - - update() { - this.staleAreaWatcher.update(); - - const sender = enumUndergroundBeltMode.sender; - const now = this.root.time.now(); - - for (let i = 0; i < this.allEntities.length; ++i) { - const entity = this.allEntities[i]; - const undergroundComp = entity.components.UndergroundBelt; - if (undergroundComp.mode === sender) { - this.handleSender(entity); - } else { - this.handleReceiver(entity, now); - } - } - } - - /** - * Finds the receiver for a given sender - * @param {Entity} entity - * @returns {import("../components/underground_belt").LinkedUndergroundBelt} - */ - findRecieverForSender(entity) { - const staticComp = entity.components.StaticMapEntity; - const undergroundComp = entity.components.UndergroundBelt; - const searchDirection = staticComp.localDirectionToWorld(enumDirection.top); - const searchVector = enumDirectionToVector[searchDirection]; - const targetRotation = enumDirectionToAngle[searchDirection]; - let currentTile = staticComp.origin; - - // Search in the direction of the tunnel - for ( - let searchOffset = 0; - searchOffset < globalConfig.undergroundBeltMaxTilesByTier[undergroundComp.tier]; - ++searchOffset - ) { - currentTile = currentTile.add(searchVector); - - const potentialReceiver = this.root.map.getTileContent(currentTile, "regular"); - if (!potentialReceiver) { - // Empty tile - continue; - } - const receiverUndergroundComp = potentialReceiver.components.UndergroundBelt; - if (!receiverUndergroundComp || receiverUndergroundComp.tier !== undergroundComp.tier) { - // Not a tunnel, or not on the same tier - continue; - } - - const receiverStaticComp = potentialReceiver.components.StaticMapEntity; - if (receiverStaticComp.rotation !== targetRotation) { - // Wrong rotation - continue; - } - - if (receiverUndergroundComp.mode !== enumUndergroundBeltMode.receiver) { - // Not a receiver, but a sender -> Abort to make sure we don't deliver double - break; - } - - return { entity: potentialReceiver, distance: searchOffset }; - } - - // None found - return { entity: null, distance: 0 }; - } - - /** - * - * @param {Entity} entity - */ - handleSender(entity) { - const undergroundComp = entity.components.UndergroundBelt; - - // Find the current receiver - let cacheEntry = undergroundComp.cachedLinkedEntity; - if (!cacheEntry) { - // Need to recompute cache - cacheEntry = undergroundComp.cachedLinkedEntity = this.findRecieverForSender(entity); - } - - if (!cacheEntry.entity) { - // If there is no connection to a receiver, ignore this one - return; - } - - // Check if we have any items to eject - const nextItemAndDuration = undergroundComp.pendingItems[0]; - if (nextItemAndDuration) { - assert(undergroundComp.pendingItems.length === 1, "more than 1 pending"); - - // Check if the receiver can accept it - if ( - cacheEntry.entity.components.UndergroundBelt.tryAcceptTunneledItem( - nextItemAndDuration[0], - cacheEntry.distance, - this.root.hubGoals.getUndergroundBeltBaseSpeed(), - this.root.time.now() - ) - ) { - // Drop this item - fastArrayDelete(undergroundComp.pendingItems, 0); - } - } - } - - /** - * - * @param {Entity} entity - * @param {number} now - */ - handleReceiver(entity, now) { - const undergroundComp = entity.components.UndergroundBelt; - - // Try to eject items, we only check the first one because it is sorted by remaining time - const nextItemAndDuration = undergroundComp.pendingItems[0]; - if (nextItemAndDuration) { - if (now > nextItemAndDuration[1]) { - const ejectorComp = entity.components.ItemEjector; - - const nextSlotIndex = ejectorComp.getFirstFreeSlot(); - if (nextSlotIndex !== null) { - if (ejectorComp.tryEject(nextSlotIndex, nextItemAndDuration[0])) { - undergroundComp.pendingItems.shift(); - } - } - } - } - } -} +import { globalConfig } from "../../core/config"; +import { Loader } from "../../core/loader"; +import { createLogger } from "../../core/logging"; +import { Rectangle } from "../../core/rectangle"; +import { StaleAreaDetector } from "../../core/stale_area_detector"; +import { fastArrayDelete } from "../../core/utils"; +import { + enumAngleToDirection, + enumDirection, + enumDirectionToAngle, + enumDirectionToVector, + enumInvertedDirections, +} from "../../core/vector"; +import { UndergroundBeltComponent, enumUndergroundBeltMode } from "../components/underground_belt"; +import { Entity } from "../entity"; +import { GameSystemWithFilter } from "../game_system_with_filter"; + +const logger = createLogger("tunnels"); + +export class UndergroundBeltSystem extends GameSystemWithFilter { + constructor(root) { + super(root, [UndergroundBeltComponent]); + + this.beltSprites = { + [enumUndergroundBeltMode.sender]: Loader.getSprite( + "sprites/buildings/underground_belt_entry.png" + ), + [enumUndergroundBeltMode.receiver]: Loader.getSprite( + "sprites/buildings/underground_belt_exit.png" + ), + }; + + this.staleAreaWatcher = new StaleAreaDetector({ + root: this.root, + name: "underground-belt", + recomputeMethod: this.recomputeArea.bind(this), + }); + + this.root.signals.entityManuallyPlaced.add(this.onEntityManuallyPlaced, this); + + // NOTICE: Once we remove a tunnel, we need to update the whole area to + // clear outdated handles + this.staleAreaWatcher.recomputeOnComponentsChanged( + [UndergroundBeltComponent], + globalConfig.undergroundBeltMaxTilesByTier[globalConfig.undergroundBeltMaxTilesByTier.length - 1] + ); + } + + /** + * Callback when an entity got placed, used to remove belts between underground belts + * @param {Entity} entity + */ + onEntityManuallyPlaced(entity) { + if (!this.root.app.settings.getAllSettings().enableTunnelSmartplace) { + // Smart-place disabled + return; + } + + const undergroundComp = entity.components.UndergroundBelt; + if (undergroundComp && undergroundComp.mode === enumUndergroundBeltMode.receiver) { + const staticComp = entity.components.StaticMapEntity; + const tile = staticComp.origin; + + const direction = enumAngleToDirection[staticComp.rotation]; + const inverseDirection = enumInvertedDirections[direction]; + const offset = enumDirectionToVector[inverseDirection]; + + let currentPos = tile.copy(); + + const tier = undergroundComp.tier; + const range = globalConfig.undergroundBeltMaxTilesByTier[tier]; + + // FIND ENTRANCE + // Search for the entrance which is farthest apart (this is why we can't reuse logic here) + let matchingEntrance = null; + for (let i = 0; i < range; ++i) { + currentPos.addInplace(offset); + const contents = this.root.map.getTileContent(currentPos, entity.layer); + if (!contents) { + continue; + } + + const contentsUndergroundComp = contents.components.UndergroundBelt; + const contentsStaticComp = contents.components.StaticMapEntity; + if ( + contentsUndergroundComp && + contentsUndergroundComp.tier === undergroundComp.tier && + contentsUndergroundComp.mode === enumUndergroundBeltMode.sender && + enumAngleToDirection[contentsStaticComp.rotation] === direction + ) { + matchingEntrance = { + entity: contents, + range: i, + }; + } + } + + if (!matchingEntrance) { + // Nothing found + return; + } + + // DETECT OBSOLETE BELTS BETWEEN + // Remove any belts between entrance and exit which have the same direction, + // but only if they *all* have the right direction + currentPos = tile.copy(); + let allBeltsMatch = true; + for (let i = 0; i < matchingEntrance.range; ++i) { + currentPos.addInplace(offset); + + const contents = this.root.map.getTileContent(currentPos, entity.layer); + if (!contents) { + allBeltsMatch = false; + break; + } + + const contentsStaticComp = contents.components.StaticMapEntity; + const contentsBeltComp = contents.components.Belt; + if (!contentsBeltComp) { + allBeltsMatch = false; + break; + } + + // It's a belt + if ( + contentsBeltComp.direction !== enumDirection.top || + enumAngleToDirection[contentsStaticComp.rotation] !== direction + ) { + allBeltsMatch = false; + break; + } + } + + currentPos = tile.copy(); + if (allBeltsMatch) { + // All belts between this are obsolete, so drop them + for (let i = 0; i < matchingEntrance.range; ++i) { + currentPos.addInplace(offset); + const contents = this.root.map.getTileContent(currentPos, entity.layer); + assert(contents, "Invalid smart underground belt logic"); + this.root.logic.tryDeleteBuilding(contents); + } + } + + // REMOVE OBSOLETE TUNNELS + // Remove any double tunnels, by checking the tile plus the tile above + currentPos = tile.copy().add(offset); + for (let i = 0; i < matchingEntrance.range - 1; ++i) { + const posBefore = currentPos.copy(); + currentPos.addInplace(offset); + + const entityBefore = this.root.map.getTileContent(posBefore, entity.layer); + const entityAfter = this.root.map.getTileContent(currentPos, entity.layer); + + if (!entityBefore || !entityAfter) { + continue; + } + + const undergroundBefore = entityBefore.components.UndergroundBelt; + const undergroundAfter = entityAfter.components.UndergroundBelt; + + if (!undergroundBefore || !undergroundAfter) { + // Not an underground belt + continue; + } + + if ( + // Both same tier + undergroundBefore.tier !== undergroundAfter.tier || + // And same tier as our original entity + undergroundBefore.tier !== undergroundComp.tier + ) { + // Mismatching tier + continue; + } + + if ( + undergroundBefore.mode !== enumUndergroundBeltMode.sender || + undergroundAfter.mode !== enumUndergroundBeltMode.receiver + ) { + // Not the right mode + continue; + } + + // Check rotations + const staticBefore = entityBefore.components.StaticMapEntity; + const staticAfter = entityAfter.components.StaticMapEntity; + + if ( + enumAngleToDirection[staticBefore.rotation] !== direction || + enumAngleToDirection[staticAfter.rotation] !== direction + ) { + // Wrong rotation + continue; + } + + // All good, can remove + this.root.logic.tryDeleteBuilding(entityBefore); + this.root.logic.tryDeleteBuilding(entityAfter); + } + } + } + + /** + * Recomputes the cache in the given area, invalidating all entries there + * @param {Rectangle} area + */ + recomputeArea(area) { + for (let x = area.x; x < area.right(); ++x) { + for (let y = area.y; y < area.bottom(); ++y) { + const entities = this.root.map.getLayersContentsMultipleXY(x, y); + for (let i = 0; i < entities.length; ++i) { + const entity = entities[i]; + const undergroundComp = entity.components.UndergroundBelt; + if (!undergroundComp) { + continue; + } + undergroundComp.cachedLinkedEntity = null; + } + } + } + } + + update() { + this.staleAreaWatcher.update(); + + const sender = enumUndergroundBeltMode.sender; + const now = this.root.time.now(); + + for (let i = 0; i < this.allEntities.length; ++i) { + const entity = this.allEntities[i]; + const undergroundComp = entity.components.UndergroundBelt; + if (undergroundComp.mode === sender) { + this.handleSender(entity); + } else { + this.handleReceiver(entity, now); + } + } + } + + /** + * Finds the receiver for a given sender + * @param {Entity} entity + * @returns {import("../components/underground_belt").LinkedUndergroundBelt} + */ + findReceiverForSender(entity) { + const staticComp = entity.components.StaticMapEntity; + const undergroundComp = entity.components.UndergroundBelt; + const searchDirection = staticComp.localDirectionToWorld(enumDirection.top); + const searchVector = enumDirectionToVector[searchDirection]; + const targetRotation = enumDirectionToAngle[searchDirection]; + let currentTile = staticComp.origin; + + // Search in the direction of the tunnel + for ( + let searchOffset = 0; + searchOffset < globalConfig.undergroundBeltMaxTilesByTier[undergroundComp.tier]; + ++searchOffset + ) { + currentTile = currentTile.add(searchVector); + + const potentialReceiver = this.root.map.getTileContent(currentTile, "regular"); + if (!potentialReceiver) { + // Empty tile + continue; + } + const receiverUndergroundComp = potentialReceiver.components.UndergroundBelt; + if (!receiverUndergroundComp || receiverUndergroundComp.tier !== undergroundComp.tier) { + // Not a tunnel, or not on the same tier + continue; + } + + const receiverStaticComp = potentialReceiver.components.StaticMapEntity; + if (receiverStaticComp.rotation !== targetRotation) { + // Wrong rotation + continue; + } + + if (receiverUndergroundComp.mode !== enumUndergroundBeltMode.receiver) { + // Not a receiver, but a sender -> Abort to make sure we don't deliver double + break; + } + + return { entity: potentialReceiver, distance: searchOffset }; + } + + // None found + return { entity: null, distance: 0 }; + } + + /** + * + * @param {Entity} entity + */ + handleSender(entity) { + const undergroundComp = entity.components.UndergroundBelt; + + // Find the current receiver + let cacheEntry = undergroundComp.cachedLinkedEntity; + if (!cacheEntry) { + // Need to recompute cache + cacheEntry = undergroundComp.cachedLinkedEntity = this.findReceiverForSender(entity); + } + + if (!cacheEntry.entity) { + // If there is no connection to a receiver, ignore this one + return; + } + + // Check if we have any items to eject + const nextItemAndDuration = undergroundComp.pendingItems[0]; + if (nextItemAndDuration) { + assert(undergroundComp.pendingItems.length === 1, "more than 1 pending"); + + // Check if the receiver can accept it + if ( + cacheEntry.entity.components.UndergroundBelt.tryAcceptTunneledItem( + nextItemAndDuration[0], + cacheEntry.distance, + this.root.hubGoals.getUndergroundBeltBaseSpeed(), + this.root.time.now() + ) + ) { + // Drop this item + fastArrayDelete(undergroundComp.pendingItems, 0); + } + } + } + + /** + * + * @param {Entity} entity + * @param {number} now + */ + handleReceiver(entity, now) { + const undergroundComp = entity.components.UndergroundBelt; + + // Try to eject items, we only check the first one because it is sorted by remaining time + const nextItemAndDuration = undergroundComp.pendingItems[0]; + if (nextItemAndDuration) { + if (now > nextItemAndDuration[1]) { + const ejectorComp = entity.components.ItemEjector; + + const nextSlotIndex = ejectorComp.getFirstFreeSlot(); + if (nextSlotIndex !== null) { + if (ejectorComp.tryEject(nextSlotIndex, nextItemAndDuration[0])) { + undergroundComp.pendingItems.shift(); + } + } + } + } + } +} diff --git a/src/js/game/systems/wire.js b/src/js/game/systems/wire.js index 4a255866..964a3e03 100644 --- a/src/js/game/systems/wire.js +++ b/src/js/game/systems/wire.js @@ -1,762 +1,756 @@ -import { globalConfig } from "../../core/config"; -import { gMetaBuildingRegistry } from "../../core/global_registries"; -import { Loader } from "../../core/loader"; -import { createLogger } from "../../core/logging"; -import { Rectangle } from "../../core/rectangle"; -import { AtlasSprite } from "../../core/sprites"; -import { StaleAreaDetector } from "../../core/stale_area_detector"; -import { fastArrayDeleteValueIfContained } from "../../core/utils"; -import { - arrayAllDirections, - enumDirection, - enumDirectionToVector, - enumInvertedDirections, - Vector, -} from "../../core/vector"; -import { ACHIEVEMENTS } from "../../platform/achievement_provider"; -import { BaseItem } from "../base_item"; -import { arrayWireRotationVariantToType, MetaWireBuilding } from "../buildings/wire"; -import { getCodeFromBuildingData } from "../building_codes"; -import { enumWireType, enumWireVariant, WireComponent } from "../components/wire"; -import { enumPinSlotType, WiredPinsComponent } from "../components/wired_pins"; -import { WireTunnelComponent } from "../components/wire_tunnel"; -import { Entity } from "../entity"; -import { GameSystem } from "../game_system"; -import { GameSystemWithFilter } from "../game_system_with_filter"; -import { isTruthyItem } from "../items/boolean_item"; -import { MapChunkView } from "../map_chunk_view"; - -const logger = createLogger("wires"); - -let networkUidCounter = 0; - -const VERBOSE_WIRES = G_IS_DEV && false; - -export class WireNetwork { - constructor() { - /** - * Who contributes to this network - * @type {Array<{ entity: Entity, slot: import("../components/wired_pins").WirePinSlot }>} */ - this.providers = []; - - /** - * Who takes values from this network - * @type {Array<{ entity: Entity, slot: import("../components/wired_pins").WirePinSlot }>} */ - this.receivers = []; - - /** - * All connected slots - * @type {Array<{ entity: Entity, slot: import("../components/wired_pins").WirePinSlot }>} - */ - this.allSlots = []; - - /** - * All connected tunnels - * @type {Array} - */ - this.tunnels = []; - - /** - * Which wires are in this network - * @type {Array} - */ - this.wires = []; - - /** - * The current value of this network - * @type {BaseItem} - */ - this.currentValue = null; - - /** - * Whether this network has a value conflict, that is, more than one - * sender has sent a value - * @type {boolean} - */ - this.valueConflict = false; - - /** - * Unique network identifier - * @type {number} - */ - this.uid = ++networkUidCounter; - } - - /** - * Returns whether this network currently has a value - * @returns {boolean} - */ - hasValue() { - return !!this.currentValue && !this.valueConflict; - } -} - -export class WireSystem extends GameSystem { - constructor(root) { - super(root); - - /** - * @type {Object>} - */ - this.wireSprites = {}; - - const variants = ["conflict", ...Object.keys(enumWireVariant)]; - for (let i = 0; i < variants.length; ++i) { - const wireVariant = variants[i]; - const sprites = {}; - for (const wireType in enumWireType) { - sprites[wireType] = Loader.getSprite( - "sprites/wires/sets/" + wireVariant + "_" + wireType + ".png" - ); - } - this.wireSprites[wireVariant] = sprites; - } - - this.root.signals.entityDestroyed.add(this.queuePlacementUpdate, this); - this.root.signals.entityAdded.add(this.queuePlacementUpdate, this); - - this.root.signals.entityDestroyed.add(this.queueRecomputeIfWire, this); - this.root.signals.entityChanged.add(this.queueRecomputeIfWire, this); - this.root.signals.entityAdded.add(this.queueRecomputeIfWire, this); - - this.needsRecompute = true; - this.isFirstRecompute = true; - - this.staleArea = new StaleAreaDetector({ - root: this.root, - name: "wires", - recomputeMethod: this.updateSurroundingWirePlacement.bind(this), - }); - - /** - * @type {Array} - */ - this.networks = []; - } - - /** - * Invalidates the wires network if the given entity is relevant for it - * @param {Entity} entity - */ - queueRecomputeIfWire(entity) { - if (!this.root.gameInitialized) { - return; - } - - if (this.isEntityRelevantForWires(entity)) { - this.needsRecompute = true; - this.networks = []; - } - } - - /** - * Recomputes the whole wires network - */ - recomputeWiresNetwork() { - this.needsRecompute = false; - logger.log("Recomputing wires network"); - - this.networks = []; - - const wireEntities = this.root.entityMgr.getAllWithComponent(WireComponent); - const tunnelEntities = this.root.entityMgr.getAllWithComponent(WireTunnelComponent); - const pinEntities = this.root.entityMgr.getAllWithComponent(WiredPinsComponent); - - // Clear all network references, but not on the first update since that's the deserializing one - if (!this.isFirstRecompute) { - for (let i = 0; i < wireEntities.length; ++i) { - wireEntities[i].components.Wire.linkedNetwork = null; - } - for (let i = 0; i < tunnelEntities.length; ++i) { - tunnelEntities[i].components.WireTunnel.linkedNetworks = []; - } - - for (let i = 0; i < pinEntities.length; ++i) { - const slots = pinEntities[i].components.WiredPins.slots; - for (let k = 0; k < slots.length; ++k) { - slots[k].linkedNetwork = null; - } - } - } else { - logger.log("Recomputing wires first time"); - this.isFirstRecompute = false; - } - - VERBOSE_WIRES && logger.log("Recomputing slots"); - - // Iterate over all ejector slots - for (let i = 0; i < pinEntities.length; ++i) { - const entity = pinEntities[i]; - const slots = entity.components.WiredPins.slots; - for (let k = 0; k < slots.length; ++k) { - const slot = slots[k]; - - // Ejectors are computed directly, acceptors are just set - if (slot.type === enumPinSlotType.logicalEjector && !slot.linkedNetwork) { - this.findNetworkForEjector(entity, slot); - } - } - } - } - - /** - * Finds the network for the given slot - * @param {Entity} initialEntity - * @param {import("../components/wired_pins").WirePinSlot} slot - */ - findNetworkForEjector(initialEntity, slot) { - let currentNetwork = new WireNetwork(); - VERBOSE_WIRES && - logger.log( - "Finding network for entity", - initialEntity.uid, - initialEntity.components.StaticMapEntity.origin.toString(), - "(nw-id:", - currentNetwork.uid, - ")" - ); - const entitiesToVisit = [ - { - entity: initialEntity, - slot, - }, - ]; - - /** - * Once we occur a wire, we store its variant so we don't connect to - * mismatching ones - * @type {enumWireVariant} - */ - let variantMask = null; - - while (entitiesToVisit.length > 0) { - const nextData = entitiesToVisit.pop(); - const nextEntity = nextData.entity; - - const wireComp = nextEntity.components.Wire; - const staticComp = nextEntity.components.StaticMapEntity; - - VERBOSE_WIRES && logger.log("Visiting", staticComp.origin.toString(), "(", nextEntity.uid, ")"); - - // Where to search for neighbours - let newSearchDirections = []; - let newSearchTile = null; - - //// WIRE - if (wireComp) { - // Sanity check - assert( - !wireComp.linkedNetwork || wireComp.linkedNetwork === currentNetwork, - "Mismatching wire network on wire entity " + - (wireComp.linkedNetwork ? wireComp.linkedNetwork.uid : "") + - " vs " + - currentNetwork.uid + - " @ " + - staticComp.origin.toString() - ); - - if (!wireComp.linkedNetwork) { - if (variantMask && wireComp.variant !== variantMask) { - // Mismatching variant - } else { - // This one is new! :D - VERBOSE_WIRES && logger.log(" Visited new wire:", staticComp.origin.toString()); - wireComp.linkedNetwork = currentNetwork; - currentNetwork.wires.push(nextEntity); - - newSearchDirections = arrayAllDirections; - newSearchTile = nextEntity.components.StaticMapEntity.origin; - variantMask = wireComp.variant; - } - } - } - - //// PINS - const pinsComp = nextEntity.components.WiredPins; - if (pinsComp) { - const slot = nextData.slot; - assert(slot, "No slot set for next entity"); - - if (slot.type === enumPinSlotType.logicalEjector) { - VERBOSE_WIRES && - logger.log(" Visiting ejector slot", staticComp.origin.toString(), "->", slot.type); - } else if (slot.type === enumPinSlotType.logicalAcceptor) { - VERBOSE_WIRES && - logger.log(" Visiting acceptor slot", staticComp.origin.toString(), "->", slot.type); - } else { - assertAlways(false, "Bad slot type: " + slot.type); - } - - // Sanity check - assert( - !slot.linkedNetwork || slot.linkedNetwork === currentNetwork, - "Mismatching wire network on pin slot entity " + - (slot.linkedNetwork ? slot.linkedNetwork.uid : "") + - " vs " + - currentNetwork.uid - ); - if (!slot.linkedNetwork) { - // This one is new - VERBOSE_WIRES && logger.log(" Visited new slot:", staticComp.origin.toString()); - - // Add to the right list - if (slot.type === enumPinSlotType.logicalEjector) { - currentNetwork.providers.push({ entity: nextEntity, slot }); - } else if (slot.type === enumPinSlotType.logicalAcceptor) { - currentNetwork.receivers.push({ entity: nextEntity, slot }); - } else { - assertAlways(false, "unknown slot type:" + slot.type); - } - - // Register on the network - currentNetwork.allSlots.push({ entity: nextEntity, slot }); - slot.linkedNetwork = currentNetwork; - - // Specify where to search next - newSearchDirections = [staticComp.localDirectionToWorld(slot.direction)]; - newSearchTile = staticComp.localTileToWorld(slot.pos); - } - } - - if (newSearchTile) { - // Find new surrounding wire targets - const newTargets = this.findSurroundingWireTargets( - newSearchTile, - newSearchDirections, - currentNetwork, - variantMask - ); - - VERBOSE_WIRES && logger.log(" Found", newTargets, "new targets to visit!"); - for (let i = 0; i < newTargets.length; ++i) { - entitiesToVisit.push(newTargets[i]); - } - } - } - - if ( - currentNetwork.providers.length > 0 && - (currentNetwork.wires.length > 0 || - currentNetwork.receivers.length > 0 || - currentNetwork.tunnels.length > 0) - ) { - this.networks.push(currentNetwork); - VERBOSE_WIRES && logger.log("Attached new network with uid", currentNetwork); - } else { - // Unregister network again - for (let i = 0; i < currentNetwork.wires.length; ++i) { - currentNetwork.wires[i].components.Wire.linkedNetwork = null; - } - - for (let i = 0; i < currentNetwork.tunnels.length; ++i) { - fastArrayDeleteValueIfContained( - currentNetwork.tunnels[i].components.WireTunnel.linkedNetworks, - currentNetwork - ); - } - - for (let i = 0; i < currentNetwork.allSlots.length; ++i) { - currentNetwork.allSlots[i].slot.linkedNetwork = null; - } - } - } - - /** - * Finds surrounding entities which are not yet assigned to a network - * @param {Vector} initialTile - * @param {Array} directions - * @param {WireNetwork} network - * @param {enumWireVariant=} variantMask Only accept connections to this mask - * @returns {Array} - */ - findSurroundingWireTargets(initialTile, directions, network, variantMask = null) { - let result = []; - - VERBOSE_WIRES && - logger.log( - " Searching for new targets at", - initialTile.toString(), - "and d=", - directions, - "with mask=", - variantMask - ); - - // Go over all directions we should search for - for (let i = 0; i < directions.length; ++i) { - const direction = directions[i]; - const offset = enumDirectionToVector[direction]; - const initialSearchTile = initialTile.add(offset); - - // Store which tunnels we already visited to avoid infinite loops - const visitedTunnels = new Set(); - - // First, find the initial connected entities - const initialContents = this.root.map.getLayersContentsMultipleXY( - initialSearchTile.x, - initialSearchTile.y - ); - - // Link the initial tile to the initial entities, since it may change - /** @type {Array<{entity: Entity, tile: Vector}>} */ - const contents = []; - for (let j = 0; j < initialContents.length; ++j) { - contents.push({ - entity: initialContents[j], - tile: initialSearchTile, - }); - } - - for (let k = 0; k < contents.length; ++k) { - const { entity, tile } = contents[k]; - const wireComp = entity.components.Wire; - - // Check for wire - if ( - wireComp && - !wireComp.linkedNetwork && - (!variantMask || wireComp.variant === variantMask) - ) { - // Wires accept connections from everywhere - result.push({ - entity, - }); - } - - // Check for connected slots - const pinComp = entity.components.WiredPins; - if (pinComp) { - const staticComp = entity.components.StaticMapEntity; - - // Go over all slots and see if they are connected - const pinSlots = pinComp.slots; - for (let j = 0; j < pinSlots.length; ++j) { - const slot = pinSlots[j]; - - // Check if the position matches - const pinPos = staticComp.localTileToWorld(slot.pos); - if (!pinPos.equals(tile)) { - continue; - } - - // Check if the direction (inverted) matches - const pinDirection = staticComp.localDirectionToWorld(slot.direction); - if (pinDirection !== enumInvertedDirections[direction]) { - continue; - } - - if (!slot.linkedNetwork) { - result.push({ - entity, - slot, - }); - } - } - - // Pin slots mean it can be nothing else - continue; - } - - // Check if it's a tunnel, if so, go to the forwarded item - const tunnelComp = entity.components.WireTunnel; - if (tunnelComp) { - if (visitedTunnels.has(entity.uid)) { - continue; - } - - const staticComp = entity.components.StaticMapEntity; - - // Compute where this tunnel connects to - const forwardedTile = staticComp.origin.add(offset); - VERBOSE_WIRES && - logger.log( - " Found tunnel", - entity.uid, - "at", - tile, - "-> forwarding to", - forwardedTile - ); - - // Figure out which entities are connected - const connectedContents = this.root.map.getLayersContentsMultipleXY( - forwardedTile.x, - forwardedTile.y - ); - - // Attach the entities and the tile we search at, because it may change - for (let h = 0; h < connectedContents.length; ++h) { - contents.push({ - entity: connectedContents[h], - tile: forwardedTile, - }); - } - - // Add the tunnel to the network - if (tunnelComp.linkedNetworks.indexOf(network) < 0) { - tunnelComp.linkedNetworks.push(network); - } - if (network.tunnels.indexOf(entity) < 0) { - network.tunnels.push(entity); - } - - // Remember this tunnel - visitedTunnels.add(entity.uid); - } - } - } - - VERBOSE_WIRES && logger.log(" -> Found", result.length); - - return result; - } - - /** - * Updates the wires network - */ - update() { - this.staleArea.update(); - - if (this.needsRecompute) { - this.recomputeWiresNetwork(); - } - - // Re-compute values of all networks - for (let i = 0; i < this.networks.length; ++i) { - const network = this.networks[i]; - - // Reset conflicts - network.valueConflict = false; - - // Aggregate values of all senders - const senders = network.providers; - let value = null; - for (let k = 0; k < senders.length; ++k) { - const senderSlot = senders[k]; - const slotValue = senderSlot.slot.value; - - // The first sender can just put in his value - if (!value) { - value = slotValue; - continue; - } - - // If the slot is empty itself, just skip it - if (!slotValue) { - continue; - } - - // If there is already an value, compare if it matches -> - // otherwise there is a conflict - if (value.equals(slotValue)) { - // All good - continue; - } - - // There is a conflict, this means the value will be null anyways - network.valueConflict = true; - break; - } - - // Assign value - if (network.valueConflict) { - network.currentValue = null; - } else { - network.currentValue = value; - } - } - } - - /** - * Returns the given tileset and opacity - * @param {WireComponent} wireComp - * @returns {{ spriteSet: Object, opacity: number}} - */ - getSpriteSetAndOpacityForWire(wireComp) { - if (!wireComp.linkedNetwork) { - // There is no network, it's empty - return { - spriteSet: this.wireSprites[wireComp.variant], - opacity: 0.5, - }; - } - - const network = wireComp.linkedNetwork; - if (network.valueConflict) { - // There is a conflict - return { - spriteSet: this.wireSprites.conflict, - opacity: 1, - }; - } - - return { - spriteSet: this.wireSprites[wireComp.variant], - opacity: isTruthyItem(network.currentValue) ? 1 : 0.5, - }; - } - - /** - * Draws a given chunk - * @param {import("../../core/draw_utils").DrawParameters} parameters - * @param {MapChunkView} chunk - */ - drawChunk(parameters, chunk) { - const contents = chunk.wireContents; - for (let y = 0; y < globalConfig.mapChunkSize; ++y) { - for (let x = 0; x < globalConfig.mapChunkSize; ++x) { - const entity = contents[x][y]; - if (entity && entity.components.Wire) { - const wireComp = entity.components.Wire; - const wireType = wireComp.type; - - const { opacity, spriteSet } = this.getSpriteSetAndOpacityForWire(wireComp); - - const sprite = spriteSet[wireType]; - - assert(sprite, "Unknown wire type: " + wireType); - const staticComp = entity.components.StaticMapEntity; - parameters.context.globalAlpha = opacity; - staticComp.drawSpriteOnBoundsClipped(parameters, sprite, 0); - - // DEBUG Rendering - if (G_IS_DEV && globalConfig.debug.renderWireRotations) { - parameters.context.globalAlpha = 1; - parameters.context.fillStyle = "red"; - parameters.context.font = "5px Tahoma"; - parameters.context.fillText( - "" + staticComp.originalRotation, - staticComp.origin.x * globalConfig.tileSize, - staticComp.origin.y * globalConfig.tileSize + 5 - ); - - parameters.context.fillStyle = "rgba(255, 0, 0, 0.2)"; - if (staticComp.originalRotation % 180 === 0) { - parameters.context.fillRect( - (staticComp.origin.x + 0.5) * globalConfig.tileSize, - staticComp.origin.y * globalConfig.tileSize, - 3, - globalConfig.tileSize - ); - } else { - parameters.context.fillRect( - staticComp.origin.x * globalConfig.tileSize, - (staticComp.origin.y + 0.5) * globalConfig.tileSize, - globalConfig.tileSize, - 3 - ); - } - } - } - - // DEBUG Rendering - if (G_IS_DEV && globalConfig.debug.renderWireNetworkInfos) { - if (entity) { - const staticComp = entity.components.StaticMapEntity; - const wireComp = entity.components.Wire; - - // Draw network info for wires - if (wireComp && wireComp.linkedNetwork) { - parameters.context.fillStyle = "red"; - parameters.context.font = "5px Tahoma"; - parameters.context.fillText( - "W" + wireComp.linkedNetwork.uid, - (staticComp.origin.x + 0.5) * globalConfig.tileSize, - (staticComp.origin.y + 0.5) * globalConfig.tileSize - ); - } - } - } - } - } - - parameters.context.globalAlpha = 1; - } - - /** - * Returns whether this entity is relevant for the wires network - * @param {Entity} entity - */ - isEntityRelevantForWires(entity) { - return entity.components.Wire || entity.components.WiredPins || entity.components.WireTunnel; - } - - /** - * - * @param {Entity} entity - */ - queuePlacementUpdate(entity) { - if (!this.root.gameInitialized) { - return; - } - - if (!this.isEntityRelevantForWires(entity)) { - return; - } - - const staticComp = entity.components.StaticMapEntity; - if (!staticComp) { - return; - } - - this.root.signals.achievementCheck.dispatch(ACHIEVEMENTS.place5000Wires, entity); - - // Invalidate affected area - const originalRect = staticComp.getTileSpaceBounds(); - const affectedArea = originalRect.expandedInAllDirections(1); - this.staleArea.invalidate(affectedArea); - } - - /** - * Updates the wire placement after an entity has been added / deleted - * @param {Rectangle} affectedArea - */ - updateSurroundingWirePlacement(affectedArea) { - const metaWire = gMetaBuildingRegistry.findByClass(MetaWireBuilding); - - for (let x = affectedArea.x; x < affectedArea.right(); ++x) { - for (let y = affectedArea.y; y < affectedArea.bottom(); ++y) { - const targetEntities = this.root.map.getLayersContentsMultipleXY(x, y); - for (let i = 0; i < targetEntities.length; ++i) { - const targetEntity = targetEntities[i]; - - const targetWireComp = targetEntity.components.Wire; - const targetStaticComp = targetEntity.components.StaticMapEntity; - - if (!targetWireComp) { - // Not a wire - continue; - } - - const variant = targetStaticComp.getVariant(); - - const { - rotation, - rotationVariant, - } = metaWire.computeOptimalDirectionAndRotationVariantAtTile({ - root: this.root, - tile: new Vector(x, y), - rotation: targetStaticComp.originalRotation, - variant, - layer: targetEntity.layer, - }); - - // Compute delta to see if anything changed - const newType = arrayWireRotationVariantToType[rotationVariant]; - - if (targetStaticComp.rotation !== rotation || newType !== targetWireComp.type) { - // Change stuff - targetStaticComp.rotation = rotation; - metaWire.updateVariants(targetEntity, rotationVariant, variant); - - // Update code as well - targetStaticComp.code = getCodeFromBuildingData(metaWire, variant, rotationVariant); - - // Make sure the chunks know about the update - this.root.signals.entityChanged.dispatch(targetEntity); - } - } - } - } - } -} +import { globalConfig } from "../../core/config"; +import { gMetaBuildingRegistry } from "../../core/global_registries"; +import { Loader } from "../../core/loader"; +import { createLogger } from "../../core/logging"; +import { Rectangle } from "../../core/rectangle"; +import { AtlasSprite } from "../../core/sprites"; +import { StaleAreaDetector } from "../../core/stale_area_detector"; +import { fastArrayDeleteValueIfContained } from "../../core/utils"; +import { + arrayAllDirections, + enumDirection, + enumDirectionToVector, + enumInvertedDirections, + Vector, +} from "../../core/vector"; +import { BaseItem } from "../base_item"; +import { getCodeFromBuildingData } from "../building_codes"; +import { arrayWireRotationVariantToType, MetaWireBuilding } from "../buildings/wire"; +import { enumWireType, enumWireVariant, WireComponent } from "../components/wire"; +import { WireTunnelComponent } from "../components/wire_tunnel"; +import { enumPinSlotType, WiredPinsComponent } from "../components/wired_pins"; +import { Entity } from "../entity"; +import { GameSystem } from "../game_system"; +import { isTruthyItem } from "../items/boolean_item"; +import { MapChunkView } from "../map_chunk_view"; + +const logger = createLogger("wires"); + +let networkUidCounter = 0; + +const VERBOSE_WIRES = G_IS_DEV && false; + +export class WireNetwork { + constructor() { + /** + * Who contributes to this network + * @type {Array<{ entity: Entity, slot: import("../components/wired_pins").WirePinSlot }>} */ + this.providers = []; + + /** + * Who takes values from this network + * @type {Array<{ entity: Entity, slot: import("../components/wired_pins").WirePinSlot }>} */ + this.receivers = []; + + /** + * All connected slots + * @type {Array<{ entity: Entity, slot: import("../components/wired_pins").WirePinSlot }>} + */ + this.allSlots = []; + + /** + * All connected tunnels + * @type {Array} + */ + this.tunnels = []; + + /** + * Which wires are in this network + * @type {Array} + */ + this.wires = []; + + /** + * The current value of this network + * @type {BaseItem} + */ + this.currentValue = null; + + /** + * Whether this network has a value conflict, that is, more than one + * sender has sent a value + * @type {boolean} + */ + this.valueConflict = false; + + /** + * Unique network identifier + * @type {number} + */ + this.uid = ++networkUidCounter; + } + + /** + * Returns whether this network currently has a value + * @returns {boolean} + */ + hasValue() { + return !!this.currentValue && !this.valueConflict; + } +} + +export class WireSystem extends GameSystem { + constructor(root) { + super(root); + + /** + * @type {Object>} + */ + this.wireSprites = {}; + + const variants = ["conflict", ...Object.keys(enumWireVariant)]; + for (let i = 0; i < variants.length; ++i) { + const wireVariant = variants[i]; + const sprites = {}; + for (const wireType in enumWireType) { + sprites[wireType] = Loader.getSprite( + "sprites/wires/sets/" + wireVariant + "_" + wireType + ".png" + ); + } + this.wireSprites[wireVariant] = sprites; + } + + this.root.signals.entityDestroyed.add(this.queuePlacementUpdate, this); + this.root.signals.entityAdded.add(this.queuePlacementUpdate, this); + + this.root.signals.entityDestroyed.add(this.queueRecomputeIfWire, this); + this.root.signals.entityChanged.add(this.queueRecomputeIfWire, this); + this.root.signals.entityAdded.add(this.queueRecomputeIfWire, this); + + this.needsRecompute = true; + this.isFirstRecompute = true; + + this.staleArea = new StaleAreaDetector({ + root: this.root, + name: "wires", + recomputeMethod: this.updateSurroundingWirePlacement.bind(this), + }); + + /** + * @type {Array} + */ + this.networks = []; + } + + /** + * Invalidates the wires network if the given entity is relevant for it + * @param {Entity} entity + */ + queueRecomputeIfWire(entity) { + if (!this.root.gameInitialized) { + return; + } + + if (this.isEntityRelevantForWires(entity)) { + this.needsRecompute = true; + this.networks = []; + } + } + + /** + * Recomputes the whole wires network + */ + recomputeWiresNetwork() { + this.needsRecompute = false; + logger.log("Recomputing wires network"); + + this.networks = []; + + const wireEntities = this.root.entityMgr.getAllWithComponent(WireComponent); + const tunnelEntities = this.root.entityMgr.getAllWithComponent(WireTunnelComponent); + const pinEntities = this.root.entityMgr.getAllWithComponent(WiredPinsComponent); + + // Clear all network references, but not on the first update since that's the deserializing one + if (!this.isFirstRecompute) { + for (let i = 0; i < wireEntities.length; ++i) { + wireEntities[i].components.Wire.linkedNetwork = null; + } + for (let i = 0; i < tunnelEntities.length; ++i) { + tunnelEntities[i].components.WireTunnel.linkedNetworks = []; + } + + for (let i = 0; i < pinEntities.length; ++i) { + const slots = pinEntities[i].components.WiredPins.slots; + for (let k = 0; k < slots.length; ++k) { + slots[k].linkedNetwork = null; + } + } + } else { + logger.log("Recomputing wires first time"); + this.isFirstRecompute = false; + } + + VERBOSE_WIRES && logger.log("Recomputing slots"); + + // Iterate over all ejector slots + for (let i = 0; i < pinEntities.length; ++i) { + const entity = pinEntities[i]; + const slots = entity.components.WiredPins.slots; + for (let k = 0; k < slots.length; ++k) { + const slot = slots[k]; + + // Ejectors are computed directly, acceptors are just set + if (slot.type === enumPinSlotType.logicalEjector && !slot.linkedNetwork) { + this.findNetworkForEjector(entity, slot); + } + } + } + } + + /** + * Finds the network for the given slot + * @param {Entity} initialEntity + * @param {import("../components/wired_pins").WirePinSlot} slot + */ + findNetworkForEjector(initialEntity, slot) { + let currentNetwork = new WireNetwork(); + VERBOSE_WIRES && + logger.log( + "Finding network for entity", + initialEntity.uid, + initialEntity.components.StaticMapEntity.origin.toString(), + "(nw-id:", + currentNetwork.uid, + ")" + ); + const entitiesToVisit = [ + { + entity: initialEntity, + slot, + }, + ]; + + /** + * Once we occur a wire, we store its variant so we don't connect to + * mismatching ones + * @type {enumWireVariant} + */ + let variantMask = null; + + while (entitiesToVisit.length > 0) { + const nextData = entitiesToVisit.pop(); + const nextEntity = nextData.entity; + + const wireComp = nextEntity.components.Wire; + const staticComp = nextEntity.components.StaticMapEntity; + + VERBOSE_WIRES && logger.log("Visiting", staticComp.origin.toString(), "(", nextEntity.uid, ")"); + + // Where to search for neighbours + let newSearchDirections = []; + let newSearchTile = null; + + //// WIRE + if (wireComp) { + // Sanity check + assert( + !wireComp.linkedNetwork || wireComp.linkedNetwork === currentNetwork, + "Mismatching wire network on wire entity " + + (wireComp.linkedNetwork ? wireComp.linkedNetwork.uid : "") + + " vs " + + currentNetwork.uid + + " @ " + + staticComp.origin.toString() + ); + + if (!wireComp.linkedNetwork) { + if (variantMask && wireComp.variant !== variantMask) { + // Mismatching variant + } else { + // This one is new! :D + VERBOSE_WIRES && logger.log(" Visited new wire:", staticComp.origin.toString()); + wireComp.linkedNetwork = currentNetwork; + currentNetwork.wires.push(nextEntity); + + newSearchDirections = arrayAllDirections; + newSearchTile = nextEntity.components.StaticMapEntity.origin; + variantMask = wireComp.variant; + } + } + } + + //// PINS + const pinsComp = nextEntity.components.WiredPins; + if (pinsComp) { + const slot = nextData.slot; + assert(slot, "No slot set for next entity"); + + if (slot.type === enumPinSlotType.logicalEjector) { + VERBOSE_WIRES && + logger.log(" Visiting ejector slot", staticComp.origin.toString(), "->", slot.type); + } else if (slot.type === enumPinSlotType.logicalAcceptor) { + VERBOSE_WIRES && + logger.log(" Visiting acceptor slot", staticComp.origin.toString(), "->", slot.type); + } else { + assertAlways(false, "Bad slot type: " + slot.type); + } + + // Sanity check + assert( + !slot.linkedNetwork || slot.linkedNetwork === currentNetwork, + "Mismatching wire network on pin slot entity " + + (slot.linkedNetwork ? slot.linkedNetwork.uid : "") + + " vs " + + currentNetwork.uid + ); + if (!slot.linkedNetwork) { + // This one is new + VERBOSE_WIRES && logger.log(" Visited new slot:", staticComp.origin.toString()); + + // Add to the right list + if (slot.type === enumPinSlotType.logicalEjector) { + currentNetwork.providers.push({ entity: nextEntity, slot }); + } else if (slot.type === enumPinSlotType.logicalAcceptor) { + currentNetwork.receivers.push({ entity: nextEntity, slot }); + } else { + assertAlways(false, "unknown slot type:" + slot.type); + } + + // Register on the network + currentNetwork.allSlots.push({ entity: nextEntity, slot }); + slot.linkedNetwork = currentNetwork; + + // Specify where to search next + newSearchDirections = [staticComp.localDirectionToWorld(slot.direction)]; + newSearchTile = staticComp.localTileToWorld(slot.pos); + } + } + + if (newSearchTile) { + // Find new surrounding wire targets + const newTargets = this.findSurroundingWireTargets( + newSearchTile, + newSearchDirections, + currentNetwork, + variantMask + ); + + VERBOSE_WIRES && logger.log(" Found", newTargets, "new targets to visit!"); + for (let i = 0; i < newTargets.length; ++i) { + entitiesToVisit.push(newTargets[i]); + } + } + } + + if ( + currentNetwork.providers.length > 0 && + (currentNetwork.wires.length > 0 || + currentNetwork.receivers.length > 0 || + currentNetwork.tunnels.length > 0) + ) { + this.networks.push(currentNetwork); + VERBOSE_WIRES && logger.log("Attached new network with uid", currentNetwork); + } else { + // Unregister network again + for (let i = 0; i < currentNetwork.wires.length; ++i) { + currentNetwork.wires[i].components.Wire.linkedNetwork = null; + } + + for (let i = 0; i < currentNetwork.tunnels.length; ++i) { + fastArrayDeleteValueIfContained( + currentNetwork.tunnels[i].components.WireTunnel.linkedNetworks, + currentNetwork + ); + } + + for (let i = 0; i < currentNetwork.allSlots.length; ++i) { + currentNetwork.allSlots[i].slot.linkedNetwork = null; + } + } + } + + /** + * Finds surrounding entities which are not yet assigned to a network + * @param {Vector} initialTile + * @param {Array} directions + * @param {WireNetwork} network + * @param {enumWireVariant=} variantMask Only accept connections to this mask + * @returns {Array} + */ + findSurroundingWireTargets(initialTile, directions, network, variantMask = null) { + let result = []; + + VERBOSE_WIRES && + logger.log( + " Searching for new targets at", + initialTile.toString(), + "and d=", + directions, + "with mask=", + variantMask + ); + + // Go over all directions we should search for + for (let i = 0; i < directions.length; ++i) { + const direction = directions[i]; + const offset = enumDirectionToVector[direction]; + const initialSearchTile = initialTile.add(offset); + + // Store which tunnels we already visited to avoid infinite loops + const visitedTunnels = new Set(); + + // First, find the initial connected entities + const initialContents = this.root.map.getLayersContentsMultipleXY( + initialSearchTile.x, + initialSearchTile.y + ); + + // Link the initial tile to the initial entities, since it may change + /** @type {Array<{entity: Entity, tile: Vector}>} */ + const contents = []; + for (let j = 0; j < initialContents.length; ++j) { + contents.push({ + entity: initialContents[j], + tile: initialSearchTile, + }); + } + + for (let k = 0; k < contents.length; ++k) { + const { entity, tile } = contents[k]; + const wireComp = entity.components.Wire; + + // Check for wire + if ( + wireComp && + !wireComp.linkedNetwork && + (!variantMask || wireComp.variant === variantMask) + ) { + // Wires accept connections from everywhere + result.push({ + entity, + }); + } + + // Check for connected slots + const pinComp = entity.components.WiredPins; + if (pinComp) { + const staticComp = entity.components.StaticMapEntity; + + // Go over all slots and see if they are connected + const pinSlots = pinComp.slots; + for (let j = 0; j < pinSlots.length; ++j) { + const slot = pinSlots[j]; + + // Check if the position matches + const pinPos = staticComp.localTileToWorld(slot.pos); + if (!pinPos.equals(tile)) { + continue; + } + + // Check if the direction (inverted) matches + const pinDirection = staticComp.localDirectionToWorld(slot.direction); + if (pinDirection !== enumInvertedDirections[direction]) { + continue; + } + + if (!slot.linkedNetwork) { + result.push({ + entity, + slot, + }); + } + } + + // Pin slots mean it can be nothing else + continue; + } + + // Check if it's a tunnel, if so, go to the forwarded item + const tunnelComp = entity.components.WireTunnel; + if (tunnelComp) { + if (visitedTunnels.has(entity.uid)) { + continue; + } + + const staticComp = entity.components.StaticMapEntity; + + // Compute where this tunnel connects to + const forwardedTile = staticComp.origin.add(offset); + VERBOSE_WIRES && + logger.log( + " Found tunnel", + entity.uid, + "at", + tile, + "-> forwarding to", + forwardedTile + ); + + // Figure out which entities are connected + const connectedContents = this.root.map.getLayersContentsMultipleXY( + forwardedTile.x, + forwardedTile.y + ); + + // Attach the entities and the tile we search at, because it may change + for (let h = 0; h < connectedContents.length; ++h) { + contents.push({ + entity: connectedContents[h], + tile: forwardedTile, + }); + } + + // Add the tunnel to the network + if (tunnelComp.linkedNetworks.indexOf(network) < 0) { + tunnelComp.linkedNetworks.push(network); + } + if (network.tunnels.indexOf(entity) < 0) { + network.tunnels.push(entity); + } + + // Remember this tunnel + visitedTunnels.add(entity.uid); + } + } + } + + VERBOSE_WIRES && logger.log(" -> Found", result.length); + + return result; + } + + /** + * Updates the wires network + */ + update() { + this.staleArea.update(); + + if (this.needsRecompute) { + this.recomputeWiresNetwork(); + } + + // Re-compute values of all networks + for (let i = 0; i < this.networks.length; ++i) { + const network = this.networks[i]; + + // Reset conflicts + network.valueConflict = false; + + // Aggregate values of all senders + const senders = network.providers; + let value = null; + for (let k = 0; k < senders.length; ++k) { + const senderSlot = senders[k]; + const slotValue = senderSlot.slot.value; + + // The first sender can just put in his value + if (!value) { + value = slotValue; + continue; + } + + // If the slot is empty itself, just skip it + if (!slotValue) { + continue; + } + + // If there is already an value, compare if it matches -> + // otherwise there is a conflict + if (value.equals(slotValue)) { + // All good + continue; + } + + // There is a conflict, this means the value will be null anyways + network.valueConflict = true; + break; + } + + // Assign value + if (network.valueConflict) { + network.currentValue = null; + } else { + network.currentValue = value; + } + } + } + + /** + * Returns the given tileset and opacity + * @param {WireComponent} wireComp + * @returns {{ spriteSet: Object, opacity: number}} + */ + getSpriteSetAndOpacityForWire(wireComp) { + if (!wireComp.linkedNetwork) { + // There is no network, it's empty + return { + spriteSet: this.wireSprites[wireComp.variant], + opacity: 0.5, + }; + } + + const network = wireComp.linkedNetwork; + if (network.valueConflict) { + // There is a conflict + return { + spriteSet: this.wireSprites.conflict, + opacity: 1, + }; + } + + return { + spriteSet: this.wireSprites[wireComp.variant], + opacity: isTruthyItem(network.currentValue) ? 1 : 0.5, + }; + } + + /** + * Draws a given chunk + * @param {import("../../core/draw_utils").DrawParameters} parameters + * @param {MapChunkView} chunk + */ + drawChunk(parameters, chunk) { + const contents = chunk.wireContents; + for (let y = 0; y < globalConfig.mapChunkSize; ++y) { + for (let x = 0; x < globalConfig.mapChunkSize; ++x) { + const entity = contents[x][y]; + if (entity && entity.components.Wire) { + const wireComp = entity.components.Wire; + const wireType = wireComp.type; + + const { opacity, spriteSet } = this.getSpriteSetAndOpacityForWire(wireComp); + + const sprite = spriteSet[wireType]; + + assert(sprite, "Unknown wire type: " + wireType); + const staticComp = entity.components.StaticMapEntity; + parameters.context.globalAlpha = opacity; + staticComp.drawSpriteOnBoundsClipped(parameters, sprite, 0, null, true); + + // DEBUG Rendering + if (G_IS_DEV && globalConfig.debug.renderWireRotations) { + parameters.context.globalAlpha = 1; + parameters.context.fillStyle = "red"; + parameters.context.font = "5px Tahoma"; + parameters.context.fillText( + "" + staticComp.originalRotation, + staticComp.origin.x * globalConfig.tileSize, + staticComp.origin.y * globalConfig.tileSize + 5 + ); + + parameters.context.fillStyle = "rgba(255, 0, 0, 0.2)"; + if (staticComp.originalRotation % 180 === 0) { + parameters.context.fillRect( + (staticComp.origin.x + 0.5) * globalConfig.tileSize, + staticComp.origin.y * globalConfig.tileSize, + 3, + globalConfig.tileSize + ); + } else { + parameters.context.fillRect( + staticComp.origin.x * globalConfig.tileSize, + (staticComp.origin.y + 0.5) * globalConfig.tileSize, + globalConfig.tileSize, + 3 + ); + } + } + } + + // DEBUG Rendering + if (G_IS_DEV && globalConfig.debug.renderWireNetworkInfos) { + if (entity) { + const staticComp = entity.components.StaticMapEntity; + const wireComp = entity.components.Wire; + + // Draw network info for wires + if (wireComp && wireComp.linkedNetwork) { + parameters.context.fillStyle = "red"; + parameters.context.font = "5px Tahoma"; + parameters.context.fillText( + "W" + wireComp.linkedNetwork.uid, + (staticComp.origin.x + 0.5) * globalConfig.tileSize, + (staticComp.origin.y + 0.5) * globalConfig.tileSize + ); + } + } + } + } + } + + parameters.context.globalAlpha = 1; + } + + /** + * Returns whether this entity is relevant for the wires network + * @param {Entity} entity + */ + isEntityRelevantForWires(entity) { + return entity.components.Wire || entity.components.WiredPins || entity.components.WireTunnel; + } + + /** + * + * @param {Entity} entity + */ + queuePlacementUpdate(entity) { + if (!this.root.gameInitialized) { + return; + } + + if (!this.isEntityRelevantForWires(entity)) { + return; + } + + const staticComp = entity.components.StaticMapEntity; + if (!staticComp) { + return; + } + + // Invalidate affected area + const originalRect = staticComp.getTileSpaceBounds(); + const affectedArea = originalRect.expandedInAllDirections(1); + this.staleArea.invalidate(affectedArea); + } + + /** + * Updates the wire placement after an entity has been added / deleted + * @param {Rectangle} affectedArea + */ + updateSurroundingWirePlacement(affectedArea) { + const metaWire = gMetaBuildingRegistry.findByClass(MetaWireBuilding); + + for (let x = affectedArea.x; x < affectedArea.right(); ++x) { + for (let y = affectedArea.y; y < affectedArea.bottom(); ++y) { + const targetEntities = this.root.map.getLayersContentsMultipleXY(x, y); + for (let i = 0; i < targetEntities.length; ++i) { + const targetEntity = targetEntities[i]; + + const targetWireComp = targetEntity.components.Wire; + const targetStaticComp = targetEntity.components.StaticMapEntity; + + if (!targetWireComp) { + // Not a wire + continue; + } + + const variant = targetStaticComp.getVariant(); + + const { rotation, rotationVariant } = + metaWire.computeOptimalDirectionAndRotationVariantAtTile({ + root: this.root, + tile: new Vector(x, y), + rotation: targetStaticComp.originalRotation, + variant, + layer: targetEntity.layer, + }); + + // Compute delta to see if anything changed + const newType = arrayWireRotationVariantToType[rotationVariant]; + + if (targetStaticComp.rotation !== rotation || newType !== targetWireComp.type) { + // Change stuff + targetStaticComp.rotation = rotation; + metaWire.updateVariants(targetEntity, rotationVariant, variant); + + // Update code as well + targetStaticComp.code = getCodeFromBuildingData(metaWire, variant, rotationVariant); + + // Make sure the chunks know about the update + this.root.signals.entityChanged.dispatch(targetEntity); + } + } + } + } + } +} diff --git a/src/js/game/theme.js b/src/js/game/theme.js index 251f4433..4950023f 100644 --- a/src/js/game/theme.js +++ b/src/js/game/theme.js @@ -1,6 +1,9 @@ +import dark from "./themes/dark.json"; +import light from "./themes/light.json"; + export const THEMES = { - dark: require("./themes/dark.json"), - light: require("./themes/light.json"), + dark, + light, }; export let THEME = THEMES.light; diff --git a/src/js/game/themes/dark.json b/src/js/game/themes/dark.json index 8650d56f..18431c22 100644 --- a/src/js/game/themes/dark.json +++ b/src/js/game/themes/dark.json @@ -1,75 +1,75 @@ -{ - "map": { - "background": "#3e3f47", - "gridRegular": "rgba(255, 255, 255, 0.02)", - "gridPlacing": "rgba(255, 255, 255, 0.06)", - - "gridLineWidth": 0.5, - - "selectionOverlay": "rgba(74, 163, 223, 0.7)", - "selectionOutline": "rgba(74, 163, 223, 0.5)", - "selectionBackground": "rgba(74, 163, 223, 0.2)", - - "chunkBorders": "rgba(127, 190, 255, 0.04)", - - "tutorialDragText": "rgb(74, 237, 134)", - - "directionLock": { - "regular": { - "color": "rgb(74, 237, 134)", - "background": "rgba(74, 237, 134, 0.2)" - }, - "wires": { - "color": "rgb(74, 237, 134)", - "background": "rgba(74, 237, 134, 0.2)" - }, - "error": { - "color": "rgb(255, 137, 137)", - "background": "rgba(255, 137, 137, 0.2)" - } - }, - - "colorBlindPickerTile": "rgba(255, 255, 255, 0.5)", - - "resources": { - "shape": "#5d5f6a", - "red": "#854f56", - "green": "#667964", - "blue": "#5e7ca4" - }, - "chunkOverview": { - "empty": "#444856", - "filled": "#646b7d", - "beltColor": "#9096a3" - }, - - "wires": { - "overlayColor": "rgba(97, 161, 152, 0.75)", - "previewColor": "rgb(97, 161, 152, 0.5)", - "highlightColor": "rgba(0, 0, 255, 0.5)" - }, - - "connectedMiners": { - "overlay": "rgba(40, 50, 60, 0.5)", - "textColor": "#fff", - "textColorCapped": "#ef5072", - "background": "rgba(40, 50, 60, 0.8)" - }, - - "zone": { - "borderSolid": "rgba(23, 192, 255, 1)", - "outerColor": "rgba(20 , 20, 25, 0.5)" - } - }, - - "items": { - "outline": "#111418", - "outlineWidth": 0.75, - "circleBackground": "rgba(20, 30, 40, 0.3)" - }, - - "shapeTooltip": { - "background": "rgba(242, 245, 254, 0.9)", - "outline": "#44464e" - } -} +{ + "map": { + "background": "#3e3f47", + "gridRegular": "rgba(255, 255, 255, 0.02)", + "gridPlacing": "rgba(255, 255, 255, 0.06)", + + "gridLineWidth": 0.5, + + "selectionOverlay": "rgba(74, 163, 223, 0.7)", + "selectionOutline": "rgba(74, 163, 223, 0.5)", + "selectionBackground": "rgba(74, 163, 223, 0.2)", + + "chunkBorders": "rgba(127, 190, 255, 0.04)", + + "tutorialDragText": "rgb(74, 237, 134)", + + "directionLock": { + "regular": { + "color": "rgb(74, 237, 134)", + "background": "rgba(74, 237, 134, 0.2)" + }, + "wires": { + "color": "rgb(74, 237, 134)", + "background": "rgba(74, 237, 134, 0.2)" + }, + "error": { + "color": "rgb(255, 137, 137)", + "background": "rgba(255, 137, 137, 0.2)" + } + }, + + "colorBlindPickerTile": "rgba(255, 255, 255, 0.5)", + + "resources": { + "shape": "#5d5f6a", + "red": "#854f56", + "green": "#667964", + "blue": "#5e7ca4" + }, + "chunkOverview": { + "empty": "#444856", + "filled": "#646b7d", + "beltColor": "#9096a3" + }, + + "wires": { + "overlayColor": "rgba(97, 161, 152, 0.75)", + "previewColor": "rgb(97, 161, 152, 0.5)", + "highlightColor": "rgba(0, 0, 255, 0.5)" + }, + + "connectedMiners": { + "overlay": "rgba(40, 50, 60, 0.5)", + "textColor": "#fff", + "textColorCapped": "#ef5072", + "background": "rgba(40, 50, 60, 0.8)" + }, + + "zone": { + "borderSolid": "rgba(23, 192, 255, 1)", + "outerColor": "rgba(20 , 20, 25, 0.5)" + } + }, + + "items": { + "outline": "#111418", + "outlineWidth": 0.75, + "circleBackground": "rgba(20, 30, 40, 0.3)" + }, + + "shapeTooltip": { + "background": "rgba(242, 245, 254, 0.9)", + "outline": "#44464e" + } +} diff --git a/src/js/game/themes/light.json b/src/js/game/themes/light.json index cd7677e2..0d2ba270 100644 --- a/src/js/game/themes/light.json +++ b/src/js/game/themes/light.json @@ -1,77 +1,77 @@ -{ - "map": { - "background": "#eceef2", - - "gridRegular": "#e3e7ea", - "gridPlacing": "#dadff0", - - "gridLineWidth": 0.5, - - "selectionOverlay": "rgba(74, 163, 223, 0.7)", - "selectionOutline": "rgba(74, 163, 223, 0.5)", - "selectionBackground": "rgba(74, 163, 223, 0.2)", - - "chunkBorders": "rgba(0, 30, 50, 0.03)", - - "tutorialDragText": "rgb(30, 40, 60)", - - "directionLock": { - "regular": { - "color": "rgb(74, 237, 134)", - "background": "rgba(74, 237, 134, 0.2)" - }, - "wires": { - "color": "rgb(74, 237, 134)", - "background": "rgba(74, 237, 134, 0.2)" - }, - "error": { - "color": "rgb(255, 137, 137)", - "background": "rgba(255, 137, 137, 0.2)" - } - }, - - "colorBlindPickerTile": "rgba(50, 50, 50, 0.4)", - - "resources": { - "shape": "#e0e2e8", - "red": "#f3bcb6", - "green": "#cfefa0", - "blue": "#b2e0fa" - }, - - "chunkOverview": { - "empty": "#a6afbb", - "filled": "#c5ccd6", - "beltColor": "#777" - }, - - "wires": { - "overlayColor": "rgba(97, 161, 152, 0.75)", - "previewColor": "rgb(97, 161, 152, 0.4)", - "highlightColor": "rgba(72, 137, 255, 1)" - }, - - "connectedMiners": { - "overlay": "rgba(40, 50, 60, 0.5)", - "textColor": "#fff", - "textColorCapped": "#ef5072", - "background": "rgba(40, 50, 60, 0.8)" - }, - - "zone": { - "borderSolid": "rgba(23, 192, 255, 1)", - "outerColor": "rgba(240, 240, 255, 0.5)" - } - }, - - "items": { - "outline": "#55575a", - "outlineWidth": 0.75, - "circleBackground": "rgba(40, 50, 65, 0.1)" - }, - - "shapeTooltip": { - "background": "#dee1ea", - "outline": "#54565e" - } -} +{ + "map": { + "background": "#eceef2", + + "gridRegular": "#e3e7ea", + "gridPlacing": "#dadff0", + + "gridLineWidth": 0.5, + + "selectionOverlay": "rgba(74, 163, 223, 0.7)", + "selectionOutline": "rgba(74, 163, 223, 0.5)", + "selectionBackground": "rgba(74, 163, 223, 0.2)", + + "chunkBorders": "rgba(0, 30, 50, 0.03)", + + "tutorialDragText": "rgb(30, 40, 60)", + + "directionLock": { + "regular": { + "color": "rgb(74, 237, 134)", + "background": "rgba(74, 237, 134, 0.2)" + }, + "wires": { + "color": "rgb(74, 237, 134)", + "background": "rgba(74, 237, 134, 0.2)" + }, + "error": { + "color": "rgb(255, 137, 137)", + "background": "rgba(255, 137, 137, 0.2)" + } + }, + + "colorBlindPickerTile": "rgba(50, 50, 50, 0.4)", + + "resources": { + "shape": "#e0e2e8", + "red": "#f3bcb6", + "green": "#cfefa0", + "blue": "#b2e0fa" + }, + + "chunkOverview": { + "empty": "#a6afbb", + "filled": "#c5ccd6", + "beltColor": "#777" + }, + + "wires": { + "overlayColor": "rgba(97, 161, 152, 0.75)", + "previewColor": "rgb(97, 161, 152, 0.4)", + "highlightColor": "rgba(72, 137, 255, 1)" + }, + + "connectedMiners": { + "overlay": "rgba(40, 50, 60, 0.5)", + "textColor": "#fff", + "textColorCapped": "#ef5072", + "background": "rgba(40, 50, 60, 0.8)" + }, + + "zone": { + "borderSolid": "rgba(23, 192, 255, 1)", + "outerColor": "rgba(240, 240, 255, 0.5)" + } + }, + + "items": { + "outline": "#55575a", + "outlineWidth": 0.75, + "circleBackground": "rgba(40, 50, 65, 0.1)" + }, + + "shapeTooltip": { + "background": "#dee1ea", + "outline": "#54565e" + } +} diff --git a/src/js/game/time/base_game_speed.js b/src/js/game/time/base_game_speed.js index 8898b6cf..926ebc11 100644 --- a/src/js/game/time/base_game_speed.js +++ b/src/js/game/time/base_game_speed.js @@ -21,8 +21,7 @@ export class BaseGameSpeed extends BasicSerializableObject { } getId() { - // @ts-ignore - return this.constructor.getId(); + return /** @type {typeof BaseGameSpeed} */ (this.constructor).getId(); } static getSchema() { diff --git a/src/js/game/time/game_time.js b/src/js/game/time/game_time.js index 07b224a7..ff196984 100644 --- a/src/js/game/time/game_time.js +++ b/src/js/game/time/game_time.js @@ -1,200 +1,203 @@ -/* typehints:start */ -import { GameRoot } from "../root"; -/* typehints:end */ - -import { types, BasicSerializableObject } from "../../savegame/serialization"; -import { RegularGameSpeed } from "./regular_game_speed"; -import { BaseGameSpeed } from "./base_game_speed"; -import { PausedGameSpeed } from "./paused_game_speed"; -import { gGameSpeedRegistry } from "../../core/global_registries"; -import { globalConfig } from "../../core/config"; -import { createLogger } from "../../core/logging"; - -const logger = createLogger("game_time"); - -export class GameTime extends BasicSerializableObject { - /** - * @param {GameRoot} root - */ - constructor(root) { - super(); - this.root = root; - - // Current ingame time seconds, not incremented while paused - this.timeSeconds = 0; - - // Current "realtime", a timer which always is incremented no matter whether the game is paused or no - this.realtimeSeconds = 0; - - // The adjustment, used when loading savegames so we can continue where we were - this.realtimeAdjust = 0; - - /** @type {BaseGameSpeed} */ - this.speed = new RegularGameSpeed(this.root); - - // Store how much time we have in bucket - this.logicTimeBudget = 0; - } - - static getId() { - return "GameTime"; - } - - static getSchema() { - return { - timeSeconds: types.float, - speed: types.obj(gGameSpeedRegistry), - realtimeSeconds: types.float, - }; - } - - /** - * Fetches the new "real" time, called from the core once per frame, since performance now() is kinda slow - */ - updateRealtimeNow() { - this.realtimeSeconds = performance.now() / 1000.0 + this.realtimeAdjust; - } - - /** - * Returns the ingame time in milliseconds - */ - getTimeMs() { - return this.timeSeconds * 1000.0; - } - - /** - * Returns how many seconds we are in the grace period - * @returns {number} - */ - getRemainingGracePeriodSeconds() { - return 0; - } - - /** - * Returns if we are currently in the grace period - * @returns {boolean} - */ - getIsWithinGracePeriod() { - return this.getRemainingGracePeriodSeconds() > 0; - } - - /** - * Internal method to generate new logic time budget - * @param {number} deltaMs - */ - internalAddDeltaToBudget(deltaMs) { - // Only update if game is supposed to update - if (this.root.hud.shouldPauseGame()) { - this.logicTimeBudget = 0; - } else { - const multiplier = this.getSpeed().getTimeMultiplier(); - this.logicTimeBudget += deltaMs * multiplier; - } - - // Check for too big pile of updates -> reduce it to 1 - let maxLogicSteps = Math.max( - 3, - (this.speed.getMaxLogicStepsInQueue() * this.root.dynamicTickrate.currentTickRate) / 60 - ); - if (G_IS_DEV && globalConfig.debug.framePausesBetweenTicks) { - maxLogicSteps *= 1 + globalConfig.debug.framePausesBetweenTicks; - } - - if (this.logicTimeBudget > this.root.dynamicTickrate.deltaMs * maxLogicSteps) { - this.logicTimeBudget = this.root.dynamicTickrate.deltaMs * maxLogicSteps; - } - } - - /** - * Performs update ticks based on the queued logic budget - * @param {number} deltaMs - * @param {function():boolean} updateMethod - */ - performTicks(deltaMs, updateMethod) { - this.internalAddDeltaToBudget(deltaMs); - - const speedAtStart = this.root.time.getSpeed(); - - let effectiveDelta = this.root.dynamicTickrate.deltaMs; - if (G_IS_DEV && globalConfig.debug.framePausesBetweenTicks) { - effectiveDelta += globalConfig.debug.framePausesBetweenTicks * this.root.dynamicTickrate.deltaMs; - } - - // Update physics & logic - while (this.logicTimeBudget >= effectiveDelta) { - this.logicTimeBudget -= effectiveDelta; - - if (!updateMethod()) { - // Gameover happened or so, do not update anymore - return; - } - - // Step game time - this.timeSeconds += this.root.dynamicTickrate.deltaSeconds; - - // Game time speed changed, need to abort since our logic steps are no longer valid - if (speedAtStart.getId() !== this.speed.getId()) { - logger.warn( - "Skipping update because speed changed from", - speedAtStart.getId(), - "to", - this.speed.getId() - ); - break; - } - } - } - - /** - * Returns ingame time in seconds - * @returns {number} seconds - */ - now() { - return this.timeSeconds; - } - - /** - * Returns "real" time in seconds - * @returns {number} seconds - */ - realtimeNow() { - return this.realtimeSeconds; - } - - /** - * Returns "real" time in seconds - * @returns {number} seconds - */ - systemNow() { - return (this.realtimeSeconds - this.realtimeAdjust) * 1000.0; - } - - getIsPaused() { - return this.speed.getId() === PausedGameSpeed.getId(); - } - - getSpeed() { - return this.speed; - } - - setSpeed(speed) { - assert(speed instanceof BaseGameSpeed, "Not a valid game speed"); - if (this.speed.getId() === speed.getId()) { - logger.warn("Same speed set than current one:", speed.constructor.getId()); - } - this.speed = speed; - } - - deserialize(data) { - const errorCode = super.deserialize(data); - if (errorCode) { - return errorCode; - } - - // Adjust realtime now difference so they match - this.realtimeAdjust = this.realtimeSeconds - performance.now() / 1000.0; - this.updateRealtimeNow(); - - this.speed.initializeAfterDeserialize(this.root); - } -} +/* typehints:start */ +import { GameRoot } from "../root"; +/* typehints:end */ + +import { types, BasicSerializableObject } from "../../savegame/serialization"; +import { RegularGameSpeed } from "./regular_game_speed"; +import { BaseGameSpeed } from "./base_game_speed"; +import { PausedGameSpeed } from "./paused_game_speed"; +import { gGameSpeedRegistry } from "../../core/global_registries"; +import { globalConfig } from "../../core/config"; +import { createLogger } from "../../core/logging"; + +const logger = createLogger("game_time"); + +export class GameTime extends BasicSerializableObject { + /** + * @param {GameRoot} root + */ + constructor(root) { + super(); + this.root = root; + + // Current ingame time seconds, not incremented while paused + this.timeSeconds = 0; + + // Current "realtime", a timer which always is incremented no matter whether the game is paused or no + this.realtimeSeconds = 0; + + // The adjustment, used when loading savegames so we can continue where we were + this.realtimeAdjust = 0; + + /** @type {BaseGameSpeed} */ + this.speed = new RegularGameSpeed(this.root); + + // Store how much time we have in bucket + this.logicTimeBudget = 0; + } + + static getId() { + return "GameTime"; + } + + static getSchema() { + return { + timeSeconds: types.float, + speed: types.obj(gGameSpeedRegistry), + realtimeSeconds: types.float, + }; + } + + /** + * Fetches the new "real" time, called from the core once per frame, since performance now() is kinda slow + */ + updateRealtimeNow() { + this.realtimeSeconds = performance.now() / 1000.0 + this.realtimeAdjust; + } + + /** + * Returns the ingame time in milliseconds + */ + getTimeMs() { + return this.timeSeconds * 1000.0; + } + + /** + * Returns how many seconds we are in the grace period + * @returns {number} + */ + getRemainingGracePeriodSeconds() { + return 0; + } + + /** + * Returns if we are currently in the grace period + * @returns {boolean} + */ + getIsWithinGracePeriod() { + return this.getRemainingGracePeriodSeconds() > 0; + } + + /** + * Internal method to generate new logic time budget + * @param {number} deltaMs + */ + internalAddDeltaToBudget(deltaMs) { + // Only update if game is supposed to update + if (this.root.hud.shouldPauseGame()) { + this.logicTimeBudget = 0; + } else { + const multiplier = this.getSpeed().getTimeMultiplier(); + this.logicTimeBudget += deltaMs * multiplier; + } + + // Check for too big pile of updates -> reduce it to 1 + let maxLogicSteps = Math.max( + 3, + (this.speed.getMaxLogicStepsInQueue() * this.root.dynamicTickrate.currentTickRate) / 60 + ); + if (G_IS_DEV && globalConfig.debug.framePausesBetweenTicks) { + maxLogicSteps *= 1 + globalConfig.debug.framePausesBetweenTicks; + } + + if (this.logicTimeBudget > this.root.dynamicTickrate.deltaMs * maxLogicSteps) { + this.logicTimeBudget = this.root.dynamicTickrate.deltaMs * maxLogicSteps; + } + } + + /** + * Performs update ticks based on the queued logic budget + * @param {number} deltaMs + * @param {function():boolean} updateMethod + */ + performTicks(deltaMs, updateMethod) { + this.internalAddDeltaToBudget(deltaMs); + + const speedAtStart = this.root.time.getSpeed(); + + let effectiveDelta = this.root.dynamicTickrate.deltaMs; + if (G_IS_DEV && globalConfig.debug.framePausesBetweenTicks) { + effectiveDelta += globalConfig.debug.framePausesBetweenTicks * this.root.dynamicTickrate.deltaMs; + } + + // Update physics & logic + while (this.logicTimeBudget >= effectiveDelta) { + this.logicTimeBudget -= effectiveDelta; + + if (!updateMethod()) { + // Gameover happened or so, do not update anymore + return; + } + + // Step game time + this.timeSeconds += this.root.dynamicTickrate.deltaSeconds; + + // Game time speed changed, need to abort since our logic steps are no longer valid + if (speedAtStart.getId() !== this.speed.getId()) { + logger.warn( + "Skipping update because speed changed from", + speedAtStart.getId(), + "to", + this.speed.getId() + ); + break; + } + } + } + + /** + * Returns ingame time in seconds + * @returns {number} seconds + */ + now() { + return this.timeSeconds; + } + + /** + * Returns "real" time in seconds + * @returns {number} seconds + */ + realtimeNow() { + return this.realtimeSeconds; + } + + /** + * Returns "real" time in seconds + * @returns {number} seconds + */ + systemNow() { + return (this.realtimeSeconds - this.realtimeAdjust) * 1000.0; + } + + getIsPaused() { + return this.speed.getId() === PausedGameSpeed.getId(); + } + + getSpeed() { + return this.speed; + } + + setSpeed(speed) { + assert(speed instanceof BaseGameSpeed, "Not a valid game speed"); + if (this.speed.getId() === speed.getId()) { + logger.warn( + "Same speed set than current one:", + /** @type {typeof BaseGameSpeed} */ (speed.constructor).getId() + ); + } + this.speed = speed; + } + + deserialize(data) { + const errorCode = super.deserialize(data); + if (errorCode) { + return errorCode; + } + + // Adjust realtime now difference so they match + this.realtimeAdjust = this.realtimeSeconds - performance.now() / 1000.0; + this.updateRealtimeNow(); + + this.speed.initializeAfterDeserialize(this.root); + } +} diff --git a/src/js/game/tutorial_goals.js b/src/js/game/tutorial_goals.js index 84634b0a..8249e8dc 100644 --- a/src/js/game/tutorial_goals.js +++ b/src/js/game/tutorial_goals.js @@ -1,38 +1,38 @@ -/** - * Don't forget to also update tutorial_goals_mappings.js as well as the translations! - * @enum {string} - */ -export const enumHubGoalRewards = { - reward_cutter_and_trash: "reward_cutter_and_trash", - reward_rotater: "reward_rotater", - reward_painter: "reward_painter", - reward_mixer: "reward_mixer", - reward_stacker: "reward_stacker", - reward_balancer: "reward_balancer", - reward_tunnel: "reward_tunnel", - - reward_rotater_ccw: "reward_rotater_ccw", - reward_rotater_180: "reward_rotater_180", - reward_miner_chainable: "reward_miner_chainable", - reward_underground_belt_tier_2: "reward_underground_belt_tier_2", - reward_belt_reader: "reward_belt_reader", - reward_splitter: "reward_splitter", - reward_cutter_quad: "reward_cutter_quad", - reward_painter_double: "reward_painter_double", - reward_storage: "reward_storage", - reward_merger: "reward_merger", - reward_wires_painter_and_levers: "reward_wires_painter_and_levers", - reward_display: "reward_display", - reward_constant_signal: "reward_constant_signal", - reward_logic_gates: "reward_logic_gates", - reward_virtual_processing: "reward_virtual_processing", - reward_filter: "reward_filter", - - reward_demo_end: "reward_demo_end", - - reward_blueprints: "reward_blueprints", - reward_freeplay: "reward_freeplay", - - no_reward: "no_reward", - no_reward_freeplay: "no_reward_freeplay", -}; +/** + * Don't forget to also update tutorial_goals_mappings.js as well as the translations! + * @enum {string} + */ +export const enumHubGoalRewards = { + reward_cutter_and_trash: "reward_cutter_and_trash", + reward_rotator: "reward_rotator", + reward_painter: "reward_painter", + reward_mixer: "reward_mixer", + reward_stacker: "reward_stacker", + reward_balancer: "reward_balancer", + reward_tunnel: "reward_tunnel", + + reward_rotator_ccw: "reward_rotator_ccw", + reward_rotator_180: "reward_rotator_180", + reward_miner_chainable: "reward_miner_chainable", + reward_underground_belt_tier_2: "reward_underground_belt_tier_2", + reward_belt_reader: "reward_belt_reader", + reward_splitter: "reward_splitter", + reward_cutter_quad: "reward_cutter_quad", + reward_painter_double: "reward_painter_double", + reward_storage: "reward_storage", + reward_merger: "reward_merger", + reward_wires_painter_and_levers: "reward_wires_painter_and_levers", + reward_display: "reward_display", + reward_constant_signal: "reward_constant_signal", + reward_logic_gates: "reward_logic_gates", + reward_virtual_processing: "reward_virtual_processing", + reward_filter: "reward_filter", + + reward_demo_end: "reward_demo_end", + + reward_blueprints: "reward_blueprints", + reward_freeplay: "reward_freeplay", + + no_reward: "no_reward", + no_reward_freeplay: "no_reward_freeplay", +}; diff --git a/src/js/game/tutorial_goals_mappings.js b/src/js/game/tutorial_goals_mappings.js index e0d17cbc..36f28996 100644 --- a/src/js/game/tutorial_goals_mappings.js +++ b/src/js/game/tutorial_goals_mappings.js @@ -1,87 +1,87 @@ -import { T } from "../translations"; -import { enumBalancerVariants, MetaBalancerBuilding } from "./buildings/balancer"; -import { MetaConstantSignalBuilding } from "./buildings/constant_signal"; -import { enumCutterVariants, MetaCutterBuilding } from "./buildings/cutter"; -import { MetaDisplayBuilding } from "./buildings/display"; -import { MetaFilterBuilding } from "./buildings/filter"; -import { MetaLogicGateBuilding } from "./buildings/logic_gate"; -import { enumMinerVariants, MetaMinerBuilding } from "./buildings/miner"; -import { MetaMixerBuilding } from "./buildings/mixer"; -import { enumPainterVariants, MetaPainterBuilding } from "./buildings/painter"; -import { MetaReaderBuilding } from "./buildings/reader"; -import { enumRotaterVariants, MetaRotaterBuilding } from "./buildings/rotater"; -import { MetaStackerBuilding } from "./buildings/stacker"; -import { MetaStorageBuilding } from "./buildings/storage"; -import { enumUndergroundBeltVariants, MetaUndergroundBeltBuilding } from "./buildings/underground_belt"; -import { defaultBuildingVariant, MetaBuilding } from "./meta_building"; -/** @typedef {Array<[typeof MetaBuilding, string]>} TutorialGoalReward */ -import { enumHubGoalRewards } from "./tutorial_goals"; - -/** - * Helper method for proper types - * @returns {TutorialGoalReward} - */ -const typed = x => x; - -/** - * Stores which reward unlocks what - * @enum {TutorialGoalReward?} - */ -export const enumHubGoalRewardsToContentUnlocked = { - [enumHubGoalRewards.reward_cutter_and_trash]: typed([[MetaCutterBuilding, defaultBuildingVariant]]), - [enumHubGoalRewards.reward_rotater]: typed([[MetaRotaterBuilding, defaultBuildingVariant]]), - [enumHubGoalRewards.reward_painter]: typed([[MetaPainterBuilding, defaultBuildingVariant]]), - [enumHubGoalRewards.reward_mixer]: typed([[MetaMixerBuilding, defaultBuildingVariant]]), - [enumHubGoalRewards.reward_stacker]: typed([[MetaStackerBuilding, defaultBuildingVariant]]), - [enumHubGoalRewards.reward_balancer]: typed([[MetaBalancerBuilding, defaultBuildingVariant]]), - [enumHubGoalRewards.reward_tunnel]: typed([[MetaUndergroundBeltBuilding, defaultBuildingVariant]]), - - [enumHubGoalRewards.reward_rotater_ccw]: typed([[MetaRotaterBuilding, enumRotaterVariants.ccw]]), - [enumHubGoalRewards.reward_rotater_180]: typed([[MetaRotaterBuilding, enumRotaterVariants.rotate180]]), - [enumHubGoalRewards.reward_miner_chainable]: typed([[MetaMinerBuilding, enumMinerVariants.chainable]]), - [enumHubGoalRewards.reward_underground_belt_tier_2]: typed([ - [MetaUndergroundBeltBuilding, enumUndergroundBeltVariants.tier2], - ]), - [enumHubGoalRewards.reward_splitter]: typed([[MetaBalancerBuilding, enumBalancerVariants.splitter]]), - [enumHubGoalRewards.reward_merger]: typed([[MetaBalancerBuilding, enumBalancerVariants.merger]]), - [enumHubGoalRewards.reward_cutter_quad]: typed([[MetaCutterBuilding, enumCutterVariants.quad]]), - [enumHubGoalRewards.reward_painter_double]: typed([[MetaPainterBuilding, enumPainterVariants.double]]), - [enumHubGoalRewards.reward_storage]: typed([[MetaStorageBuilding, defaultBuildingVariant]]), - - [enumHubGoalRewards.reward_belt_reader]: typed([[MetaReaderBuilding, defaultBuildingVariant]]), - [enumHubGoalRewards.reward_display]: typed([[MetaDisplayBuilding, defaultBuildingVariant]]), - [enumHubGoalRewards.reward_constant_signal]: typed([ - [MetaConstantSignalBuilding, defaultBuildingVariant], - ]), - [enumHubGoalRewards.reward_logic_gates]: typed([[MetaLogicGateBuilding, defaultBuildingVariant]]), - [enumHubGoalRewards.reward_filter]: typed([[MetaFilterBuilding, defaultBuildingVariant]]), - [enumHubGoalRewards.reward_virtual_processing]: null, - - [enumHubGoalRewards.reward_wires_painter_and_levers]: typed([ - [MetaPainterBuilding, enumPainterVariants.quad], - ]), - [enumHubGoalRewards.reward_freeplay]: null, - [enumHubGoalRewards.reward_blueprints]: null, - [enumHubGoalRewards.no_reward]: null, - [enumHubGoalRewards.no_reward_freeplay]: null, - [enumHubGoalRewards.reward_demo_end]: null, -}; - -if (G_IS_DEV) { - // Sanity check - for (const rewardId in enumHubGoalRewards) { - const mapping = enumHubGoalRewardsToContentUnlocked[rewardId]; - - if (typeof mapping === "undefined") { - assertAlways( - false, - "Please define a mapping for the reward " + rewardId + " in tutorial_goals_mappings.js" - ); - } - - const translation = T.storyRewards[rewardId]; - if (!translation || !translation.title || !translation.desc) { - assertAlways(false, "Translation for reward " + rewardId + "missing"); - } - } -} +import { T } from "../translations"; +import { enumBalancerVariants, MetaBalancerBuilding } from "./buildings/balancer"; +import { MetaConstantSignalBuilding } from "./buildings/constant_signal"; +import { enumCutterVariants, MetaCutterBuilding } from "./buildings/cutter"; +import { MetaDisplayBuilding } from "./buildings/display"; +import { MetaFilterBuilding } from "./buildings/filter"; +import { MetaLogicGateBuilding } from "./buildings/logic_gate"; +import { enumMinerVariants, MetaMinerBuilding } from "./buildings/miner"; +import { MetaMixerBuilding } from "./buildings/mixer"; +import { enumPainterVariants, MetaPainterBuilding } from "./buildings/painter"; +import { MetaReaderBuilding } from "./buildings/reader"; +import { enumRotatorVariants, MetaRotatorBuilding } from "./buildings/rotator"; +import { MetaStackerBuilding } from "./buildings/stacker"; +import { MetaStorageBuilding } from "./buildings/storage"; +import { enumUndergroundBeltVariants, MetaUndergroundBeltBuilding } from "./buildings/underground_belt"; +import { defaultBuildingVariant, MetaBuilding } from "./meta_building"; +/** @typedef {Array<[typeof MetaBuilding, string]>} TutorialGoalReward */ +import { enumHubGoalRewards } from "./tutorial_goals"; + +/** + * Helper method for proper types + * @returns {TutorialGoalReward} + */ +const typed = x => x; + +/** + * Stores which reward unlocks what + * @enum {TutorialGoalReward?} + */ +export const enumHubGoalRewardsToContentUnlocked = { + [enumHubGoalRewards.reward_cutter_and_trash]: typed([[MetaCutterBuilding, defaultBuildingVariant]]), + [enumHubGoalRewards.reward_rotator]: typed([[MetaRotatorBuilding, defaultBuildingVariant]]), + [enumHubGoalRewards.reward_painter]: typed([[MetaPainterBuilding, defaultBuildingVariant]]), + [enumHubGoalRewards.reward_mixer]: typed([[MetaMixerBuilding, defaultBuildingVariant]]), + [enumHubGoalRewards.reward_stacker]: typed([[MetaStackerBuilding, defaultBuildingVariant]]), + [enumHubGoalRewards.reward_balancer]: typed([[MetaBalancerBuilding, defaultBuildingVariant]]), + [enumHubGoalRewards.reward_tunnel]: typed([[MetaUndergroundBeltBuilding, defaultBuildingVariant]]), + + [enumHubGoalRewards.reward_rotator_ccw]: typed([[MetaRotatorBuilding, enumRotatorVariants.ccw]]), + [enumHubGoalRewards.reward_rotator_180]: typed([[MetaRotatorBuilding, enumRotatorVariants.rotate180]]), + [enumHubGoalRewards.reward_miner_chainable]: typed([[MetaMinerBuilding, enumMinerVariants.chainable]]), + [enumHubGoalRewards.reward_underground_belt_tier_2]: typed([ + [MetaUndergroundBeltBuilding, enumUndergroundBeltVariants.tier2], + ]), + [enumHubGoalRewards.reward_splitter]: typed([[MetaBalancerBuilding, enumBalancerVariants.splitter]]), + [enumHubGoalRewards.reward_merger]: typed([[MetaBalancerBuilding, enumBalancerVariants.merger]]), + [enumHubGoalRewards.reward_cutter_quad]: typed([[MetaCutterBuilding, enumCutterVariants.quad]]), + [enumHubGoalRewards.reward_painter_double]: typed([[MetaPainterBuilding, enumPainterVariants.double]]), + [enumHubGoalRewards.reward_storage]: typed([[MetaStorageBuilding, defaultBuildingVariant]]), + + [enumHubGoalRewards.reward_belt_reader]: typed([[MetaReaderBuilding, defaultBuildingVariant]]), + [enumHubGoalRewards.reward_display]: typed([[MetaDisplayBuilding, defaultBuildingVariant]]), + [enumHubGoalRewards.reward_constant_signal]: typed([ + [MetaConstantSignalBuilding, defaultBuildingVariant], + ]), + [enumHubGoalRewards.reward_logic_gates]: typed([[MetaLogicGateBuilding, defaultBuildingVariant]]), + [enumHubGoalRewards.reward_filter]: typed([[MetaFilterBuilding, defaultBuildingVariant]]), + [enumHubGoalRewards.reward_virtual_processing]: null, + + [enumHubGoalRewards.reward_wires_painter_and_levers]: typed([ + [MetaPainterBuilding, enumPainterVariants.quad], + ]), + [enumHubGoalRewards.reward_freeplay]: null, + [enumHubGoalRewards.reward_blueprints]: null, + [enumHubGoalRewards.no_reward]: null, + [enumHubGoalRewards.no_reward_freeplay]: null, + [enumHubGoalRewards.reward_demo_end]: null, +}; + +if (G_IS_DEV) { + // Sanity check + for (const rewardId in enumHubGoalRewards) { + const mapping = enumHubGoalRewardsToContentUnlocked[rewardId]; + + if (typeof mapping === "undefined") { + assertAlways( + false, + "Please define a mapping for the reward " + rewardId + " in tutorial_goals_mappings.js" + ); + } + + const translation = T.storyRewards[rewardId]; + if (!translation || !translation.title || !translation.desc) { + assertAlways(false, "Translation for reward " + rewardId + "missing"); + } + } +} diff --git a/src/js/globals.d.ts b/src/js/globals.d.ts index 37eb4c3f..6a4b1292 100644 --- a/src/js/globals.d.ts +++ b/src/js/globals.d.ts @@ -1,43 +1,30 @@ +/// + // Globals defined by webpack declare const G_IS_DEV: boolean; -declare function assert(condition: boolean | object | string, ...errorMessage: string[]): void; -declare function assertAlways(condition: boolean | object | string, ...errorMessage: string[]): void; +declare function assert(condition: boolean | object | string, ...errorMessage: string[]): asserts condition; +declare function assertAlways( + condition: boolean | object | string, + ...errorMessage: string[] +): asserts condition; declare const abstract: void; declare const G_APP_ENVIRONMENT: string; -declare const G_HAVE_ASSERT: boolean; declare const G_BUILD_TIME: number; -declare const G_IS_STANDALONE: boolean; -declare const G_IS_STEAM_DEMO: boolean; -declare const G_IS_BROWSER: boolean; declare const G_BUILD_COMMIT_HASH: string; declare const G_BUILD_VERSION: string; declare const G_ALL_UI_IMAGES: Array; declare const G_IS_RELEASE: boolean; -declare const G_CHINA_VERSION: boolean; -declare const G_WEGAME_VERSION: boolean; -declare const G_GOG_VERSION: boolean; - declare const shapez: any; declare const ipcRenderer: any; -// Polyfills -declare interface String { - replaceAll(search: string, replacement: string): string; -} - declare interface CanvasRenderingContext2D { - beginRoundedRect(x: number, y: number, w: number, h: number, r: number): void; beginCircle(x: number, y: number, r: number): void; - - msImageSmoothingEnabled: boolean; - mozImageSmoothingEnabled: boolean; - webkitImageSmoothingEnabled: boolean; } // Just for compatibility with the shared code @@ -48,91 +35,16 @@ declare interface Logger { error(...args); } -// Cordova -declare interface Device { - uuid: string; - platform: string; - available: boolean; - version: string; - cordova: string; - model: string; - manufacturer: string; - isVirtual: boolean; - serial: string; -} - -declare interface MobileAccessibility { - usePreferredTextZoom(boolean); -} - declare interface Window { - // Cordova - device: Device; - StatusBar: any; - AndroidFullScreen: any; - AndroidNotch: any; - plugins: any; - - // Adinplay - aiptag: any; - adPlayer: any; - aipPlayer: any; - MobileAccessibility: MobileAccessibility; - LocalFileSystem: any; - // Debugging activeClickDetectors: Array; - // Experimental/Newer apis - FontFace: any; - TouchEvent: undefined | TouchEvent; - - // Thirdparty - XPayStationWidget: any; - Sentry: any; - LogRocket: any; - grecaptcha: any; - gtag: any; - cpmstarAPI: any; - CrazyGames: any; - // Mods - $shapez_registerMod: any; - anyModLoaded: any; - shapez: any; APP_ERROR_OCCURED?: boolean; - webkitRequestAnimationFrame(); - assert(condition: boolean, failureMessage: string); - - coreThreadLoadedCb(); -} - -declare interface Navigator { - app: any; - device: any; - splashscreen: any; -} - -// FontFace -declare interface Document { - fonts: any; -} - -// Webpack -declare interface WebpackContext { - keys(): Array; -} - -declare interface NodeRequire { - context(src: string, flag: boolean, regexp: RegExp): WebpackContext; -} - -declare interface Object { - entries(obj: object): Array<[string, any]>; } declare interface Math { @@ -142,46 +54,6 @@ declare interface Math { declare type Class = new (...args: any[]) => T; -declare interface String { - padStart(size: number, fill?: string): string; - padEnd(size: number, fill: string): string; -} - -declare interface FactoryTemplate { - entries: Array>; - entryIds: Array; - idToEntry: any; - - getId(): string; - getAllIds(): Array; - register(entry: Class): void; - hasId(id: string): boolean; - findById(id: string): Class; - getEntries(): Array>; - getNumEntries(): number; -} - -declare interface SingletonFactoryTemplate { - entries: Array; - idToEntry: any; - - getId(): string; - getAllIds(): Array; - register(classHandle: Class): void; - hasId(id: string): boolean; - findById(id: string): T; - findByClass(classHandle: Class): T; - getEntries(): Array; - getNumEntries(): number; -} - -declare interface SignalTemplate0 { - add(receiver: () => string | void, scope: null | any); - dispatch(): string | void; - remove(receiver: () => string | void); - removeAll(); -} - declare class TypedTrackedState { constructor(callbackMethod?: (value: T) => void, callbackScope?: any); @@ -191,18 +63,6 @@ declare class TypedTrackedState { get(): T; } -declare const STOP_PROPAGATION = "stop_propagation"; - -declare interface TypedSignal> { - add(receiver: (...args: T) => /* STOP_PROPAGATION */ string | void, scope?: object); - addToTop(receiver: (...args: T) => /* STOP_PROPAGATION */ string | void, scope?: object); - remove(receiver: (...args: T) => /* STOP_PROPAGATION */ string | void); - - dispatch(...args: T): /* STOP_PROPAGATION */ string | void; - - removeAll(); -} - declare type Layer = "regular" | "wires"; declare type ItemType = "shape" | "color" | "boolean"; @@ -213,3 +73,48 @@ declare module "worker-loader?inline=true&fallback=false!*" { export default WebpackWorker; } + +// JSX type support - https://www.typescriptlang.org/docs/handbook/jsx.html +// modified from https://stackoverflow.com/a/68238924 +declare namespace JSX { + /** + * The return type of a JSX expression. + * + * In reality, Fragments can return arbitrary values, but we ignore this for convenience. + */ + type Element = HTMLElement; + /** + * Key-value list of intrinsic element names and their allowed properties. + * + * Because children are treated as a property, the Node type cannot be excluded from the index signature. + */ + type IntrinsicElements = { + [K in keyof HTMLElementTagNameMap]: { + children?: JSXNode | JSXNode[]; + [k: string]: JSXNode | JSXNode[] | string | number | boolean; + }; + }; + /** + * The property of the attributes object storing the children. + */ + type ElementChildrenAttribute = { children: unknown }; + + // The following do not have special meaning to TypeScript. + + /** + * An attributes object. + */ + type Props = { [k: string]: unknown }; + + /** + * A functional component requiring attributes to match `T`. + */ + type Component = { + (props: T): Element; + }; + + /** + * A child of a JSX element. + */ + type JSXNode = Node | string | boolean | null | undefined; +} diff --git a/src/js/jsconfig.json b/src/js/jsconfig.json deleted file mode 100644 index 99d65145..00000000 --- a/src/js/jsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "compilerOptions": { - "target": "es6", - "checkJs": true - }, - "include": ["./**/*.js"] -} diff --git a/src/js/jsx-runtime.ts b/src/js/jsx-runtime.ts new file mode 100644 index 00000000..1db25d1f --- /dev/null +++ b/src/js/jsx-runtime.ts @@ -0,0 +1,41 @@ +function isDisplayed(node: JSX.JSXNode): node is Exclude { + return typeof node !== "boolean" && node != null; +} + +/** + * JSX factory. + */ +function jsx(tag: T, props: JSX.IntrinsicElements[T]): HTMLElement; +function jsx(tag: JSX.Component, props: U): Element; +function jsx( + tag: keyof JSX.IntrinsicElements | JSX.Component, + props: U +): JSX.Element { + if (typeof tag === "function") return tag(props); + + const { children, ...attrs } = props as JSX.IntrinsicElements[keyof JSX.IntrinsicElements]; + + const element = document.createElement(tag); + Object.entries(attrs).forEach(([key, value]) => { + switch (typeof value) { + case "boolean": + if (!value) return; + return element.setAttribute(key, ""); + case "number": + case "string": + return element.setAttribute(key, `${value}`); + } + throw new TypeError("JSX element attribute assigned invalid type"); + }); + element.append(...([children].flat(Infinity) as JSX.JSXNode[]).filter(isDisplayed)); + return element; +} + +// functional component, called indirectly as `jsx(Fragment, props)` +/** + * Groups elements without introducing a parent element. + */ +const Fragment = (props: JSX.Props) => props.children as JSX.Element; + +// jsxs is used when there are multiple children +export { Fragment, jsx, jsx as jsxs }; diff --git a/src/js/languages.js b/src/js/languages.ts similarity index 60% rename from src/js/languages.js rename to src/js/languages.ts index 6e7bedf6..acd94358 100644 --- a/src/js/languages.js +++ b/src/js/languages.ts @@ -1,197 +1,182 @@ -/** - * @type {Object} - */ -export const LANGUAGES = { - "en": { - name: "English", - data: null, - code: "en", - region: "", - }, - - "zh-CN": { - // simplified chinese - name: "简体中文", - data: G_WEGAME_VERSION - ? require("./built-temp/base-zh-CN-ISBN.json") - : require("./built-temp/base-zh-CN.json"), - code: "zh", - region: "CN", - }, - - "zh-TW": { - // traditional chinese - name: "繁體中文", - data: require("./built-temp/base-zh-TW.json"), - code: "zh", - region: "TW", - }, - - "ja": { - // japanese - name: "日本語", - data: require("./built-temp/base-ja.json"), - code: "ja", - region: "", - }, - - "kor": { - // korean - name: "한국어", - data: require("./built-temp/base-kor.json"), - code: "ko", - region: "", - }, - - "cs": { - // czech - name: "Čeština", - data: require("./built-temp/base-cz.json"), - code: "cs", - region: "", - }, - - "da": { - // danish - name: "Dansk", - data: require("./built-temp/base-da.json"), - code: "da", - region: "", - }, - - "de": { - // german - name: "Deutsch", - data: require("./built-temp/base-de.json"), - code: "de", - region: "", - }, - - "es-419": { - // spanish - name: "Español", - data: require("./built-temp/base-es.json"), - code: "es", - region: "", - }, - - "fr": { - // french - name: "Français", - data: require("./built-temp/base-fr.json"), - code: "fr", - region: "", - }, - - "it": { - // italian - name: "Italiano", - data: require("./built-temp/base-it.json"), - code: "it", - region: "", - }, - - "hu": { - // hungarian - name: "Magyar", - data: require("./built-temp/base-hu.json"), - code: "hu", - region: "", - }, - - "nl": { - // dutch - name: "Nederlands", - data: require("./built-temp/base-nl.json"), - code: "nl", - region: "", - }, - - "no": { - // norwegian - name: "Norsk", - data: require("./built-temp/base-no.json"), - code: "no", - region: "", - }, - - "pl": { - // polish - name: "Polski", - data: require("./built-temp/base-pl.json"), - code: "pl", - region: "", - }, - - "pt-PT": { - // portuguese - name: "Português", - data: require("./built-temp/base-pt-PT.json"), - code: "pt", - region: "PT", - }, - - "pt-BR": { - // portuguese - brazil - name: "Português - Brasil", - data: require("./built-temp/base-pt-BR.json"), - code: "pt", - region: "BR", - }, - - "ro": { - // romanian - name: "Română", - data: require("./built-temp/base-ro.json"), - code: "ro", - region: "", - }, - - "ru": { - // russian - name: "Русский", - data: require("./built-temp/base-ru.json"), - code: "ru", - region: "", - }, - - "fi": { - // finish - name: "Suomi", - data: require("./built-temp/base-fi.json"), - code: "fi", - region: "", - }, - - "sv": { - // swedish - name: "Svenska", - data: require("./built-temp/base-sv.json"), - code: "sv", - region: "", - }, - - "tr": { - // turkish - name: "Türkçe", - data: require("./built-temp/base-tr.json"), - code: "tr", - region: "", - }, - - "uk": { - // ukrainian - name: "Українська", - data: require("./built-temp/base-uk.json"), - code: "uk", - region: "", - }, - - "he": { - // hebrew - name: "עברית", - data: require("./built-temp/base-he.json"), - code: "he", - region: "", - }, -}; +export interface Language { + name: string; + code: string; + region: string; + overrides?: object; +} + +export const LANGUAGES: Record = { + "en": { + name: "English", + code: "en", + region: "", + }, + + "zh-CN": { + // simplified chinese + name: "简体中文", + code: "zh", + region: "CN", + }, + + "zh-TW": { + // traditional chinese + name: "繁體中文", + code: "zh", + region: "TW", + }, + + "ja": { + // japanese + name: "日本語", + code: "ja", + region: "", + }, + + "kor": { + // korean + name: "한국어", + code: "ko", + region: "", + }, + + "cs": { + // czech + name: "Čeština", + code: "cs", + region: "", + }, + + "da": { + // danish + name: "Dansk", + code: "da", + region: "", + }, + + "de": { + // german + name: "Deutsch", + code: "de", + region: "", + }, + + "es-419": { + // spanish + name: "Español", + code: "es", + region: "", + }, + + "fr": { + // french + name: "Français", + code: "fr", + region: "", + }, + + "it": { + // italian + name: "Italiano", + code: "it", + region: "", + }, + + "hu": { + // hungarian + name: "Magyar", + code: "hu", + region: "", + }, + + "nl": { + // dutch + name: "Nederlands", + code: "nl", + region: "", + }, + + "no": { + // norwegian + name: "Norsk", + code: "no", + region: "", + }, + + "pl": { + // polish + name: "Polski", + code: "pl", + region: "", + }, + + "pt-PT": { + // portuguese + name: "Português", + code: "pt", + region: "PT", + }, + + "pt-BR": { + // portuguese _ brazil + name: "Português - Brasil", + code: "pt", + region: "BR", + }, + + "ro": { + // romanian + name: "Română", + code: "ro", + region: "", + }, + + "ru": { + // russian + name: "Русский", + code: "ru", + region: "", + }, + + "fi": { + // finish + name: "Suomi", + code: "fi", + region: "", + }, + + "sv": { + // swedish + name: "Svenska", + code: "sv", + region: "", + }, + + "tr": { + // turkish + name: "Türkçe", + code: "tr", + region: "", + }, + + "uk": { + // ukrainian + name: "Українська", + code: "uk", + region: "", + }, + + "he": { + // hebrew + name: "עברית", + code: "he", + region: "", + }, + + "ar": { + // arabic + name: "العربية", + code: "ar", + region: "", + }, +}; diff --git a/src/js/main.js b/src/js/main.js index 17c971ee..f1abe0d3 100644 --- a/src/js/main.js +++ b/src/js/main.js @@ -1,25 +1,18 @@ -import "./core/polyfills"; import "./core/assert"; +import "./core/polyfills"; import "./mods/modloader"; -import { createLogger, logSection } from "./core/logging"; import { Application } from "./application"; -import { IS_DEBUG } from "./core/config"; +import { createLogger, logSection } from "./core/logging"; import { initComponentRegistry } from "./game/component_registry"; -import { initDrawUtils } from "./core/draw_utils"; -import { initItemRegistry } from "./game/item_registry"; -import { initMetaBuildingRegistry } from "./game/meta_building_registry"; import { initGameModeRegistry } from "./game/game_mode_registry"; import { initGameSpeedRegistry } from "./game/game_speed_registry"; +import { initItemRegistry } from "./game/item_registry"; +import { initMetaBuildingRegistry } from "./game/meta_building_registry"; const logger = createLogger("main"); -if (window.coreThreadLoadedCb) { - logger.log("Javascript parsed, calling html thread"); - window.coreThreadLoadedCb(); -} - console.log( `%cshapez.io ️%c\n© 2022 tobspr Games\nCommit %c${G_BUILD_COMMIT_HASH}%c on %c${new Date( G_BUILD_TIME @@ -33,7 +26,7 @@ console.log( console.log("Environment: %c" + G_APP_ENVIRONMENT, "color: #fff"); -if (G_IS_DEV && IS_DEBUG) { +if (G_IS_DEV) { console.log("\n%c🛑 DEBUG ENVIRONMENT 🛑\n", "color: #f77"); } @@ -48,7 +41,6 @@ console.log("%cDEVCODE BUILT IN", "color: #f77"); logSection("Boot Process", "#f9a825"); -initDrawUtils(); initComponentRegistry(); initItemRegistry(); initMetaBuildingRegistry(); @@ -63,8 +55,4 @@ function bootApp() { app.boot(); } -if (G_IS_STANDALONE) { - window.addEventListener("load", bootApp); -} else { - bootApp(); -} +window.addEventListener("load", bootApp); diff --git a/src/js/mods/disabled_mod.ts b/src/js/mods/disabled_mod.ts new file mode 100644 index 00000000..8e78c3be --- /dev/null +++ b/src/js/mods/disabled_mod.ts @@ -0,0 +1,7 @@ +import { Mod } from "./mod"; + +export class DisabledMod extends Mod { + init(): void | Promise { + // Do nothing + } +} diff --git a/src/js/mods/errored_mod.ts b/src/js/mods/errored_mod.ts new file mode 100644 index 00000000..b7bfe9da --- /dev/null +++ b/src/js/mods/errored_mod.ts @@ -0,0 +1,11 @@ +import { Mod } from "./mod"; + +/** + * This {@link Mod} subclass is used to differentiate disabled mods and those + * that couldn't be parsed or constructed due to an error. + */ +export class ErroredMod extends Mod { + init(): void | Promise { + // Do nothing + } +} diff --git a/src/js/mods/mod.js b/src/js/mods/mod.js deleted file mode 100644 index cea152d8..00000000 --- a/src/js/mods/mod.js +++ /dev/null @@ -1,36 +0,0 @@ -/* typehints:start */ -import { Application } from "../application"; -import { ModLoader } from "./modloader"; -/* typehints:end */ - -import { MOD_SIGNALS } from "./mod_signals"; - -export class Mod { - /** - * @param {object} param0 - * @param {Application} param0.app - * @param {ModLoader} param0.modLoader - * @param {import("./modloader").ModMetadata} param0.meta - * @param {Object} param0.settings - * @param {() => Promise} param0.saveSettings - */ - constructor({ app, modLoader, meta, settings, saveSettings }) { - this.app = app; - this.modLoader = modLoader; - this.metadata = meta; - - this.signals = MOD_SIGNALS; - this.modInterface = modLoader.modInterface; - - this.settings = settings; - this.saveSettings = saveSettings; - } - - init() { - // to be overridden - } - - get dialogs() { - return this.modInterface.dialogs; - } -} diff --git a/src/js/mods/mod.ts b/src/js/mods/mod.ts new file mode 100644 index 00000000..bc1b9582 --- /dev/null +++ b/src/js/mods/mod.ts @@ -0,0 +1,47 @@ +import { Application } from "@/application"; +import { ModInterfaceV2 } from "./mod_interface_v2"; +import { FrozenModMetadata, ModMetadata } from "./mod_metadata"; +import { MOD_SIGNALS } from "./mod_signals"; +import { ModLoader } from "./modloader"; + +export type ModConstructor = new (metadata: ModMetadata, app: Application, modLoader: ModLoader) => Mod; + +function freezeMetadata(metadata: ModMetadata): FrozenModMetadata { + // Note: Object.freeze doesn't create a copy of the object + for (const author of metadata.authors) { + Object.freeze(author); + } + + Object.freeze(metadata.authors); + return Object.freeze(metadata); +} + +export abstract class Mod { + // TODO: Review what properties are necessary while improving ModInterface + protected readonly app: Application; + protected readonly modLoader: ModLoader; + protected readonly modInterface: ModInterfaceV2; + protected readonly signals = MOD_SIGNALS; + + // Exposed for convenience + readonly id: string; + readonly metadata: FrozenModMetadata; + readonly errors: Error[] = []; + + constructor(metadata: ModMetadata, app: Application, modLoader: ModLoader) { + this.app = app; + this.modLoader = modLoader; + + this.id = metadata.id; + this.metadata = freezeMetadata(metadata); + + // ModInterfaceV2 assumes id to be set + this.modInterface = new ModInterfaceV2(this, modLoader); + } + + abstract init(): void | Promise; + + get dialogs() { + return this.modInterface.dialogs; + } +} diff --git a/src/js/mods/mod_interface.js b/src/js/mods/mod_interface.js index 8130dc5a..578d2bfe 100644 --- a/src/js/mods/mod_interface.js +++ b/src/js/mods/mod_interface.js @@ -1,35 +1,35 @@ /* typehints:start */ -import { ModLoader } from "./modloader"; -import { GameSystem } from "../game/game_system"; import { Component } from "../game/component"; +import { GameSystem } from "../game/game_system"; import { MetaBuilding } from "../game/meta_building"; +import { ModLoader } from "./modloader"; /* typehints:end */ -import { defaultBuildingVariant } from "../game/meta_building"; +import { gComponentRegistry, gItemRegistry, gMetaBuildingRegistry } from "../core/global_registries"; +import { Loader } from "../core/loader"; import { AtlasSprite, SpriteAtlasLink } from "../core/sprites"; +import { Vector } from "../core/vector"; +import { BaseItem } from "../game/base_item"; +import { gBuildingVariants, registerBuildingVariant } from "../game/building_codes"; +import { MODS_ADDITIONAL_SYSTEMS } from "../game/game_system_manager"; +import { BaseHUDPart } from "../game/hud/base_hud_part"; +import { HUDModalDialogs } from "../game/hud/parts/modal_dialogs"; +import { MODS_ADDITIONAL_ITEMS } from "../game/item_resolver"; +import { KEYMAPPINGS } from "../game/key_action_mapper"; +import { MODS_ADDITIONAL_SHAPE_MAP_WEIGHTS } from "../game/map_chunk"; +import { MOD_CHUNK_DRAW_HOOKS } from "../game/map_chunk_view"; +import { defaultBuildingVariant } from "../game/meta_building"; +import { GameRoot } from "../game/root"; import { enumShortcodeToSubShape, enumSubShape, enumSubShapeToShortcode, MODS_ADDITIONAL_SUB_SHAPE_DRAWERS, } from "../game/shape_definition"; -import { Loader } from "../core/loader"; +import { THEMES } from "../game/theme"; import { LANGUAGES } from "../languages"; import { matchDataRecursive, T } from "../translations"; -import { gBuildingVariants, registerBuildingVariant } from "../game/building_codes"; -import { gComponentRegistry, gItemRegistry, gMetaBuildingRegistry } from "../core/global_registries"; -import { MODS_ADDITIONAL_SHAPE_MAP_WEIGHTS } from "../game/map_chunk"; -import { MODS_ADDITIONAL_SYSTEMS } from "../game/game_system_manager"; -import { MOD_CHUNK_DRAW_HOOKS } from "../game/map_chunk_view"; -import { KEYMAPPINGS } from "../game/key_action_mapper"; -import { HUDModalDialogs } from "../game/hud/parts/modal_dialogs"; -import { THEMES } from "../game/theme"; import { ModMetaBuilding } from "./mod_meta_building"; -import { BaseHUDPart } from "../game/hud/base_hud_part"; -import { Vector } from "../core/vector"; -import { GameRoot } from "../game/root"; -import { BaseItem } from "../game/base_item"; -import { MODS_ADDITIONAL_ITEMS } from "../game/item_resolver"; /** * @typedef {{new(...args: any[]): any, prototype: any}} constructable @@ -67,18 +67,14 @@ export class ModInterface { this.modLoader = modLoader; } + /** @deprecated */ registerCss(cssString) { - // Preprocess css - cssString = cssString.replace(/\$scaled\(([^)]*)\)/gim, (substr, expression) => { - return "calc((" + expression + ") * var(--ui-scale))"; - }); const element = document.createElement("style"); element.textContent = cssString; document.head.appendChild(element); } registerSprite(spriteId, base64string) { - assert(base64string.startsWith("data:image")); const img = new Image(); const sprite = new AtlasSprite(spriteId); @@ -186,10 +182,7 @@ export class ModInterface { throw new Error("Unknown language: " + language); } - matchDataRecursive(data.data, translations, true); - if (language === "en") { - matchDataRecursive(T, translations, true); - } + matchDataRecursive((data.overrides ??= {}), translations, true); } /** diff --git a/src/js/mods/mod_interface_v2.ts b/src/js/mods/mod_interface_v2.ts new file mode 100644 index 00000000..f67c056c --- /dev/null +++ b/src/js/mods/mod_interface_v2.ts @@ -0,0 +1,38 @@ +import { Mod } from "./mod"; +import { ModInterface } from "./mod_interface"; +import { ModLoader } from "./modloader"; + +export class ModInterfaceV2 extends ModInterface { + private readonly mod: Mod; + private readonly baseUrl: string; + + constructor(mod: Mod, modLoader: ModLoader) { + super(modLoader); + this.mod = mod; + this.baseUrl = `mod://${mod.id}`; + } + + resolve(path: string) { + path = path + .split("/") + .map(p => encodeURIComponent(p)) + .join("/"); + + if (!path.startsWith("./")) { + // Assume relative if not specified + path = `./${path}`; + } + + // Cannot use import.meta in webpack context + return new URL(path, this.baseUrl).toString(); + } + + addStylesheet(path: string) { + const link = document.createElement("link"); + link.rel = "stylesheet"; + link.href = this.resolve(path); + link.setAttribute("data-mod-id", this.mod.id); + + document.head.append(link); + } +} diff --git a/src/js/mods/mod_metadata.ts b/src/js/mods/mod_metadata.ts new file mode 100644 index 00000000..cd1987d0 --- /dev/null +++ b/src/js/mods/mod_metadata.ts @@ -0,0 +1,38 @@ +import { Mod } from "./mod"; + +export interface ModAuthor { + name: string; + website?: string; +} + +export interface ModMetadata { + // format: 1; + id: string; + entry: string; + name: string; + description?: string; + authors: ModAuthor[]; + version: string; + savegameResident: boolean; + website?: string; + source?: string; +} + +export type ModSource = "user" | "distro" | "dev"; + +export interface ModQueueEntry { + source: ModSource; + file: string; + disabled: boolean; + metadata: ModMetadata; +} + +export interface ModInfo { + source: ModSource; + file: string; + mod: Mod; +} + +export interface FrozenModMetadata extends Readonly> { + authors: ReadonlyArray>; +} diff --git a/src/js/mods/mod_signals.js b/src/js/mods/mod_signals.js index a534dd89..d858da99 100644 --- a/src/js/mods/mod_signals.js +++ b/src/js/mods/mod_signals.js @@ -13,21 +13,27 @@ export const MOD_SIGNALS = { // Called when the application has booted and instances like the app settings etc are available appBooted: new Signal(), - modifyLevelDefinitions: /** @type {TypedSignal<[Array[Object]]>} */ (new Signal()), - modifyUpgrades: /** @type {TypedSignal<[Object]>} */ (new Signal()), + modifyLevelDefinitions: /** @type {Signal<[Array[Object]]>} */ (new Signal()), + modifyUpgrades: /** @type {Signal<[Object]>} */ (new Signal()), - hudElementInitialized: /** @type {TypedSignal<[BaseHUDPart]>} */ (new Signal()), - hudElementFinalized: /** @type {TypedSignal<[BaseHUDPart]>} */ (new Signal()), + hudElementInitialized: /** @type {Signal<[BaseHUDPart]>} */ (new Signal()), + hudElementFinalized: /** @type {Signal<[BaseHUDPart]>} */ (new Signal()), - hudInitializer: /** @type {TypedSignal<[GameRoot]>} */ (new Signal()), + hudInitializer: /** @type {Signal<[GameRoot]>} */ (new Signal()), - gameInitialized: /** @type {TypedSignal<[GameRoot]>} */ (new Signal()), - gameLoadingStageEntered: /** @type {TypedSignal<[InGameState, string]>} */ (new Signal()), + gameInitialized: /** @type {Signal<[GameRoot]>} */ (new Signal()), + gameLoadingStageEntered: /** @type {Signal<[InGameState, string]>} */ (new Signal()), - gameStarted: /** @type {TypedSignal<[GameRoot]>} */ (new Signal()), + gameStarted: /** @type {Signal<[GameRoot]>} */ (new Signal()), - stateEntered: /** @type {TypedSignal<[GameState]>} */ (new Signal()), + stateEntered: /** @type {Signal<[GameState]>} */ (new Signal()), - gameSerialized: /** @type {TypedSignal<[GameRoot, import("../savegame/savegame_typedefs").SerializedGame]>} */ (new Signal()), - gameDeserialized: /** @type {TypedSignal<[GameRoot, import("../savegame/savegame_typedefs").SerializedGame]>} */ (new Signal()), + gameSerialized: + /** @type {Signal<[GameRoot, import("../savegame/savegame_typedefs").SerializedGame]>} */ ( + new Signal() + ), + gameDeserialized: + /** @type {Signal<[GameRoot, import("../savegame/savegame_typedefs").SerializedGame]>} */ ( + new Signal() + ), }; diff --git a/src/js/mods/modloader.js b/src/js/mods/modloader.js deleted file mode 100644 index 1644b169..00000000 --- a/src/js/mods/modloader.js +++ /dev/null @@ -1,279 +0,0 @@ -/* typehints:start */ -import { Application } from "../application"; -/* typehints:end */ -import { globalConfig } from "../core/config"; -import { createLogger } from "../core/logging"; -import { StorageImplBrowserIndexedDB } from "../platform/browser/storage_indexed_db"; -import { StorageImplElectron } from "../platform/electron/storage"; -import { FILE_NOT_FOUND } from "../platform/storage"; -import { Mod } from "./mod"; -import { ModInterface } from "./mod_interface"; -import { MOD_SIGNALS } from "./mod_signals"; - -import semverValidRange from "semver/ranges/valid"; -import semverSatisifies from "semver/functions/satisfies"; - -const LOG = createLogger("mods"); - -/** - * @typedef {{ - * name: string; - * version: string; - * author: string; - * website: string; - * description: string; - * id: string; - * minimumGameVersion?: string; - * settings: []; - * doesNotAffectSavegame?: boolean - * }} ModMetadata - */ - -export class ModLoader { - constructor() { - LOG.log("modloader created"); - - /** - * @type {Application} - */ - this.app = undefined; - - /** @type {Mod[]} */ - this.mods = []; - - this.modInterface = new ModInterface(this); - - /** @type {({ meta: ModMetadata, modClass: typeof Mod})[]} */ - this.modLoadQueue = []; - - this.initialized = false; - - this.signals = MOD_SIGNALS; - } - - linkApp(app) { - this.app = app; - } - - anyModsActive() { - return this.mods.length > 0; - } - - /** - * - * @returns {import("../savegame/savegame_typedefs").SavegameStoredMods} - */ - getModsListForSavegame() { - return this.mods - .filter(mod => !mod.metadata.doesNotAffectSavegame) - .map(mod => ({ - id: mod.metadata.id, - version: mod.metadata.version, - website: mod.metadata.website, - name: mod.metadata.name, - author: mod.metadata.author, - })); - } - - /** - * - * @param {import("../savegame/savegame_typedefs").SavegameStoredMods} originalMods - */ - computeModDifference(originalMods) { - /** - * @type {import("../savegame/savegame_typedefs").SavegameStoredMods} - */ - let missing = []; - - const current = this.getModsListForSavegame(); - - originalMods.forEach(mod => { - for (let i = 0; i < current.length; ++i) { - const currentMod = current[i]; - if (currentMod.id === mod.id && currentMod.version === mod.version) { - current.splice(i, 1); - return; - } - } - missing.push(mod); - }); - - return { - missing, - extra: current, - }; - } - - exposeExports() { - if (G_IS_STEAM_DEMO) { - return; - } - - if (G_IS_DEV || G_IS_STANDALONE) { - let exports = {}; - const modules = require.context("../", true, /\.js$/); - Array.from(modules.keys()).forEach(key => { - // @ts-ignore - const module = modules(key); - for (const member in module) { - if (member === "default" || member === "__$S__") { - // Setter - continue; - } - if (exports[member]) { - throw new Error("Duplicate export of " + member); - } - - Object.defineProperty(exports, member, { - get() { - return module[member]; - }, - set(v) { - module.__$S__(member, v); - }, - }); - } - }); - - window.shapez = exports; - } - } - - async initMods() { - if (G_IS_STEAM_DEMO) { - this.initialized = true; - return; - } - - if (!G_IS_STANDALONE && !G_IS_DEV) { - this.initialized = true; - return; - } - - // Create a storage for reading mod settings - const storage = G_IS_STANDALONE - ? new StorageImplElectron(this.app) - : new StorageImplBrowserIndexedDB(this.app); - await storage.initialize(); - - LOG.log("hook:init", this.app, this.app.storage); - this.exposeExports(); - - let mods = []; - if (G_IS_STANDALONE) { - mods = await ipcRenderer.invoke("get-mods"); - } - if (G_IS_DEV && globalConfig.debug.externalModUrl) { - const modURLs = Array.isArray(globalConfig.debug.externalModUrl) - ? globalConfig.debug.externalModUrl - : [globalConfig.debug.externalModUrl]; - - for (let i = 0; i < modURLs.length; i++) { - const response = await fetch(modURLs[i], { - method: "GET", - }); - if (response.status !== 200) { - throw new Error( - "Failed to load " + modURLs[i] + ": " + response.status + " " + response.statusText - ); - } - mods.push(await response.text()); - } - } - - window.$shapez_registerMod = (modClass, meta) => { - if (this.initialized) { - throw new Error("Can't register mod after modloader is initialized"); - } - if (this.modLoadQueue.some(entry => entry.meta.id === meta.id)) { - console.warn("Not registering mod", meta, "since a mod with the same id is already loaded"); - return; - } - this.modLoadQueue.push({ - modClass, - meta, - }); - }; - - mods.forEach(modCode => { - modCode += ` - if (typeof Mod !== 'undefined') { - if (typeof METADATA !== 'object') { - throw new Error("No METADATA variable found"); - } - window.$shapez_registerMod(Mod, METADATA); - } - `; - try { - const func = new Function(modCode); - func(); - } catch (ex) { - console.error(ex); - alert("Failed to parse mod (launch with --dev for more info): \n\n" + ex); - } - }); - - delete window.$shapez_registerMod; - - for (let i = 0; i < this.modLoadQueue.length; i++) { - const { modClass, meta } = this.modLoadQueue[i]; - const modDataFile = "modsettings_" + meta.id + "__" + meta.version + ".json"; - - if (meta.minimumGameVersion) { - const minimumGameVersion = meta.minimumGameVersion; - if (!semverValidRange(minimumGameVersion)) { - alert("Mod " + meta.id + " has invalid minimumGameVersion: " + minimumGameVersion); - continue; - } - if (!semverSatisifies(G_BUILD_VERSION, minimumGameVersion)) { - alert( - "Mod '" + - meta.id + - "' is incompatible with this version of the game: \n\n" + - "Mod requires version " + - minimumGameVersion + - " but this game has version " + - G_BUILD_VERSION - ); - continue; - } - } - - let settings = meta.settings; - - if (meta.settings) { - try { - const storedSettings = await storage.readFileAsync(modDataFile); - settings = JSON.parse(storedSettings); - } catch (ex) { - if (ex === FILE_NOT_FOUND) { - // Write default data - await storage.writeFileAsync(modDataFile, JSON.stringify(meta.settings)); - } else { - alert("Failed to load settings for " + meta.id + ", will use defaults:\n\n" + ex); - } - } - } - - try { - const mod = new modClass({ - app: this.app, - modLoader: this, - meta, - settings, - saveSettings: () => storage.writeFileAsync(modDataFile, JSON.stringify(mod.settings)), - }); - await mod.init(); - this.mods.push(mod); - } catch (ex) { - console.error(ex); - alert("Failed to initialize mods (launch with --dev for more info): \n\n" + ex); - } - } - - this.modLoadQueue = []; - this.initialized = true; - } -} - -export const MODS = new ModLoader(); diff --git a/src/js/mods/modloader.ts b/src/js/mods/modloader.ts new file mode 100644 index 00000000..f071ed27 --- /dev/null +++ b/src/js/mods/modloader.ts @@ -0,0 +1,161 @@ +import { GLOBAL_APP } from "@/core/globals"; +import { SavegameStoredMods } from "@/savegame/savegame_typedefs"; +import { createLogger } from "../core/logging"; +import { DisabledMod } from "./disabled_mod"; +import { Mod, ModConstructor } from "./mod"; +import { ModInfo, ModMetadata, ModQueueEntry } from "./mod_metadata"; +import { MOD_SIGNALS } from "./mod_signals"; + +const LOG = createLogger("mods"); + +export class ModLoader { + private readonly mods = new Map(); + + // FIXME: Used for ModInterface, should be improved? + readonly signals = MOD_SIGNALS; + + constructor() { + LOG.log("modloader created"); + } + + get app() { + return GLOBAL_APP; + } + + get allMods(): ModInfo[] { + return [...this.mods.values()]; + } + + get activeMods(): ModInfo[] { + const mods: ModInfo[] = []; + for (const mod of this.mods.values()) { + if (mod.mod instanceof DisabledMod) { + continue; + } + + mods.push(mod); + } + + return mods; + } + + getModsListForSavegame(): SavegameStoredMods { + // FIXME: new implementation TBD + return this.activeMods + .filter(info => info.mod.metadata.savegameResident) + .map(({ mod }) => ({ + id: mod.metadata.id, + version: mod.metadata.version, + website: mod.metadata.website, + name: mod.metadata.name, + author: mod.metadata.authors.map(a => a.name).join(","), + })); + } + + computeModDifference(originalMods: SavegameStoredMods) { + // FIXME: new implementation TBD + const missing: SavegameStoredMods = []; + const current = this.getModsListForSavegame(); + + originalMods.forEach(mod => { + for (let i = 0; i < current.length; ++i) { + const currentMod = current[i]; + if (currentMod.id === mod.id && currentMod.version === mod.version) { + current.splice(i, 1); + return; + } + } + missing.push(mod); + }); + + return { + missing, + extra: current, + }; + } + + async initMods() { + this.exposeExports(); + const queue: ModQueueEntry[] = await ipcRenderer.invoke("get-mods"); + + // Mods can be parsed and constructed in parallel + const loadedMods = await Promise.all( + queue.map(async e => ({ entry: e, mod: await this.loadMod(e) })) + ); + + // Initialize all mods sequentially + // TODO: Also collect early errors from the main process + for (const { entry, mod } of loadedMods) { + this.mods.set(mod.id, { + source: entry.source, + file: entry.file, + mod, + }); + + await mod.init(); + } + } + + private exposeExports() { + const exports = {}; + const modules = import.meta.webpackContext("../", { + recursive: true, + regExp: /\.[jt]sx?$/, + // NOTE: Worker scripts are executed if not explicitly excluded, which causes + // infinite recursion! + exclude: /\/webworkers\/|\.d\.ts$/, + }); + + Array.from(modules.keys()).forEach(key => { + const module = modules(key) as object; + for (const member in module) { + if (member === "default") { + continue; + } + if (exports[member]) { + throw new Error("Duplicate export of " + member); + } + + Object.defineProperty(exports, member, { + get() { + return module[member]; + }, + }); + } + }); + + window.shapez = exports; + } + + private async loadMod(entry: ModQueueEntry): Promise { + if (entry.disabled) { + return new DisabledMod(entry.metadata, this.app, this); + } + + return await this.createModInstance(entry.metadata); + } + + private async createModInstance(metadata: ModMetadata): Promise { + const url = this.getModEntryUrl(metadata); + const module = await import(/* webpackIgnore: true */ url); + + if (!(module.default?.prototype instanceof Mod)) { + throw new Error("Default export is not a Mod constructor"); + } + + const modClass: ModConstructor = module.default; + const mod = new modClass(metadata, this.app, this); + + if (mod.id !== metadata.id) { + throw new Error(`Mod was created with invalid ID "${mod.id}"`); + } + + return mod; + } + + private getModEntryUrl(mod: ModMetadata): string { + return `mod://${mod.id}/${mod.entry}`; + } +} + +export const MODS = new ModLoader(); diff --git a/src/js/platform/achievement_provider.js b/src/js/platform/achievement_provider.js deleted file mode 100644 index 3b60ad95..00000000 --- a/src/js/platform/achievement_provider.js +++ /dev/null @@ -1,646 +0,0 @@ -/* typehints:start */ -import { Application } from "../application"; -import { Entity } from "../game/entity"; -import { GameRoot } from "../game/root"; -import { THEMES } from "../game/theme"; -/* typehints:end */ - -import { enumAnalyticsDataSource } from "../game/production_analytics"; -import { ShapeDefinition } from "../game/shape_definition"; -import { ShapeItem } from "../game/items/shape_item"; -import { globalConfig } from "../core/config"; - -export const ACHIEVEMENTS = { - belt500Tiles: "belt500Tiles", - blueprint100k: "blueprint100k", - blueprint1m: "blueprint1m", - completeLvl26: "completeLvl26", - cutShape: "cutShape", - darkMode: "darkMode", - destroy1000: "destroy1000", - irrelevantShape: "irrelevantShape", - level100: "level100", - level50: "level50", - logoBefore18: "logoBefore18", - mam: "mam", - mapMarkers15: "mapMarkers15", - noBeltUpgradesUntilBp: "noBeltUpgradesUntilBp", - noInverseRotater: "noInverseRotater", - oldLevel17: "oldLevel17", - openWires: "openWires", - paintShape: "paintShape", - place5000Wires: "place5000Wires", - placeBlueprint: "placeBlueprint", - placeBp1000: "placeBp1000", - play1h: "play1h", - play10h: "play10h", - play20h: "play20h", - produceLogo: "produceLogo", - produceMsLogo: "produceMsLogo", - produceRocket: "produceRocket", - rotateShape: "rotateShape", - speedrunBp30: "speedrunBp30", - speedrunBp60: "speedrunBp60", - speedrunBp120: "speedrunBp120", - stack4Layers: "stack4Layers", - stackShape: "stackShape", - store100Unique: "store100Unique", - storeShape: "storeShape", - throughputBp25: "throughputBp25", - throughputBp50: "throughputBp50", - throughputLogo25: "throughputLogo25", - throughputLogo50: "throughputLogo50", - throughputRocket10: "throughputRocket10", - throughputRocket20: "throughputRocket20", - trash1000: "trash1000", - unlockWires: "unlockWires", - upgradesTier5: "upgradesTier5", - upgradesTier8: "upgradesTier8", -}; - -/** @type {keyof typeof THEMES} */ -const DARK_MODE = "dark"; - -const HOUR_1 = 3600; // Seconds -const HOUR_10 = HOUR_1 * 10; -const HOUR_20 = HOUR_1 * 20; -const ITEM_SHAPE = ShapeItem.getId(); -const MINUTE_30 = 1800; // Seconds -const MINUTE_60 = MINUTE_30 * 2; -const MINUTE_120 = MINUTE_30 * 4; -const ROTATER_CCW_CODE = 12; -const ROTATER_180_CODE = 13; -const SHAPE_BP = "CbCbCbRb:CwCwCwCw"; -const SHAPE_LOGO = "RuCw--Cw:----Ru--"; -const SHAPE_MS_LOGO = "RgRyRbRr"; -const SHAPE_OLD_LEVEL_17 = "WrRgWrRg:CwCrCwCr:SgSgSgSg"; -const SHAPE_ROCKET = "CbCuCbCu:Sr------:--CrSrCr:CwCwCwCw"; - -/** @type {Layer} */ -const WIRE_LAYER = "wires"; - -export class AchievementProviderInterface { - /* typehints:start */ - collection = /** @type {AchievementCollection|undefined} */ (null); - /* typehints:end */ - - /** @param {Application} app */ - constructor(app) { - this.app = app; - } - - /** - * Initializes the achievement provider. - * @returns {Promise} - * @abstract - */ - initialize() { - abstract; - return Promise.reject(); - } - - /** - * Opportunity to do additional initialization work with the GameRoot. - * @param {GameRoot} root - * @returns {Promise} - * @abstract - */ - onLoad(root) { - abstract; - return Promise.reject(); - } - - /** @returns {boolean} */ - hasLoaded() { - abstract; - return false; - } - - /** - * Call to activate an achievement with the provider - * @param {string} key - Maps to an Achievement - * @returns {Promise} - * @abstract - */ - activate(key) { - abstract; - return Promise.reject(); - } - - /** - * Checks if achievements are supported in the current build - * @returns {boolean} - * @abstract - */ - hasAchievements() { - abstract; - return false; - } -} - -export class Achievement { - /** @param {string} key - An ACHIEVEMENTS key */ - constructor(key) { - this.key = key; - this.activate = null; - this.activatePromise = null; - this.receiver = null; - this.signal = null; - } - - init() {} - - isValid() { - return true; - } - - unlock() { - if (!this.activatePromise) { - this.activatePromise = this.activate(this.key); - } - - return this.activatePromise; - } -} - -export class AchievementCollection { - /** - * @param {function} activate - Resolves when provider activation is complete - */ - constructor(activate) { - this.map = new Map(); - this.activate = activate; - - this.add(ACHIEVEMENTS.belt500Tiles, { - isValid: this.isBelt500TilesValid, - signal: "entityAdded", - }); - this.add(ACHIEVEMENTS.blueprint100k, this.createBlueprintOptions(100000)); - this.add(ACHIEVEMENTS.blueprint1m, this.createBlueprintOptions(1000000)); - this.add(ACHIEVEMENTS.completeLvl26, this.createLevelOptions(26)); - this.add(ACHIEVEMENTS.cutShape); - this.add(ACHIEVEMENTS.darkMode, { - isValid: this.isDarkModeValid, - }); - this.add(ACHIEVEMENTS.destroy1000, { - isValid: this.isDestroy1000Valid, - }); - this.add(ACHIEVEMENTS.irrelevantShape, { - isValid: this.isIrrelevantShapeValid, - signal: "shapeDelivered", - }); - this.add(ACHIEVEMENTS.level100, this.createLevelOptions(100)); - this.add(ACHIEVEMENTS.level50, this.createLevelOptions(50)); - this.add(ACHIEVEMENTS.logoBefore18, { - isValid: this.isLogoBefore18Valid, - signal: "itemProduced", - }); - this.add(ACHIEVEMENTS.mam, { - isValid: this.isMamValid, - }); - this.add(ACHIEVEMENTS.mapMarkers15, { - isValid: this.isMapMarkers15Valid, - }); - this.add(ACHIEVEMENTS.noBeltUpgradesUntilBp, { - isValid: this.isNoBeltUpgradesUntilBpValid, - signal: "storyGoalCompleted", - }); - this.add(ACHIEVEMENTS.noInverseRotater, { - init: this.initNoInverseRotater, - isValid: this.isNoInverseRotaterValid, - signal: "storyGoalCompleted", - }); - this.add(ACHIEVEMENTS.oldLevel17, this.createShapeOptions(SHAPE_OLD_LEVEL_17)); - this.add(ACHIEVEMENTS.openWires, { - isValid: this.isOpenWiresValid, - signal: "editModeChanged", - }); - this.add(ACHIEVEMENTS.paintShape); - this.add(ACHIEVEMENTS.place5000Wires, { - isValid: this.isPlace5000WiresValid, - }); - this.add(ACHIEVEMENTS.placeBlueprint, { - isValid: this.isPlaceBlueprintValid, - }); - this.add(ACHIEVEMENTS.placeBp1000, { - isValid: this.isPlaceBp1000Valid, - }); - this.add(ACHIEVEMENTS.play1h, this.createTimeOptions(HOUR_1)); - this.add(ACHIEVEMENTS.play10h, this.createTimeOptions(HOUR_10)); - this.add(ACHIEVEMENTS.play20h, this.createTimeOptions(HOUR_20)); - this.add(ACHIEVEMENTS.produceLogo, this.createShapeOptions(SHAPE_LOGO)); - this.add(ACHIEVEMENTS.produceRocket, this.createShapeOptions(SHAPE_ROCKET)); - this.add(ACHIEVEMENTS.produceMsLogo, this.createShapeOptions(SHAPE_MS_LOGO)); - this.add(ACHIEVEMENTS.rotateShape); - this.add(ACHIEVEMENTS.speedrunBp30, this.createSpeedOptions(12, MINUTE_30)); - this.add(ACHIEVEMENTS.speedrunBp60, this.createSpeedOptions(12, MINUTE_60)); - this.add(ACHIEVEMENTS.speedrunBp120, this.createSpeedOptions(12, MINUTE_120)); - this.add(ACHIEVEMENTS.stack4Layers, { - isValid: this.isStack4LayersValid, - signal: "itemProduced", - }); - this.add(ACHIEVEMENTS.stackShape); - this.add(ACHIEVEMENTS.store100Unique, { - init: this.initStore100Unique, - isValid: this.isStore100UniqueValid, - signal: "shapeDelivered", - }); - this.add(ACHIEVEMENTS.storeShape, { - init: this.initStoreShape, - isValid: this.isStoreShapeValid, - }); - this.add(ACHIEVEMENTS.throughputBp25, this.createRateOptions(SHAPE_BP, 25)); - this.add(ACHIEVEMENTS.throughputBp50, this.createRateOptions(SHAPE_BP, 50)); - this.add(ACHIEVEMENTS.throughputLogo25, this.createRateOptions(SHAPE_LOGO, 25)); - this.add(ACHIEVEMENTS.throughputLogo50, this.createRateOptions(SHAPE_LOGO, 50)); - this.add(ACHIEVEMENTS.throughputRocket10, this.createRateOptions(SHAPE_ROCKET, 10)); - this.add(ACHIEVEMENTS.throughputRocket20, this.createRateOptions(SHAPE_ROCKET, 20)); - this.add(ACHIEVEMENTS.trash1000, { - init: this.initTrash1000, - isValid: this.isTrash1000Valid, - }); - this.add(ACHIEVEMENTS.unlockWires, this.createLevelOptions(20)); - this.add(ACHIEVEMENTS.upgradesTier5, this.createUpgradeOptions(5)); - this.add(ACHIEVEMENTS.upgradesTier8, this.createUpgradeOptions(8)); - } - - /** @param {GameRoot} root */ - initialize(root) { - this.root = root; - this.root.signals.achievementCheck.add(this.unlock, this); - this.root.signals.bulkAchievementCheck.add(this.bulkUnlock, this); - - for (let [key, achievement] of this.map.entries()) { - if (achievement.signal) { - achievement.receiver = this.unlock.bind(this, key); - this.root.signals[achievement.signal].add(achievement.receiver); - } - - if (achievement.init) { - achievement.init(); - } - } - - if (!this.hasDefaultReceivers()) { - this.root.signals.achievementCheck.remove(this.unlock); - this.root.signals.bulkAchievementCheck.remove(this.bulkUnlock); - } - } - - /** - * @param {string} key - Maps to an Achievement - * @param {object} [options] - * @param {function} [options.init] - * @param {function} [options.isValid] - * @param {string} [options.signal] - */ - add(key, options = {}) { - if (G_IS_DEV) { - assert(ACHIEVEMENTS[key], "Achievement key not found: ", key); - } - - const achievement = new Achievement(key); - - achievement.activate = this.activate; - - if (options.init) { - achievement.init = options.init.bind(this, achievement); - } - - if (options.isValid) { - achievement.isValid = options.isValid.bind(this); - } - - if (options.signal) { - achievement.signal = options.signal; - } - - this.map.set(key, achievement); - } - - bulkUnlock() { - for (let i = 0; i < arguments.length; i += 2) { - this.unlock(arguments[i], arguments[i + 1]); - } - } - - /** - * @param {string} key - Maps to an Achievement - * @param {any} data - Data received from signal dispatches for validation - */ - unlock(key, data) { - if (!this.map.has(key)) { - return; - } - - const achievement = this.map.get(key); - - if (!achievement.isValid(data)) { - return; - } - - achievement - .unlock() - .then(() => { - this.onActivate(null, key); - }) - .catch(err => { - this.onActivate(err, key); - }); - } - - /** - * Cleans up after achievement activation attempt with the provider. Could - * utilize err to retry some number of times if needed. - * @param {?Error} err - Error is null if activation was successful - * @param {string} key - Maps to an Achievement - */ - onActivate(err, key) { - this.remove(key); - - if (!this.hasDefaultReceivers()) { - this.root.signals.achievementCheck.remove(this.unlock); - } - } - - /** @param {string} key - Maps to an Achievement */ - remove(key) { - const achievement = this.map.get(key); - if (achievement) { - if (achievement.receiver) { - this.root.signals[achievement.signal].remove(achievement.receiver); - } - - this.map.delete(key); - } - } - - /** - * Check if the collection-level achievementCheck receivers are still - * necessary. - */ - hasDefaultReceivers() { - if (!this.map.size) { - return false; - } - - for (let achievement of this.map.values()) { - if (!achievement.signal) { - return true; - } - } - - return false; - } - - /* - * Remaining methods exist to extend Achievement instances within the - * collection. - */ - - hasAllUpgradesAtLeastAtTier(tier) { - const upgrades = this.root.gameMode.getUpgrades(); - - for (let upgradeId in upgrades) { - if (this.root.hubGoals.getUpgradeLevel(upgradeId) < tier - 1) { - return false; - } - } - - return true; - } - - /** - * @param {ShapeItem} item - * @param {string} shape - * @returns {boolean} - */ - isShape(item, shape) { - return item.getItemType() === ITEM_SHAPE && item.definition.getHash() === shape; - } - - createBlueprintOptions(count) { - return { - init: ({ key }) => this.unlock(key, ShapeDefinition.fromShortKey(SHAPE_BP)), - isValid: definition => - definition.cachedHash === SHAPE_BP && this.root.hubGoals.storedShapes[SHAPE_BP] >= count, - signal: "shapeDelivered", - }; - } - - createLevelOptions(level) { - return { - init: ({ key }) => this.unlock(key, this.root.hubGoals.level), - isValid: currentLevel => currentLevel > level, - signal: "storyGoalCompleted", - }; - } - - createRateOptions(shape, rate) { - return { - isValid: () => { - return ( - this.root.productionAnalytics.getCurrentShapeRateRaw( - enumAnalyticsDataSource.delivered, - this.root.shapeDefinitionMgr.getShapeFromShortKey(shape) - ) / - globalConfig.analyticsSliceDurationSeconds >= - rate - ); - }, - }; - } - - createShapeOptions(shape) { - return { - isValid: item => this.isShape(item, shape), - signal: "itemProduced", - }; - } - - createSpeedOptions(level, time) { - return { - isValid: currentLevel => currentLevel >= level && this.root.time.now() < time, - signal: "storyGoalCompleted", - }; - } - - createTimeOptions(duration) { - return { - isValid: () => this.root.time.now() >= duration, - }; - } - - createUpgradeOptions(tier) { - return { - init: ({ key }) => this.unlock(key, null), - isValid: () => this.hasAllUpgradesAtLeastAtTier(tier), - signal: "upgradePurchased", - }; - } - - /** @param {Entity} entity @returns {boolean} */ - isBelt500TilesValid(entity) { - return entity.components.Belt && entity.components.Belt.assignedPath.totalLength >= 500; - } - - /** @returns {boolean} */ - isDarkModeValid() { - return this.root.app.settings.currentData.settings.theme === DARK_MODE; - } - - /** @param {number} count @returns {boolean} */ - isDestroy1000Valid(count) { - return count >= 1000; - } - - /** @param {ShapeDefinition} definition @returns {boolean} */ - isIrrelevantShapeValid(definition) { - const levels = this.root.gameMode.getLevelDefinitions(); - for (let i = 0; i < levels.length; i++) { - if (definition.cachedHash === levels[i].shape) { - return false; - } - } - - const upgrades = this.root.gameMode.getUpgrades(); - for (let upgradeId in upgrades) { - for (const tier in upgrades[upgradeId]) { - const requiredShapes = upgrades[upgradeId][tier].required; - for (let i = 0; i < requiredShapes.length; i++) { - if (definition.cachedHash === requiredShapes[i].shape) { - return false; - } - } - } - } - - return true; - } - - /** @param {ShapeItem} item @returns {boolean} */ - isLogoBefore18Valid(item) { - return this.root.hubGoals.level < 18 && this.isShape(item, SHAPE_LOGO); - } - - /** @returns {boolean} */ - isMamValid() { - return this.root.hubGoals.level > 27 && !this.root.savegame.currentData.stats.failedMam; - } - - /** @param {number} count @returns {boolean} */ - isMapMarkers15Valid(count) { - return count >= 15; - } - - /** - * @param {number} level - * @returns {boolean} - */ - isNoBeltUpgradesUntilBpValid(level) { - return level >= 12 && this.root.hubGoals.upgradeLevels.belt === 0; - } - - initNoInverseRotater() { - if (this.root.savegame.currentData.stats.usedInverseRotater === true) { - return; - } - - const entities = this.root.entityMgr.componentToEntity.StaticMapEntity; - - let usedInverseRotater = false; - for (var i = 0; i < entities.length; i++) { - const entity = entities[i].components.StaticMapEntity; - - if (entity.code === ROTATER_CCW_CODE || entity.code === ROTATER_180_CODE) { - usedInverseRotater = true; - break; - } - } - - this.root.savegame.currentData.stats.usedInverseRotater = usedInverseRotater; - } - - /** @param {number} level @returns {boolean} */ - isNoInverseRotaterValid(level) { - return level >= 14 && !this.root.savegame.currentData.stats.usedInverseRotater; - } - - /** @param {string} currentLayer @returns {boolean} */ - isOpenWiresValid(currentLayer) { - return currentLayer === WIRE_LAYER; - } - - /** @param {Entity} entity @returns {boolean} */ - isPlace5000WiresValid(entity) { - return ( - entity.components.Wire && - entity.registered && - entity.root.entityMgr.componentToEntity.Wire.length >= 5000 - ); - } - - /** @param {number} count @returns {boolean} */ - isPlaceBlueprintValid(count) { - return count != 0; - } - - /** @param {number} count @returns {boolean} */ - isPlaceBp1000Valid(count) { - return count >= 1000; - } - - /** @param {ShapeItem} item @returns {boolean} */ - isStack4LayersValid(item) { - return item.getItemType() === ITEM_SHAPE && item.definition.layers.length === 4; - } - - /** @param {Achievement} achievement */ - initStore100Unique({ key }) { - this.unlock(key, null); - } - - /** @returns {boolean} */ - isStore100UniqueValid() { - return Object.keys(this.root.hubGoals.storedShapes).length >= 100; - } - - /** @param {Achievement} achievement */ - initStoreShape({ key }) { - this.unlock(key, null); - } - - /** @returns {boolean} */ - isStoreShapeValid() { - const entities = this.root.systemMgr.systems.storage.allEntities; - - if (entities.length === 0) { - return false; - } - - for (var i = 0; i < entities.length; i++) { - if (entities[i].components.Storage.storedCount > 0) { - return true; - } - } - - return false; - } - - /** @param {Achievement} achievement */ - initTrash1000({ key }) { - if (Number(this.root.savegame.currentData.stats.trashedCount)) { - this.unlock(key, 0); - return; - } - - this.root.savegame.currentData.stats.trashedCount = 0; - } - - /** @param {number} count @returns {boolean} */ - isTrash1000Valid(count) { - this.root.savegame.currentData.stats.trashedCount += count; - - return this.root.savegame.currentData.stats.trashedCount >= 1000; - } -} diff --git a/src/js/platform/ad_provider.js b/src/js/platform/ad_provider.js deleted file mode 100644 index 6b96768e..00000000 --- a/src/js/platform/ad_provider.js +++ /dev/null @@ -1,49 +0,0 @@ -/* typehints:start */ -import { Application } from "../application"; -/* typehints:end */ - -export class AdProviderInterface { - /** @param {Application} app */ - constructor(app) { - this.app = app; - } - - /** - * Initializes the storage - * @returns {Promise} - */ - initialize() { - return Promise.resolve(); - } - - /** - * Returns if this provider serves ads at all - * @returns {boolean} - * @abstract - */ - getHasAds() { - abstract; - return false; - } - - /** - * Returns if it would be possible to show a video ad *now*. This can be false if for - * example the last video ad is - * @returns {boolean} - * @abstract - */ - getCanShowVideoAd() { - abstract; - return false; - } - - /** - * Shows an video ad - * @returns {Promise} - */ - showVideoAd() { - return Promise.resolve(); - } - - setPlayStatus(playing) {} -} diff --git a/src/js/platform/ad_providers/adinplay.js b/src/js/platform/ad_providers/adinplay.js deleted file mode 100644 index 3897ec04..00000000 --- a/src/js/platform/ad_providers/adinplay.js +++ /dev/null @@ -1,191 +0,0 @@ -/* typehints:start */ -import { Application } from "../../application"; -/* typehints:end */ - -import { AdProviderInterface } from "../ad_provider"; -import { createLogger } from "../../core/logging"; -import { ClickDetector } from "../../core/click_detector"; -import { clamp } from "../../core/utils"; -import { T } from "../../translations"; - -const logger = createLogger("adprovider/adinplay"); - -const minimumTimeBetweenVideoAdsMs = G_IS_DEV ? 1 : 15 * 60 * 1000; - -export class AdinplayAdProvider extends AdProviderInterface { - /** - * - * @param {Application} app - */ - constructor(app) { - super(app); - - /** @type {ClickDetector} */ - this.getOnSteamClickDetector = null; - - /** @type {Element} */ - this.adContainerMainElement = null; - - /** - * The resolve function to finish the current video ad. Null if none is currently running - * @type {Function} - */ - this.videoAdResolveFunction = null; - - /** - * The current timer which will timeout the resolve - */ - this.videoAdResolveTimer = null; - - /** - * When we showed the last video ad - */ - this.lastVideoAdShowTime = -1e20; - } - - getHasAds() { - return true; - } - - getCanShowVideoAd() { - return ( - this.getHasAds() && - !this.videoAdResolveFunction && - performance.now() - this.lastVideoAdShowTime > minimumTimeBetweenVideoAdsMs - ); - } - - initialize() { - // No point to initialize everything if ads are not supported - if (!this.getHasAds()) { - return Promise.resolve(); - } - - logger.log("🎬 Initializing Adinplay"); - - // Add the preroll element - this.adContainerMainElement = document.createElement("div"); - this.adContainerMainElement.id = "adinplayVideoContainer"; - this.adContainerMainElement.innerHTML = ` -
-
- -
-
- `; - - // Add the setup script - const setupScript = document.createElement("script"); - setupScript.textContent = ` - var aiptag = aiptag || {}; - aiptag.cmd = aiptag.cmd || []; - aiptag.cmd.display = aiptag.cmd.display || []; - aiptag.cmd.player = aiptag.cmd.player || []; - `; - document.head.appendChild(setupScript); - - window.aiptag.gdprShowConsentTool = 0; - window.aiptag.gdprAlternativeConsentTool = 1; - window.aiptag.gdprConsent = 1; - - const scale = this.app.getEffectiveUiScale(); - const targetW = 960; - const targetH = 540; - - const maxScaleX = (window.innerWidth - 100 * scale) / targetW; - const maxScaleY = (window.innerHeight - 150 * scale) / targetH; - - const scaleFactor = clamp(Math.min(maxScaleX, maxScaleY), 0.25, 2); - - const w = Math.round(targetW * scaleFactor); - const h = Math.round(targetH * scaleFactor); - - // Add the player - const videoElement = this.adContainerMainElement.querySelector(".videoInner"); - /** @type {HTMLElement} */ - const adInnerElement = this.adContainerMainElement.querySelector(".adInner"); - - adInnerElement.style.maxWidth = w + "px"; - - const self = this; - window.aiptag.cmd.player.push(function () { - window.adPlayer = new window.aipPlayer({ - AD_WIDTH: w, - AD_HEIGHT: h, - AD_FULLSCREEN: false, - AD_CENTERPLAYER: false, - LOADING_TEXT: T.global.loading, - PREROLL_ELEM: function () { - return videoElement; - }, - AIP_COMPLETE: function () { - logger.log("🎬 ADINPLAY AD: completed"); - self.adContainerMainElement.classList.add("waitingForFinish"); - }, - AIP_REMOVE: function () { - logger.log("🎬 ADINPLAY AD: remove"); - if (self.videoAdResolveFunction) { - self.videoAdResolveFunction(); - } - }, - }); - }); - - // Load the ads - const aipScript = document.createElement("script"); - aipScript.src = "https://api.adinplay.com/libs/aiptag/pub/YRG/shapez.io/tag.min.js"; - aipScript.setAttribute("async", ""); - document.head.appendChild(aipScript); - - return Promise.resolve(); - } - - showVideoAd() { - assert(this.getHasAds(), "Called showVideoAd but ads are not supported!"); - assert(!this.videoAdResolveFunction, "Video ad still running, can not show again!"); - this.lastVideoAdShowTime = performance.now(); - document.body.appendChild(this.adContainerMainElement); - this.adContainerMainElement.classList.add("visible"); - this.adContainerMainElement.classList.remove("waitingForFinish"); - - try { - // @ts-ignore - window.aiptag.cmd.player.push(function () { - console.log("🎬 ADINPLAY AD: Start pre roll"); - window.adPlayer.startPreRoll(); - }); - } catch (ex) { - logger.warn("🎬 Failed to play video ad:", ex); - document.body.removeChild(this.adContainerMainElement); - this.adContainerMainElement.classList.remove("visible"); - return Promise.resolve(); - } - - return new Promise(resolve => { - // So, wait for the remove call but also remove after N seconds - this.videoAdResolveFunction = () => { - this.videoAdResolveFunction = null; - clearTimeout(this.videoAdResolveTimer); - this.videoAdResolveTimer = null; - - // When the ad closed, also set the time - this.lastVideoAdShowTime = performance.now(); - resolve(); - }; - - this.videoAdResolveTimer = setTimeout(() => { - logger.warn(this, "Automatically closing ad after not receiving callback"); - if (this.videoAdResolveFunction) { - this.videoAdResolveFunction(); - } - }, 120 * 1000); - }) - .catch(err => { - logger.error("Error while resolving video ad:", err); - }) - .then(() => { - document.body.removeChild(this.adContainerMainElement); - this.adContainerMainElement.classList.remove("visible"); - }); - } -} diff --git a/src/js/platform/ad_providers/crazygames.js b/src/js/platform/ad_providers/crazygames.js deleted file mode 100644 index 5f70cea4..00000000 --- a/src/js/platform/ad_providers/crazygames.js +++ /dev/null @@ -1,99 +0,0 @@ -import { AdProviderInterface } from "../ad_provider"; -import { createLogger } from "../../core/logging"; -import { timeoutPromise } from "../../core/utils"; - -const logger = createLogger("crazygames"); - -export class CrazygamesAdProvider extends AdProviderInterface { - getHasAds() { - return true; - } - - getCanShowVideoAd() { - return this.getHasAds() && this.sdkInstance; - } - - get sdkInstance() { - try { - return window.CrazyGames.CrazySDK.getInstance(); - } catch (ex) { - return null; - } - } - - initialize() { - if (!this.getHasAds()) { - return Promise.resolve(); - } - - logger.log("🎬 Initializing crazygames SDK"); - - const scriptTag = document.createElement("script"); - scriptTag.type = "text/javascript"; - - return timeoutPromise( - new Promise((resolve, reject) => { - scriptTag.onload = resolve; - scriptTag.onerror = reject; - scriptTag.src = "https://sdk.crazygames.com/crazygames-sdk-v1.js"; - document.head.appendChild(scriptTag); - }) - .then(() => { - logger.log("🎬 Crazygames SDK loaded, now initializing"); - this.sdkInstance.init(); - }) - .catch(ex => { - console.warn("Failed to init crazygames SDK:", ex); - }) - ); - } - - showVideoAd() { - const instance = this.sdkInstance; - if (!instance) { - return Promise.resolve(); - } - - logger.log("Set sound volume to 0"); - this.app.sound.setMusicVolume(0); - this.app.sound.setSoundVolume(0); - - return timeoutPromise( - new Promise(resolve => { - console.log("🎬 crazygames: Start ad"); - document.body.classList.add("externalAdOpen"); - - const finish = () => { - instance.removeEventListener("adError", finish); - instance.removeEventListener("adFinished", finish); - resolve(); - }; - - instance.addEventListener("adError", finish); - instance.addEventListener("adFinished", finish); - - instance.requestAd(); - }), - 60000 - ) - .catch(ex => { - console.warn("Error while resolving video ad:", ex); - }) - .then(() => { - document.body.classList.remove("externalAdOpen"); - - logger.log("Restored sound volume"); - - this.app.sound.setMusicVolume(this.app.settings.getSetting("musicVolume")); - this.app.sound.setSoundVolume(this.app.settings.getSetting("soundVolume")); - }); - } - setPlayStatus(playing) { - console.log("crazygames::playing:", playing); - if (playing) { - this.sdkInstance.gameplayStart(); - } else { - this.sdkInstance.gameplayStop(); - } - } -} diff --git a/src/js/platform/ad_providers/gamedistribution.js b/src/js/platform/ad_providers/gamedistribution.js deleted file mode 100644 index be8e41a3..00000000 --- a/src/js/platform/ad_providers/gamedistribution.js +++ /dev/null @@ -1,131 +0,0 @@ -/* typehints:start */ -import { Application } from "../../application"; -/* typehints:end */ - -import { AdProviderInterface } from "../ad_provider"; -import { createLogger } from "../../core/logging"; - -const minimumTimeBetweenVideoAdsMs = G_IS_DEV ? 1 : 5 * 60 * 1000; - -const logger = createLogger("gamedistribution"); - -export class GamedistributionAdProvider extends AdProviderInterface { - /** - * - * @param {Application} app - */ - constructor(app) { - super(app); - - /** - * The resolve function to finish the current video ad. Null if none is currently running - * @type {Function} - */ - this.videoAdResolveFunction = null; - - /** - * The current timer which will timeout the resolve - */ - this.videoAdResolveTimer = null; - - /** - * When we showed the last video ad - */ - this.lastVideoAdShowTime = -1e20; - } - - getHasAds() { - return true; - } - - getCanShowVideoAd() { - return ( - this.getHasAds() && - !this.videoAdResolveFunction && - performance.now() - this.lastVideoAdShowTime > minimumTimeBetweenVideoAdsMs - ); - } - - initialize() { - // No point to initialize everything if ads are not supported - if (!this.getHasAds()) { - return Promise.resolve(); - } - - logger.log("🎬 Initializing gamedistribution ads"); - - try { - parent.postMessage("shapezio://gd.game_loaded", "*"); - } catch (ex) { - return Promise.reject("Frame communication not allowed"); - } - - window.addEventListener( - "message", - event => { - if (event.data === "shapezio://gd.ad_started") { - console.log("🎬 Got ad started callback"); - } else if (event.data === "shapezio://gd.ad_finished") { - console.log("🎬 Got ad finished callback"); - if (this.videoAdResolveFunction) { - this.videoAdResolveFunction(); - } - } - }, - false - ); - - return Promise.resolve(); - } - - showVideoAd() { - assert(this.getHasAds(), "Called showVideoAd but ads are not supported!"); - assert(!this.videoAdResolveFunction, "Video ad still running, can not show again!"); - this.lastVideoAdShowTime = performance.now(); - - console.log("🎬 Gamedistribution: Start ad"); - try { - parent.postMessage("shapezio://gd.show_ad", "*"); - } catch (ex) { - logger.warn("🎬 Failed to send message for gd ad:", ex); - return Promise.resolve(); - } - - document.body.classList.add("externalAdOpen"); - - logger.log("Set sound volume to 0"); - this.app.sound.setMusicVolume(0); - this.app.sound.setSoundVolume(0); - - return new Promise(resolve => { - // So, wait for the remove call but also remove after N seconds - this.videoAdResolveFunction = () => { - this.videoAdResolveFunction = null; - clearTimeout(this.videoAdResolveTimer); - this.videoAdResolveTimer = null; - - // When the ad closed, also set the time - this.lastVideoAdShowTime = performance.now(); - resolve(); - }; - - this.videoAdResolveTimer = setTimeout(() => { - logger.warn("Automatically closing ad after not receiving callback"); - if (this.videoAdResolveFunction) { - this.videoAdResolveFunction(); - } - }, 35000); - }) - .catch(err => { - logger.error(this, "Error while resolving video ad:", err); - }) - .then(() => { - document.body.classList.remove("externalAdOpen"); - - logger.log("Restored sound volume"); - - this.app.sound.setMusicVolume(this.app.settings.getSetting("musicVolume")); - this.app.sound.setSoundVolume(this.app.settings.getSetting("soundVolume")); - }); - } -} diff --git a/src/js/platform/ad_providers/no_ad_provider.js b/src/js/platform/ad_providers/no_ad_provider.js deleted file mode 100644 index ca7d5db1..00000000 --- a/src/js/platform/ad_providers/no_ad_provider.js +++ /dev/null @@ -1,11 +0,0 @@ -import { AdProviderInterface } from "../ad_provider"; - -export class NoAdProvider extends AdProviderInterface { - getHasAds() { - return false; - } - - getCanShowVideoAd() { - return false; - } -} diff --git a/src/js/platform/analytics.js b/src/js/platform/analytics.js deleted file mode 100644 index 2a844522..00000000 --- a/src/js/platform/analytics.js +++ /dev/null @@ -1,41 +0,0 @@ -/* typehints:start */ -import { Application } from "../application"; -/* typehints:end */ - -export class AnalyticsInterface { - constructor(app) { - /** @type {Application} */ - this.app = app; - } - - /** - * Initializes the analytics - * @returns {Promise} - * @abstract - */ - initialize() { - abstract; - return Promise.reject(); - } - - /** - * Sets the player name for analytics - * @param {string} userName - */ - setUserContext(userName) {} - - /** - * Tracks when a new state is entered - * @param {string} stateId - */ - trackStateEnter(stateId) {} - - /** - * Tracks a new user decision - * @param {string} name - */ - trackDecision(name) {} - - // LEGACY 1.5.3 - trackUiClick() {} -} diff --git a/src/js/platform/api.js b/src/js/platform/api.js index 5dbac1da..d0accb5f 100644 --- a/src/js/platform/api.js +++ b/src/js/platform/api.js @@ -1,8 +1,10 @@ /* typehints:start */ import { Application } from "../application"; /* typehints:end */ + import { createLogger } from "../core/logging"; -import { compressX64 } from "../core/lzstring"; +import { DialogWithForm } from "../core/modal_dialog_elements"; +import { FormElementInput } from "../core/modal_dialog_forms"; import { timeoutPromise } from "../core/utils"; import { T } from "../translations"; @@ -24,12 +26,7 @@ export class ClientAPI { } getEndpoint() { - if (G_IS_DEV) { - return "http://localhost:15001"; - } - if (window.location.host === "beta.shapez.io") { - return "https://api-staging.shapez.io"; - } + // TODO: Custom Puzzle DLC server / extract API into a mod? return "https://api.shapez.io"; } @@ -100,32 +97,51 @@ export class ClientAPI { * @returns {Promise<{token: string}>} */ apiTryLogin() { - if (!G_IS_STANDALONE) { - let token = window.localStorage.getItem("steam_sso_auth_token"); - if (!token && G_IS_DEV) { - token = window.prompt( - "Please enter the auth token for the puzzle DLC (If you have none, you can't login):" - ); - window.localStorage.setItem("dev_api_auth_token", token); - } + // TODO: Wrap the dialogs hack properly (with a meaningful error at least) + // ...AND REDUCE THIS BOILERPLATE!!! + let token = window.localStorage.getItem("dev_api_auth_token"); + + if (token !== null) { return Promise.resolve({ token }); } - return timeoutPromise(ipcRenderer.invoke("steam:get-ticket"), 15000).then( - ticket => { - logger.log("Got auth ticket:", ticket); - return this._request("/v1/public/login", { - method: "POST", - body: { - token: ticket, - }, + const state = this.app.stateMgr.getCurrentState(); + if (!("dialogs" in state)) { + return Promise.reject(new Error("Failed to show token input dialog")); + } + + /** @type {import("../game/hud/parts/modal_dialogs").HUDModalDialogs} */ + // @ts-ignore + const dialogs = state.dialogs; + + const apiTokenInput = new FormElementInput({ + id: "apiToken", + placeholder: "", + validator: value => value.trim().length > 0, + }); + + const dialog = new DialogWithForm({ + app: this.app, + title: "API Login", + desc: "Enter the Puzzle DLC API token:", + formElements: [apiTokenInput], + buttons: ["cancel", "ok:good"], + closeButton: false, + }); + + return new Promise((resolve, reject) => { + dialog.buttonSignals.ok.add(() => { + resolve({ + token: apiTokenInput.getValue(), }); - }, - err => { - logger.error("Failed to get auth ticket from steam: ", err); - throw err; - } - ); + }); + + dialog.buttonSignals.cancel.add(() => { + reject(new Error("Token input dismissed")); + }); + + dialogs.internalShowDialog(dialog); + }); } /** @@ -235,7 +251,8 @@ export class ClientAPI { method: "POST", body: { ...payload, - data: compressX64(JSON.stringify(payload.data)), + // FIXME: Server expects lzstring compressed payload + data: JSON.stringify(payload.data), }, }); } diff --git a/src/js/platform/browser/game_analytics.js b/src/js/platform/browser/game_analytics.js deleted file mode 100644 index 894f877a..00000000 --- a/src/js/platform/browser/game_analytics.js +++ /dev/null @@ -1,419 +0,0 @@ -import { globalConfig } from "../../core/config"; -import { createLogger } from "../../core/logging"; -import { queryParamOptions } from "../../core/query_parameters"; -import { randomInt } from "../../core/utils"; -import { BeltComponent } from "../../game/components/belt"; -import { StaticMapEntityComponent } from "../../game/components/static_map_entity"; -import { RegularGameMode } from "../../game/modes/regular"; -import { GameRoot } from "../../game/root"; -import { InGameState } from "../../states/ingame"; -import { SteamAchievementProvider } from "../electron/steam_achievement_provider"; -import { GameAnalyticsInterface } from "../game_analytics"; -import { FILE_NOT_FOUND } from "../storage"; -import { WEB_STEAM_SSO_AUTHENTICATED } from "../../core/steam_sso"; - -const logger = createLogger("game_analytics"); - -const analyticsUrl = G_IS_DEV ? "http://localhost:8001" : "https://analytics.shapez.io"; - -// Be sure to increment the ID whenever it changes -const analyticsLocalFile = G_IS_STEAM_DEMO ? "shapez_token_steamdemo.bin" : "shapez_token_123.bin"; - -const CURRENT_ABT = "abt_bsl2"; -const CURRENT_ABT_COUNT = 1; - -export class ShapezGameAnalytics extends GameAnalyticsInterface { - constructor(app) { - super(app); - this.abtVariant = "0"; - } - - get environment() { - if (G_IS_DEV) { - return "dev"; - } - - if (G_IS_STEAM_DEMO) { - return "steam-demo"; - } - - if (G_IS_STANDALONE) { - return "steam"; - } - - if (WEB_STEAM_SSO_AUTHENTICATED) { - return "prod-full"; - } - - if (G_IS_RELEASE) { - return "prod"; - } - - if (window.location.host.indexOf("alpha") >= 0) { - return "alpha"; - } else { - return "beta"; - } - } - - fetchABVariant() { - return this.app.storage.readFileAsync("shapez_" + CURRENT_ABT + ".bin").then( - abt => { - if (typeof queryParamOptions.abtVariant === "string") { - this.abtVariant = queryParamOptions.abtVariant; - logger.log("Set", CURRENT_ABT, "to (OVERRIDE) ", this.abtVariant); - } else { - this.abtVariant = abt; - logger.log("Read abtVariant:", abt); - } - }, - err => { - if (err === FILE_NOT_FOUND) { - if (typeof queryParamOptions.abtVariant === "string") { - this.abtVariant = queryParamOptions.abtVariant; - logger.log("Set", CURRENT_ABT, "to (OVERRIDE) ", this.abtVariant); - } else { - this.abtVariant = String(randomInt(0, CURRENT_ABT_COUNT - 1)); - logger.log("Set", CURRENT_ABT, "to", this.abtVariant); - } - this.app.storage.writeFileAsync("shapez_" + CURRENT_ABT + ".bin", this.abtVariant); - } - } - ); - } - - note(action) { - if (this.app.restrictionMgr.isLimitedVersion()) { - fetch( - "https://analytics.shapez.io/campaign/" + - "action_" + - this.environment + - "_" + - action + - "_" + - CURRENT_ABT + - "_" + - this.abtVariant + - "?lpurl=nocontent", - { - method: "GET", - mode: "no-cors", - cache: "no-cache", - referrer: "no-referrer", - credentials: "omit", - } - ).catch(err => {}); - } - } - - noteMinor(action, payload = "") {} - - /** - * @returns {Promise} - */ - initialize() { - this.syncKey = null; - - if (G_WEGAME_VERSION) { - return; - } - - window.setAbt = abt => { - this.app.storage.writeFileAsync("shapez_" + CURRENT_ABT + ".bin", String(abt)); - window.location.reload(); - }; - - // Retrieve sync key from player - return this.fetchABVariant().then(() => { - setInterval(() => this.sendTimePoints(), 60 * 1000); - - if (this.app.restrictionMgr.isLimitedVersion() && !G_IS_DEV) { - fetch( - "https://analytics.shapez.io/campaign/" + - this.environment + - "_" + - CURRENT_ABT + - "_" + - this.abtVariant + - "?lpurl=nocontent", - { - method: "GET", - mode: "no-cors", - cache: "no-cache", - referrer: "no-referrer", - credentials: "omit", - } - ).catch(err => {}); - } - - return this.app.storage.readFileAsync(analyticsLocalFile).then( - syncKey => { - this.syncKey = syncKey; - logger.log("Player sync key read:", this.syncKey); - }, - error => { - // File was not found, retrieve new key - if (error === FILE_NOT_FOUND) { - logger.log("Retrieving new player key"); - - let authTicket = Promise.resolve(undefined); - - if (G_IS_STANDALONE && !G_IS_STEAM_DEMO) { - logger.log("Will retrieve auth ticket"); - authTicket = ipcRenderer.invoke("steam:get-ticket"); - } - - authTicket - .then( - ticket => { - logger.log("Got ticket:", ticket); - - // Perform call to get a new key from the API - return this.sendToApi("/v1/register", { - environment: this.environment, - standalone: - G_IS_STANDALONE && - !G_IS_STEAM_DEMO && - this.app.achievementProvider instanceof SteamAchievementProvider, - commit: G_BUILD_COMMIT_HASH, - ticket, - }); - }, - err => { - logger.warn("Failed to get steam auth ticket for register:", err); - } - ) - .then(res => { - // Try to read and parse the key from the api - if (res.key && typeof res.key === "string" && res.key.length === 40) { - this.syncKey = res.key; - logger.log("Key retrieved:", this.syncKey); - this.app.storage.writeFileAsync(analyticsLocalFile, res.key); - } else { - throw new Error("Bad response from analytics server: " + res); - } - }) - .catch(err => { - logger.error("Failed to register on analytics api:", err); - }); - } else { - logger.error("Failed to read ga key:", error); - } - return; - } - ); - }); - } - - /** - * Makes sure a DLC is activated on steam - * @param {string} dlc - */ - activateDlc(dlc) { - logger.log("Activating dlc:", dlc); - return this.sendToApi("/v1/activate-dlc/" + dlc, {}); - } - - /** - * Sends a request to the api - * @param {string} endpoint Endpoint without base url - * @param {object} data payload - * @returns {Promise} - */ - sendToApi(endpoint, data) { - if (G_WEGAME_VERSION) { - return Promise.resolve(); - } - - return new Promise((resolve, reject) => { - const timeout = setTimeout(() => reject("Request to " + endpoint + " timed out"), 20000); - - fetch(analyticsUrl + endpoint, { - method: "POST", - mode: "cors", - cache: "no-cache", - referrer: "no-referrer", - credentials: "omit", - headers: { - "Content-Type": "application/json", - "Accept": "application/json", - "x-api-key": globalConfig.info.analyticsApiKey, - }, - body: JSON.stringify(data), - }) - .then(res => { - clearTimeout(timeout); - if (!res.ok || res.status !== 200) { - reject("Fetch error: Bad status " + res.status); - } else { - return res.json(); - } - }) - .then(resolve) - .catch(reason => { - clearTimeout(timeout); - reject(reason); - }); - }); - } - - /** - * Sends a game event to the analytics - * @param {string} category - * @param {string} value - */ - sendGameEvent(category, value) { - if (G_WEGAME_VERSION) { - return; - } - - if (G_IS_DEV) { - return; - } - - if (!this.syncKey) { - logger.warn("Can not send event due to missing sync key"); - return; - } - - const gameState = this.app.stateMgr.currentState; - if (!(gameState instanceof InGameState)) { - logger.warn("Trying to send analytics event outside of ingame state"); - return; - } - - const savegame = gameState.savegame; - if (!savegame) { - logger.warn("Ingame state has empty savegame"); - return; - } - - const savegameId = savegame.internalId; - if (!gameState.core) { - logger.warn("Game state has no core"); - return; - } - const root = gameState.core.root; - if (!root) { - logger.warn("Root is not initialized"); - return; - } - - if (!(root.gameMode instanceof RegularGameMode)) { - return; - } - - logger.log("Sending event", category, value); - - this.sendToApi("/v1/game-event", { - playerKey: this.syncKey, - gameKey: savegameId, - ingameTime: root.time.now(), - environment: this.environment, - category, - value, - version: G_BUILD_VERSION, - level: root.hubGoals.level, - gameDump: this.generateGameDump(root), - }).catch(err => { - console.warn("Request failed", err); - }); - } - - sendTimePoints() { - const gameState = this.app.stateMgr.currentState; - if (gameState instanceof InGameState) { - logger.log("Syncing analytics"); - this.sendGameEvent("sync", ""); - } - } - - /** - * Returns true if the shape is interesting - * @param {GameRoot} root - * @param {string} key - */ - isInterestingShape(root, key) { - if (key === root.gameMode.getBlueprintShapeKey()) { - return true; - } - - // Check if its a story goal - const levels = root.gameMode.getLevelDefinitions(); - for (let i = 0; i < levels.length; ++i) { - if (key === levels[i].shape) { - return true; - } - } - - // Check if its required to unlock an upgrade - const upgrades = root.gameMode.getUpgrades(); - for (const upgradeKey in upgrades) { - const upgradeTiers = upgrades[upgradeKey]; - for (let i = 0; i < upgradeTiers.length; ++i) { - const tier = upgradeTiers[i]; - const required = tier.required; - for (let k = 0; k < required.length; ++k) { - if (required[k].shape === key) { - return true; - } - } - } - } - - return false; - } - - /** - * Generates a game dump - * @param {GameRoot} root - */ - generateGameDump(root) { - const shapeIds = Object.keys(root.hubGoals.storedShapes).filter(key => - this.isInterestingShape(root, key) - ); - let shapes = {}; - for (let i = 0; i < shapeIds.length; ++i) { - shapes[shapeIds[i]] = root.hubGoals.storedShapes[shapeIds[i]]; - } - return { - shapes, - upgrades: root.hubGoals.upgradeLevels, - belts: root.entityMgr.getAllWithComponent(BeltComponent).length, - buildings: - root.entityMgr.getAllWithComponent(StaticMapEntityComponent).length - - root.entityMgr.getAllWithComponent(BeltComponent).length, - }; - } - - /** - */ - handleGameStarted() { - this.sendGameEvent("game_start", ""); - } - - /** - */ - handleGameResumed() { - this.sendTimePoints(); - } - - /** - * Handles the given level completed - * @param {number} level - */ - handleLevelCompleted(level) { - logger.log("Complete level", level); - this.sendGameEvent("level_complete", "" + level); - } - - /** - * Handles the given upgrade completed - * @param {string} id - * @param {number} level - */ - handleUpgradeUnlocked(id, level) { - logger.log("Unlock upgrade", id, level); - this.sendGameEvent("upgrade_unlock", id + "@" + level); - } -} diff --git a/src/js/platform/browser/google_analytics.js b/src/js/platform/browser/google_analytics.js deleted file mode 100644 index 34b75ce5..00000000 --- a/src/js/platform/browser/google_analytics.js +++ /dev/null @@ -1,78 +0,0 @@ -import { AnalyticsInterface } from "../analytics"; -import { createLogger } from "../../core/logging"; - -const logger = createLogger("ga"); - -export class GoogleAnalyticsImpl extends AnalyticsInterface { - initialize() { - this.lastUiClickTracked = -1000; - - setInterval(() => this.internalTrackAfkEvent(), 120 * 1000); - - // Analytics is already loaded in the html - return Promise.resolve(); - } - - setUserContext(userName) { - try { - if (window.gtag) { - logger.log("📊 Setting user context:", userName); - window.gtag("set", { - player: userName, - }); - } - } catch (ex) { - logger.warn("📊 Failed to set user context:", ex); - } - } - - trackStateEnter(stateId) { - const nonInteractionStates = [ - "LoginState", - "MainMenuState", - "PreloadState", - "RegisterState", - "WatchAdState", - ]; - - try { - if (window.gtag) { - logger.log("📊 Tracking state enter:", stateId); - window.gtag("event", "enter_state", { - event_category: "ui", - event_label: stateId, - non_interaction: nonInteractionStates.indexOf(stateId) >= 0, - }); - } - } catch (ex) { - logger.warn("📊 Failed to track state analytcis:", ex); - } - } - - trackDecision(decisionName) { - try { - if (window.gtag) { - logger.log("📊 Tracking decision:", decisionName); - window.gtag("event", "decision", { - event_category: "ui", - event_label: decisionName, - non_interaction: true, - }); - } - } catch (ex) { - logger.warn("📊 Failed to track state analytcis:", ex); - } - } - - /** - * Tracks an event so GA keeps track of the user - */ - internalTrackAfkEvent() { - if (window.gtag) { - window.gtag("event", "afk", { - event_category: "ping", - event_label: "timed", - }); - } - } -} diff --git a/src/js/platform/browser/no_achievement_provider.js b/src/js/platform/browser/no_achievement_provider.js deleted file mode 100644 index 8a8a343e..00000000 --- a/src/js/platform/browser/no_achievement_provider.js +++ /dev/null @@ -1,23 +0,0 @@ -import { AchievementProviderInterface } from "../achievement_provider"; - -export class NoAchievementProvider extends AchievementProviderInterface { - hasAchievements() { - return false; - } - - hasLoaded() { - return false; - } - - initialize() { - return Promise.resolve(); - } - - onLoad() { - return Promise.reject(new Error("No achievements to load")); - } - - activate() { - return Promise.resolve(); - } -} diff --git a/src/js/platform/browser/sound.js b/src/js/platform/browser/sound.js deleted file mode 100644 index a7089665..00000000 --- a/src/js/platform/browser/sound.js +++ /dev/null @@ -1,208 +0,0 @@ -import { MusicInstanceInterface, SoundInstanceInterface, SoundInterface, MUSIC, SOUNDS } from "../sound"; -import { cachebust } from "../../core/cachebust"; -import { createLogger } from "../../core/logging"; -import { globalConfig } from "../../core/config"; - -const { Howl, Howler } = require("howler"); - -const logger = createLogger("sound/browser"); - -// @ts-ignore -const sprites = require("../../built-temp/sfx.json"); - -class SoundSpritesContainer { - constructor() { - this.howl = null; - - this.loadingPromise = null; - } - - load() { - if (this.loadingPromise) { - return this.loadingPromise; - } - return (this.loadingPromise = new Promise(resolve => { - this.howl = new Howl({ - src: cachebust("res/sounds/sfx.mp3"), - sprite: sprites.sprite, - autoplay: false, - loop: false, - volume: 0, - preload: true, - pool: 20, - onload: () => { - resolve(); - }, - onloaderror: (id, err) => { - logger.warn("SFX failed to load:", id, err); - this.howl = null; - resolve(); - }, - onplayerror: (id, err) => { - logger.warn("SFX failed to play:", id, err); - }, - }); - })); - } - - play(volume, key) { - if (this.howl) { - const instance = this.howl.play(key); - this.howl.volume(volume, instance); - } - } - - deinitialize() { - if (this.howl) { - this.howl.unload(); - this.howl = null; - } - } -} - -class WrappedSoundInstance extends SoundInstanceInterface { - /** - * - * @param {SoundSpritesContainer} spriteContainer - * @param {string} key - */ - constructor(spriteContainer, key) { - super(key, "sfx.mp3"); - this.spriteContainer = spriteContainer; - } - - /** @returns {Promise} */ - load() { - return this.spriteContainer.load(); - } - - play(volume) { - this.spriteContainer.play(volume, this.key); - } - - deinitialize() { - return this.spriteContainer.deinitialize(); - } -} - -class MusicInstance extends MusicInstanceInterface { - constructor(key, url) { - super(key, url); - this.howl = null; - this.instance = null; - this.playing = false; - } - load() { - return new Promise((resolve, reject) => { - this.howl = new Howl({ - src: cachebust("res/sounds/music/" + this.url + ".mp3"), - autoplay: false, - loop: true, - html5: true, - volume: 1, - preload: true, - pool: 2, - - onunlock: () => { - if (this.playing) { - logger.log("Playing music after manual unlock"); - this.play(); - } - }, - - onload: () => { - resolve(); - }, - onloaderror: (id, err) => { - logger.warn(this, "Music", this.url, "failed to load:", id, err); - this.howl = null; - resolve(); - }, - - onplayerror: (id, err) => { - logger.warn(this, "Music", this.url, "failed to play:", id, err); - }, - }); - }); - } - - stop() { - if (this.howl && this.instance) { - this.playing = false; - this.howl.pause(this.instance); - } - } - - isPlaying() { - return this.playing; - } - - play(volume) { - if (this.howl) { - this.playing = true; - this.howl.volume(volume); - if (this.instance) { - this.howl.play(this.instance); - } else { - this.instance = this.howl.play(); - } - } - } - - setVolume(volume) { - if (this.howl) { - this.howl.volume(volume); - } - } - - deinitialize() { - if (this.howl) { - this.howl.unload(); - this.howl = null; - this.instance = null; - } - } -} - -export class SoundImplBrowser extends SoundInterface { - constructor(app) { - Howler.mobileAutoEnable = true; - Howler.autoUnlock = true; - Howler.autoSuspend = false; - Howler.html5PoolSize = 20; - Howler.pos(0, 0, 0); - - super(app, WrappedSoundInstance, MusicInstance); - } - - initialize() { - // NOTICE: We override the initialize() method here with custom logic because - // we have a sound sprites instance - - this.sfxHandle = new SoundSpritesContainer(); - - // @ts-ignore - const keys = Object.values(SOUNDS); - keys.forEach(key => { - this.sounds[key] = new WrappedSoundInstance(this.sfxHandle, key); - }); - for (const musicKey in MUSIC) { - const musicPath = MUSIC[musicKey]; - const music = new this.musicClass(musicKey, musicPath); - this.music[musicPath] = music; - } - - this.musicVolume = this.app.settings.getAllSettings().musicVolume; - this.soundVolume = this.app.settings.getAllSettings().soundVolume; - - if (G_IS_DEV && globalConfig.debug.disableMusic) { - this.musicVolume = 0.0; - } - - return Promise.resolve(); - } - - deinitialize() { - return super.deinitialize().then(() => Howler.unload()); - } -} diff --git a/src/js/platform/browser/storage.js b/src/js/platform/browser/storage.js deleted file mode 100644 index ac0fa4ca..00000000 --- a/src/js/platform/browser/storage.js +++ /dev/null @@ -1,93 +0,0 @@ -import { FILE_NOT_FOUND, StorageInterface } from "../storage"; -import { createLogger } from "../../core/logging"; - -const logger = createLogger("storage/browser"); - -const LOCAL_STORAGE_UNAVAILABLE = "local-storage-unavailable"; -const LOCAL_STORAGE_NO_WRITE_PERMISSION = "local-storage-no-write-permission"; - -let randomDelay = () => 0; - -if (G_IS_DEV) { - // Random delay for testing - // randomDelay = () => 500; -} - -export class StorageImplBrowser extends StorageInterface { - constructor(app) { - super(app); - this.currentBusyFilename = false; - } - - initialize() { - logger.error("Using localStorage, please update to a newer browser"); - return new Promise((resolve, reject) => { - // Check for local storage availability in general - if (!window.localStorage) { - alert("Local storage is not available! Please upgrade to a newer browser!"); - reject(LOCAL_STORAGE_UNAVAILABLE); - } - - // Check if we can set and remove items - try { - window.localStorage.setItem("storage_availability_test", "1"); - window.localStorage.removeItem("storage_availability_test"); - } catch (e) { - alert( - "It seems we don't have permission to write to local storage! Please update your browsers settings or use a different browser!" - ); - reject(LOCAL_STORAGE_NO_WRITE_PERMISSION); - return; - } - setTimeout(resolve, 0); - }); - } - - writeFileAsync(filename, contents) { - if (this.currentBusyFilename === filename) { - logger.warn("Attempt to write", filename, "while write process is not finished!"); - } - - this.currentBusyFilename = filename; - window.localStorage.setItem(filename, contents); - return new Promise((resolve, reject) => { - setTimeout(() => { - this.currentBusyFilename = false; - resolve(); - }, 0); - }); - } - - readFileAsync(filename) { - if (this.currentBusyFilename === filename) { - logger.warn("Attempt to read", filename, "while write progress on it is ongoing!"); - } - - return new Promise((resolve, reject) => { - const contents = window.localStorage.getItem(filename); - if (!contents) { - // File not found - setTimeout(() => reject(FILE_NOT_FOUND), randomDelay()); - return; - } - - // File read, simulate delay - setTimeout(() => resolve(contents), 0); - }); - } - - deleteFileAsync(filename) { - if (this.currentBusyFilename === filename) { - logger.warn("Attempt to delete", filename, "while write progres on it is ongoing!"); - } - - this.currentBusyFilename = filename; - return new Promise((resolve, reject) => { - window.localStorage.removeItem(filename); - setTimeout(() => { - this.currentBusyFilename = false; - resolve(); - }, 0); - }); - } -} diff --git a/src/js/platform/browser/storage_indexed_db.js b/src/js/platform/browser/storage_indexed_db.js deleted file mode 100644 index 1028eed3..00000000 --- a/src/js/platform/browser/storage_indexed_db.js +++ /dev/null @@ -1,154 +0,0 @@ -import { FILE_NOT_FOUND, StorageInterface } from "../storage"; -import { createLogger } from "../../core/logging"; - -const logger = createLogger("storage/browserIDB"); - -const LOCAL_STORAGE_UNAVAILABLE = "local-storage-unavailable"; -const LOCAL_STORAGE_NO_WRITE_PERMISSION = "local-storage-no-write-permission"; - -let randomDelay = () => 0; - -if (G_IS_DEV) { - // Random delay for testing - // randomDelay = () => 500; -} - -export class StorageImplBrowserIndexedDB extends StorageInterface { - constructor(app) { - super(app); - this.currentBusyFilename = false; - - /** @type {IDBDatabase} */ - this.database = null; - } - - initialize() { - logger.log("Using indexed DB storage"); - return new Promise((resolve, reject) => { - const request = window.indexedDB.open("app_storage", 10); - request.onerror = event => { - logger.error("IDB error:", event); - alert( - "Sorry, it seems your browser has blocked the access to the storage system. This might be the case if you are browsing in private mode for example. I recommend to use google chrome or disable private browsing." - ); - reject("Indexed DB access error"); - }; - - // @ts-ignore - request.onsuccess = event => resolve(event.target.result); - - request.onupgradeneeded = /** @type {IDBVersionChangeEvent} */ event => { - /** @type {IDBDatabase} */ - // @ts-ignore - const database = event.target.result; - - const objectStore = database.createObjectStore("files", { - keyPath: "filename", - }); - - objectStore.createIndex("filename", "filename", { unique: true }); - - objectStore.transaction.onerror = event => { - logger.error("IDB transaction error:", event); - reject("Indexed DB transaction error during migration, check console output."); - }; - - objectStore.transaction.oncomplete = event => { - logger.log("Object store completely initialized"); - resolve(database); - }; - }; - }).then(database => { - this.database = database; - }); - } - - writeFileAsync(filename, contents) { - if (this.currentBusyFilename === filename) { - logger.warn("Attempt to write", filename, "while write process is not finished!"); - } - if (!this.database) { - return Promise.reject("Storage not ready"); - } - - this.currentBusyFilename = filename; - const transaction = this.database.transaction(["files"], "readwrite"); - - return new Promise((resolve, reject) => { - transaction.oncomplete = () => { - this.currentBusyFilename = null; - resolve(); - }; - - transaction.onerror = error => { - this.currentBusyFilename = null; - logger.error("Error while writing", filename, ":", error); - reject(error); - }; - - const store = transaction.objectStore("files"); - store.put({ - filename, - contents, - }); - }); - } - - readFileAsync(filename) { - if (!this.database) { - return Promise.reject("Storage not ready"); - } - - this.currentBusyFilename = filename; - const transaction = this.database.transaction(["files"], "readonly"); - - return new Promise((resolve, reject) => { - const store = transaction.objectStore("files"); - const request = store.get(filename); - - request.onsuccess = event => { - this.currentBusyFilename = null; - if (!request.result) { - reject(FILE_NOT_FOUND); - return; - } - resolve(request.result.contents); - }; - - request.onerror = error => { - this.currentBusyFilename = null; - logger.error("Error while reading", filename, ":", error); - reject(error); - }; - }); - } - - deleteFileAsync(filename) { - if (this.currentBusyFilename === filename) { - logger.warn("Attempt to delete", filename, "while write progres on it is ongoing!"); - } - - if (!this.database) { - return Promise.reject("Storage not ready"); - } - - this.currentBusyFilename = filename; - const transaction = this.database.transaction(["files"], "readwrite"); - - return new Promise((resolve, reject) => { - transaction.oncomplete = () => { - this.currentBusyFilename = null; - resolve(); - }; - - transaction.onerror = error => { - this.currentBusyFilename = null; - logger.error("Error while deleting", filename, ":", error); - reject(error); - }; - - const store = transaction.objectStore("files"); - store.delete(filename); - }); - } -} diff --git a/src/js/platform/browser/wrapper.js b/src/js/platform/browser/wrapper.js deleted file mode 100644 index 94d174f3..00000000 --- a/src/js/platform/browser/wrapper.js +++ /dev/null @@ -1,211 +0,0 @@ -import { globalConfig, IS_MOBILE } from "../../core/config"; -import { createLogger } from "../../core/logging"; -import { queryParamOptions } from "../../core/query_parameters"; -import { WEB_STEAM_SSO_AUTHENTICATED } from "../../core/steam_sso"; -import { clamp } from "../../core/utils"; -import { CrazygamesAdProvider } from "../ad_providers/crazygames"; -import { GamedistributionAdProvider } from "../ad_providers/gamedistribution"; -import { NoAdProvider } from "../ad_providers/no_ad_provider"; -import { SteamAchievementProvider } from "../electron/steam_achievement_provider"; -import { PlatformWrapperInterface } from "../wrapper"; -import { NoAchievementProvider } from "./no_achievement_provider"; -import { StorageImplBrowser } from "./storage"; -import { StorageImplBrowserIndexedDB } from "./storage_indexed_db"; - -const logger = createLogger("platform/browser"); - -export class PlatformWrapperImplBrowser extends PlatformWrapperInterface { - initialize() { - this.recaptchaTokenCallback = null; - - this.embedProvider = { - id: "shapezio-website", - adProvider: NoAdProvider, - iframed: false, - externalLinks: true, - }; - - if (!G_IS_STANDALONE && !WEB_STEAM_SSO_AUTHENTICATED && queryParamOptions.embedProvider) { - const providerId = queryParamOptions.embedProvider; - this.embedProvider.iframed = true; - - switch (providerId) { - case "armorgames": { - this.embedProvider.id = "armorgames"; - break; - } - - case "iogames.space": { - this.embedProvider.id = "iogames.space"; - break; - } - - case "miniclip": { - this.embedProvider.id = "miniclip"; - break; - } - - case "gamedistribution": { - this.embedProvider.id = "gamedistribution"; - this.embedProvider.externalLinks = false; - this.embedProvider.adProvider = GamedistributionAdProvider; - break; - } - - case "kongregate": { - this.embedProvider.id = "kongregate"; - break; - } - - case "crazygames": { - this.embedProvider.id = "crazygames"; - this.embedProvider.adProvider = CrazygamesAdProvider; - break; - } - - default: { - logger.error("Got unsupported embed provider:", providerId); - } - } - } - - logger.log("Embed provider:", this.embedProvider.id); - - return this.detectStorageImplementation() - .then(() => this.initializeAdProvider()) - .then(() => this.initializeAchievementProvider()) - .then(() => super.initialize()); - } - - detectStorageImplementation() { - return new Promise(resolve => { - logger.log("Detecting storage"); - - if (!window.indexedDB) { - logger.log("Indexed DB not supported"); - this.app.storage = new StorageImplBrowser(this.app); - resolve(); - return; - } - - // Try accessing the indexedb - let request; - try { - request = window.indexedDB.open("indexeddb_feature_detection", 1); - } catch (ex) { - logger.warn("Error while opening indexed db:", ex); - this.app.storage = new StorageImplBrowser(this.app); - resolve(); - return; - } - request.onerror = err => { - logger.log("Indexed DB can *not* be accessed: ", err); - logger.log("Using fallback to local storage"); - this.app.storage = new StorageImplBrowser(this.app); - resolve(); - }; - request.onsuccess = () => { - logger.log("Indexed DB *can* be accessed"); - this.app.storage = new StorageImplBrowserIndexedDB(this.app); - resolve(); - }; - }); - } - - getId() { - return "browser@" + this.embedProvider.id; - } - - getUiScale() { - if (IS_MOBILE) { - return 1; - } - - const avgDims = Math.min(this.app.screenWidth, this.app.screenHeight); - return clamp((avgDims / 1000.0) * 1.9, 0.1, 10); - } - - getSupportsRestart() { - return true; - } - - getTouchPanStrength() { - return IS_MOBILE ? 1 : 0.5; - } - - openExternalLink(url, force = false) { - logger.log("Opening external:", url); - window.open(url); - } - - performRestart() { - logger.log("Performing restart"); - window.location.reload(true); - } - - /** - * Detects if there is an adblocker installed - * @returns {Promise} - */ - detectAdblock() { - return Promise.race([ - new Promise(resolve => { - // If the request wasn't blocked within a very short period of time, this means - // the adblocker is not active and the request was actually made -> ignore it then - setTimeout(() => resolve(false), 30); - }), - new Promise(resolve => { - fetch("https://googleads.g.doubleclick.net/pagead/id", { - method: "HEAD", - mode: "no-cors", - }) - .then(res => { - resolve(false); - }) - .catch(err => { - resolve(true); - }); - }), - ]); - } - - initializeAdProvider() { - if (G_IS_DEV && !globalConfig.debug.testAds) { - logger.log("Ads disabled in local environment"); - return Promise.resolve(); - } - - // First, detect adblocker - return this.detectAdblock().then(hasAdblocker => { - if (hasAdblocker) { - logger.log("Adblock detected"); - return; - } - - const adProvider = this.embedProvider.adProvider; - this.app.adProvider = new adProvider(this.app); - return this.app.adProvider.initialize().catch(err => { - logger.error("Failed to initialize ad provider, disabling ads:", err); - this.app.adProvider = new NoAdProvider(this.app); - }); - }); - } - - initializeAchievementProvider() { - if (G_IS_DEV && globalConfig.debug.testAchievements) { - this.app.achievementProvider = new SteamAchievementProvider(this.app); - - return this.app.achievementProvider.initialize().catch(err => { - logger.error("Failed to initialize achievement provider, disabling:", err); - - this.app.achievementProvider = new NoAchievementProvider(this.app); - }); - } - - return this.app.achievementProvider.initialize(); - } - - exitApp() { - // Can not exit app - } -} diff --git a/src/js/platform/electron/steam_achievement_provider.js b/src/js/platform/electron/steam_achievement_provider.js deleted file mode 100644 index 638cdbc5..00000000 --- a/src/js/platform/electron/steam_achievement_provider.js +++ /dev/null @@ -1,148 +0,0 @@ -/* typehints:start */ -import { Application } from "../../application"; -import { GameRoot } from "../../game/root"; -/* typehints:end */ - -import { createLogger } from "../../core/logging"; -import { ACHIEVEMENTS, AchievementCollection, AchievementProviderInterface } from "../achievement_provider"; - -const logger = createLogger("achievements/steam"); - -const ACHIEVEMENT_IDS = { - [ACHIEVEMENTS.belt500Tiles]: "belt_500_tiles", - [ACHIEVEMENTS.blueprint100k]: "blueprint_100k", - [ACHIEVEMENTS.blueprint1m]: "blueprint_1m", - [ACHIEVEMENTS.completeLvl26]: "complete_lvl_26", - [ACHIEVEMENTS.cutShape]: "cut_shape", - [ACHIEVEMENTS.darkMode]: "dark_mode", - [ACHIEVEMENTS.destroy1000]: "destroy_1000", - [ACHIEVEMENTS.irrelevantShape]: "irrelevant_shape", - [ACHIEVEMENTS.level100]: "level_100", - [ACHIEVEMENTS.level50]: "level_50", - [ACHIEVEMENTS.logoBefore18]: "logo_before_18", - [ACHIEVEMENTS.mam]: "mam", - [ACHIEVEMENTS.mapMarkers15]: "map_markers_15", - [ACHIEVEMENTS.openWires]: "open_wires", - [ACHIEVEMENTS.oldLevel17]: "old_level_17", - [ACHIEVEMENTS.noBeltUpgradesUntilBp]: "no_belt_upgrades_until_bp", - [ACHIEVEMENTS.noInverseRotater]: "no_inverse_rotator", // [sic] - [ACHIEVEMENTS.paintShape]: "paint_shape", - [ACHIEVEMENTS.place5000Wires]: "place_5000_wires", - [ACHIEVEMENTS.placeBlueprint]: "place_blueprint", - [ACHIEVEMENTS.placeBp1000]: "place_bp_1000", - [ACHIEVEMENTS.play1h]: "play_1h", - [ACHIEVEMENTS.play10h]: "play_10h", - [ACHIEVEMENTS.play20h]: "play_20h", - [ACHIEVEMENTS.produceLogo]: "produce_logo", - [ACHIEVEMENTS.produceMsLogo]: "produce_ms_logo", - [ACHIEVEMENTS.produceRocket]: "produce_rocket", - [ACHIEVEMENTS.rotateShape]: "rotate_shape", - [ACHIEVEMENTS.speedrunBp30]: "speedrun_bp_30", - [ACHIEVEMENTS.speedrunBp60]: "speedrun_bp_60", - [ACHIEVEMENTS.speedrunBp120]: "speedrun_bp_120", - [ACHIEVEMENTS.stack4Layers]: "stack_4_layers", - [ACHIEVEMENTS.stackShape]: "stack_shape", - [ACHIEVEMENTS.store100Unique]: "store_100_unique", - [ACHIEVEMENTS.storeShape]: "store_shape", - [ACHIEVEMENTS.throughputBp25]: "throughput_bp_25", - [ACHIEVEMENTS.throughputBp50]: "throughput_bp_50", - [ACHIEVEMENTS.throughputLogo25]: "throughput_logo_25", - [ACHIEVEMENTS.throughputLogo50]: "throughput_logo_50", - [ACHIEVEMENTS.throughputRocket10]: "throughput_rocket_10", - [ACHIEVEMENTS.throughputRocket20]: "throughput_rocket_20", - [ACHIEVEMENTS.trash1000]: "trash_1000", - [ACHIEVEMENTS.unlockWires]: "unlock_wires", - [ACHIEVEMENTS.upgradesTier5]: "upgrades_tier_5", - [ACHIEVEMENTS.upgradesTier8]: "upgrades_tier_8", -}; - -export class SteamAchievementProvider extends AchievementProviderInterface { - /** @param {Application} app */ - constructor(app) { - super(app); - - this.initialized = false; - this.collection = new AchievementCollection(this.activate.bind(this)); - - if (G_IS_DEV) { - for (let key in ACHIEVEMENT_IDS) { - assert(this.collection.map.has(key), "Key not found in collection: " + key); - } - } - - logger.log("Collection created with", this.collection.map.size, "achievements"); - } - - /** @returns {boolean} */ - hasAchievements() { - return true; - } - - /** - * @param {GameRoot} root - * @returns {Promise} - */ - onLoad(root) { - this.root = root; - - try { - this.collection = new AchievementCollection(this.activate.bind(this)); - this.collection.initialize(root); - - logger.log("Initialized", this.collection.map.size, "relevant achievements"); - return Promise.resolve(); - } catch (err) { - logger.error("Failed to initialize the collection"); - return Promise.reject(err); - } - } - - /** @returns {Promise} */ - initialize() { - if (!G_IS_STANDALONE) { - logger.warn("Steam unavailable. Achievements won't sync."); - return Promise.resolve(); - } - - if (G_WEGAME_VERSION) { - return Promise.resolve(); - } - - return ipcRenderer.invoke("steam:is-initialized").then(initialized => { - this.initialized = initialized; - - if (!this.initialized) { - logger.warn("Steam failed to intialize. Achievements won't sync."); - } else { - logger.log("Steam achievement provider initialized"); - } - }); - } - - /** - * @param {string} key - * @returns {Promise} - */ - activate(key) { - let promise; - - if (G_WEGAME_VERSION) { - return Promise.resolve(); - } - - if (!this.initialized) { - promise = Promise.resolve(); - } else { - promise = ipcRenderer.invoke("steam:activate-achievement", ACHIEVEMENT_IDS[key]); - } - - return promise - .then(() => { - logger.log("Achievement activated:", key); - }) - .catch(err => { - logger.error("Failed to activate achievement:", key, err); - throw err; - }); - } -} diff --git a/src/js/platform/electron/storage.js b/src/js/platform/electron/storage.js deleted file mode 100644 index 65f0e507..00000000 --- a/src/js/platform/electron/storage.js +++ /dev/null @@ -1,41 +0,0 @@ -import { FILE_NOT_FOUND, StorageInterface } from "../storage"; - -export class StorageImplElectron extends StorageInterface { - constructor(app) { - super(app); - } - - initialize() { - return Promise.resolve(); - } - - writeFileAsync(filename, contents) { - return ipcRenderer.invoke("fs-job", { - type: "write", - filename, - contents, - }); - } - - readFileAsync(filename) { - return ipcRenderer - .invoke("fs-job", { - type: "read", - filename, - }) - .then(res => { - if (res && res.error === FILE_NOT_FOUND) { - throw FILE_NOT_FOUND; - } - - return res; - }); - } - - deleteFileAsync(filename) { - return ipcRenderer.invoke("fs-job", { - type: "delete", - filename, - }); - } -} diff --git a/src/js/platform/electron/wrapper.js b/src/js/platform/electron/wrapper.js deleted file mode 100644 index 65451395..00000000 --- a/src/js/platform/electron/wrapper.js +++ /dev/null @@ -1,117 +0,0 @@ -import { NoAchievementProvider } from "../browser/no_achievement_provider"; -import { PlatformWrapperImplBrowser } from "../browser/wrapper"; -import { createLogger } from "../../core/logging"; -import { StorageImplElectron } from "./storage"; -import { SteamAchievementProvider } from "./steam_achievement_provider"; -import { PlatformWrapperInterface } from "../wrapper"; - -const logger = createLogger("electron-wrapper"); - -export class PlatformWrapperImplElectron extends PlatformWrapperImplBrowser { - initialize() { - this.dlcs = { - puzzle: false, - }; - - this.steamOverlayCanvasFix = document.createElement("canvas"); - this.steamOverlayCanvasFix.width = 1; - this.steamOverlayCanvasFix.height = 1; - this.steamOverlayCanvasFix.id = "steamOverlayCanvasFix"; - - this.steamOverlayContextFix = this.steamOverlayCanvasFix.getContext("2d"); - document.documentElement.appendChild(this.steamOverlayCanvasFix); - - this.app.ticker.frameEmitted.add(this.steamOverlayFixRedrawCanvas, this); - - this.app.storage = new StorageImplElectron(this); - this.app.achievementProvider = new SteamAchievementProvider(this.app); - - return this.initializeAchievementProvider() - .then(() => this.initializeDlcStatus()) - .then(() => PlatformWrapperInterface.prototype.initialize.call(this)); - } - - steamOverlayFixRedrawCanvas() { - this.steamOverlayContextFix.clearRect(0, 0, 1, 1); - } - - getId() { - return "electron"; - } - - getSupportsRestart() { - return true; - } - - openExternalLink(url) { - logger.log(this, "Opening external:", url); - window.open(url, "about:blank"); - } - - getSupportsAds() { - return false; - } - - performRestart() { - logger.log(this, "Performing restart"); - window.location.reload(true); - } - - initializeAdProvider() { - return Promise.resolve(); - } - - initializeAchievementProvider() { - return this.app.achievementProvider.initialize().catch(err => { - logger.error("Failed to initialize achievement provider, disabling:", err); - - this.app.achievementProvider = new NoAchievementProvider(this.app); - }); - } - - initializeDlcStatus() { - if (G_WEGAME_VERSION) { - return Promise.resolve(); - } - - logger.log("Checking DLC ownership ..."); - // @todo: Don't hardcode the app id - return ipcRenderer.invoke("steam:check-app-ownership", 1625400).then( - res => { - logger.log("Got DLC ownership:", res); - this.dlcs.puzzle = Boolean(res); - - if (this.dlcs.puzzle && !G_IS_DEV) { - this.app.gameAnalytics.activateDlc("puzzle").then( - () => { - logger.log("Puzzle DLC successfully activated"); - }, - error => { - logger.error("Failed to activate puzzle DLC:", error); - } - ); - } - }, - err => { - logger.error("Failed to get DLC ownership:", err); - } - ); - } - - getSupportsFullscreen() { - return true; - } - - setFullscreen(flag) { - ipcRenderer.send("set-fullscreen", flag); - } - - getSupportsAppExit() { - return true; - } - - exitApp() { - logger.log(this, "Sending app exit signal"); - ipcRenderer.send("exit-app"); - } -} diff --git a/src/js/platform/fs_error.ts b/src/js/platform/fs_error.ts new file mode 100644 index 00000000..9e15aa31 --- /dev/null +++ b/src/js/platform/fs_error.ts @@ -0,0 +1,23 @@ +/** + * Represents a filesystem error as reported by the main process. + */ +export class FsError extends Error { + code?: string; + + constructor(message?: string, options?: ErrorOptions) { + super(message, options); + Error.captureStackTrace(this, FsError); + this.name = "FsError"; + + // Take the code from the error message, quite ugly + if (options?.cause && options.cause instanceof Error) { + // Example message: + // Error invoking remote method 'fs-job': Error: ENOENT: no such... + this.code = options.cause.message.split(":")[2].trim(); + } + } + + isFileNotFound(): boolean { + return this.code === "ENOENT"; + } +} diff --git a/src/js/platform/game_analytics.js b/src/js/platform/game_analytics.js deleted file mode 100644 index 19fdf752..00000000 --- a/src/js/platform/game_analytics.js +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @typedef {import("../application").Application} Application - */ - -export class GameAnalyticsInterface { - constructor(app) { - /** @type {Application} */ - this.app = app; - } - - /** - * Initializes the analytics - * @returns {Promise} - * @abstract - */ - initialize() { - abstract; - return Promise.reject(); - } - - /** - * Handles a new game which was started - */ - handleGameStarted() {} - - /** - * Handles a resumed game - */ - handleGameResumed() {} - - /** - * Handles the given level completed - * @param {number} level - */ - handleLevelCompleted(level) {} - - /** - * Handles the given upgrade completed - * @param {string} id - * @param {number} level - */ - handleUpgradeUnlocked(id, level) {} - - /** - * Activates a DLC - * @param {string} dlc - * @abstract - */ - activateDlc(dlc) { - abstract; - return Promise.resolve(); - } -} diff --git a/src/js/platform/sound.js b/src/js/platform/sound.js index 28274111..d9cc0afc 100644 --- a/src/js/platform/sound.js +++ b/src/js/platform/sound.js @@ -7,6 +7,10 @@ import { GameRoot } from "../game/root"; import { newEmptyMap, clamp } from "../core/utils"; import { createLogger } from "../core/logging"; import { globalConfig } from "../core/config"; +import { Howl, Howler } from "howler"; + +// @ts-ignore +import sprites from "../built-temp/sfx.json"; const logger = createLogger("sound"); @@ -33,16 +37,12 @@ export const SOUNDS = { export const MUSIC = { // The theme always depends on the standalone only, even if running the full // version in the browser - theme: G_IS_STANDALONE ? "theme-full" : "theme-short", + theme: "theme-full", }; -if (G_IS_STANDALONE) { - MUSIC.menu = "menu"; -} +MUSIC.menu = "menu"; -if (G_IS_STANDALONE) { - MUSIC.puzzle = "puzzle-full"; -} +MUSIC.puzzle = "puzzle-full"; export class SoundInstanceInterface { constructor(key, url) { @@ -292,3 +292,200 @@ export class SoundInterface { } } } + +class SoundSpritesContainer { + constructor() { + this.howl = null; + + this.loadingPromise = null; + } + + load() { + if (this.loadingPromise) { + return this.loadingPromise; + } + return (this.loadingPromise = new Promise(resolve => { + this.howl = new Howl({ + src: "res/sounds/sfx.mp3", + sprite: sprites.sprite, + autoplay: false, + loop: false, + volume: 0, + preload: true, + pool: 20, + onload: () => { + resolve(); + }, + onloaderror: (id, err) => { + logger.warn("SFX failed to load:", id, err); + this.howl = null; + resolve(); + }, + onplayerror: (id, err) => { + logger.warn("SFX failed to play:", id, err); + }, + }); + })); + } + + play(volume, key) { + if (this.howl) { + const instance = this.howl.play(key); + this.howl.volume(volume, instance); + } + } + + deinitialize() { + if (this.howl) { + this.howl.unload(); + this.howl = null; + } + } +} + +class WrappedSoundInstance extends SoundInstanceInterface { + /** + * + * @param {SoundSpritesContainer} spriteContainer + * @param {string} key + */ + constructor(spriteContainer, key) { + super(key, "sfx.mp3"); + this.spriteContainer = spriteContainer; + } + + /** @returns {Promise} */ + load() { + return this.spriteContainer.load(); + } + + play(volume) { + this.spriteContainer.play(volume, this.key); + } + + deinitialize() { + return this.spriteContainer.deinitialize(); + } +} + +class MusicInstance extends MusicInstanceInterface { + constructor(key, url) { + super(key, url); + this.howl = null; + this.instance = null; + this.playing = false; + } + load() { + return new Promise((resolve, reject) => { + this.howl = new Howl({ + src: "res/sounds/music/" + this.url + ".mp3", + autoplay: false, + loop: true, + html5: true, + volume: 1, + preload: true, + pool: 2, + + onunlock: () => { + if (this.playing) { + logger.log("Playing music after manual unlock"); + this.play(); + } + }, + + onload: () => { + resolve(); + }, + onloaderror: (id, err) => { + logger.warn(this, "Music", this.url, "failed to load:", id, err); + this.howl = null; + resolve(); + }, + + onplayerror: (id, err) => { + logger.warn(this, "Music", this.url, "failed to play:", id, err); + }, + }); + }); + } + + stop() { + if (this.howl && this.instance) { + this.playing = false; + this.howl.pause(this.instance); + } + } + + isPlaying() { + return this.playing; + } + + play(volume) { + if (this.howl) { + this.playing = true; + this.howl.volume(volume); + if (this.instance) { + this.howl.play(this.instance); + } else { + this.instance = this.howl.play(); + } + } + } + + setVolume(volume) { + if (this.howl) { + this.howl.volume(volume); + } + } + + deinitialize() { + if (this.howl) { + this.howl.unload(); + this.howl = null; + this.instance = null; + } + } +} + +export class Sound extends SoundInterface { + constructor(app) { + Howler.mobileAutoEnable = true; + Howler.autoUnlock = true; + Howler.autoSuspend = false; + Howler.html5PoolSize = 20; + Howler.pos(0, 0, 0); + + super(app, WrappedSoundInstance, MusicInstance); + } + + initialize() { + // NOTICE: We override the initialize() method here with custom logic because + // we have a sound sprites instance + + this.sfxHandle = new SoundSpritesContainer(); + + // @ts-ignore + const keys = Object.values(SOUNDS); + keys.forEach(key => { + this.sounds[key] = new WrappedSoundInstance(this.sfxHandle, key); + }); + for (const musicKey in MUSIC) { + const musicPath = MUSIC[musicKey]; + const music = new this.musicClass(musicKey, musicPath); + this.music[musicPath] = music; + } + + this.musicVolume = this.app.settings.getAllSettings().musicVolume; + this.soundVolume = this.app.settings.getAllSettings().soundVolume; + + if (G_IS_DEV && globalConfig.debug.disableMusic) { + this.musicVolume = 0.0; + } + + return Promise.resolve(); + } + + deinitialize() { + return super.deinitialize().then(() => Howler.unload()); + } +} diff --git a/src/js/platform/storage.js b/src/js/platform/storage.js deleted file mode 100644 index c5c3701c..00000000 --- a/src/js/platform/storage.js +++ /dev/null @@ -1,55 +0,0 @@ -/* typehints:start */ -import { Application } from "../application"; -/* typehints:end */ - -export const FILE_NOT_FOUND = "file_not_found"; - -export class StorageInterface { - constructor(app) { - /** @type {Application} */ - this.app = app; - } - - /** - * Initializes the storage - * @returns {Promise} - * @abstract - */ - initialize() { - abstract; - return Promise.reject(); - } - - /** - * Writes a string to a file asynchronously - * @param {string} filename - * @param {string} contents - * @returns {Promise} - * @abstract - */ - writeFileAsync(filename, contents) { - abstract; - return Promise.reject(); - } - - /** - * Reads a string asynchronously. Returns Promise if file was not found. - * @param {string} filename - * @returns {Promise} - * @abstract - */ - readFileAsync(filename) { - abstract; - return Promise.reject(); - } - - /** - * Tries to delete a file - * @param {string} filename - * @returns {Promise} - */ - deleteFileAsync(filename) { - // Default implementation does not allow deleting files - return Promise.reject(); - } -} diff --git a/src/js/platform/storage.ts b/src/js/platform/storage.ts new file mode 100644 index 00000000..c69df6dc --- /dev/null +++ b/src/js/platform/storage.ts @@ -0,0 +1,92 @@ +import { Application } from "@/application"; +import { Compression, DefaultCompression } from "@/core/compression"; +import { FsError } from "./fs_error"; + +export const STORAGE_SAVES = "saves"; +export const STORAGE_MOD_PREFIX = "mod/"; + +interface FsJob { + type: string; + filename?: string; + contents?: Uint8Array; + extension?: string; +} + +export class Storage { + readonly app: Application; + readonly id: string; + readonly compression: Compression; + + constructor(app: Application, id: string, compression?: Compression) { + this.app = app; + this.id = id; + this.compression = compression ?? new DefaultCompression(); + } + + /** + * Initializes the storage + */ + initialize(): Promise { + return this.invokeFsJob({ type: "initialize" }); + } + + /** + * Reads a string asynchronously + */ + readFileAsync(filename: string): Promise { + return this.invokeFsJob({ type: "read", filename }).then(contents => + this.compression.decompress(contents) + ); + } + + /** + * Writes a string to a file asynchronously + */ + writeFileAsync(filename: string, contents: unknown): Promise { + return this.compression + .compress(contents) + .then(contents => this.invokeFsJob({ type: "write", filename, contents })); + } + + /** + * Tries to delete a file + */ + deleteFileAsync(filename: string): Promise { + return this.invokeFsJob({ type: "delete", filename }); + } + + /** + * Displays the "Open File" dialog to let user pick a file. Returns the + * decompressed file contents, or undefined if the operation was canceled + */ + requestOpenFile(extension: string): Promise { + return this.invokeFsJob({ type: "open-external", extension }).then(contents => + contents ? this.compression.decompress(contents) : undefined + ); + } + + /** + * Displays the "Save File" dialog to let user pick a file. If the user + * picks a file, the passed contents will be compressed and written to + * that file. + */ + requestSaveFile(filename: string, contents: unknown): Promise { + return this.compression + .compress(contents) + .then(contents => this.invokeFsJob({ type: "save-external", filename, contents })); + } + + private invokeFsJob(data: FsJob) { + return ipcRenderer + .invoke("fs-job", { + id: this.id, + ...data, + }) + .catch(e => this.wrapError(e)); + } + + private wrapError(err: unknown): Promise { + const message = err instanceof Error ? err.message : err.toString(); + return Promise.reject(new FsError(message, { cause: err })); + } +} diff --git a/src/js/platform/wrapper.js b/src/js/platform/wrapper.js index e0a896fb..cb47e6a7 100644 --- a/src/js/platform/wrapper.js +++ b/src/js/platform/wrapper.js @@ -1,135 +1,118 @@ -/* typehints:start */ -import { Application } from "../application"; -/* typehints:end */ - -import { IS_MOBILE } from "../core/config"; - -export class PlatformWrapperInterface { - constructor(app) { - /** @type {Application} */ - this.app = app; - } - - /** @returns {string} */ - getId() { - abstract; - return "unknown-platform"; - } - - /** - * Returns the UI scale, called on every resize - * @returns {number} */ - getUiScale() { - return 1; - } - - /** @returns {boolean} */ - getSupportsRestart() { - abstract; - return false; - } - - /** - * Returns the strength of touch pans with the mouse - */ - getTouchPanStrength() { - return 1; - } - - /** @returns {Promise} */ - initialize() { - document.documentElement.classList.add("p-" + this.getId()); - return Promise.resolve(); - } - - /** - * Should initialize the apps ad provider in case supported - * @returns {Promise} - */ - initializeAdProvider() { - return Promise.resolve(); - } - - /** - * Should return the minimum supported zoom level - * @returns {number} - */ - getMinimumZoom() { - return 0.1 * this.getScreenScale(); - } - - /** - * Should return the maximum supported zoom level - * @returns {number} - */ - getMaximumZoom() { - return 3.5 * this.getScreenScale(); - } - - getScreenScale() { - return Math.min(window.innerWidth, window.innerHeight) / 1024.0; - } - - /** - * Should return if this platform supports ads at all - */ - getSupportsAds() { - return false; - } - - /** - * Attempt to open an external url - * @param {string} url - * @param {boolean=} force Whether to always open the url even if not allowed - * @abstract - */ - openExternalLink(url, force = false) { - abstract; - } - - /** - * Attempt to restart the app - * @abstract - */ - performRestart() { - abstract; - } - - /** - * Returns whether this platform supports a toggleable fullscreen - */ - getSupportsFullscreen() { - return false; - } - - /** - * Should set the apps fullscreen state to the desired state - * @param {boolean} flag - * @abstract - */ - setFullscreen(flag) { - abstract; - } - - /** - * Returns whether this platform supports quitting the app - */ - getSupportsAppExit() { - return false; - } - - /** - * Attempts to quit the app - * @abstract - */ - exitApp() { - abstract; - } - - /** - * Whether this platform supports a keyboard - */ - getSupportsKeyboard() { - return !IS_MOBILE; - } -} +/* typehints:start */ +import { Application } from "../application"; +/* typehints:end */ + +import { IS_MOBILE } from "../core/config"; +import { createLogger } from "../core/logging"; +import { clamp } from "../core/utils"; + +const logger = createLogger("electron-wrapper"); + +export class PlatformWrapperImplElectron { + constructor(app) { + /** @type {Application} */ + this.app = app; + } + + initialize() { + document.documentElement.classList.add("p-" + this.getId()); + return Promise.resolve(); + } + + getId() { + return "electron"; + } + + getSupportsRestart() { + return true; + } + + /** + * Attempt to open an external url + * @param {string} url + */ + openExternalLink(url) { + logger.log(this, "Opening external:", url); + location.replace(url); + } + + /** + * Returns the strength of touch pans with the mouse + */ + getTouchPanStrength() { + return 1; + } + + /** + * Attempt to restart the app + */ + performRestart() { + logger.log(this, "Performing restart"); + window.location.reload(); + } + + /** + * Returns the UI scale, called on every resize + * @returns {number} */ + getUiScale() { + if (IS_MOBILE) { + return 1; + } + + const avgDims = Math.min(this.app.screenWidth, this.app.screenHeight); + return clamp((avgDims / 1000.0) * 1.9, 0.1, 10); + } + + /** + * Returns whether this platform supports a toggleable fullscreen + */ + getSupportsFullscreen() { + return true; + } + + /** + * Should set the apps fullscreen state to the desired state + * @param {boolean} flag + */ + setFullscreen(flag) { + ipcRenderer.invoke("set-fullscreen", flag); + } + + getSupportsAppExit() { + return true; + } + + /** + * Attempts to quit the app + */ + exitApp() { + window.close(); + } + + /** + * Whether this platform supports a keyboard + */ + getSupportsKeyboard() { + return true; + } + + /** + * Should return the minimum supported zoom level + * @returns {number} + */ + getMinimumZoom() { + return 0.1 * this.getScreenScale(); + } + + /** + * Should return the maximum supported zoom level + * @returns {number} + */ + getMaximumZoom() { + return 3.5 * this.getScreenScale(); + } + + getScreenScale() { + return Math.min(window.innerWidth, window.innerHeight) / 1024.0; + } +} diff --git a/src/js/profile/application_settings.js b/src/js/profile/application_settings.js index 5fb4a156..6f98a14c 100644 --- a/src/js/profile/application_settings.js +++ b/src/js/profile/application_settings.js @@ -2,13 +2,13 @@ import { Application } from "../application"; /* typehints:end */ -import { ReadWriteProxy } from "../core/read_write_proxy"; -import { BoolSetting, EnumSetting, RangeSetting, BaseSetting } from "./setting_types"; -import { createLogger } from "../core/logging"; import { ExplainedResult } from "../core/explained_result"; +import { createLogger } from "../core/logging"; +import { ReadWriteProxy } from "../core/read_write_proxy"; import { THEMES, applyGameTheme } from "../game/theme"; -import { T } from "../translations"; import { LANGUAGES } from "../languages"; +import { T } from "../translations"; +import { BaseSetting, BoolSetting, EnumSetting, RangeSetting } from "./setting_types"; const logger = createLogger("application_settings"); @@ -140,7 +140,7 @@ function initializeSettings() { options: Object.keys(LANGUAGES), valueGetter: key => key, textGetter: key => LANGUAGES[key].name, - category: G_CHINA_VERSION || G_WEGAME_VERSION ? null : enumCategories.general, + category: enumCategories.general, restartRequired: true, changeCb: (app, id) => null, magicValue: "auto-detect", @@ -189,7 +189,7 @@ function initializeSettings() { }, /** * @param {Application} app - */ app => G_IS_STANDALONE + */ app => true ), new BoolSetting( @@ -217,9 +217,6 @@ function initializeSettings() { applyGameTheme(id); document.documentElement.setAttribute("data-theme", id); }, - enabledCb: /** - * @param {Application} app - */ app => app.restrictionMgr.getHasExtendedSettings(), }), new EnumSetting("autosaveInterval", { @@ -278,9 +275,6 @@ function initializeSettings() { category: enumCategories.performance, restartRequired: false, changeCb: (app, id) => {}, - enabledCb: /** - * @param {Application} app - */ app => app.restrictionMgr.getHasExtendedSettings(), }), new BoolSetting("lowQualityMapResources", enumCategories.performance, (app, value) => {}), @@ -294,7 +288,7 @@ function initializeSettings() { class SettingsStorage { constructor() { this.uiScale = "regular"; - this.fullscreen = G_IS_STANDALONE; + this.fullscreen = true; this.soundVolume = 1.0; this.musicVolume = 1.0; @@ -336,8 +330,11 @@ class SettingsStorage { } export class ApplicationSettings extends ReadWriteProxy { - constructor(app) { - super(app, "app_settings.bin"); + constructor(app, storage) { + super(storage, "app_settings.bin"); + + /** @type {Application} */ + this.app = app; this.settingHandles = initializeSettings(); } @@ -378,7 +375,7 @@ export class ApplicationSettings extends ReadWriteProxy { * @param {string} key */ getSetting(key) { - assert(this.getAllSettings().hasOwnProperty(key), "Setting not known: " + key); + assert(Object.hasOwn(this.getAllSettings(), key), "Setting not known: " + key); return this.getAllSettings()[key]; } @@ -513,7 +510,7 @@ export class ApplicationSettings extends ReadWriteProxy { } // MODS - if (!THEMES[data.settings.theme] || !this.app.restrictionMgr.getHasExtendedSettings()) { + if (!THEMES[data.settings.theme]) { console.log("Resetting theme because its no longer available: " + data.settings.theme); data.settings.theme = "light"; } @@ -705,7 +702,7 @@ export class ApplicationSettings extends ReadWriteProxy { } // MODS - if (!THEMES[data.settings.theme] || !this.app.restrictionMgr.getHasExtendedSettings()) { + if (!THEMES[data.settings.theme]) { console.log("Resetting theme because its no longer available: " + data.settings.theme); data.settings.theme = "light"; } diff --git a/src/js/profile/setting_types.js b/src/js/profile/setting_types.js index ccc90d70..d8a92840 100644 --- a/src/js/profile/setting_types.js +++ b/src/js/profile/setting_types.js @@ -3,7 +3,6 @@ import { Application } from "../application"; /* typehints:end */ import { createLogger } from "../core/logging"; -import { WEB_STEAM_SSO_AUTHENTICATED } from "../core/steam_sso"; import { T } from "../translations"; const logger = createLogger("setting_types"); @@ -153,13 +152,7 @@ export class EnumSetting extends BaseSetting { return `
- ${ - available - ? "" - : `${ - WEB_STEAM_SSO_AUTHENTICATED ? "" : T.demo.settingNotAvailable - }` - } + ${available ? "" : `${T.demo.settingNotAvailable}`}
@@ -234,16 +227,11 @@ export class BoolSetting extends BaseSetting { * @param {Application} app */ getHtml(app) { + // TODO: Rewrite the settings system entirely const available = this.getIsAvailable(app); return `
- ${ - available - ? "" - : `${ - WEB_STEAM_SSO_AUTHENTICATED ? "" : T.demo.settingNotAvailable - }` - } + ${available ? "" : `${T.demo.settingNotAvailable}`}
@@ -303,21 +291,15 @@ export class RangeSetting extends BaseSetting { const available = this.getIsAvailable(app); return `
- ${ - available - ? "" - : `${ - WEB_STEAM_SSO_AUTHENTICATED ? "" : T.demo.settingNotAvailable - }` - } + ${available ? "" : `${T.demo.settingNotAvailable}`}
+ this.minValue + }" max="${this.maxValue}" step="${this.stepSize}">
diff --git a/src/js/savegame/puzzle_serializer.js b/src/js/savegame/puzzle_serializer.js index c502142b..e2769cd1 100644 --- a/src/js/savegame/puzzle_serializer.js +++ b/src/js/savegame/puzzle_serializer.js @@ -11,7 +11,6 @@ import { gMetaBuildingRegistry } from "../core/global_registries"; import { MetaGoalAcceptorBuilding } from "../game/buildings/goal_acceptor"; import { createLogger } from "../core/logging"; import { BaseItem } from "../game/base_item"; -import trim from "trim"; import { enumColors } from "../game/colors"; import { COLOR_ITEM_SINGLETONS } from "../game/items/color_item"; import { ShapeDefinition } from "../game/shape_definition"; @@ -117,7 +116,7 @@ export class PuzzleSerializer { return null; } - code = trim(code); + code = code.trim(); const codeLower = code.toLowerCase(); if (enumColors[codeLower]) { diff --git a/src/js/savegame/savegame.js b/src/js/savegame/savegame.js index b4472b2b..37dc4772 100644 --- a/src/js/savegame/savegame.js +++ b/src/js/savegame/savegame.js @@ -1,328 +1,259 @@ -import { ReadWriteProxy } from "../core/read_write_proxy"; -import { ExplainedResult } from "../core/explained_result"; -import { SavegameSerializer } from "./savegame_serializer"; -import { BaseSavegameInterface } from "./savegame_interface"; -import { createLogger } from "../core/logging"; -import { globalConfig } from "../core/config"; -import { getSavegameInterface, savegameInterfaces } from "./savegame_interface_registry"; -import { SavegameInterface_V1001 } from "./schemas/1001"; -import { SavegameInterface_V1002 } from "./schemas/1002"; -import { SavegameInterface_V1003 } from "./schemas/1003"; -import { SavegameInterface_V1004 } from "./schemas/1004"; -import { SavegameInterface_V1005 } from "./schemas/1005"; -import { SavegameInterface_V1006 } from "./schemas/1006"; -import { SavegameInterface_V1007 } from "./schemas/1007"; -import { SavegameInterface_V1008 } from "./schemas/1008"; -import { SavegameInterface_V1009 } from "./schemas/1009"; -import { MODS } from "../mods/modloader"; -import { SavegameInterface_V1010 } from "./schemas/1010"; - -const logger = createLogger("savegame"); - -/** - * @typedef {import("../application").Application} Application - * @typedef {import("../game/root").GameRoot} GameRoot - * @typedef {import("./savegame_typedefs").SavegameData} SavegameData - * @typedef {import("./savegame_typedefs").SavegameMetadata} SavegameMetadata - * @typedef {import("./savegame_typedefs").SavegameStats} SavegameStats - * @typedef {import("./savegame_typedefs").SerializedGame} SerializedGame - */ - -export class Savegame extends ReadWriteProxy { - /** - * - * @param {Application} app - * @param {object} param0 - * @param {string} param0.internalId - * @param {SavegameMetadata} param0.metaDataRef Handle to the meta data - */ - constructor(app, { internalId, metaDataRef }) { - super(app, "savegame-" + internalId + ".bin"); - this.internalId = internalId; - this.metaDataRef = metaDataRef; - - /** @type {SavegameData} */ - this.currentData = this.getDefaultData(); - - assert( - savegameInterfaces[Savegame.getCurrentVersion()], - "Savegame interface not defined: " + Savegame.getCurrentVersion() - ); - } - - //////// RW Proxy Impl ////////// - - /** - * @returns {number} - */ - static getCurrentVersion() { - return 1010; - } - - /** - * @returns {typeof BaseSavegameInterface} - */ - static getReaderClass() { - return savegameInterfaces[Savegame.getCurrentVersion()]; - } - - /** - * - * @param {Application} app - * @returns - */ - static createPuzzleSavegame(app) { - return new Savegame(app, { - internalId: "puzzle", - metaDataRef: { - internalId: "puzzle", - lastUpdate: 0, - version: 0, - level: 0, - name: "puzzle", - }, - }); - } - - /** - * @returns {number} - */ - getCurrentVersion() { - return /** @type {typeof Savegame} */ (this.constructor).getCurrentVersion(); - } - - /** - * Returns the savegames default data - * @returns {SavegameData} - */ - getDefaultData() { - return { - version: this.getCurrentVersion(), - dump: null, - stats: { - failedMam: false, - trashedCount: 0, - usedInverseRotater: false, - }, - lastUpdate: Date.now(), - mods: MODS.getModsListForSavegame(), - }; - } - - /** - * Migrates the savegames data - * @param {SavegameData} data - */ - migrate(data) { - if (data.version < 1000) { - return ExplainedResult.bad("Can not migrate savegame, too old"); - } - - if (data.version === 1000) { - SavegameInterface_V1001.migrate1000to1001(data); - data.version = 1001; - } - - if (data.version === 1001) { - SavegameInterface_V1002.migrate1001to1002(data); - data.version = 1002; - } - - if (data.version === 1002) { - SavegameInterface_V1003.migrate1002to1003(data); - data.version = 1003; - } - - if (data.version === 1003) { - SavegameInterface_V1004.migrate1003to1004(data); - data.version = 1004; - } - - if (data.version === 1004) { - SavegameInterface_V1005.migrate1004to1005(data); - data.version = 1005; - } - - if (data.version === 1005) { - SavegameInterface_V1006.migrate1005to1006(data); - data.version = 1006; - } - - if (data.version === 1006) { - SavegameInterface_V1007.migrate1006to1007(data); - data.version = 1007; - } - - if (data.version === 1007) { - SavegameInterface_V1008.migrate1007to1008(data); - data.version = 1008; - } - - if (data.version === 1008) { - SavegameInterface_V1009.migrate1008to1009(data); - data.version = 1009; - } - - if (data.version === 1009) { - SavegameInterface_V1010.migrate1009to1010(data); - data.version = 1010; - } - - return ExplainedResult.good(); - } - - /** - * Verifies the savegames data - * @param {SavegameData} data - */ - verify(data) { - if (!data.dump) { - // Well, guess that works - return ExplainedResult.good(); - } - - if (!this.getDumpReaderForExternalData(data).validate()) { - return ExplainedResult.bad("dump-reader-failed-validation"); - } - return ExplainedResult.good(); - } - - //////// Subclasses interface //////// - - /** - * Returns if this game can be saved on disc - * @returns {boolean} - */ - isSaveable() { - return true; - } - /** - * Returns the statistics of the savegame - * @returns {SavegameStats} - */ - getStatistics() { - return this.currentData.stats; - } - - /** - * Returns the *real* last update of the savegame, not the one of the metadata - * which could also be the servers one - */ - getRealLastUpdate() { - return this.currentData.lastUpdate; - } - - /** - * Returns if this game has a serialized game dump - */ - hasGameDump() { - return !!this.currentData.dump && this.currentData.dump.entities.length > 0; - } - - /** - * Returns the current game dump - * @returns {SerializedGame} - */ - getCurrentDump() { - return this.currentData.dump; - } - - /** - * Returns a reader to access the data - * @returns {BaseSavegameInterface} - */ - getDumpReader() { - if (!this.currentData.dump) { - logger.warn("Getting reader on null-savegame dump"); - } - - const cls = /** @type {typeof Savegame} */ (this.constructor).getReaderClass(); - return new cls(this.currentData); - } - - /** - * Returns a reader to access external data - * @returns {BaseSavegameInterface} - */ - getDumpReaderForExternalData(data) { - assert(data.version, "External data contains no version"); - return getSavegameInterface(data); - } - - ///////// Public Interface /////////// - - /** - * Updates the last update field so we can send the savegame to the server, - * WITHOUT Saving! - */ - setLastUpdate(time) { - this.currentData.lastUpdate = time; - } - - /** - * - * @param {GameRoot} root - */ - updateData(root) { - // Construct a new serializer - const serializer = new SavegameSerializer(); - - // let timer = performance.now(); - const dump = serializer.generateDumpFromGameRoot(root); - if (!dump) { - return false; - } - - const shadowData = Object.assign({}, this.currentData); - shadowData.dump = dump; - shadowData.lastUpdate = new Date().getTime(); - shadowData.version = this.getCurrentVersion(); - shadowData.mods = MODS.getModsListForSavegame(); - - const reader = this.getDumpReaderForExternalData(shadowData); - - // Validate (not in prod though) - if (!G_IS_RELEASE) { - const validationResult = reader.validate(); - if (!validationResult) { - return false; - } - } - - // Save data - this.currentData = shadowData; - } - - /** - * Writes the savegame as well as its metadata - */ - writeSavegameAndMetadata() { - return this.writeAsync().then(() => this.saveMetadata()); - } - - /** - * Updates the savegames metadata - */ - saveMetadata() { - this.metaDataRef.lastUpdate = new Date().getTime(); - this.metaDataRef.version = this.getCurrentVersion(); - if (!this.hasGameDump()) { - this.metaDataRef.level = 0; - } else { - this.metaDataRef.level = this.currentData.dump.hubGoals.level; - } - - return this.app.savegameMgr.writeAsync(); - } - - /** - * @see ReadWriteProxy.writeAsync - * @returns {Promise} - */ - writeAsync() { - if (G_IS_DEV && globalConfig.debug.disableSavegameWrite) { - return Promise.resolve(); - } - return super.writeAsync(); - } -} +import { globalConfig } from "../core/config"; +import { ExplainedResult } from "../core/explained_result"; +import { createLogger } from "../core/logging"; +import { ReadWriteProxy } from "../core/read_write_proxy"; +import { MODS } from "../mods/modloader"; +import { BaseSavegameInterface } from "./savegame_interface"; +import { getSavegameInterface, savegameInterfaces } from "./savegame_interface_registry"; +import { SavegameSerializer } from "./savegame_serializer"; + +const logger = createLogger("savegame"); + +/** + * @typedef {import("../application").Application} Application + * @typedef {import("../game/root").GameRoot} GameRoot + * @typedef {import("./savegame_typedefs").SavegameData} SavegameData + * @typedef {import("./savegame_typedefs").SavegameMetadata} SavegameMetadata + * @typedef {import("./savegame_typedefs").SerializedGame} SerializedGame + */ + +export class Savegame extends ReadWriteProxy { + /** + * + * @param {Application} app + * @param {object} param0 + * @param {string} param0.internalId + * @param {SavegameMetadata} param0.metaDataRef Handle to the meta data + */ + constructor(app, { internalId, metaDataRef }) { + super(app.storage, "savegame-" + internalId + ".bin"); + + /** @type {Application} */ + this.app = app; + + this.internalId = internalId; + this.metaDataRef = metaDataRef; + + /** @type {SavegameData} */ + this.currentData = this.getDefaultData(); + + assert( + savegameInterfaces[Savegame.getCurrentVersion()], + "Savegame interface not defined: " + Savegame.getCurrentVersion() + ); + } + + //////// RW Proxy Impl ////////// + + /** + * @returns {number} + */ + static getCurrentVersion() { + return 1010; + } + + /** + * @returns {typeof BaseSavegameInterface} + */ + static getReaderClass() { + return savegameInterfaces[Savegame.getCurrentVersion()]; + } + + /** + * + * @param {Application} app + * @returns + */ + static createPuzzleSavegame(app) { + return new Savegame(app, { + internalId: "puzzle", + metaDataRef: { + internalId: "puzzle", + lastUpdate: 0, + version: 0, + level: 0, + name: "puzzle", + }, + }); + } + + /** + * @returns {number} + */ + getCurrentVersion() { + return /** @type {typeof Savegame} */ (this.constructor).getCurrentVersion(); + } + + /** + * Returns the savegames default data + * @returns {SavegameData} + */ + getDefaultData() { + return { + version: this.getCurrentVersion(), + dump: null, + lastUpdate: Date.now(), + mods: MODS.getModsListForSavegame(), + }; + } + + /** + * Migrates the savegames data + * @param {SavegameData} data + */ + migrate(data) { + if (data.version !== this.getCurrentVersion()) { + return ExplainedResult.bad("Savegame upgrade is not supported"); + } + + return ExplainedResult.good(); + } + + /** + * Verifies the savegames data + * @param {SavegameData} data + */ + verify(data) { + if (!data.dump) { + // Well, guess that works + return ExplainedResult.good(); + } + + if (!this.getDumpReaderForExternalData(data).validate()) { + return ExplainedResult.bad("dump-reader-failed-validation"); + } + return ExplainedResult.good(); + } + + //////// Subclasses interface //////// + + /** + * Returns if this game can be saved on disc + * @returns {boolean} + */ + isSaveable() { + return true; + } + + /** + * Returns the *real* last update of the savegame, not the one of the metadata + * which could also be the servers one + */ + getRealLastUpdate() { + return this.currentData.lastUpdate; + } + + /** + * Returns if this game has a serialized game dump + */ + hasGameDump() { + return !!this.currentData.dump && this.currentData.dump.entities.length > 0; + } + + /** + * Returns the current game dump + * @returns {SerializedGame} + */ + getCurrentDump() { + return this.currentData.dump; + } + + /** + * Returns a reader to access the data + * @returns {BaseSavegameInterface} + */ + getDumpReader() { + if (!this.currentData.dump) { + logger.warn("Getting reader on null-savegame dump"); + } + + const cls = /** @type {typeof Savegame} */ (this.constructor).getReaderClass(); + return new cls(this.currentData); + } + + /** + * Returns a reader to access external data + * @returns {BaseSavegameInterface} + */ + getDumpReaderForExternalData(data) { + assert(data.version, "External data contains no version"); + return getSavegameInterface(data); + } + + ///////// Public Interface /////////// + + /** + * Updates the last update field so we can send the savegame to the server, + * WITHOUT Saving! + */ + setLastUpdate(time) { + this.currentData.lastUpdate = time; + } + + /** + * + * @param {GameRoot} root + */ + updateData(root) { + // Construct a new serializer + const serializer = new SavegameSerializer(); + + // let timer = performance.now(); + const dump = serializer.generateDumpFromGameRoot(root); + if (!dump) { + return false; + } + + const shadowData = {}; + shadowData.dump = dump; + shadowData.lastUpdate = new Date().getTime(); + shadowData.version = this.getCurrentVersion(); + shadowData.mods = MODS.getModsListForSavegame(); + + const reader = this.getDumpReaderForExternalData(shadowData); + + // Validate (not in prod though) + if (!G_IS_RELEASE) { + const validationResult = reader.validate(); + if (!validationResult) { + return false; + } + } + + // Save data + this.currentData = shadowData; + } + + /** + * Writes the savegame as well as its metadata + */ + writeSavegameAndMetadata() { + return this.writeAsync().then(() => this.saveMetadata()); + } + + /** + * Updates the savegames metadata + */ + saveMetadata() { + this.metaDataRef.lastUpdate = new Date().getTime(); + this.metaDataRef.version = this.getCurrentVersion(); + if (!this.hasGameDump()) { + this.metaDataRef.level = 0; + } else { + this.metaDataRef.level = this.currentData.dump.hubGoals.level; + } + + return this.app.savegameMgr.writeAsync(); + } + + /** + * @see ReadWriteProxy.writeAsync + * @returns {Promise} + */ + writeAsync() { + if (G_IS_DEV && globalConfig.debug.disableSavegameWrite) { + return Promise.resolve(); + } + return super.writeAsync(); + } +} diff --git a/src/js/savegame/savegame_compressor.js b/src/js/savegame/savegame_compressor.js deleted file mode 100644 index d8797bad..00000000 --- a/src/js/savegame/savegame_compressor.js +++ /dev/null @@ -1,168 +0,0 @@ -const charmap = - "!#%&'()*+,-./:;<=>?@[]^_`{|}~¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿABCDEFGHIJKLMNOPQRSTUVWXYZ"; - -let compressionCache = {}; -let decompressionCache = {}; - -/** - * Compresses an integer into a tight string representation - * @param {number} i - * @returns {string} - */ -function compressInt(i) { - // Zero value breaks - i += 1; - - // save `i` as the cache key - // to avoid it being modified by the - // rest of the function. - const cache_key = i; - - if (compressionCache[cache_key]) { - return compressionCache[cache_key]; - } - let result = ""; - do { - result += charmap[i % charmap.length]; - i = Math.floor(i / charmap.length); - } while (i > 0); - return (compressionCache[cache_key] = result); -} - -/** - * Decompresses an integer from its tight string representation - * @param {string} s - * @returns {number} - */ -function decompressInt(s) { - if (decompressionCache[s]) { - return decompressionCache[s]; - } - s = "" + s; - let result = 0; - for (let i = s.length - 1; i >= 0; --i) { - result = result * charmap.length + charmap.indexOf(s.charAt(i)); - } - // Fixes zero value break fix from above - result -= 1; - return (decompressionCache[s] = result); -} - -// Sanity -if (G_IS_DEV) { - for (let i = 0; i < 10000; ++i) { - if (decompressInt(compressInt(i)) !== i) { - throw new Error( - "Bad compression for: " + - i + - " compressed: " + - compressInt(i) + - " decompressed: " + - decompressInt(compressInt(i)) - ); - } - } -} - -/** - * @param {any} obj - * @param {Map} keys - * @param {Map} values - * @returns {any[]|object|number|string} - */ -function compressObjectInternal(obj, keys, values) { - if (Array.isArray(obj)) { - let result = []; - for (let i = 0; i < obj.length; ++i) { - result.push(compressObjectInternal(obj[i], keys, values)); - } - return result; - } else if (typeof obj === "object" && obj !== null) { - let result = {}; - for (const key in obj) { - let index = keys.get(key); - if (index === undefined) { - index = keys.size; - keys.set(key, index); - } - const value = obj[key]; - result[compressInt(index)] = compressObjectInternal(value, keys, values); - } - return result; - } else if (typeof obj === "string") { - let index = values.get(obj); - if (index === undefined) { - index = values.size; - values.set(obj, index); - } - return compressInt(index); - } - return obj; -} - -/** - * @param {Map} hashMap - * @returns {Array} - */ -function indexMapToArray(hashMap) { - const result = new Array(hashMap.size); - hashMap.forEach((index, key) => { - result[index] = key; - }); - return result; -} - -/** - * @param {object} obj - */ -export function compressObject(obj) { - const keys = new Map(); - const values = new Map(); - const data = compressObjectInternal(obj, keys, values); - return { - keys: indexMapToArray(keys), - values: indexMapToArray(values), - data, - }; -} - -/** - * @param {object} obj - * @param {string[]} keys - * @param {any[]} values - * @returns {object} - */ -function decompressObjectInternal(obj, keys = [], values = []) { - if (Array.isArray(obj)) { - let result = []; - for (let i = 0; i < obj.length; ++i) { - result.push(decompressObjectInternal(obj[i], keys, values)); - } - return result; - } else if (typeof obj === "object" && obj !== null) { - let result = {}; - for (const key in obj) { - const realIndex = decompressInt(key); - const value = obj[key]; - result[keys[realIndex]] = decompressObjectInternal(value, keys, values); - } - return result; - } else if (typeof obj === "string") { - const realIndex = decompressInt(obj); - return values[realIndex]; - } - return obj; -} - -/** - * @param {object} obj - */ -export function decompressObject(obj) { - if (obj.keys && obj.values && obj.data) { - const keys = obj.keys; - const values = obj.values; - const result = decompressObjectInternal(obj.data, keys, values); - return result; - } - return obj; -} diff --git a/src/js/savegame/savegame_interface.js b/src/js/savegame/savegame_interface.js index 55fd27e7..f6a63ad4 100644 --- a/src/js/savegame/savegame_interface.js +++ b/src/js/savegame/savegame_interface.js @@ -1,6 +1,6 @@ import { createLogger } from "../core/logging"; -const Ajv = require("ajv"); +import Ajv from "ajv"; const ajv = new Ajv({ allErrors: false, uniqueItems: false, diff --git a/src/js/savegame/savegame_interface_registry.js b/src/js/savegame/savegame_interface_registry.js index 089b15fc..49507cc5 100644 --- a/src/js/savegame/savegame_interface_registry.js +++ b/src/js/savegame/savegame_interface_registry.js @@ -1,55 +1,55 @@ -import { BaseSavegameInterface } from "./savegame_interface"; -import { SavegameInterface_V1000 } from "./schemas/1000"; -import { createLogger } from "../core/logging"; -import { SavegameInterface_V1001 } from "./schemas/1001"; -import { SavegameInterface_V1002 } from "./schemas/1002"; -import { SavegameInterface_V1003 } from "./schemas/1003"; -import { SavegameInterface_V1004 } from "./schemas/1004"; -import { SavegameInterface_V1005 } from "./schemas/1005"; -import { SavegameInterface_V1006 } from "./schemas/1006"; -import { SavegameInterface_V1007 } from "./schemas/1007"; -import { SavegameInterface_V1008 } from "./schemas/1008"; -import { SavegameInterface_V1009 } from "./schemas/1009"; -import { SavegameInterface_V1010 } from "./schemas/1010"; - -/** @type {Object.} */ -export const savegameInterfaces = { - 1000: SavegameInterface_V1000, - 1001: SavegameInterface_V1001, - 1002: SavegameInterface_V1002, - 1003: SavegameInterface_V1003, - 1004: SavegameInterface_V1004, - 1005: SavegameInterface_V1005, - 1006: SavegameInterface_V1006, - 1007: SavegameInterface_V1007, - 1008: SavegameInterface_V1008, - 1009: SavegameInterface_V1009, - 1010: SavegameInterface_V1010, -}; - -const logger = createLogger("savegame_interface_registry"); - -/** - * Returns if the given savegame has any supported interface - * @param {any} savegame - * @returns {BaseSavegameInterface|null} - */ -export function getSavegameInterface(savegame) { - if (!savegame || !savegame.version) { - logger.warn("Savegame does not contain a valid version (undefined)"); - return null; - } - const version = savegame.version; - if (!Number.isInteger(version)) { - logger.warn("Savegame does not contain a valid version (non-integer):", version); - return null; - } - - const interfaceClass = savegameInterfaces[version]; - if (!interfaceClass) { - logger.warn("Version", version, "has no implemented interface!"); - return null; - } - - return new interfaceClass(savegame); -} +import { BaseSavegameInterface } from "./savegame_interface"; +import { SavegameInterface_V1000 } from "./schemas/1000"; +import { createLogger } from "../core/logging"; +import { SavegameInterface_V1001 } from "./schemas/1001"; +import { SavegameInterface_V1002 } from "./schemas/1002"; +import { SavegameInterface_V1003 } from "./schemas/1003"; +import { SavegameInterface_V1004 } from "./schemas/1004"; +import { SavegameInterface_V1005 } from "./schemas/1005"; +import { SavegameInterface_V1006 } from "./schemas/1006"; +import { SavegameInterface_V1007 } from "./schemas/1007"; +import { SavegameInterface_V1008 } from "./schemas/1008"; +import { SavegameInterface_V1009 } from "./schemas/1009"; +import { SavegameInterface_V1010 } from "./schemas/1010"; + +/** @type {Object.} */ +export const savegameInterfaces = { + 1000: SavegameInterface_V1000, + 1001: SavegameInterface_V1001, + 1002: SavegameInterface_V1002, + 1003: SavegameInterface_V1003, + 1004: SavegameInterface_V1004, + 1005: SavegameInterface_V1005, + 1006: SavegameInterface_V1006, + 1007: SavegameInterface_V1007, + 1008: SavegameInterface_V1008, + 1009: SavegameInterface_V1009, + 1010: SavegameInterface_V1010, +}; + +const logger = createLogger("savegame_interface_registry"); + +/** + * Returns if the given savegame has any supported interface + * @param {any} savegame + * @returns {BaseSavegameInterface|null} + */ +export function getSavegameInterface(savegame) { + if (!savegame || !savegame.version) { + logger.warn("Savegame does not contain a valid version (undefined)"); + return null; + } + const version = savegame.version; + if (!Number.isInteger(version)) { + logger.warn("Savegame does not contain a valid version (non-integer):", version); + return null; + } + + const interfaceClass = savegameInterfaces[version]; + if (!interfaceClass) { + logger.warn("Version", version, "has no implemented interface!"); + return null; + } + + return new interfaceClass(savegame); +} diff --git a/src/js/savegame/savegame_manager.js b/src/js/savegame/savegame_manager.js index 571d8442..6ad3b98a 100644 --- a/src/js/savegame/savegame_manager.js +++ b/src/js/savegame/savegame_manager.js @@ -1,237 +1,240 @@ -import { ExplainedResult } from "../core/explained_result"; -import { createLogger } from "../core/logging"; -import { ReadWriteProxy } from "../core/read_write_proxy"; -import { globalConfig } from "../core/config"; -import { Savegame } from "./savegame"; -const logger = createLogger("savegame_manager"); - -const Rusha = require("rusha"); - -/** - * @typedef {import("./savegame_typedefs").SavegamesData} SavegamesData - * @typedef {import("./savegame_typedefs").SavegameMetadata} SavegameMetadata - */ - -/** @enum {string} */ -export const enumLocalSavegameStatus = { - offline: "offline", - synced: "synced", -}; - -export class SavegameManager extends ReadWriteProxy { - constructor(app) { - super(app, "savegames.bin"); - - this.currentData = this.getDefaultData(); - } - - // RW Proxy Impl - /** - * @returns {SavegamesData} - */ - getDefaultData() { - return { - version: this.getCurrentVersion(), - savegames: [], - }; - } - - getCurrentVersion() { - return 1002; - } - - verify(data) { - // @TODO - return ExplainedResult.good(); - } - - /** - * - * @param {SavegamesData} data - */ - migrate(data) { - if (data.version < 1001) { - data.savegames.forEach(savegame => { - savegame.level = 0; - }); - data.version = 1001; - } - - if (data.version < 1002) { - data.savegames.forEach(savegame => { - savegame.name = null; - }); - data.version = 1002; - } - - return ExplainedResult.good(); - } - - // End rw proxy - - /** - * @returns {Array} - */ - getSavegamesMetaData() { - return this.currentData.savegames; - } - - /** - * - * @param {string} internalId - * @returns {Savegame} - */ - getSavegameById(internalId) { - const metadata = this.getGameMetaDataByInternalId(internalId); - if (!metadata) { - return null; - } - return new Savegame(this.app, { internalId, metaDataRef: metadata }); - } - - /** - * Deletes a savegame - * @param {SavegameMetadata} game - */ - deleteSavegame(game) { - const handle = new Savegame(this.app, { - internalId: game.internalId, - metaDataRef: game, - }); - - return handle - .deleteAsync() - .catch(err => { - console.warn("Failed to unlink physical savegame file, still removing:", err); - }) - .then(() => { - for (let i = 0; i < this.currentData.savegames.length; ++i) { - const potentialGame = this.currentData.savegames[i]; - if (potentialGame.internalId === handle.internalId) { - this.currentData.savegames.splice(i, 1); - break; - } - } - - return this.writeAsync(); - }); - } - - /** - * Returns a given games metadata by id - * @param {string} id - * @returns {SavegameMetadata} - */ - getGameMetaDataByInternalId(id) { - for (let i = 0; i < this.currentData.savegames.length; ++i) { - const data = this.currentData.savegames[i]; - if (data.internalId === id) { - return data; - } - } - logger.error("Savegame internal id not found:", id); - return null; - } - - /** - * Creates a new savegame - * @returns {Savegame} - */ - createNewSavegame() { - const id = this.generateInternalId(); - - const metaData = /** @type {SavegameMetadata} */ ({ - lastUpdate: Date.now(), - version: Savegame.getCurrentVersion(), - internalId: id, - }); - - this.currentData.savegames.push(metaData); - - // Notice: This is async and happening in the background - this.updateAfterSavegamesChanged(); - - return new Savegame(this.app, { - internalId: id, - metaDataRef: metaData, - }); - } - - /** - * Attempts to import a savegame - * @param {object} data - */ - importSavegame(data) { - const savegame = this.createNewSavegame(); - - const migrationResult = savegame.migrate(data); - if (migrationResult.isBad()) { - return Promise.reject("Failed to migrate: " + migrationResult.reason); - } - - savegame.currentData = data; - const verification = savegame.verify(data); - if (verification.isBad()) { - return Promise.reject("Verification failed: " + verification.result); - } - - return savegame.writeSavegameAndMetadata().then(() => this.updateAfterSavegamesChanged()); - } - - /** - * Hook after the savegames got changed - */ - updateAfterSavegamesChanged() { - return this.sortSavegames().then(() => this.writeAsync()); - } - - /** - * Sorts all savegames by their creation time descending - * @returns {Promise} - */ - sortSavegames() { - this.currentData.savegames.sort((a, b) => b.lastUpdate - a.lastUpdate); - let promiseChain = Promise.resolve(); - while (this.currentData.savegames.length > 30) { - const toRemove = this.currentData.savegames.pop(); - - // Try to remove the savegame since its no longer available - const game = new Savegame(this.app, { - internalId: toRemove.internalId, - metaDataRef: toRemove, - }); - promiseChain = promiseChain - .then(() => game.deleteAsync()) - .then( - () => {}, - err => { - logger.error(this, "Failed to remove old savegame:", toRemove, ":", err); - } - ); - } - - return promiseChain; - } - - /** - * Helper method to generate a new internal savegame id - */ - generateInternalId() { - return Rusha.createHash() - .update(Date.now() + "/" + Math.random()) - .digest("hex"); - } - - // End - - initialize() { - // First read, then directly write to ensure we have the latest data - // @ts-ignore - return this.readAsync().then(() => { - if (G_IS_DEV && globalConfig.debug.disableSavegameWrite) { - return Promise.resolve(); - } - return this.updateAfterSavegamesChanged(); - }); - } -} +/* typehints:start */ +import { Application } from "@/application"; +/* typehints:end */ + +import { globalConfig } from "../core/config"; +import { ExplainedResult } from "../core/explained_result"; +import { createLogger } from "../core/logging"; +import { ReadWriteProxy } from "../core/read_write_proxy"; +import { Savegame } from "./savegame"; +const logger = createLogger("savegame_manager"); + +/** + * @typedef {import("./savegame_typedefs").SavegamesData} SavegamesData + * @typedef {import("./savegame_typedefs").SavegameMetadata} SavegameMetadata + */ + +/** @enum {string} */ +export const enumLocalSavegameStatus = { + offline: "offline", + synced: "synced", +}; + +export class SavegameManager extends ReadWriteProxy { + constructor(app, storage) { + super(storage, "savegames.bin"); + + /** @type {Application} */ + this.app = app; + + this.currentData = this.getDefaultData(); + } + + // RW Proxy Impl + /** + * @returns {SavegamesData} + */ + getDefaultData() { + return { + version: this.getCurrentVersion(), + savegames: [], + }; + } + + getCurrentVersion() { + return 1002; + } + + verify(data) { + // @TODO + return ExplainedResult.good(); + } + + /** + * + * @param {SavegamesData} data + */ + migrate(data) { + if (data.version < 1001) { + data.savegames.forEach(savegame => { + savegame.level = 0; + }); + data.version = 1001; + } + + if (data.version < 1002) { + data.savegames.forEach(savegame => { + savegame.name = null; + }); + data.version = 1002; + } + + return ExplainedResult.good(); + } + + // End rw proxy + + /** + * @returns {Array} + */ + getSavegamesMetaData() { + return this.currentData.savegames; + } + + /** + * + * @param {string} internalId + * @returns {Savegame} + */ + getSavegameById(internalId) { + const metadata = this.getGameMetaDataByInternalId(internalId); + if (!metadata) { + return null; + } + return new Savegame(this.app, { internalId, metaDataRef: metadata }); + } + + /** + * Deletes a savegame + * @param {SavegameMetadata} game + */ + deleteSavegame(game) { + const handle = new Savegame(this.app, { + internalId: game.internalId, + metaDataRef: game, + }); + + return handle + .deleteAsync() + .catch(err => { + console.warn("Failed to unlink physical savegame file, still removing:", err); + }) + .then(() => { + for (let i = 0; i < this.currentData.savegames.length; ++i) { + const potentialGame = this.currentData.savegames[i]; + if (potentialGame.internalId === handle.internalId) { + this.currentData.savegames.splice(i, 1); + break; + } + } + + return this.writeAsync(); + }); + } + + /** + * Returns a given games metadata by id + * @param {string} id + * @returns {SavegameMetadata} + */ + getGameMetaDataByInternalId(id) { + for (let i = 0; i < this.currentData.savegames.length; ++i) { + const data = this.currentData.savegames[i]; + if (data.internalId === id) { + return data; + } + } + logger.error("Savegame internal id not found:", id); + return null; + } + + /** + * Creates a new savegame + * @returns {Savegame} + */ + createNewSavegame() { + const id = this.generateInternalId(); + + const metaData = /** @type {SavegameMetadata} */ ({ + lastUpdate: Date.now(), + version: Savegame.getCurrentVersion(), + internalId: id, + }); + + this.currentData.savegames.push(metaData); + + // Notice: This is async and happening in the background + this.updateAfterSavegamesChanged(); + + return new Savegame(this.app, { + internalId: id, + metaDataRef: metaData, + }); + } + + /** + * Attempts to import a savegame + * @param {object} data + */ + importSavegame(data) { + const savegame = this.createNewSavegame(); + + const migrationResult = savegame.migrate(data); + if (migrationResult.isBad()) { + return Promise.reject("Failed to migrate: " + migrationResult.reason); + } + + savegame.currentData = data; + const verification = savegame.verify(data); + if (verification.isBad()) { + return Promise.reject("Verification failed: " + verification.result); + } + + return savegame.writeSavegameAndMetadata().then(() => this.updateAfterSavegamesChanged()); + } + + /** + * Hook after the savegames got changed + */ + updateAfterSavegamesChanged() { + return this.sortSavegames().then(() => this.writeAsync()); + } + + /** + * Sorts all savegames by their creation time descending + * @returns {Promise} + */ + sortSavegames() { + this.currentData.savegames.sort((a, b) => b.lastUpdate - a.lastUpdate); + let promiseChain = Promise.resolve(); + while (this.currentData.savegames.length > 30) { + const toRemove = this.currentData.savegames.pop(); + + // Try to remove the savegame since its no longer available + const game = new Savegame(this.app, { + internalId: toRemove.internalId, + metaDataRef: toRemove, + }); + promiseChain = promiseChain + .then(() => game.deleteAsync()) + .then( + () => {}, + err => { + logger.error(this, "Failed to remove old savegame:", toRemove, ":", err); + } + ); + } + + return promiseChain; + } + + /** + * Helper method to generate a new internal savegame id + */ + generateInternalId() { + return self.crypto.randomUUID(); + } + + // End + + initialize() { + // First read, then directly write to ensure we have the latest data + // @ts-ignore + return this.readAsync().then(() => { + if (G_IS_DEV && globalConfig.debug.disableSavegameWrite) { + return Promise.resolve(); + } + return this.updateAfterSavegamesChanged(); + }); + } +} diff --git a/src/js/savegame/savegame_serializer.js b/src/js/savegame/savegame_serializer.js index f95c9896..0ce695ed 100644 --- a/src/js/savegame/savegame_serializer.js +++ b/src/js/savegame/savegame_serializer.js @@ -1,162 +1,162 @@ -import { ExplainedResult } from "../core/explained_result"; -import { gComponentRegistry } from "../core/global_registries"; -import { createLogger } from "../core/logging"; -import { MOD_SIGNALS } from "../mods/mod_signals"; -import { SerializerInternal } from "./serializer_internal"; - -/** - * @typedef {import("../game/component").Component} Component - * @typedef {import("../game/component").StaticComponent} StaticComponent - * @typedef {import("../game/entity").Entity} Entity - * @typedef {import("../game/root").GameRoot} GameRoot - * @typedef {import("../savegame/savegame_typedefs").SerializedGame} SerializedGame - */ - -const logger = createLogger("savegame_serializer"); - -/** - * Serializes a savegame - */ -export class SavegameSerializer { - constructor() { - this.internal = new SerializerInternal(); - } - - /** - * Serializes the game root into a dump - * @param {GameRoot} root - * @param {boolean=} sanityChecks Whether to check for validity - * @returns {object} - */ - generateDumpFromGameRoot(root, sanityChecks = true) { - /** @type {SerializedGame} */ - const data = { - camera: root.camera.serialize(), - time: root.time.serialize(), - map: root.map.serialize(), - gameMode: root.gameMode.serialize(), - entityMgr: root.entityMgr.serialize(), - hubGoals: root.hubGoals.serialize(), - entities: this.internal.serializeEntityArray(root.entityMgr.entities), - beltPaths: root.systemMgr.systems.belt.serializePaths(), - pinnedShapes: root.hud.parts.pinnedShapes ? root.hud.parts.pinnedShapes.serialize() : null, - waypoints: root.hud.parts.waypoints ? root.hud.parts.waypoints.serialize() : null, - - modExtraData: {}, - }; - - MOD_SIGNALS.gameSerialized.dispatch(root, data); - - if (G_IS_DEV) { - if (sanityChecks) { - // Sanity check - const sanity = this.verifyLogicalErrors(data); - if (!sanity.result) { - logger.error("Created invalid savegame:", sanity.reason, "savegame:", data); - return null; - } - } - } - return data; - } - - /** - * Verifies if there are logical errors in the savegame - * @param {SerializedGame} savegame - * @returns {ExplainedResult} - */ - verifyLogicalErrors(savegame) { - if (!savegame.entities) { - return ExplainedResult.bad("Savegame has no entities"); - } - - const seenUids = new Set(); - - // Check for duplicate UIDS - for (let i = 0; i < savegame.entities.length; ++i) { - /** @type {Entity} */ - const entity = savegame.entities[i]; - - const uid = entity.uid; - if (!Number.isInteger(uid)) { - return ExplainedResult.bad("Entity has invalid uid: " + uid); - } - if (seenUids.has(uid)) { - return ExplainedResult.bad("Duplicate uid " + uid); - } - seenUids.add(uid); - - // Verify components - if (!entity.components) { - return ExplainedResult.bad("Entity is missing key 'components': " + JSON.stringify(entity)); - } - - const components = entity.components; - for (const componentId in components) { - const componentClass = gComponentRegistry.findById(componentId); - - // Check component id is known - if (!componentClass) { - return ExplainedResult.bad("Unknown component id: " + componentId); - } - - // Verify component data - const componentData = components[componentId]; - const componentVerifyError = /** @type {StaticComponent} */ (componentClass).verify( - componentData - ); - - // Check component data is ok - if (componentVerifyError) { - return ExplainedResult.bad( - "Component " + componentId + " has invalid data: " + componentVerifyError - ); - } - } - } - - return ExplainedResult.good(); - } - - /** - * Tries to load the savegame from a given dump - * @param {SerializedGame} savegame - * @param {GameRoot} root - * @returns {ExplainedResult} - */ - deserialize(savegame, root) { - // Sanity - const verifyResult = this.verifyLogicalErrors(savegame); - if (!verifyResult.result) { - return ExplainedResult.bad(verifyResult.reason); - } - let errorReason = null; - - errorReason = errorReason || root.entityMgr.deserialize(savegame.entityMgr); - errorReason = errorReason || root.time.deserialize(savegame.time); - errorReason = errorReason || root.camera.deserialize(savegame.camera); - errorReason = errorReason || root.map.deserialize(savegame.map); - errorReason = errorReason || root.gameMode.deserialize(savegame.gameMode); - errorReason = errorReason || root.hubGoals.deserialize(savegame.hubGoals, root); - errorReason = errorReason || this.internal.deserializeEntityArray(root, savegame.entities); - errorReason = errorReason || root.systemMgr.systems.belt.deserializePaths(savegame.beltPaths); - - if (root.hud.parts.pinnedShapes) { - errorReason = errorReason || root.hud.parts.pinnedShapes.deserialize(savegame.pinnedShapes); - } - - if (root.hud.parts.waypoints) { - errorReason = errorReason || root.hud.parts.waypoints.deserialize(savegame.waypoints); - } - - // Check for errors - if (errorReason) { - return ExplainedResult.bad(errorReason); - } - - // Mods - MOD_SIGNALS.gameDeserialized.dispatch(root, savegame); - - return ExplainedResult.good(); - } -} +import { ExplainedResult } from "../core/explained_result"; +import { gComponentRegistry } from "../core/global_registries"; +import { createLogger } from "../core/logging"; +import { MOD_SIGNALS } from "../mods/mod_signals"; +import { SerializerInternal } from "./serializer_internal"; + +/** + * @typedef {import("../game/component").Component} Component + * @typedef {import("../game/component").StaticComponent} StaticComponent + * @typedef {import("../game/entity").Entity} Entity + * @typedef {import("../game/root").GameRoot} GameRoot + * @typedef {import("../savegame/savegame_typedefs").SerializedGame} SerializedGame + */ + +const logger = createLogger("savegame_serializer"); + +/** + * Serializes a savegame + */ +export class SavegameSerializer { + constructor() { + this.internal = new SerializerInternal(); + } + + /** + * Serializes the game root into a dump + * @param {GameRoot} root + * @param {boolean=} sanityChecks Whether to check for validity + * @returns {object} + */ + generateDumpFromGameRoot(root, sanityChecks = true) { + /** @type {SerializedGame} */ + const data = { + camera: root.camera.serialize(), + time: root.time.serialize(), + map: root.map.serialize(), + gameMode: root.gameMode.serialize(), + entityMgr: root.entityMgr.serialize(), + hubGoals: root.hubGoals.serialize(), + entities: this.internal.serializeEntityMap(root.entityMgr.entities), + beltPaths: root.systemMgr.systems.belt.serializePaths(), + pinnedShapes: root.hud.parts.pinnedShapes ? root.hud.parts.pinnedShapes.serialize() : null, + waypoints: root.hud.parts.waypoints ? root.hud.parts.waypoints.serialize() : null, + + modExtraData: {}, + }; + + MOD_SIGNALS.gameSerialized.dispatch(root, data); + + if (G_IS_DEV) { + if (sanityChecks) { + // Sanity check + const sanity = this.verifyLogicalErrors(data); + if (!sanity.result) { + logger.error("Created invalid savegame:", sanity.reason, "savegame:", data); + return null; + } + } + } + return data; + } + + /** + * Verifies if there are logical errors in the savegame + * @param {SerializedGame} savegame + * @returns {ExplainedResult} + */ + verifyLogicalErrors(savegame) { + if (!savegame.entities) { + return ExplainedResult.bad("Savegame has no entities"); + } + + const seenUids = new Set(); + + // Check for duplicate UIDS + for (let i = 0; i < savegame.entities.length; ++i) { + /** @type {Entity} */ + const entity = savegame.entities[i]; + + const uid = entity.uid; + if (!Number.isInteger(uid)) { + return ExplainedResult.bad("Entity has invalid uid: " + uid); + } + if (seenUids.has(uid)) { + return ExplainedResult.bad("Duplicate uid " + uid); + } + seenUids.add(uid); + + // Verify components + if (!entity.components) { + return ExplainedResult.bad("Entity is missing key 'components': " + JSON.stringify(entity)); + } + + const components = entity.components; + for (const componentId in components) { + const componentClass = gComponentRegistry.findById(componentId); + + // Check component id is known + if (!componentClass) { + return ExplainedResult.bad("Unknown component id: " + componentId); + } + + // Verify component data + const componentData = components[componentId]; + const componentVerifyError = /** @type {StaticComponent} */ (componentClass).verify( + componentData + ); + + // Check component data is ok + if (componentVerifyError) { + return ExplainedResult.bad( + "Component " + componentId + " has invalid data: " + componentVerifyError + ); + } + } + } + + return ExplainedResult.good(); + } + + /** + * Tries to load the savegame from a given dump + * @param {SerializedGame} savegame + * @param {GameRoot} root + * @returns {ExplainedResult} + */ + deserialize(savegame, root) { + // Sanity + const verifyResult = this.verifyLogicalErrors(savegame); + if (!verifyResult.result) { + return ExplainedResult.bad(verifyResult.reason); + } + let errorReason = null; + + errorReason = errorReason || root.entityMgr.deserialize(savegame.entityMgr); + errorReason = errorReason || root.time.deserialize(savegame.time); + errorReason = errorReason || root.camera.deserialize(savegame.camera); + errorReason = errorReason || root.map.deserialize(savegame.map); + errorReason = errorReason || root.gameMode.deserialize(savegame.gameMode); + errorReason = errorReason || root.hubGoals.deserialize(savegame.hubGoals, root); + errorReason = errorReason || this.internal.deserializeEntityArray(root, savegame.entities); + errorReason = errorReason || root.systemMgr.systems.belt.deserializePaths(savegame.beltPaths); + + if (root.hud.parts.pinnedShapes) { + errorReason = errorReason || root.hud.parts.pinnedShapes.deserialize(savegame.pinnedShapes); + } + + if (root.hud.parts.waypoints) { + errorReason = errorReason || root.hud.parts.waypoints.deserialize(savegame.waypoints); + } + + // Check for errors + if (errorReason) { + return ExplainedResult.bad(errorReason); + } + + // Mods + MOD_SIGNALS.gameDeserialized.dispatch(root, savegame); + + return ExplainedResult.good(); + } +} diff --git a/src/js/savegame/savegame_typedefs.js b/src/js/savegame/savegame_typedefs.js index b1980115..710ac68c 100644 --- a/src/js/savegame/savegame_typedefs.js +++ b/src/js/savegame/savegame_typedefs.js @@ -1,111 +1,102 @@ -/** - * @typedef {import("../game/entity").Entity} Entity - * - * @typedef {{ - * id: string; - * version: string; - * website: string; - * name: string; - * author: string; - * }[]} SavegameStoredMods - * - * @typedef {{ - * failedMam: boolean, - * trashedCount: number, - * usedInverseRotater: boolean - * }} SavegameStats - * - * @typedef {{ - * camera: any, - * time: any, - * entityMgr: any, - * map: any, - * gameMode: object, - * hubGoals: any, - * pinnedShapes: any, - * waypoints: any, - * entities: Array, - * beltPaths: Array, - * modExtraData: Object - * }} SerializedGame - * - * @typedef {{ - * version: number, - * dump: SerializedGame, - * stats: SavegameStats, - * lastUpdate: number, - * mods: SavegameStoredMods - * }} SavegameData - * - * @typedef {{ - * lastUpdate: number, - * version: number, - * internalId: string, - * level: number - * name: string|null - * }} SavegameMetadata - * - * @typedef {{ - * version: number, - * savegames: Array - * }} SavegamesData - */ - -import { MetaBuilding } from "../game/meta_building"; - -// Notice: Update backend too -/** - * @typedef {{ - * id: number; - * shortKey: string; - * likes: number; - * downloads: number; - * completions: number; - * difficulty: number | null; - * averageTime: number | null; - * title: string; - * author: string; - * completed: boolean; - * }} PuzzleMetadata - */ - -/** - * @typedef {{ - * type: "emitter"; - * item: string; - * pos: { x: number; y: number; r: number } - * }} PuzzleGameBuildingConstantProducer - */ - -/** - * @typedef {{ - * type: "goal"; - * item: string; - * pos: { x: number; y: number; r: number } - * }} PuzzleGameBuildingGoal - */ - -/** - * @typedef {{ - * type: "block"; - * pos: { x: number; y: number; r: number } - * }} PuzzleGameBuildingBlock - */ - -/** - * @typedef {{ - * version: number; - * bounds: { w: number; h: number; }, - * buildings: (PuzzleGameBuildingGoal | PuzzleGameBuildingConstantProducer | PuzzleGameBuildingBlock)[], - * excludedBuildings: Array, - * }} PuzzleGameData - */ - -/** - * @typedef {{ - * meta: PuzzleMetadata, - * game: PuzzleGameData - * }} PuzzleFullData - */ - -export default {}; +/** + * @typedef {import("../game/entity").Entity} Entity + * + * @typedef {{ + * id: string; + * version: string; + * website: string; + * name: string; + * author: string; + * }[]} SavegameStoredMods + * + * @typedef {{ + * camera: any, + * time: any, + * entityMgr: any, + * map: any, + * gameMode: object, + * hubGoals: any, + * pinnedShapes: any, + * waypoints: any, + * entities: Array, + * beltPaths: Array, + * modExtraData: Object + * }} SerializedGame + * + * @typedef {{ + * version: number, + * dump: SerializedGame, + * lastUpdate: number, + * mods: SavegameStoredMods + * }} SavegameData + * + * @typedef {{ + * lastUpdate: number, + * version: number, + * internalId: string, + * level: number + * name: string|null + * }} SavegameMetadata + * + * @typedef {{ + * version: number, + * savegames: Array + * }} SavegamesData + */ + +// Notice: Update backend too +/** + * @typedef {{ + * id: number; + * shortKey: string; + * likes: number; + * downloads: number; + * completions: number; + * difficulty: number | null; + * averageTime: number | null; + * title: string; + * author: string; + * completed: boolean; + * }} PuzzleMetadata + */ + +/** + * @typedef {{ + * type: "emitter"; + * item: string; + * pos: { x: number; y: number; r: number } + * }} PuzzleGameBuildingConstantProducer + */ + +/** + * @typedef {{ + * type: "goal"; + * item: string; + * pos: { x: number; y: number; r: number } + * }} PuzzleGameBuildingGoal + */ + +/** + * @typedef {{ + * type: "block"; + * pos: { x: number; y: number; r: number } + * }} PuzzleGameBuildingBlock + */ + +/** + * @typedef {{ + * version: number; + * bounds: { w: number; h: number; }, + * buildings: (PuzzleGameBuildingGoal | PuzzleGameBuildingConstantProducer | PuzzleGameBuildingBlock)[], + * excludedBuildings: Array, + * }} PuzzleGameData + */ + +/** + * @typedef {{ + * meta: PuzzleMetadata, + * game: PuzzleGameData + * }} PuzzleFullData + */ + +export default {}; diff --git a/src/js/savegame/schemas/1000.js b/src/js/savegame/schemas/1000.js index 29cd0b1a..ff2d3705 100644 --- a/src/js/savegame/schemas/1000.js +++ b/src/js/savegame/schemas/1000.js @@ -1,6 +1,6 @@ import { BaseSavegameInterface } from "../savegame_interface.js"; -const schema = require("./1000.json"); +import schema from "./1000.json"; export class SavegameInterface_V1000 extends BaseSavegameInterface { getVersion() { diff --git a/src/js/savegame/schemas/1001.js b/src/js/savegame/schemas/1001.js index af86b09d..8a4601e1 100644 --- a/src/js/savegame/schemas/1001.js +++ b/src/js/savegame/schemas/1001.js @@ -1,9 +1,9 @@ -import { SavegameInterface_V1000 } from "./1000.js"; -import { createLogger } from "../../core/logging.js"; +import { createLogger } from "../../core/logging"; import { T } from "../../translations.js"; -import { TypeVector, TypeNumber, TypeString, TypeNullable } from "../serialization_data_types.js"; +import { TypeNumber, TypeVector } from "../serialization_data_types.js"; +import { SavegameInterface_V1000 } from "./1000.js"; -const schema = require("./1001.json"); +import schema from "./1001.json"; const logger = createLogger("savegame_interface/1001"); diff --git a/src/js/savegame/schemas/1002.js b/src/js/savegame/schemas/1002.js index 866bc1e8..0a94001f 100644 --- a/src/js/savegame/schemas/1002.js +++ b/src/js/savegame/schemas/1002.js @@ -1,8 +1,7 @@ -import { createLogger } from "../../core/logging.js"; -import { T } from "../../translations.js"; +import { createLogger } from "../../core/logging"; import { SavegameInterface_V1001 } from "./1001.js"; -const schema = require("./1002.json"); +import schema from "./1002.json"; const logger = createLogger("savegame_interface/1002"); export class SavegameInterface_V1002 extends SavegameInterface_V1001 { diff --git a/src/js/savegame/schemas/1003.js b/src/js/savegame/schemas/1003.js index 4a4a3ee3..b463b017 100644 --- a/src/js/savegame/schemas/1003.js +++ b/src/js/savegame/schemas/1003.js @@ -1,7 +1,7 @@ -import { createLogger } from "../../core/logging.js"; +import { createLogger } from "../../core/logging"; import { SavegameInterface_V1002 } from "./1002.js"; -const schema = require("./1003.json"); +import schema from "./1003.json"; const logger = createLogger("savegame_interface/1003"); export class SavegameInterface_V1003 extends SavegameInterface_V1002 { diff --git a/src/js/savegame/schemas/1004.js b/src/js/savegame/schemas/1004.js index c9feda1a..801ea138 100644 --- a/src/js/savegame/schemas/1004.js +++ b/src/js/savegame/schemas/1004.js @@ -1,7 +1,7 @@ -import { createLogger } from "../../core/logging.js"; +import { createLogger } from "../../core/logging"; import { SavegameInterface_V1003 } from "./1003.js"; -const schema = require("./1004.json"); +import schema from "./1004.json"; const logger = createLogger("savegame_interface/1004"); export class SavegameInterface_V1004 extends SavegameInterface_V1003 { diff --git a/src/js/savegame/schemas/1005.js b/src/js/savegame/schemas/1005.js index 0380f8eb..cde7cc2c 100644 --- a/src/js/savegame/schemas/1005.js +++ b/src/js/savegame/schemas/1005.js @@ -1,7 +1,7 @@ -import { createLogger } from "../../core/logging.js"; +import { createLogger } from "../../core/logging"; import { SavegameInterface_V1004 } from "./1004.js"; -const schema = require("./1005.json"); +import schema from "./1005.json"; const logger = createLogger("savegame_interface/1005"); export class SavegameInterface_V1005 extends SavegameInterface_V1004 { diff --git a/src/js/savegame/schemas/1006.js b/src/js/savegame/schemas/1006.js index 79226772..be38fa8a 100644 --- a/src/js/savegame/schemas/1006.js +++ b/src/js/savegame/schemas/1006.js @@ -1,304 +1,304 @@ -import { gMetaBuildingRegistry } from "../../core/global_registries.js"; -import { createLogger } from "../../core/logging.js"; -import { enumBalancerVariants, MetaBalancerBuilding } from "../../game/buildings/balancer.js"; -import { MetaBeltBuilding } from "../../game/buildings/belt.js"; -import { enumCutterVariants, MetaCutterBuilding } from "../../game/buildings/cutter.js"; -import { MetaHubBuilding } from "../../game/buildings/hub.js"; -import { enumMinerVariants, MetaMinerBuilding } from "../../game/buildings/miner.js"; -import { MetaMixerBuilding } from "../../game/buildings/mixer.js"; -import { enumPainterVariants, MetaPainterBuilding } from "../../game/buildings/painter.js"; -import { enumRotaterVariants, MetaRotaterBuilding } from "../../game/buildings/rotater.js"; -import { MetaStackerBuilding } from "../../game/buildings/stacker.js"; -import { MetaStorageBuilding } from "../../game/buildings/storage.js"; -import { MetaTrashBuilding } from "../../game/buildings/trash.js"; -import { - enumUndergroundBeltVariants, - MetaUndergroundBeltBuilding, -} from "../../game/buildings/underground_belt.js"; -import { getCodeFromBuildingData } from "../../game/building_codes.js"; -import { StaticMapEntityComponent } from "../../game/components/static_map_entity.js"; -import { Entity } from "../../game/entity.js"; -import { defaultBuildingVariant, MetaBuilding } from "../../game/meta_building.js"; -import { SavegameInterface_V1005 } from "./1005.js"; - -const schema = require("./1006.json"); -const logger = createLogger("savegame_interface/1006"); - -/** - * - * @param {typeof MetaBuilding} metaBuilding - * @param {string=} variant - * @param {number=} rotationVariant - */ -function findCode(metaBuilding, variant = defaultBuildingVariant, rotationVariant = 0) { - return getCodeFromBuildingData(gMetaBuildingRegistry.findByClass(metaBuilding), variant, rotationVariant); -} - -/** - * Rebalances a value from the old balancing to the new one - * @param {number} value - * @returns {number} - */ -function rebalance(value) { - return Math.round(Math.pow(value, 0.75)); -} - -export class SavegameInterface_V1006 extends SavegameInterface_V1005 { - getVersion() { - return 1006; - } - - getSchemaUncached() { - return schema; - } - - static computeSpriteMapping() { - return { - // Belt - "sprites/blueprints/belt_top.png": findCode(MetaBeltBuilding, defaultBuildingVariant, 0), - "sprites/blueprints/belt_left.png": findCode(MetaBeltBuilding, defaultBuildingVariant, 1), - "sprites/blueprints/belt_right.png": findCode(MetaBeltBuilding, defaultBuildingVariant, 2), - - // Splitter (=Balancer) - "sprites/blueprints/splitter.png": findCode(MetaBalancerBuilding), - "sprites/blueprints/splitter-compact.png": findCode( - MetaBalancerBuilding, - enumBalancerVariants.merger - ), - "sprites/blueprints/splitter-compact-inverse.png": findCode( - MetaBalancerBuilding, - enumBalancerVariants.mergerInverse - ), - - // Underground belt - "sprites/blueprints/underground_belt_entry.png": findCode( - MetaUndergroundBeltBuilding, - defaultBuildingVariant, - 0 - ), - "sprites/blueprints/underground_belt_exit.png": findCode( - MetaUndergroundBeltBuilding, - defaultBuildingVariant, - 1 - ), - - "sprites/blueprints/underground_belt_entry-tier2.png": findCode( - MetaUndergroundBeltBuilding, - enumUndergroundBeltVariants.tier2, - 0 - ), - "sprites/blueprints/underground_belt_exit-tier2.png": findCode( - MetaUndergroundBeltBuilding, - enumUndergroundBeltVariants.tier2, - 1 - ), - - // Miner - "sprites/blueprints/miner.png": findCode(MetaMinerBuilding), - "sprites/blueprints/miner-chainable.png": findCode( - MetaMinerBuilding, - enumMinerVariants.chainable, - 0 - ), - - // Cutter - "sprites/blueprints/cutter.png": findCode(MetaCutterBuilding), - "sprites/blueprints/cutter-quad.png": findCode(MetaCutterBuilding, enumCutterVariants.quad), - - // Rotater - "sprites/blueprints/rotater.png": findCode(MetaRotaterBuilding), - "sprites/blueprints/rotater-ccw.png": findCode(MetaRotaterBuilding, enumRotaterVariants.ccw), - - // Stacker - "sprites/blueprints/stacker.png": findCode(MetaStackerBuilding), - - // Mixer - "sprites/blueprints/mixer.png": findCode(MetaMixerBuilding), - - // Painter - "sprites/blueprints/painter.png": findCode(MetaPainterBuilding), - "sprites/blueprints/painter-mirrored.png": findCode( - MetaPainterBuilding, - enumPainterVariants.mirrored - ), - "sprites/blueprints/painter-double.png": findCode( - MetaPainterBuilding, - enumPainterVariants.double - ), - "sprites/blueprints/painter-quad.png": findCode(MetaPainterBuilding, enumPainterVariants.quad), - - // Trash - "sprites/blueprints/trash.png": findCode(MetaTrashBuilding), - - // Storage - "sprites/blueprints/trash-storage.png": findCode(MetaStorageBuilding), - }; - } - - /** - * @param {import("../savegame_typedefs.js").SavegameData} data - */ - static migrate1005to1006(data) { - logger.log("Migrating 1005 to 1006"); - const dump = data.dump; - if (!dump) { - return true; - } - - // Reduce stored shapes - const stored = dump.hubGoals.storedShapes; - for (const shapeKey in stored) { - stored[shapeKey] = rebalance(stored[shapeKey]); - } - - // Reset final game shape - stored["RuCw--Cw:----Ru--"] = 0; - - // Reduce goals - if (dump.hubGoals.currentGoal) { - dump.hubGoals.currentGoal.required = rebalance(dump.hubGoals.currentGoal.required); - } - - let level = Math.min(19, dump.hubGoals.level); - - const levelMapping = { - 14: 15, - 15: 16, - 16: 17, - 17: 18, - 18: 19, - 19: 20, - }; - - dump.hubGoals.level = levelMapping[level] || level; - - // Update entities - const entities = dump.entities; - for (let i = 0; i < entities.length; ++i) { - const entity = entities[i]; - const components = entity.components; - this.migrateStaticComp1005to1006(entity); - - // HUB - if (components.Hub) { - // @ts-ignore - components.Hub = {}; - } - - // Item Processor - if (components.ItemProcessor) { - // @ts-ignore - components.ItemProcessor = { - nextOutputSlot: 0, - }; - } - - // OLD: Unremovable component - // @ts-ignore - if (components.Unremovable) { - // @ts-ignore - delete components.Unremovable; - } - - // OLD: ReplaceableMapEntity - // @ts-ignore - if (components.ReplaceableMapEntity) { - // @ts-ignore - delete components.ReplaceableMapEntity; - } - - // ItemAcceptor - if (components.ItemAcceptor) { - // @ts-ignore - components.ItemAcceptor = {}; - } - - // Belt - if (components.Belt) { - // @ts-ignore - components.Belt = {}; - } - - // Item Ejector - if (components.ItemEjector) { - // @ts-ignore - components.ItemEjector = { - slots: [], - }; - } - - // UndergroundBelt - if (components.UndergroundBelt) { - // @ts-ignore - components.UndergroundBelt = { - pendingItems: [], - }; - } - - // Miner - if (components.Miner) { - // @ts-ignore - delete components.Miner.chainable; - - components.Miner.lastMiningTime = 0; - components.Miner.itemChainBuffer = []; - } - - // Storage - if (components.Storage) { - // @ts-ignore - components.Storage = { - storedCount: rebalance(components.Storage.storedCount), - storedItem: null, - }; - } - } - } - - /** - * - * @param {Entity} entity - */ - static migrateStaticComp1005to1006(entity) { - const spriteMapping = this.computeSpriteMapping(); - const staticComp = entity.components.StaticMapEntity; - - /** @type {StaticMapEntityComponent} */ - const newStaticComp = {}; - newStaticComp.origin = staticComp.origin; - newStaticComp.originalRotation = staticComp.originalRotation; - newStaticComp.rotation = staticComp.rotation; - - // @ts-ignore - newStaticComp.code = spriteMapping[staticComp.blueprintSpriteKey]; - - // Hub special case - if (entity.components.Hub) { - newStaticComp.code = findCode(MetaHubBuilding); - } - - // Belt special case - if (entity.components.Belt) { - const actualCode = { - top: findCode(MetaBeltBuilding, defaultBuildingVariant, 0), - left: findCode(MetaBeltBuilding, defaultBuildingVariant, 1), - right: findCode(MetaBeltBuilding, defaultBuildingVariant, 2), - }[entity.components.Belt.direction]; - if (actualCode !== newStaticComp.code) { - if (G_IS_DEV) { - console.warn("Belt mismatch"); - } - newStaticComp.code = actualCode; - } - } - - if (!newStaticComp.code) { - throw new Error( - // @ts-ignore - "1006 Migration: Could not reconstruct code for " + staticComp.blueprintSpriteKey - ); - } - - entity.components.StaticMapEntity = newStaticComp; - } -} +import { gMetaBuildingRegistry } from "../../core/global_registries"; +import { createLogger } from "../../core/logging"; +import { getCodeFromBuildingData } from "../../game/building_codes.js"; +import { enumBalancerVariants, MetaBalancerBuilding } from "../../game/buildings/balancer.js"; +import { MetaBeltBuilding } from "../../game/buildings/belt.js"; +import { enumCutterVariants, MetaCutterBuilding } from "../../game/buildings/cutter.js"; +import { MetaHubBuilding } from "../../game/buildings/hub.js"; +import { enumMinerVariants, MetaMinerBuilding } from "../../game/buildings/miner.js"; +import { MetaMixerBuilding } from "../../game/buildings/mixer.js"; +import { enumPainterVariants, MetaPainterBuilding } from "../../game/buildings/painter.js"; +import { enumRotatorVariants, MetaRotatorBuilding } from "../../game/buildings/rotator.js"; +import { MetaStackerBuilding } from "../../game/buildings/stacker.js"; +import { MetaStorageBuilding } from "../../game/buildings/storage.js"; +import { MetaTrashBuilding } from "../../game/buildings/trash.js"; +import { + enumUndergroundBeltVariants, + MetaUndergroundBeltBuilding, +} from "../../game/buildings/underground_belt.js"; +import { StaticMapEntityComponent } from "../../game/components/static_map_entity.js"; +import { Entity } from "../../game/entity.js"; +import { defaultBuildingVariant, MetaBuilding } from "../../game/meta_building.js"; +import { SavegameInterface_V1005 } from "./1005.js"; + +import schema from "./1006.json"; +const logger = createLogger("savegame_interface/1006"); + +/** + * + * @param {typeof MetaBuilding} metaBuilding + * @param {string=} variant + * @param {number=} rotationVariant + */ +function findCode(metaBuilding, variant = defaultBuildingVariant, rotationVariant = 0) { + return getCodeFromBuildingData(gMetaBuildingRegistry.findByClass(metaBuilding), variant, rotationVariant); +} + +/** + * Rebalances a value from the old balancing to the new one + * @param {number} value + * @returns {number} + */ +function rebalance(value) { + return Math.round(Math.pow(value, 0.75)); +} + +export class SavegameInterface_V1006 extends SavegameInterface_V1005 { + getVersion() { + return 1006; + } + + getSchemaUncached() { + return schema; + } + + static computeSpriteMapping() { + return { + // Belt + "sprites/blueprints/belt_top.png": findCode(MetaBeltBuilding, defaultBuildingVariant, 0), + "sprites/blueprints/belt_left.png": findCode(MetaBeltBuilding, defaultBuildingVariant, 1), + "sprites/blueprints/belt_right.png": findCode(MetaBeltBuilding, defaultBuildingVariant, 2), + + // Splitter (=Balancer) + "sprites/blueprints/splitter.png": findCode(MetaBalancerBuilding), + "sprites/blueprints/splitter-compact.png": findCode( + MetaBalancerBuilding, + enumBalancerVariants.merger + ), + "sprites/blueprints/splitter-compact-inverse.png": findCode( + MetaBalancerBuilding, + enumBalancerVariants.mergerInverse + ), + + // Underground belt + "sprites/blueprints/underground_belt_entry.png": findCode( + MetaUndergroundBeltBuilding, + defaultBuildingVariant, + 0 + ), + "sprites/blueprints/underground_belt_exit.png": findCode( + MetaUndergroundBeltBuilding, + defaultBuildingVariant, + 1 + ), + + "sprites/blueprints/underground_belt_entry-tier2.png": findCode( + MetaUndergroundBeltBuilding, + enumUndergroundBeltVariants.tier2, + 0 + ), + "sprites/blueprints/underground_belt_exit-tier2.png": findCode( + MetaUndergroundBeltBuilding, + enumUndergroundBeltVariants.tier2, + 1 + ), + + // Miner + "sprites/blueprints/miner.png": findCode(MetaMinerBuilding), + "sprites/blueprints/miner-chainable.png": findCode( + MetaMinerBuilding, + enumMinerVariants.chainable, + 0 + ), + + // Cutter + "sprites/blueprints/cutter.png": findCode(MetaCutterBuilding), + "sprites/blueprints/cutter-quad.png": findCode(MetaCutterBuilding, enumCutterVariants.quad), + + // Rotator + "sprites/blueprints/rotator.png": findCode(MetaRotatorBuilding), + "sprites/blueprints/rotator-ccw.png": findCode(MetaRotatorBuilding, enumRotatorVariants.ccw), + + // Stacker + "sprites/blueprints/stacker.png": findCode(MetaStackerBuilding), + + // Mixer + "sprites/blueprints/mixer.png": findCode(MetaMixerBuilding), + + // Painter + "sprites/blueprints/painter.png": findCode(MetaPainterBuilding), + "sprites/blueprints/painter-mirrored.png": findCode( + MetaPainterBuilding, + enumPainterVariants.mirrored + ), + "sprites/blueprints/painter-double.png": findCode( + MetaPainterBuilding, + enumPainterVariants.double + ), + "sprites/blueprints/painter-quad.png": findCode(MetaPainterBuilding, enumPainterVariants.quad), + + // Trash + "sprites/blueprints/trash.png": findCode(MetaTrashBuilding), + + // Storage + "sprites/blueprints/trash-storage.png": findCode(MetaStorageBuilding), + }; + } + + /** + * @param {import("../savegame_typedefs.js").SavegameData} data + */ + static migrate1005to1006(data) { + logger.log("Migrating 1005 to 1006"); + const dump = data.dump; + if (!dump) { + return true; + } + + // Reduce stored shapes + const stored = dump.hubGoals.storedShapes; + for (const shapeKey in stored) { + stored[shapeKey] = rebalance(stored[shapeKey]); + } + + // Reset final game shape + stored["RuCw--Cw:----Ru--"] = 0; + + // Reduce goals + if (dump.hubGoals.currentGoal) { + dump.hubGoals.currentGoal.required = rebalance(dump.hubGoals.currentGoal.required); + } + + let level = Math.min(19, dump.hubGoals.level); + + const levelMapping = { + 14: 15, + 15: 16, + 16: 17, + 17: 18, + 18: 19, + 19: 20, + }; + + dump.hubGoals.level = levelMapping[level] || level; + + // Update entities + const entities = dump.entities; + for (let i = 0; i < entities.length; ++i) { + const entity = entities[i]; + const components = entity.components; + this.migrateStaticComp1005to1006(entity); + + // HUB + if (components.Hub) { + // @ts-ignore + components.Hub = {}; + } + + // Item Processor + if (components.ItemProcessor) { + // @ts-ignore + components.ItemProcessor = { + nextOutputSlot: 0, + }; + } + + // OLD: Unremovable component + // @ts-ignore + if (components.Unremovable) { + // @ts-ignore + delete components.Unremovable; + } + + // OLD: ReplaceableMapEntity + // @ts-ignore + if (components.ReplaceableMapEntity) { + // @ts-ignore + delete components.ReplaceableMapEntity; + } + + // ItemAcceptor + if (components.ItemAcceptor) { + // @ts-ignore + components.ItemAcceptor = {}; + } + + // Belt + if (components.Belt) { + // @ts-ignore + components.Belt = {}; + } + + // Item Ejector + if (components.ItemEjector) { + // @ts-ignore + components.ItemEjector = { + slots: [], + }; + } + + // UndergroundBelt + if (components.UndergroundBelt) { + // @ts-ignore + components.UndergroundBelt = { + pendingItems: [], + }; + } + + // Miner + if (components.Miner) { + // @ts-ignore + delete components.Miner.chainable; + + components.Miner.lastMiningTime = 0; + components.Miner.itemChainBuffer = []; + } + + // Storage + if (components.Storage) { + // @ts-ignore + components.Storage = { + storedCount: rebalance(components.Storage.storedCount), + storedItem: null, + }; + } + } + } + + /** + * + * @param {Entity} entity + */ + static migrateStaticComp1005to1006(entity) { + const spriteMapping = this.computeSpriteMapping(); + const staticComp = entity.components.StaticMapEntity; + + /** @type {StaticMapEntityComponent} */ + const newStaticComp = {}; + newStaticComp.origin = staticComp.origin; + newStaticComp.originalRotation = staticComp.originalRotation; + newStaticComp.rotation = staticComp.rotation; + + // @ts-ignore + newStaticComp.code = spriteMapping[staticComp.blueprintSpriteKey]; + + // Hub special case + if (entity.components.Hub) { + newStaticComp.code = findCode(MetaHubBuilding); + } + + // Belt special case + if (entity.components.Belt) { + const actualCode = { + top: findCode(MetaBeltBuilding, defaultBuildingVariant, 0), + left: findCode(MetaBeltBuilding, defaultBuildingVariant, 1), + right: findCode(MetaBeltBuilding, defaultBuildingVariant, 2), + }[entity.components.Belt.direction]; + if (actualCode !== newStaticComp.code) { + if (G_IS_DEV) { + console.warn("Belt mismatch"); + } + newStaticComp.code = actualCode; + } + } + + if (!newStaticComp.code) { + throw new Error( + // @ts-ignore + "1006 Migration: Could not reconstruct code for " + staticComp.blueprintSpriteKey + ); + } + + entity.components.StaticMapEntity = newStaticComp; + } +} diff --git a/src/js/savegame/schemas/1006.json b/src/js/savegame/schemas/1006.json index b0916986..6682f615 100644 --- a/src/js/savegame/schemas/1006.json +++ b/src/js/savegame/schemas/1006.json @@ -1,5 +1,5 @@ -{ - "type": "object", - "required": [], - "additionalProperties": true -} +{ + "type": "object", + "required": [], + "additionalProperties": true +} diff --git a/src/js/savegame/schemas/1007.js b/src/js/savegame/schemas/1007.js index f6b1d194..64550e93 100644 --- a/src/js/savegame/schemas/1007.js +++ b/src/js/savegame/schemas/1007.js @@ -1,7 +1,7 @@ -import { createLogger } from "../../core/logging.js"; +import { createLogger } from "../../core/logging"; import { SavegameInterface_V1006 } from "./1006.js"; -const schema = require("./1007.json"); +import schema from "./1007.json"; const logger = createLogger("savegame_interface/1007"); export class SavegameInterface_V1007 extends SavegameInterface_V1006 { diff --git a/src/js/savegame/schemas/1008.js b/src/js/savegame/schemas/1008.js index 350a75a8..c89e99d7 100644 --- a/src/js/savegame/schemas/1008.js +++ b/src/js/savegame/schemas/1008.js @@ -1,7 +1,7 @@ -import { createLogger } from "../../core/logging.js"; +import { createLogger } from "../../core/logging"; import { SavegameInterface_V1007 } from "./1007.js"; -const schema = require("./1008.json"); +import schema from "./1008.json"; const logger = createLogger("savegame_interface/1008"); export class SavegameInterface_V1008 extends SavegameInterface_V1007 { @@ -17,16 +17,7 @@ export class SavegameInterface_V1008 extends SavegameInterface_V1007 { * @param {import("../savegame_typedefs.js").SavegameData} data */ static migrate1007to1008(data) { + // Note: no-op since achievement removal logger.log("Migrating 1007 to 1008"); - const dump = data.dump; - if (!dump) { - return true; - } - - Object.assign(data.stats, { - failedMam: true, - trashedCount: 0, - usedInverseRotater: true, - }); } } diff --git a/src/js/savegame/schemas/1009.js b/src/js/savegame/schemas/1009.js index e6e1abc6..0dd97937 100644 --- a/src/js/savegame/schemas/1009.js +++ b/src/js/savegame/schemas/1009.js @@ -1,8 +1,8 @@ -import { createLogger } from "../../core/logging.js"; +import { createLogger } from "../../core/logging"; import { RegularGameMode } from "../../game/modes/regular.js"; import { SavegameInterface_V1008 } from "./1008.js"; -const schema = require("./1009.json"); +import schema from "./1009.json"; const logger = createLogger("savegame_interface/1009"); export class SavegameInterface_V1009 extends SavegameInterface_V1008 { diff --git a/src/js/savegame/schemas/1010.js b/src/js/savegame/schemas/1010.js index 8f480800..52ec81e4 100644 --- a/src/js/savegame/schemas/1010.js +++ b/src/js/savegame/schemas/1010.js @@ -1,7 +1,7 @@ -import { createLogger } from "../../core/logging.js"; +import { createLogger } from "../../core/logging"; import { SavegameInterface_V1009 } from "./1009.js"; -const schema = require("./1010.json"); +import schema from "./1010.json"; const logger = createLogger("savegame_interface/1010"); export class SavegameInterface_V1010 extends SavegameInterface_V1009 { diff --git a/src/js/savegame/serialization.js b/src/js/savegame/serialization.js index 682558b6..2e587f85 100644 --- a/src/js/savegame/serialization.js +++ b/src/js/savegame/serialization.js @@ -1,353 +1,359 @@ -import { createLogger } from "../core/logging"; -import { - BaseDataType, - TypeArray, - TypeBoolean, - TypeClass, - TypeClassData, - TypeClassFromMetaclass, - TypeClassId, - TypeEntity, - TypeEntityWeakref, - TypeEnum, - TypeFixedClass, - TypeInteger, - TypeKeyValueMap, - TypeMetaClass, - TypeNullable, - TypeNumber, - TypePair, - TypePositiveInteger, - TypePositiveNumber, - TypeString, - TypeStructuredObject, - TypeVector, - TypePositiveIntegerOrString, -} from "./serialization_data_types"; - -const logger = createLogger("serialization"); - -// Schema declarations -export const types = { - int: new TypeInteger(), - uint: new TypePositiveInteger(), - float: new TypeNumber(), - ufloat: new TypePositiveNumber(), - string: new TypeString(), - entity: new TypeEntity(), - weakEntityRef: new TypeEntityWeakref(), - vector: new TypeVector(), - tileVector: new TypeVector(), - bool: new TypeBoolean(), - uintOrString: new TypePositiveIntegerOrString(), - - /** - * @param {BaseDataType} wrapped - */ - nullable(wrapped) { - return new TypeNullable(wrapped); - }, - - /** - * @param {FactoryTemplate<*>|SingletonFactoryTemplate<*>} registry - */ - classId(registry) { - return new TypeClassId(registry); - }, - /** - * @param {BaseDataType} valueType - * @param {boolean=} includeEmptyValues - */ - keyValueMap(valueType, includeEmptyValues = true) { - return new TypeKeyValueMap(valueType, includeEmptyValues); - }, - - /** - * @param {Object} values - */ - enum(values) { - return new TypeEnum(values); - }, - - /** - * @param {FactoryTemplate<*>} registry - * @param {(GameRoot, any) => object=} resolver - */ - obj(registry, resolver = null) { - return new TypeClass(registry, resolver); - }, - - /** - * @param {FactoryTemplate<*>} registry - */ - objData(registry) { - return new TypeClassData(registry); - }, - - /** - * @param {typeof BasicSerializableObject} cls - */ - knownType(cls) { - return new TypeFixedClass(cls); - }, - - /** - * @param {BaseDataType} innerType - */ - array(innerType) { - return new TypeArray(innerType); - }, - - /** - * @param {BaseDataType} innerType - */ - fixedSizeArray(innerType) { - return new TypeArray(innerType, true); - }, - - /** - * @param {SingletonFactoryTemplate<*>} innerType - */ - classRef(registry) { - return new TypeMetaClass(registry); - }, - - /** - * @param {Object.} descriptor - */ - structured(descriptor) { - return new TypeStructuredObject(descriptor); - }, - - /** - * @param {BaseDataType} a - * @param {BaseDataType} b - */ - pair(a, b) { - return new TypePair(a, b); - }, - - /** - * @param {typeof BasicSerializableObject} classHandle - * @param {SingletonFactoryTemplate<*>} registry - */ - classWithMetaclass(classHandle, registry) { - return new TypeClassFromMetaclass(classHandle, registry); - }, -}; - -/** - * A full schema declaration - * @typedef {Object. | object} Schema - */ - -const globalSchemaCache = {}; - -/* dev:start */ -const classnamesCache = {}; -/* dev:end*/ - -export class BasicSerializableObject { - /* dev:start */ - /** - * Fixes typeof DerivedComponent is not assignable to typeof Component, compiled out - * in non-dev builds - */ - constructor(...args) {} - - /* dev:end */ - - static getId() { - abstract; - } - - /** - * Should return the serialization schema - * @returns {Schema} - */ - static getSchema() { - return {}; - } - - // Implementation - /** @returns {Schema} */ - static getCachedSchema() { - const id = this.getId(); - - /* dev:start */ - assert( - classnamesCache[id] === this || classnamesCache[id] === undefined, - "Class name taken twice: " + id + " (from " + this.name + ")" - ); - classnamesCache[id] = this; - /* dev:end */ - - const entry = globalSchemaCache[id]; - if (entry) { - return entry; - } - - const schema = this.getSchema(); - globalSchemaCache[id] = schema; - return schema; - } - - /** @returns {object | string | number} */ - serialize() { - return serializeSchema( - this, - /** @type {typeof BasicSerializableObject} */ (this.constructor).getCachedSchema() - ); - } - - /** - * @param {any} data - * @param {import("./savegame_serializer").GameRoot} root - * @returns {string|void} - */ - deserialize(data, root = null) { - return deserializeSchema( - this, - /** @type {typeof BasicSerializableObject} */ (this.constructor).getCachedSchema(), - data, - null, - root - ); - } - - /** @returns {string|void} */ - static verify(data) { - return verifySchema(this.getCachedSchema(), data); - } -} - -/** - * Serializes an object using the given schema, mergin with the given properties - * @param {object} obj The object to serialize - * @param {Schema} schema The schema to use - * @param {object=} mergeWith Any additional properties to merge with the schema, useful for super calls - * @returns {object} Serialized data object - */ -export function serializeSchema(obj, schema, mergeWith = {}) { - for (const key in schema) { - if (!obj.hasOwnProperty(key)) { - logger.error("Invalid schema, property", key, "does not exist on", obj, "(schema=", schema, ")"); - assert( - obj.hasOwnProperty(key), - "serialization: invalid schema, property does not exist on object: " + key - ); - } - if (!schema[key]) { - assert(false, "Invalid schema (bad key '" + key + "'): " + JSON.stringify(schema)); - } - - if (G_IS_DEV) { - try { - mergeWith[key] = schema[key].serialize(obj[key]); - } catch (ex) { - logger.error( - "Serialization of", - obj, - "failed on key '" + key + "' ->", - ex, - "(schema was", - schema, - ")" - ); - throw ex; - } - } else { - mergeWith[key] = schema[key].serialize(obj[key]); - } - } - return mergeWith; -} - -/** - * Deserializes data into an object - * @param {object} obj The object to store the deserialized data into - * @param {Schema} schema The schema to use - * @param {object} data The serialized data - * @param {string|void|null=} baseclassErrorResult Convenience, if this is a string error code, do nothing and return it - * @param {import("../game/root").GameRoot=} root Optional game root reference - * @returns {string|void} String error code or nothing on success - */ -export function deserializeSchema(obj, schema, data, baseclassErrorResult = null, root) { - if (baseclassErrorResult) { - return baseclassErrorResult; - } - - if (data === null || typeof data === "undefined") { - logger.error("Got 'NULL' data for", obj, "and schema", schema, "!"); - return "Got null data"; - } - - for (const key in schema) { - if (!data.hasOwnProperty(key)) { - logger.error("Data", data, "does not contain", key, "(schema:", schema, ")"); - return "Missing key in schema: " + key + " of class " + obj.constructor.name; - } - if (!schema[key].allowNull() && (data[key] === null || data[key] === undefined)) { - logger.error("Data", data, "has null value for", key, "(schema:", schema, ")"); - return "Non-nullable entry is null: " + key + " of class " + obj.constructor.name; - } - - const errorStatus = schema[key].deserializeWithVerify(data[key], obj, key, obj.root || root); - if (errorStatus) { - logger.error( - "Deserialization failed with error '" + errorStatus + "' on object", - obj, - "and key", - key, - "(root? =", - obj.root ? "y" : "n", - ")" - ); - return errorStatus; - } - } -} - -/** - * Verifies stored data using the given schema - * @param {Schema} schema The schema to use - * @param {object} data The data to verify - * @returns {string|void} String error code or nothing on success - */ -export function verifySchema(schema, data) { - for (const key in schema) { - if (!data.hasOwnProperty(key)) { - logger.error("Data", data, "does not contain", key, "(schema:", schema, ")"); - return "verify: missing key required by schema in stored data: " + key; - } - if (!schema[key].allowNull() && (data[key] === null || data[key] === undefined)) { - logger.error("Data", data, "has null value for", key, "(schema:", schema, ")"); - return "verify: non-nullable entry is null: " + key; - } - - const errorStatus = schema[key].verifySerializedValue(data[key]); - if (errorStatus) { - logger.error(errorStatus); - return "verify: " + errorStatus; - } - } -} - -/** - * Extends a schema by adding the properties from the new schema to the existing base schema - * @param {Schema} base - * @param {Schema} newOne - * @returns {Schema} - */ -export function extendSchema(base, newOne) { - /** @type {Schema} */ - const result = Object.assign({}, base); - for (const key in newOne) { - if (result.hasOwnProperty(key)) { - logger.error("Extend schema got duplicate key:", key); - continue; - } - result[key] = newOne[key]; - } - return result; -} +import { createLogger } from "../core/logging"; +import { + BaseDataType, + TypeArray, + TypeBoolean, + TypeClass, + TypeClassData, + TypeClassFromMetaclass, + TypeClassId, + TypeEntity, + TypeEntityWeakref, + TypeEnum, + TypeFixedClass, + TypeInteger, + TypeKeyValueMap, + TypeMetaClass, + TypeNullable, + TypeNumber, + TypePair, + TypePositiveInteger, + TypePositiveIntegerOrString, + TypePositiveNumber, + TypeString, + TypeStructuredObject, + TypeVector, +} from "./serialization_data_types"; + +/** + * @typedef {import("../core/factory").Factory} FactoryTemplate + * @template T + */ +/** + * @typedef {import("../core/singleton_factory").SingletonFactory} SingletonFactoryTemplate + * @template {{ getId(): string }} T + */ + +const logger = createLogger("serialization"); + +// Schema declarations +export const types = { + int: new TypeInteger(), + uint: new TypePositiveInteger(), + float: new TypeNumber(), + ufloat: new TypePositiveNumber(), + string: new TypeString(), + entity: new TypeEntity(), + weakEntityRef: new TypeEntityWeakref(), + vector: new TypeVector(), + tileVector: new TypeVector(), + bool: new TypeBoolean(), + uintOrString: new TypePositiveIntegerOrString(), + + /** + * @param {BaseDataType} wrapped + */ + nullable(wrapped) { + return new TypeNullable(wrapped); + }, + + /** + * @param {FactoryTemplate<*>|SingletonFactoryTemplate<*>} registry + */ + classId(registry) { + return new TypeClassId(registry); + }, + /** + * @param {BaseDataType} valueType + * @param {boolean=} includeEmptyValues + */ + keyValueMap(valueType, includeEmptyValues = true) { + return new TypeKeyValueMap(valueType, includeEmptyValues); + }, + + /** + * @param {Object} values + */ + enum(values) { + return new TypeEnum(values); + }, + + /** + * @param {FactoryTemplate<*>} registry + * @param {(GameRoot, any) => object=} resolver + */ + obj(registry, resolver = null) { + return new TypeClass(registry, resolver); + }, + + /** + * @param {FactoryTemplate<*>} registry + */ + objData(registry) { + return new TypeClassData(registry); + }, + + /** + * @param {typeof BasicSerializableObject} cls + */ + knownType(cls) { + return new TypeFixedClass(cls); + }, + + /** + * @param {BaseDataType} innerType + */ + array(innerType) { + return new TypeArray(innerType); + }, + + /** + * @param {BaseDataType} innerType + */ + fixedSizeArray(innerType) { + return new TypeArray(innerType, true); + }, + + /** + * @param {SingletonFactoryTemplate<*>} registry + */ + classRef(registry) { + return new TypeMetaClass(registry); + }, + + /** + * @param {Object.} descriptor + */ + structured(descriptor) { + return new TypeStructuredObject(descriptor); + }, + + /** + * @param {BaseDataType} a + * @param {BaseDataType} b + */ + pair(a, b) { + return new TypePair(a, b); + }, + + /** + * @param {typeof BasicSerializableObject} classHandle + * @param {SingletonFactoryTemplate<*>} registry + */ + classWithMetaclass(classHandle, registry) { + return new TypeClassFromMetaclass(classHandle, registry); + }, +}; + +/** + * A full schema declaration + * @typedef {Object. | object} Schema + */ + +const globalSchemaCache = {}; + +/* dev:start */ +const classnamesCache = {}; +/* dev:end*/ + +export class BasicSerializableObject { + /* dev:start */ + /** + * Fixes typeof DerivedComponent is not assignable to typeof Component, compiled out + * in non-dev builds + */ + constructor(...args) {} + + /* dev:end */ + + static getId() { + abstract; + } + + /** + * Should return the serialization schema + * @returns {Schema} + */ + static getSchema() { + return {}; + } + + // Implementation + /** @returns {Schema} */ + static getCachedSchema() { + const id = this.getId(); + + /* dev:start */ + assert( + classnamesCache[id] === this || classnamesCache[id] === undefined, + "Class name taken twice: " + id + " (from " + this.name + ")" + ); + classnamesCache[id] = this; + /* dev:end */ + + const entry = globalSchemaCache[id]; + if (entry) { + return entry; + } + + const schema = this.getSchema(); + globalSchemaCache[id] = schema; + return schema; + } + + /** @returns {object | string | number} */ + serialize() { + return serializeSchema( + this, + /** @type {typeof BasicSerializableObject} */ (this.constructor).getCachedSchema() + ); + } + + /** + * @param {any} data + * @param {import("./savegame_serializer").GameRoot} root + * @returns {string|void} + */ + deserialize(data, root = null) { + return deserializeSchema( + this, + /** @type {typeof BasicSerializableObject} */ (this.constructor).getCachedSchema(), + data, + null, + root + ); + } + + /** @returns {string|void} */ + static verify(data) { + return verifySchema(this.getCachedSchema(), data); + } +} + +/** + * Serializes an object using the given schema, mergin with the given properties + * @param {object} obj The object to serialize + * @param {Schema} schema The schema to use + * @param {object=} mergeWith Any additional properties to merge with the schema, useful for super calls + * @returns {object} Serialized data object + */ +export function serializeSchema(obj, schema, mergeWith = {}) { + for (const key in schema) { + if (!Object.hasOwn(obj, key)) { + logger.error("Invalid schema, property", key, "does not exist on", obj, "(schema=", schema, ")"); + assert(false, "serialization: invalid schema, property does not exist on object: " + key); + } + if (!schema[key]) { + assert(false, "Invalid schema (bad key '" + key + "'): " + JSON.stringify(schema)); + } + + if (G_IS_DEV) { + try { + mergeWith[key] = schema[key].serialize(obj[key]); + } catch (ex) { + logger.error( + "Serialization of", + obj, + "failed on key '" + key + "' ->", + ex, + "(schema was", + schema, + ")" + ); + throw ex; + } + } else { + mergeWith[key] = schema[key].serialize(obj[key]); + } + } + return mergeWith; +} + +/** + * Deserializes data into an object + * @param {object} obj The object to store the deserialized data into + * @param {Schema} schema The schema to use + * @param {object} data The serialized data + * @param {string|void|null=} baseclassErrorResult Convenience, if this is a string error code, do nothing and return it + * @param {import("../game/root").GameRoot=} root Optional game root reference + * @returns {string|void} String error code or nothing on success + */ +export function deserializeSchema(obj, schema, data, baseclassErrorResult = null, root) { + if (baseclassErrorResult) { + return baseclassErrorResult; + } + + if (data === null || typeof data === "undefined") { + logger.error("Got 'NULL' data for", obj, "and schema", schema, "!"); + return "Got null data"; + } + + for (const key in schema) { + if (!Object.hasOwn(data, key)) { + logger.error("Data", data, "does not contain", key, "(schema:", schema, ")"); + return "Missing key in schema: " + key + " of class " + obj.constructor.name; + } + if (!schema[key].allowNull() && (data[key] === null || data[key] === undefined)) { + logger.error("Data", data, "has null value for", key, "(schema:", schema, ")"); + return "Non-nullable entry is null: " + key + " of class " + obj.constructor.name; + } + + const errorStatus = schema[key].deserializeWithVerify(data[key], obj, key, obj.root || root); + if (errorStatus) { + logger.error( + "Deserialization failed with error '" + errorStatus + "' on object", + obj, + "and key", + key, + "(root? =", + obj.root ? "y" : "n", + ")" + ); + return errorStatus; + } + } +} + +/** + * Verifies stored data using the given schema + * @param {Schema} schema The schema to use + * @param {object} data The data to verify + * @returns {string|void} String error code or nothing on success + */ +export function verifySchema(schema, data) { + for (const key in schema) { + if (!Object.hasOwn(data, key)) { + logger.error("Data", data, "does not contain", key, "(schema:", schema, ")"); + return "verify: missing key required by schema in stored data: " + key; + } + if (!schema[key].allowNull() && (data[key] === null || data[key] === undefined)) { + logger.error("Data", data, "has null value for", key, "(schema:", schema, ")"); + return "verify: non-nullable entry is null: " + key; + } + + const errorStatus = schema[key].verifySerializedValue(data[key]); + if (errorStatus) { + logger.error(errorStatus); + return "verify: " + errorStatus; + } + } +} + +/** + * Extends a schema by adding the properties from the new schema to the existing base schema + * @param {Schema} base + * @param {Schema} newOne + * @returns {Schema} + */ +export function extendSchema(base, newOne) { + /** @type {Schema} */ + const result = Object.assign({}, base); + for (const key in newOne) { + if (Object.hasOwn(result, key)) { + logger.error("Extend schema got duplicate key:", key); + continue; + } + result[key] = newOne[key]; + } + return result; +} diff --git a/src/js/savegame/serialization_data_types.js b/src/js/savegame/serialization_data_types.js index c27e2295..1760cd36 100644 --- a/src/js/savegame/serialization_data_types.js +++ b/src/js/savegame/serialization_data_types.js @@ -1,1346 +1,1357 @@ -/* typehints:start */ -import { GameRoot } from "../game/root"; -import { BasicSerializableObject } from "./serialization"; -/* typehints:end */ - -import { Vector } from "../core/vector"; -import { round4Digits } from "../core/utils"; -export const globalJsonSchemaDefs = {}; - -/** - * - * @param {import("./serialization").Schema} schema - */ -export function schemaToJsonSchema(schema) { - const jsonSchema = { - type: "object", - additionalProperties: false, - required: [], - properties: {}, - }; - - for (const key in schema) { - const subSchema = schema[key].getAsJsonSchema(); - jsonSchema.required.push(key); - jsonSchema.properties[key] = subSchema; - } - - return jsonSchema; -} - -/** - * Helper function to create a json schema object - * @param {any} properties - */ -function schemaObject(properties) { - return { - type: "object", - required: Object.keys(properties).slice(), - additionalProperties: false, - properties, - }; -} - -/** - * Base serialization data type - */ -export class BaseDataType { - /** - * Serializes a given raw value - * @param {any} value - * @abstract - */ - serialize(value) { - abstract; - return {}; - } - - /** - * Verifies a given serialized value - * @param {any} value - * @returns {string|void} String error code or null on success - */ - verifySerializedValue(value) {} - - /** - * Deserializes a serialized value into the target object under the given key - * @param {any} value - * @param {GameRoot} root - * @param {object} targetObject - * @param {string|number} targetKey - * @returns {string|void} String error code or null on success - * @abstract - */ - deserialize(value, targetObject, targetKey, root) { - abstract; - } - - /** - * Returns the json schema - */ - getAsJsonSchema() { - const key = this.getCacheKey(); - const schema = this.getAsJsonSchemaUncached(); - - if (!globalJsonSchemaDefs[key]) { - // schema.$id = key; - globalJsonSchemaDefs[key] = schema; - } - - return { - $ref: "#/definitions/" + key, - }; - } - - /** - * INTERNAL Should return the json schema representation - * @abstract - */ - getAsJsonSchemaUncached() { - abstract; - } - - /** - * Returns whether null values are okay - * @returns {boolean} - */ - allowNull() { - return false; - } - - // Helper methods - - /** - * Deserializes a serialized value, but performs integrity checks before - * @param {any} value - * @param {GameRoot} root - * @param {object} targetObject - * @param {string|number} targetKey - * @returns {string|void} String error code or null on success - */ - deserializeWithVerify(value, targetObject, targetKey, root) { - const errorCode = this.verifySerializedValue(value); - if (errorCode) { - return ( - "serialization verify failed: " + - errorCode + - " [value " + - (JSON.stringify(value) || "").substr(0, 100) + - "]" - ); - } - return this.deserialize(value, targetObject, targetKey, root); - } - - /** - * Should return a cacheable key - * @abstract - */ - getCacheKey() { - abstract; - return ""; - } -} - -export class TypeInteger extends BaseDataType { - serialize(value) { - assert(Number.isInteger(value), "Type integer got non integer for serialize: " + value); - return value; - } - - /** - * @see BaseDataType.deserialize - * @param {any} value - * @param {GameRoot} root - * @param {object} targetObject - * @param {string|number} targetKey - * @returns {string|void} String error code or null on success - */ - deserialize(value, targetObject, targetKey, root) { - targetObject[targetKey] = value; - } - - getAsJsonSchemaUncached() { - return { - type: "integer", - }; - } - - verifySerializedValue(value) { - if (!Number.isInteger(value)) { - return "Not a valid number"; - } - } - - getCacheKey() { - return "int"; - } -} - -export class TypePositiveInteger extends BaseDataType { - serialize(value) { - assert(Number.isInteger(value), "Type integer got non integer for serialize: " + value); - assert(value >= 0, "value < 0: " + value); - return value; - } - - /** - * @see BaseDataType.deserialize - * @param {any} value - * @param {GameRoot} root - * @param {object} targetObject - * @param {string|number} targetKey - * @returns {string|void} String error code or null on success - */ - deserialize(value, targetObject, targetKey, root) { - targetObject[targetKey] = value; - } - - getAsJsonSchemaUncached() { - return { - type: "integer", - minimum: 0, - }; - } - - verifySerializedValue(value) { - if (!Number.isInteger(value)) { - return "Not a valid number"; - } - if (value < 0) { - return "Negative value for positive integer"; - } - } - - getCacheKey() { - return "uint"; - } -} - -export class TypePositiveIntegerOrString extends BaseDataType { - serialize(value) { - if (Number.isInteger(value)) { - assert(value >= 0, "type integer got negative value: " + value); - } else if (typeof value === "string") { - // all good - } else { - assertAlways(false, "Type integer|string got non integer or string for serialize: " + value); - } - return value; - } - - /** - * @see BaseDataType.deserialize - * @param {any} value - * @param {GameRoot} root - * @param {object} targetObject - * @param {string|number} targetKey - * @returns {string|void} String error code or null on success - */ - deserialize(value, targetObject, targetKey, root) { - targetObject[targetKey] = value; - } - - getAsJsonSchemaUncached() { - return { - oneOf: [{ type: "integer", minimum: 0 }, { type: "string" }], - }; - } - - verifySerializedValue(value) { - if (Number.isInteger(value)) { - if (value < 0) { - return "Negative value for positive integer"; - } - } else if (typeof value === "string") { - // all good - } else { - return "Not a valid number or string: " + value; - } - } - - getCacheKey() { - return "uint_str"; - } -} - -export class TypeBoolean extends BaseDataType { - serialize(value) { - assert(value === true || value === false, "Type bool got non bool for serialize: " + value); - return value; - } - - /** - * @see BaseDataType.deserialize - * @param {any} value - * @param {GameRoot} root - * @param {object} targetObject - * @param {string|number} targetKey - * @returns {string|void} String error code or null on success - */ - deserialize(value, targetObject, targetKey, root) { - targetObject[targetKey] = value; - } - - getAsJsonSchemaUncached() { - return { - type: "boolean", - }; - } - - verifySerializedValue(value) { - if (value !== true && value !== false) { - return "Not a boolean"; - } - } - - getCacheKey() { - return "bool"; - } -} - -export class TypeString extends BaseDataType { - serialize(value) { - assert(typeof value === "string", "Type string got non string for serialize: " + value); - return value; - } - - /** - * @see BaseDataType.deserialize - * @param {any} value - * @param {GameRoot} root - * @param {object} targetObject - * @param {string|number} targetKey - * @returns {string|void} String error code or null on success - */ - deserialize(value, targetObject, targetKey, root) { - targetObject[targetKey] = value; - } - getAsJsonSchemaUncached() { - return { - type: "string", - }; - } - - verifySerializedValue(value) { - if (typeof value !== "string") { - return "Not a valid string"; - } - } - - getCacheKey() { - return "string"; - } -} - -export class TypeVector extends BaseDataType { - serialize(value) { - assert(value instanceof Vector, "Type vector got non vector for serialize: " + value); - return { - x: round4Digits(value.x), - y: round4Digits(value.y), - }; - } - - getAsJsonSchemaUncached() { - return schemaObject({ - x: { - type: "number", - }, - y: { - type: "number", - }, - }); - } - - /** - * @see BaseDataType.deserialize - * @param {any} value - * @param {GameRoot} root - * @param {object} targetObject - * @param {string|number} targetKey - * @returns {string|void} String error code or null on success - */ - deserialize(value, targetObject, targetKey, root) { - targetObject[targetKey] = new Vector(value.x, value.y); - } - - verifySerializedValue(value) { - if (!Number.isFinite(value.x) || !Number.isFinite(value.y)) { - return "Not a valid vector, missing x/y or bad data type"; - } - } - - getCacheKey() { - return "vector"; - } -} - -export class TypeTileVector extends BaseDataType { - serialize(value) { - assert(value instanceof Vector, "Type vector got non vector for serialize: " + value); - assert(Number.isInteger(value.x) && value.x > 0, "Invalid tile x:" + value.x); - assert(Number.isInteger(value.y) && value.y > 0, "Invalid tile x:" + value.y); - return { x: value.x, y: value.y }; - } - - getAsJsonSchemaUncached() { - return schemaObject({ - x: { - type: "integer", - minimum: 0, - maximum: 256, - }, - y: { - type: "integer", - minimum: 0, - maximum: 256, - }, - }); - } - - /** - * @see BaseDataType.deserialize - * @param {any} value - * @param {GameRoot} root - * @param {object} targetObject - * @param {string|number} targetKey - * @returns {string|void} String error code or null on success - */ - deserialize(value, targetObject, targetKey, root) { - targetObject[targetKey] = new Vector(value.x, value.y); - } - - verifySerializedValue(value) { - if (!Number.isInteger(value.x) || !Number.isInteger(value.y)) { - return "Not a valid tile vector, missing x/y or bad data type"; - } - if (value.x < 0 || value.y < 0) { - return "Invalid tile vector, x or y < 0"; - } - } - - getCacheKey() { - return "tilevector"; - } -} - -export class TypeNumber extends BaseDataType { - serialize(value) { - assert(Number.isFinite(value), "Type number got non number for serialize: " + value); - assert(!Number.isNaN(value), "Value is nan: " + value); - return round4Digits(value); - } - - getAsJsonSchemaUncached() { - return { - type: "number", - }; - } - - /** - * @see BaseDataType.deserialize - * @param {any} value - * @param {GameRoot} root - * @param {object} targetObject - * @param {string|number} targetKey - * @returns {string|void} String error code or null on success - */ - deserialize(value, targetObject, targetKey, root) { - targetObject[targetKey] = value; - } - - verifySerializedValue(value) { - if (!Number.isFinite(value)) { - return "Not a valid number: " + value; - } - } - - getCacheKey() { - return "float"; - } -} - -export class TypePositiveNumber extends BaseDataType { - serialize(value) { - assert(Number.isFinite(value), "Type number got non number for serialize: " + value); - assert(value >= 0, "Postitive number got negative value: " + value); - return round4Digits(value); - } - - /** - * @see BaseDataType.deserialize - * @param {any} value - * @param {GameRoot} root - * @param {object} targetObject - * @param {string|number} targetKey - * @returns {string|void} String error code or null on success - */ - deserialize(value, targetObject, targetKey, root) { - targetObject[targetKey] = value; - } - - getAsJsonSchemaUncached() { - return { - type: "number", - minimum: 0, - }; - } - - verifySerializedValue(value) { - if (!Number.isFinite(value)) { - return "Not a valid number: " + value; - } - if (value < 0) { - return "Positive number got negative value: " + value; - } - } - - getCacheKey() { - return "ufloat"; - } -} - -export class TypeEnum extends BaseDataType { - /** - * @param {Object.} enumeration - */ - constructor(enumeration = {}) { - super(); - this.availableValues = Object.values(enumeration); - } - - serialize(value) { - assert(this.availableValues.indexOf(value) >= 0, "Unknown value: " + value); - return value; - } - - /** - * @see BaseDataType.deserialize - * @param {any} value - * @param {GameRoot} root - * @param {object} targetObject - * @param {string|number} targetKey - * @returns {string|void} String error code or null on success - */ - deserialize(value, targetObject, targetKey, root) { - targetObject[targetKey] = value; - } - - getAsJsonSchemaUncached() { - return { - type: "string", - enum: this.availableValues, - }; - } - - verifySerializedValue(value) { - if (this.availableValues.indexOf(value) < 0) { - return "Unknown enum value: " + value; - } - } - - getCacheKey() { - return "enum." + this.availableValues.join(","); - } -} - -export class TypeEntity extends BaseDataType { - serialize(value) { - // assert(value instanceof Entity, "Not a valid entity ref: " + value); - assert(value.uid, "Entity has no uid yet"); - assert(!value.destroyed, "Entity already destroyed"); - assert(!value.queuedForDestroy, "Entity queued for destroy"); - - return value.uid; - } - - getAsJsonSchemaUncached() { - return { - type: "integer", - minimum: 0, - }; - } - - /** - * @see BaseDataType.deserialize - * @param {any} value - * @param {GameRoot} root - * @param {object} targetObject - * @param {string|number} targetKey - * @returns {string|void} String error code or null on success - */ - deserialize(value, targetObject, targetKey, root) { - const entity = root.entityMgr.findByUid(value); - if (!entity) { - return "Entity not found by uid: " + value; - } - targetObject[targetKey] = entity; - } - - verifySerializedValue(value) { - if (!Number.isFinite(value)) { - return "Not a valid uuid: " + value; - } - } - - getCacheKey() { - return "entity"; - } -} - -export class TypeEntityWeakref extends BaseDataType { - serialize(value) { - if (value === null) { - return null; - } - - // assert(value instanceof Entity, "Not a valid entity ref (weak): " + value); - assert(value.uid, "Entity has no uid yet"); - if (value.destroyed || value.queuedForDestroy) { - return null; - } - return value.uid; - } - - /** - * @see BaseDataType.deserialize - * @param {any} value - * @param {GameRoot} root - * @param {object} targetObject - * @param {string|number} targetKey - * @returns {string|void} String error code or null on success - */ - deserialize(value, targetObject, targetKey, root) { - if (value === null) { - targetObject[targetKey] = null; - return; - } - const entity = root.entityMgr.findByUid(value, false); - targetObject[targetKey] = entity; - } - - getAsJsonSchemaUncached() { - return { - type: ["null", "integer"], - minimum: 0, - }; - } - - allowNull() { - return true; - } - - verifySerializedValue(value) { - if (value !== null && !Number.isFinite(value)) { - return "Not a valid uuid: " + value; - } - } - - getCacheKey() { - return "entity-weakref"; - } -} - -export class TypeClass extends BaseDataType { - /** - * - * @param {FactoryTemplate<*>} registry - * @param {(GameRoot, object) => object} customResolver - */ - constructor(registry, customResolver = null) { - super(); - this.registry = registry; - this.customResolver = customResolver; - } - - serialize(value) { - assert(typeof value === "object", "Not a class instance: " + value); - return { - $: value.constructor.getId(), - data: value.serialize(), - }; - } - - getAsJsonSchemaUncached() { - const options = []; - const entries = this.registry.getEntries(); - for (let i = 0; i < entries.length; ++i) { - const entry = entries[i]; - - options.push( - schemaObject({ - $: { - type: "string", - // @ts-ignore - enum: [entry.getId()], - }, - // @ts-ignore - data: schemaToJsonSchema(entry.getCachedSchema()), - }) - ); - } - - return { oneOf: options }; - } - - /** - * @see BaseDataType.deserialize - * @param {any} value - * @param {GameRoot} root - * @param {object} targetObject - * @param {string|number} targetKey - * @returns {string|void} String error code or null on success - */ - deserialize(value, targetObject, targetKey, root) { - let instance; - - if (this.customResolver) { - instance = this.customResolver(root, value); - if (!instance) { - return "Failed to call custom resolver"; - } - } else { - const instanceClass = this.registry.findById(value.$); - if (!instanceClass || !instanceClass.prototype) { - return "Invalid class id (runtime-err): " + value.$ + "->" + instanceClass; - } - instance = Object.create(instanceClass.prototype); - const errorState = instance.deserialize(value.data); - if (errorState) { - return errorState; - } - } - targetObject[targetKey] = instance; - } - - verifySerializedValue(value) { - if (!value) { - return "Got null data"; - } - - if (!this.registry.hasId(value.$)) { - return "Invalid class id: " + value.$ + " (factory is " + this.registry.getId() + ")"; - } - } - - getCacheKey() { - return "class." + this.registry.getId(); - } -} - -export class TypeClassData extends BaseDataType { - /** - * - * @param {FactoryTemplate<*>} registry - */ - constructor(registry) { - super(); - this.registry = registry; - } - - serialize(value) { - assert(typeof value === "object", "Not a class instance: " + value); - return value.serialize(); - } - - getAsJsonSchemaUncached() { - const options = []; - const entries = this.registry.getEntries(); - for (let i = 0; i < entries.length; ++i) { - const entry = entries[i]; - options.push( - schemaToJsonSchema(/** @type {typeof BasicSerializableObject} */ (entry).getCachedSchema()) - ); - } - return { oneOf: options }; - } - - /** - * @see BaseDataType.deserialize - * @param {any} value - * @param {GameRoot} root - * @param {object} targetObject - * @param {string|number} targetKey - * @returns {string|void} String error code or null on success - */ - deserialize(value, targetObject, targetKey, root) { - assert(false, "can not deserialize class data of type " + this.registry.getId()); - } - - verifySerializedValue(value) { - if (!value) { - return "Got null data"; - } - } - - getCacheKey() { - return "class." + this.registry.getId(); - } -} - -export class TypeClassFromMetaclass extends BaseDataType { - /** - * - * @param {typeof BasicSerializableObject} classHandle - * @param {SingletonFactoryTemplate<*>} registry - */ - constructor(classHandle, registry) { - super(); - this.registry = registry; - this.classHandle = classHandle; - } - - serialize(value) { - assert(typeof value === "object", "Not a class instance: " + value); - return { - $: value.getMetaclass().getId(), - data: value.serialize(), - }; - } - - getAsJsonSchemaUncached() { - // const options = []; - const ids = this.registry.getAllIds(); - - return { - $: { - type: "string", - enum: ids, - }, - data: schemaToJsonSchema(this.classHandle.getCachedSchema()), - }; - } - - /** - * @see BaseDataType.deserialize - * @param {any} value - * @param {GameRoot} root - * @param {object} targetObject - * @param {string|number} targetKey - * @returns {string|void} String error code or null on success - */ - deserialize(value, targetObject, targetKey, root) { - const metaClassInstance = this.registry.findById(value.$); - if (!metaClassInstance || !metaClassInstance.prototype) { - return "Invalid meta class id (runtime-err): " + value.$ + "->" + metaClassInstance; - } - - const instanceClass = metaClassInstance.getInstanceClass(); - const instance = Object.create(instanceClass.prototype); - const errorState = instance.deserialize(value.data); - if (errorState) { - return errorState; - } - targetObject[targetKey] = instance; - } - - verifySerializedValue(value) { - if (!value) { - return "Got null data"; - } - - if (!this.registry.hasId(value.$)) { - return "Invalid class id: " + value.$ + " (factory is " + this.registry.getId() + ")"; - } - } - - getCacheKey() { - return "classofmetaclass." + this.registry.getId(); - } -} - -export class TypeMetaClass extends BaseDataType { - /** - * - * @param {SingletonFactoryTemplate<*>} registry - */ - constructor(registry) { - super(); - this.registry = registry; - } - - serialize(value) { - return value.getId(); - } - - /** - * @see BaseDataType.deserialize - * @param {any} value - * @param {GameRoot} root - * @param {object} targetObject - * @param {string|number} targetKey - * @returns {string|void} String error code or null on success - */ - deserialize(value, targetObject, targetKey, root) { - const instanceClass = this.registry.findById(value); - if (!instanceClass) { - return "Invalid class id (runtime-err): " + value; - } - targetObject[targetKey] = instanceClass; - } - - getAsJsonSchemaUncached() { - return { - type: "string", - enum: this.registry.getAllIds(), - }; - } - - verifySerializedValue(value) { - if (!value) { - return "Got null data"; - } - - if (typeof value !== "string") { - return "Got non string data"; - } - - if (!this.registry.hasId(value)) { - return "Invalid class id: " + value + " (factory is " + this.registry.getId() + ")"; - } - } - - getCacheKey() { - return "metaclass." + this.registry.getId(); - } -} - -export class TypeArray extends BaseDataType { - /** - * @param {BaseDataType} innerType - */ - constructor(innerType, fixedSize = false) { - super(); - this.fixedSize = fixedSize; - this.innerType = innerType; - } - - serialize(value) { - assert(Array.isArray(value), "Not an array"); - const result = new Array(value.length); - for (let i = 0; i < value.length; ++i) { - result[i] = this.innerType.serialize(value[i]); - } - return result; - } - - /** - * @see BaseDataType.deserialize - * @param {any} value - * @param {GameRoot} root - * @param {object} targetObject - * @param {string|number} targetKey - * @returns {string|void} String error code or null on success - */ - deserialize(value, targetObject, targetKey, root) { - let destination = targetObject[targetKey]; - if (!destination) { - targetObject[targetKey] = destination = new Array(value.length); - } - - const size = this.fixedSize ? Math.min(value.length, destination.length) : value.length; - - for (let i = 0; i < size; ++i) { - const errorStatus = this.innerType.deserializeWithVerify(value[i], destination, i, root); - if (errorStatus) { - return errorStatus; - } - } - } - - getAsJsonSchemaUncached() { - return { - type: "array", - items: this.innerType.getAsJsonSchema(), - }; - } - - verifySerializedValue(value) { - if (!Array.isArray(value)) { - return "Not an array: " + value; - } - } - - getCacheKey() { - return "array." + this.innerType.getCacheKey(); - } -} - -export class TypeFixedClass extends BaseDataType { - /** - * - * @param {typeof BasicSerializableObject} baseclass - */ - constructor(baseclass) { - super(); - this.baseclass = baseclass; - } - - serialize(value) { - assert(value instanceof this.baseclass, "Not a valid class instance"); - return value.serialize(); - } - - /** - * @see BaseDataType.deserialize - * @param {any} value - * @param {GameRoot} root - * @param {object} targetObject - * @param {string|number} targetKey - * @returns {string|void} String error code or null on success - */ - deserialize(value, targetObject, targetKey, root) { - const instance = Object.create(this.baseclass.prototype); - const errorState = instance.deserialize(value); - if (errorState) { - return "Failed to deserialize class: " + errorState; - } - targetObject[targetKey] = instance; - } - - getAsJsonSchemaUncached() { - this.baseclass.getSchema(); - this.baseclass.getCachedSchema(); - return schemaToJsonSchema(this.baseclass.getCachedSchema()); - } - - verifySerializedValue(value) { - if (!value) { - return "Got null data"; - } - } - - getCacheKey() { - return "fixedclass." + this.baseclass.getId(); - } -} - -export class TypeKeyValueMap extends BaseDataType { - /** - * @param {BaseDataType} valueType - * @param {boolean=} includeEmptyValues - */ - constructor(valueType, includeEmptyValues = true) { - super(); - this.valueType = valueType; - this.includeEmptyValues = includeEmptyValues; - } - - serialize(value) { - assert(typeof value === "object", "not an object"); - let result = {}; - for (const key in value) { - const serialized = this.valueType.serialize(value[key]); - if (!this.includeEmptyValues && typeof serialized === "object") { - if ( - serialized.$ && - typeof serialized.data === "object" && - Object.keys(serialized.data).length === 0 - ) { - continue; - } else if (Object.keys(serialized).length === 0) { - continue; - } - } - - result[key] = serialized; - } - return result; - } - - /** - * @see BaseDataType.deserialize - * @param {any} value - * @param {GameRoot} root - * @param {object} targetObject - * @param {string|number} targetKey - * @returns {string|void} String error code or null on success - */ - deserialize(value, targetObject, targetKey, root) { - let result = {}; - for (const key in value) { - const errorCode = this.valueType.deserializeWithVerify(value[key], result, key, root); - if (errorCode) { - return errorCode; - } - } - targetObject[targetKey] = result; - } - - getAsJsonSchemaUncached() { - return { - type: "object", - additionalProperties: this.valueType.getAsJsonSchema(), - }; - } - - verifySerializedValue(value) { - if (typeof value !== "object") { - return "KV map is not an object"; - } - } - - getCacheKey() { - return "kvmap." + this.valueType.getCacheKey(); - } -} - -export class TypeClassId extends BaseDataType { - /** - * @param {FactoryTemplate<*>|SingletonFactoryTemplate<*>} registry - */ - constructor(registry) { - super(); - this.registry = registry; - } - - serialize(value) { - assert(typeof value === "string", "Not a valid string"); - assert(this.registry.hasId(value), "Id " + value + " not found in registry"); - return value; - } - - /** - * @see BaseDataType.deserialize - * @param {any} value - * @param {GameRoot} root - * @param {object} targetObject - * @param {string|number} targetKey - * @returns {string|void} String error code or null on success - */ - deserialize(value, targetObject, targetKey, root) { - targetObject[targetKey] = value; - } - - getAsJsonSchemaUncached() { - return { - type: "string", - enum: this.registry.getAllIds(), - }; - } - - verifySerializedValue(value) { - if (typeof value !== "string") { - return "Not a valid registry id key: " + value; - } - if (!this.registry.hasId(value)) { - return "Id " + value + " not known to registry"; - } - } - - getCacheKey() { - return "classid." + this.registry.getId(); - } -} - -export class TypePair extends BaseDataType { - /** - * @param {BaseDataType} type1 - * @param {BaseDataType} type2 - */ - constructor(type1, type2) { - super(); - assert(type1 && type1 instanceof BaseDataType, "bad first type given for pair"); - assert(type2 && type2 instanceof BaseDataType, "bad second type given for pair"); - this.type1 = type1; - this.type2 = type2; - } - - serialize(value) { - assert(Array.isArray(value), "pair: not an array"); - assert(value.length === 2, "pair: length != 2"); - return [this.type1.serialize(value[0]), this.type2.serialize(value[1])]; - } - - /** - * @see BaseDataType.deserialize - * @param {any} value - * @param {GameRoot} root - * @param {object} targetObject - * @param {string|number} targetKey - * @returns {string|void} String error code or null on success - */ - deserialize(value, targetObject, targetKey, root) { - const result = [undefined, undefined]; - - let errorCode = this.type1.deserialize(value[0], result, 0, root); - if (errorCode) { - return errorCode; - } - errorCode = this.type2.deserialize(value[1], result, 1, root); - if (errorCode) { - return errorCode; - } - - targetObject[targetKey] = result; - } - - getAsJsonSchemaUncached() { - return { - type: "array", - minLength: 2, - maxLength: 2, - items: [this.type1.getAsJsonSchema(), this.type2.getAsJsonSchema()], - }; - } - - verifySerializedValue(value) { - if (!Array.isArray(value)) { - return "Pair is not an array"; - } - if (value.length !== 2) { - return "Pair length != 2"; - } - let errorCode = this.type1.verifySerializedValue(value[0]); - if (errorCode) { - return errorCode; - } - errorCode = this.type2.verifySerializedValue(value[1]); - if (errorCode) { - return errorCode; - } - } - - getCacheKey() { - return "pair.(" + this.type1.getCacheKey() + "," + this.type2.getCacheKey + ")"; - } -} - -export class TypeNullable extends BaseDataType { - /** - * @param {BaseDataType} wrapped - */ - constructor(wrapped) { - super(); - this.wrapped = wrapped; - } - - serialize(value) { - if (value === null || value === undefined) { - return null; - } - return this.wrapped.serialize(value); - } - - /** - * @see BaseDataType.deserialize - * @param {any} value - * @param {GameRoot} root - * @param {object} targetObject - * @param {string|number} targetKey - * @returns {string|void} String error code or null on success - */ - deserialize(value, targetObject, targetKey, root) { - if (value === null || value === undefined) { - targetObject[targetKey] = null; - return; - } - return this.wrapped.deserialize(value, targetObject, targetKey, root); - } - - verifySerializedValue(value) { - if (value === null) { - return; - } - return this.wrapped.verifySerializedValue(value); - } - - getAsJsonSchemaUncached() { - return { - oneOf: [ - { - type: "null", - }, - this.wrapped.getAsJsonSchema(), - ], - }; - } - - allowNull() { - return true; - } - - getCacheKey() { - return "nullable." + this.wrapped.getCacheKey(); - } -} - -export class TypeStructuredObject extends BaseDataType { - /** - * @param {Object.} descriptor - */ - constructor(descriptor) { - super(); - this.descriptor = descriptor; - } - - serialize(value) { - assert(typeof value === "object", "not an object"); - let result = {}; - for (const key in this.descriptor) { - // assert(value.hasOwnProperty(key), "Serialization: Object does not have", key, "property!"); - result[key] = this.descriptor[key].serialize(value[key]); - } - return result; - } - - /** - * @see BaseDataType.deserialize - * @param {any} value - * @param {GameRoot} root - * @param {object} targetObject - * @param {string|number} targetKey - * @returns {string|void} String error code or null on success - */ - deserialize(value, targetObject, targetKey, root) { - let target = targetObject[targetKey]; - if (!target) { - targetObject[targetKey] = target = {}; - } - - for (const key in value) { - const valueType = this.descriptor[key]; - const errorCode = valueType.deserializeWithVerify(value[key], target, key, root); - if (errorCode) { - return errorCode; - } - } - } - - getAsJsonSchemaUncached() { - let properties = {}; - for (const key in this.descriptor) { - properties[key] = this.descriptor[key].getAsJsonSchema(); - } - - return { - type: "object", - required: Object.keys(this.descriptor), - properties, - }; - } - - verifySerializedValue(value) { - if (typeof value !== "object") { - return "structured object is not an object"; - } - for (const key in this.descriptor) { - if (!value.hasOwnProperty(key)) { - return "structured object is missing key " + key; - } - const subError = this.descriptor[key].verifySerializedValue(value[key]); - if (subError) { - return "structured object::" + subError; - } - } - } - - getCacheKey() { - let props = []; - for (const key in this.descriptor) { - props.push(key + "=" + this.descriptor[key].getCacheKey()); - } - return "structured[" + props.join(",") + "]"; - } -} +/* typehints:start */ +import { GameRoot } from "../game/root"; +import { BasicSerializableObject } from "./serialization"; +/* typehints:end */ + +import { Vector } from "../core/vector"; +import { round4Digits } from "../core/utils"; +export const globalJsonSchemaDefs = {}; + +/** + * @typedef {import("../core/factory").Factory} FactoryTemplate + * @template T + */ +/** + * @typedef {import("../core/singleton_factory").SingletonFactory} SingletonFactoryTemplate + * @template {{ getId(): string }} T + */ + +/** + * + * @param {import("./serialization").Schema} schema + */ +export function schemaToJsonSchema(schema) { + const jsonSchema = { + type: "object", + additionalProperties: false, + required: [], + properties: {}, + }; + + for (const key in schema) { + const subSchema = schema[key].getAsJsonSchema(); + jsonSchema.required.push(key); + jsonSchema.properties[key] = subSchema; + } + + return jsonSchema; +} + +/** + * Helper function to create a json schema object + * @param {any} properties + */ +function schemaObject(properties) { + return { + type: "object", + required: Object.keys(properties).slice(), + additionalProperties: false, + properties, + }; +} + +/** + * Base serialization data type + */ +export class BaseDataType { + /** + * Serializes a given raw value + * @param {any} value + * @returns {unknown} + * @abstract + */ + serialize(value) { + abstract; + return {}; + } + + /** + * Verifies a given serialized value + * @param {any} value + * @returns {string|void} String error code or null on success + */ + verifySerializedValue(value) {} + + /** + * Deserializes a serialized value into the target object under the given key + * @param {any} value + * @param {GameRoot} root + * @param {object} targetObject + * @param {string|number} targetKey + * @returns {string|void} String error code or null on success + * @abstract + */ + deserialize(value, targetObject, targetKey, root) { + abstract; + } + + /** + * Returns the json schema + */ + getAsJsonSchema() { + const key = this.getCacheKey(); + const schema = this.getAsJsonSchemaUncached(); + + if (!globalJsonSchemaDefs[key]) { + // schema.$id = key; + globalJsonSchemaDefs[key] = schema; + } + + return { + $ref: "#/definitions/" + key, + }; + } + + /** + * INTERNAL Should return the json schema representation + * @abstract + */ + getAsJsonSchemaUncached() { + abstract; + } + + /** + * Returns whether null values are okay + * @returns {boolean} + */ + allowNull() { + return false; + } + + // Helper methods + + /** + * Deserializes a serialized value, but performs integrity checks before + * @param {any} value + * @param {GameRoot} root + * @param {object} targetObject + * @param {string|number} targetKey + * @returns {string|void} String error code or null on success + */ + deserializeWithVerify(value, targetObject, targetKey, root) { + const errorCode = this.verifySerializedValue(value); + if (errorCode) { + return ( + "serialization verify failed: " + + errorCode + + " [value " + + (JSON.stringify(value) || "").substr(0, 100) + + "]" + ); + } + return this.deserialize(value, targetObject, targetKey, root); + } + + /** + * Should return a cacheable key + * @abstract + */ + getCacheKey() { + abstract; + return ""; + } +} + +export class TypeInteger extends BaseDataType { + serialize(value) { + assert(Number.isInteger(value), "Type integer got non integer for serialize: " + value); + return value; + } + + /** + * @see BaseDataType.deserialize + * @param {any} value + * @param {GameRoot} root + * @param {object} targetObject + * @param {string|number} targetKey + * @returns {string|void} String error code or null on success + */ + deserialize(value, targetObject, targetKey, root) { + targetObject[targetKey] = value; + } + + getAsJsonSchemaUncached() { + return { + type: "integer", + }; + } + + verifySerializedValue(value) { + if (!Number.isInteger(value)) { + return "Not a valid number"; + } + } + + getCacheKey() { + return "int"; + } +} + +export class TypePositiveInteger extends BaseDataType { + serialize(value) { + assert(Number.isInteger(value), "Type integer got non integer for serialize: " + value); + assert(value >= 0, "value < 0: " + value); + return value; + } + + /** + * @see BaseDataType.deserialize + * @param {any} value + * @param {GameRoot} root + * @param {object} targetObject + * @param {string|number} targetKey + * @returns {string|void} String error code or null on success + */ + deserialize(value, targetObject, targetKey, root) { + targetObject[targetKey] = value; + } + + getAsJsonSchemaUncached() { + return { + type: "integer", + minimum: 0, + }; + } + + verifySerializedValue(value) { + if (!Number.isInteger(value)) { + return "Not a valid number"; + } + if (value < 0) { + return "Negative value for positive integer"; + } + } + + getCacheKey() { + return "uint"; + } +} + +export class TypePositiveIntegerOrString extends BaseDataType { + serialize(value) { + if (Number.isInteger(value)) { + assert(value >= 0, "type integer got negative value: " + value); + } else if (typeof value === "string") { + // all good + } else { + assertAlways(false, "Type integer|string got non integer or string for serialize: " + value); + } + return value; + } + + /** + * @see BaseDataType.deserialize + * @param {any} value + * @param {GameRoot} root + * @param {object} targetObject + * @param {string|number} targetKey + * @returns {string|void} String error code or null on success + */ + deserialize(value, targetObject, targetKey, root) { + targetObject[targetKey] = value; + } + + getAsJsonSchemaUncached() { + return { + oneOf: [{ type: "integer", minimum: 0 }, { type: "string" }], + }; + } + + verifySerializedValue(value) { + if (Number.isInteger(value)) { + if (value < 0) { + return "Negative value for positive integer"; + } + } else if (typeof value === "string") { + // all good + } else { + return "Not a valid number or string: " + value; + } + } + + getCacheKey() { + return "uint_str"; + } +} + +export class TypeBoolean extends BaseDataType { + serialize(value) { + assert(value === true || value === false, "Type bool got non bool for serialize: " + value); + return value; + } + + /** + * @see BaseDataType.deserialize + * @param {any} value + * @param {GameRoot} root + * @param {object} targetObject + * @param {string|number} targetKey + * @returns {string|void} String error code or null on success + */ + deserialize(value, targetObject, targetKey, root) { + targetObject[targetKey] = value; + } + + getAsJsonSchemaUncached() { + return { + type: "boolean", + }; + } + + verifySerializedValue(value) { + if (value !== true && value !== false) { + return "Not a boolean"; + } + } + + getCacheKey() { + return "bool"; + } +} + +export class TypeString extends BaseDataType { + serialize(value) { + assert(typeof value === "string", "Type string got non string for serialize: " + value); + return value; + } + + /** + * @see BaseDataType.deserialize + * @param {any} value + * @param {GameRoot} root + * @param {object} targetObject + * @param {string|number} targetKey + * @returns {string|void} String error code or null on success + */ + deserialize(value, targetObject, targetKey, root) { + targetObject[targetKey] = value; + } + getAsJsonSchemaUncached() { + return { + type: "string", + }; + } + + verifySerializedValue(value) { + if (typeof value !== "string") { + return "Not a valid string"; + } + } + + getCacheKey() { + return "string"; + } +} + +export class TypeVector extends BaseDataType { + serialize(value) { + assert(value instanceof Vector, "Type vector got non vector for serialize: " + value); + return { + x: round4Digits(value.x), + y: round4Digits(value.y), + }; + } + + getAsJsonSchemaUncached() { + return schemaObject({ + x: { + type: "number", + }, + y: { + type: "number", + }, + }); + } + + /** + * @see BaseDataType.deserialize + * @param {any} value + * @param {GameRoot} root + * @param {object} targetObject + * @param {string|number} targetKey + * @returns {string|void} String error code or null on success + */ + deserialize(value, targetObject, targetKey, root) { + targetObject[targetKey] = new Vector(value.x, value.y); + } + + verifySerializedValue(value) { + if (!Number.isFinite(value.x) || !Number.isFinite(value.y)) { + return "Not a valid vector, missing x/y or bad data type"; + } + } + + getCacheKey() { + return "vector"; + } +} + +export class TypeTileVector extends BaseDataType { + serialize(value) { + assert(value instanceof Vector, "Type vector got non vector for serialize: " + value); + assert(Number.isInteger(value.x) && value.x > 0, "Invalid tile x:" + value.x); + assert(Number.isInteger(value.y) && value.y > 0, "Invalid tile x:" + value.y); + return { x: value.x, y: value.y }; + } + + getAsJsonSchemaUncached() { + return schemaObject({ + x: { + type: "integer", + minimum: 0, + maximum: 256, + }, + y: { + type: "integer", + minimum: 0, + maximum: 256, + }, + }); + } + + /** + * @see BaseDataType.deserialize + * @param {any} value + * @param {GameRoot} root + * @param {object} targetObject + * @param {string|number} targetKey + * @returns {string|void} String error code or null on success + */ + deserialize(value, targetObject, targetKey, root) { + targetObject[targetKey] = new Vector(value.x, value.y); + } + + verifySerializedValue(value) { + if (!Number.isInteger(value.x) || !Number.isInteger(value.y)) { + return "Not a valid tile vector, missing x/y or bad data type"; + } + if (value.x < 0 || value.y < 0) { + return "Invalid tile vector, x or y < 0"; + } + } + + getCacheKey() { + return "tilevector"; + } +} + +export class TypeNumber extends BaseDataType { + serialize(value) { + assert(Number.isFinite(value), "Type number got non number for serialize: " + value); + assert(!Number.isNaN(value), "Value is nan: " + value); + return round4Digits(value); + } + + getAsJsonSchemaUncached() { + return { + type: "number", + }; + } + + /** + * @see BaseDataType.deserialize + * @param {any} value + * @param {GameRoot} root + * @param {object} targetObject + * @param {string|number} targetKey + * @returns {string|void} String error code or null on success + */ + deserialize(value, targetObject, targetKey, root) { + targetObject[targetKey] = value; + } + + verifySerializedValue(value) { + if (!Number.isFinite(value)) { + return "Not a valid number: " + value; + } + } + + getCacheKey() { + return "float"; + } +} + +export class TypePositiveNumber extends BaseDataType { + serialize(value) { + assert(Number.isFinite(value), "Type number got non number for serialize: " + value); + assert(value >= 0, "Postitive number got negative value: " + value); + return round4Digits(value); + } + + /** + * @see BaseDataType.deserialize + * @param {any} value + * @param {GameRoot} root + * @param {object} targetObject + * @param {string|number} targetKey + * @returns {string|void} String error code or null on success + */ + deserialize(value, targetObject, targetKey, root) { + targetObject[targetKey] = value; + } + + getAsJsonSchemaUncached() { + return { + type: "number", + minimum: 0, + }; + } + + verifySerializedValue(value) { + if (!Number.isFinite(value)) { + return "Not a valid number: " + value; + } + if (value < 0) { + return "Positive number got negative value: " + value; + } + } + + getCacheKey() { + return "ufloat"; + } +} + +export class TypeEnum extends BaseDataType { + /** + * @param {Object.} enumeration + */ + constructor(enumeration = {}) { + super(); + this.availableValues = Object.values(enumeration); + } + + serialize(value) { + assert(this.availableValues.indexOf(value) >= 0, "Unknown value: " + value); + return value; + } + + /** + * @see BaseDataType.deserialize + * @param {any} value + * @param {GameRoot} root + * @param {object} targetObject + * @param {string|number} targetKey + * @returns {string|void} String error code or null on success + */ + deserialize(value, targetObject, targetKey, root) { + targetObject[targetKey] = value; + } + + getAsJsonSchemaUncached() { + return { + type: "string", + enum: this.availableValues, + }; + } + + verifySerializedValue(value) { + if (this.availableValues.indexOf(value) < 0) { + return "Unknown enum value: " + value; + } + } + + getCacheKey() { + return "enum." + this.availableValues.join(","); + } +} + +export class TypeEntity extends BaseDataType { + serialize(value) { + // assert(value instanceof Entity, "Not a valid entity ref: " + value); + assert(value.uid, "Entity has no uid yet"); + assert(!value.destroyed, "Entity already destroyed"); + assert(!value.queuedForDestroy, "Entity queued for destroy"); + + return value.uid; + } + + getAsJsonSchemaUncached() { + return { + type: "integer", + minimum: 0, + }; + } + + /** + * @see BaseDataType.deserialize + * @param {any} value + * @param {GameRoot} root + * @param {object} targetObject + * @param {string|number} targetKey + * @returns {string|void} String error code or null on success + */ + deserialize(value, targetObject, targetKey, root) { + const entity = root.entityMgr.findByUid(value); + if (!entity) { + return "Entity not found by uid: " + value; + } + targetObject[targetKey] = entity; + } + + verifySerializedValue(value) { + if (!Number.isFinite(value)) { + return "Not a valid uuid: " + value; + } + } + + getCacheKey() { + return "entity"; + } +} + +export class TypeEntityWeakref extends BaseDataType { + serialize(value) { + if (value === null) { + return null; + } + + // assert(value instanceof Entity, "Not a valid entity ref (weak): " + value); + assert(value.uid, "Entity has no uid yet"); + if (value.destroyed || value.queuedForDestroy) { + return null; + } + return value.uid; + } + + /** + * @see BaseDataType.deserialize + * @param {any} value + * @param {GameRoot} root + * @param {object} targetObject + * @param {string|number} targetKey + * @returns {string|void} String error code or null on success + */ + deserialize(value, targetObject, targetKey, root) { + if (value === null) { + targetObject[targetKey] = null; + return; + } + const entity = root.entityMgr.findByUid(value, false); + targetObject[targetKey] = entity; + } + + getAsJsonSchemaUncached() { + return { + type: ["null", "integer"], + minimum: 0, + }; + } + + allowNull() { + return true; + } + + verifySerializedValue(value) { + if (value !== null && !Number.isFinite(value)) { + return "Not a valid uuid: " + value; + } + } + + getCacheKey() { + return "entity-weakref"; + } +} + +export class TypeClass extends BaseDataType { + /** + * + * @param {FactoryTemplate<*>} registry + * @param {(GameRoot, object) => object} customResolver + */ + constructor(registry, customResolver = null) { + super(); + this.registry = registry; + this.customResolver = customResolver; + } + + serialize(value) { + assert(typeof value === "object", "Not a class instance: " + value); + return { + $: value.constructor.getId(), + data: value.serialize(), + }; + } + + getAsJsonSchemaUncached() { + const options = []; + const entries = this.registry.getEntries(); + for (let i = 0; i < entries.length; ++i) { + const entry = entries[i]; + + options.push( + schemaObject({ + $: { + type: "string", + // @ts-ignore + enum: [entry.getId()], + }, + // @ts-ignore + data: schemaToJsonSchema(entry.getCachedSchema()), + }) + ); + } + + return { oneOf: options }; + } + + /** + * @see BaseDataType.deserialize + * @param {any} value + * @param {GameRoot} root + * @param {object} targetObject + * @param {string|number} targetKey + * @returns {string|void} String error code or null on success + */ + deserialize(value, targetObject, targetKey, root) { + let instance; + + if (this.customResolver) { + instance = this.customResolver(root, value); + if (!instance) { + return "Failed to call custom resolver"; + } + } else { + const instanceClass = this.registry.findById(value.$); + if (!instanceClass || !instanceClass.prototype) { + return "Invalid class id (runtime-err): " + value.$ + "->" + instanceClass; + } + instance = Object.create(instanceClass.prototype); + const errorState = instance.deserialize(value.data); + if (errorState) { + return errorState; + } + } + targetObject[targetKey] = instance; + } + + verifySerializedValue(value) { + if (!value) { + return "Got null data"; + } + + if (!this.registry.hasId(value.$)) { + return "Invalid class id: " + value.$ + " (factory is " + this.registry.getId() + ")"; + } + } + + getCacheKey() { + return "class." + this.registry.getId(); + } +} + +export class TypeClassData extends BaseDataType { + /** + * + * @param {FactoryTemplate<*>} registry + */ + constructor(registry) { + super(); + this.registry = registry; + } + + serialize(value) { + assert(typeof value === "object", "Not a class instance: " + value); + return value.serialize(); + } + + getAsJsonSchemaUncached() { + const options = []; + const entries = this.registry.getEntries(); + for (let i = 0; i < entries.length; ++i) { + const entry = entries[i]; + options.push( + schemaToJsonSchema(/** @type {typeof BasicSerializableObject} */ (entry).getCachedSchema()) + ); + } + return { oneOf: options }; + } + + /** + * @see BaseDataType.deserialize + * @param {any} value + * @param {GameRoot} root + * @param {object} targetObject + * @param {string|number} targetKey + * @returns {string|void} String error code or null on success + */ + deserialize(value, targetObject, targetKey, root) { + assert(false, "can not deserialize class data of type " + this.registry.getId()); + } + + verifySerializedValue(value) { + if (!value) { + return "Got null data"; + } + } + + getCacheKey() { + return "class." + this.registry.getId(); + } +} + +export class TypeClassFromMetaclass extends BaseDataType { + /** + * + * @param {typeof BasicSerializableObject} classHandle + * @param {SingletonFactoryTemplate<*>} registry + */ + constructor(classHandle, registry) { + super(); + this.registry = registry; + this.classHandle = classHandle; + } + + serialize(value) { + assert(typeof value === "object", "Not a class instance: " + value); + return { + $: value.getMetaclass().getId(), + data: value.serialize(), + }; + } + + getAsJsonSchemaUncached() { + // const options = []; + const ids = this.registry.getAllIds(); + + return { + $: { + type: "string", + enum: ids, + }, + data: schemaToJsonSchema(this.classHandle.getCachedSchema()), + }; + } + + /** + * @see BaseDataType.deserialize + * @param {any} value + * @param {GameRoot} root + * @param {object} targetObject + * @param {string|number} targetKey + * @returns {string|void} String error code or null on success + */ + deserialize(value, targetObject, targetKey, root) { + const metaClassInstance = this.registry.findById(value.$); + if (!metaClassInstance || !metaClassInstance.prototype) { + return "Invalid meta class id (runtime-err): " + value.$ + "->" + metaClassInstance; + } + + const instanceClass = metaClassInstance.getInstanceClass(); + const instance = Object.create(instanceClass.prototype); + const errorState = instance.deserialize(value.data); + if (errorState) { + return errorState; + } + targetObject[targetKey] = instance; + } + + verifySerializedValue(value) { + if (!value) { + return "Got null data"; + } + + if (!this.registry.hasId(value.$)) { + return "Invalid class id: " + value.$ + " (factory is " + this.registry.getId() + ")"; + } + } + + getCacheKey() { + return "classofmetaclass." + this.registry.getId(); + } +} + +export class TypeMetaClass extends BaseDataType { + /** + * + * @param {SingletonFactoryTemplate<*>} registry + */ + constructor(registry) { + super(); + this.registry = registry; + } + + serialize(value) { + return value.getId(); + } + + /** + * @see BaseDataType.deserialize + * @param {any} value + * @param {GameRoot} root + * @param {object} targetObject + * @param {string|number} targetKey + * @returns {string|void} String error code or null on success + */ + deserialize(value, targetObject, targetKey, root) { + const instanceClass = this.registry.findById(value); + if (!instanceClass) { + return "Invalid class id (runtime-err): " + value; + } + targetObject[targetKey] = instanceClass; + } + + getAsJsonSchemaUncached() { + return { + type: "string", + enum: this.registry.getAllIds(), + }; + } + + verifySerializedValue(value) { + if (!value) { + return "Got null data"; + } + + if (typeof value !== "string") { + return "Got non string data"; + } + + if (!this.registry.hasId(value)) { + return "Invalid class id: " + value + " (factory is " + this.registry.getId() + ")"; + } + } + + getCacheKey() { + return "metaclass." + this.registry.getId(); + } +} + +export class TypeArray extends BaseDataType { + /** + * @param {BaseDataType} innerType + */ + constructor(innerType, fixedSize = false) { + super(); + this.fixedSize = fixedSize; + this.innerType = innerType; + } + + serialize(value) { + assert(Array.isArray(value), "Not an array"); + const result = new Array(value.length); + for (let i = 0; i < value.length; ++i) { + result[i] = this.innerType.serialize(value[i]); + } + return result; + } + + /** + * @see BaseDataType.deserialize + * @param {any} value + * @param {GameRoot} root + * @param {object} targetObject + * @param {string|number} targetKey + * @returns {string|void} String error code or null on success + */ + deserialize(value, targetObject, targetKey, root) { + let destination = targetObject[targetKey]; + if (!destination) { + targetObject[targetKey] = destination = new Array(value.length); + } + + const size = this.fixedSize ? Math.min(value.length, destination.length) : value.length; + + for (let i = 0; i < size; ++i) { + const errorStatus = this.innerType.deserializeWithVerify(value[i], destination, i, root); + if (errorStatus) { + return errorStatus; + } + } + } + + getAsJsonSchemaUncached() { + return { + type: "array", + items: this.innerType.getAsJsonSchema(), + }; + } + + verifySerializedValue(value) { + if (!Array.isArray(value)) { + return "Not an array: " + value; + } + } + + getCacheKey() { + return "array." + this.innerType.getCacheKey(); + } +} + +export class TypeFixedClass extends BaseDataType { + /** + * + * @param {typeof BasicSerializableObject} baseclass + */ + constructor(baseclass) { + super(); + this.baseclass = baseclass; + } + + serialize(value) { + assert(value instanceof this.baseclass, "Not a valid class instance"); + return value.serialize(); + } + + /** + * @see BaseDataType.deserialize + * @param {any} value + * @param {GameRoot} root + * @param {object} targetObject + * @param {string|number} targetKey + * @returns {string|void} String error code or null on success + */ + deserialize(value, targetObject, targetKey, root) { + const instance = Object.create(this.baseclass.prototype); + const errorState = instance.deserialize(value); + if (errorState) { + return "Failed to deserialize class: " + errorState; + } + targetObject[targetKey] = instance; + } + + getAsJsonSchemaUncached() { + this.baseclass.getSchema(); + this.baseclass.getCachedSchema(); + return schemaToJsonSchema(this.baseclass.getCachedSchema()); + } + + verifySerializedValue(value) { + if (!value) { + return "Got null data"; + } + } + + getCacheKey() { + return "fixedclass." + this.baseclass.getId(); + } +} + +export class TypeKeyValueMap extends BaseDataType { + /** + * @param {BaseDataType} valueType + * @param {boolean=} includeEmptyValues + */ + constructor(valueType, includeEmptyValues = true) { + super(); + this.valueType = valueType; + this.includeEmptyValues = includeEmptyValues; + } + + serialize(value) { + assert(typeof value === "object", "not an object"); + let result = {}; + for (const key in value) { + const serialized = this.valueType.serialize(value[key]); + if (!this.includeEmptyValues && typeof serialized === "object") { + if ( + "$" in serialized && + "data" in serialized && + typeof serialized.data === "object" && + Object.keys(serialized.data).length === 0 + ) { + continue; + } else if (Object.keys(serialized).length === 0) { + continue; + } + } + + result[key] = serialized; + } + return result; + } + + /** + * @see BaseDataType.deserialize + * @param {any} value + * @param {GameRoot} root + * @param {object} targetObject + * @param {string|number} targetKey + * @returns {string|void} String error code or null on success + */ + deserialize(value, targetObject, targetKey, root) { + let result = {}; + for (const key in value) { + const errorCode = this.valueType.deserializeWithVerify(value[key], result, key, root); + if (errorCode) { + return errorCode; + } + } + targetObject[targetKey] = result; + } + + getAsJsonSchemaUncached() { + return { + type: "object", + additionalProperties: this.valueType.getAsJsonSchema(), + }; + } + + verifySerializedValue(value) { + if (typeof value !== "object") { + return "KV map is not an object"; + } + } + + getCacheKey() { + return "kvmap." + this.valueType.getCacheKey(); + } +} + +export class TypeClassId extends BaseDataType { + /** + * @param {FactoryTemplate<*>|SingletonFactoryTemplate<*>} registry + */ + constructor(registry) { + super(); + this.registry = registry; + } + + serialize(value) { + assert(typeof value === "string", "Not a valid string"); + assert(this.registry.hasId(value), "Id " + value + " not found in registry"); + return value; + } + + /** + * @see BaseDataType.deserialize + * @param {any} value + * @param {GameRoot} root + * @param {object} targetObject + * @param {string|number} targetKey + * @returns {string|void} String error code or null on success + */ + deserialize(value, targetObject, targetKey, root) { + targetObject[targetKey] = value; + } + + getAsJsonSchemaUncached() { + return { + type: "string", + enum: this.registry.getAllIds(), + }; + } + + verifySerializedValue(value) { + if (typeof value !== "string") { + return "Not a valid registry id key: " + value; + } + if (!this.registry.hasId(value)) { + return "Id " + value + " not known to registry"; + } + } + + getCacheKey() { + return "classid." + this.registry.getId(); + } +} + +export class TypePair extends BaseDataType { + /** + * @param {BaseDataType} type1 + * @param {BaseDataType} type2 + */ + constructor(type1, type2) { + super(); + assert(type1 && type1 instanceof BaseDataType, "bad first type given for pair"); + assert(type2 && type2 instanceof BaseDataType, "bad second type given for pair"); + this.type1 = type1; + this.type2 = type2; + } + + serialize(value) { + assert(Array.isArray(value), "pair: not an array"); + assert(value.length === 2, "pair: length != 2"); + return [this.type1.serialize(value[0]), this.type2.serialize(value[1])]; + } + + /** + * @see BaseDataType.deserialize + * @param {any} value + * @param {GameRoot} root + * @param {object} targetObject + * @param {string|number} targetKey + * @returns {string|void} String error code or null on success + */ + deserialize(value, targetObject, targetKey, root) { + const result = [undefined, undefined]; + + let errorCode = this.type1.deserialize(value[0], result, 0, root); + if (errorCode) { + return errorCode; + } + errorCode = this.type2.deserialize(value[1], result, 1, root); + if (errorCode) { + return errorCode; + } + + targetObject[targetKey] = result; + } + + getAsJsonSchemaUncached() { + return { + type: "array", + minLength: 2, + maxLength: 2, + items: [this.type1.getAsJsonSchema(), this.type2.getAsJsonSchema()], + }; + } + + verifySerializedValue(value) { + if (!Array.isArray(value)) { + return "Pair is not an array"; + } + if (value.length !== 2) { + return "Pair length != 2"; + } + let errorCode = this.type1.verifySerializedValue(value[0]); + if (errorCode) { + return errorCode; + } + errorCode = this.type2.verifySerializedValue(value[1]); + if (errorCode) { + return errorCode; + } + } + + getCacheKey() { + return "pair.(" + this.type1.getCacheKey() + "," + this.type2.getCacheKey + ")"; + } +} + +export class TypeNullable extends BaseDataType { + /** + * @param {BaseDataType} wrapped + */ + constructor(wrapped) { + super(); + this.wrapped = wrapped; + } + + serialize(value) { + if (value === null || value === undefined) { + return null; + } + return this.wrapped.serialize(value); + } + + /** + * @see BaseDataType.deserialize + * @param {any} value + * @param {GameRoot} root + * @param {object} targetObject + * @param {string|number} targetKey + * @returns {string|void} String error code or null on success + */ + deserialize(value, targetObject, targetKey, root) { + if (value === null || value === undefined) { + targetObject[targetKey] = null; + return; + } + return this.wrapped.deserialize(value, targetObject, targetKey, root); + } + + verifySerializedValue(value) { + if (value === null) { + return; + } + return this.wrapped.verifySerializedValue(value); + } + + getAsJsonSchemaUncached() { + return { + oneOf: [ + { + type: "null", + }, + this.wrapped.getAsJsonSchema(), + ], + }; + } + + allowNull() { + return true; + } + + getCacheKey() { + return "nullable." + this.wrapped.getCacheKey(); + } +} + +export class TypeStructuredObject extends BaseDataType { + /** + * @param {Object.} descriptor + */ + constructor(descriptor) { + super(); + this.descriptor = descriptor; + } + + serialize(value) { + assert(typeof value === "object", "not an object"); + let result = {}; + for (const key in this.descriptor) { + // assert(value.hasOwnProperty(key), "Serialization: Object does not have", key, "property!"); + result[key] = this.descriptor[key].serialize(value[key]); + } + return result; + } + + /** + * @see BaseDataType.deserialize + * @param {any} value + * @param {GameRoot} root + * @param {object} targetObject + * @param {string|number} targetKey + * @returns {string|void} String error code or null on success + */ + deserialize(value, targetObject, targetKey, root) { + let target = targetObject[targetKey]; + if (!target) { + targetObject[targetKey] = target = {}; + } + + for (const key in value) { + const valueType = this.descriptor[key]; + const errorCode = valueType.deserializeWithVerify(value[key], target, key, root); + if (errorCode) { + return errorCode; + } + } + } + + getAsJsonSchemaUncached() { + let properties = {}; + for (const key in this.descriptor) { + properties[key] = this.descriptor[key].getAsJsonSchema(); + } + + return { + type: "object", + required: Object.keys(this.descriptor), + properties, + }; + } + + verifySerializedValue(value) { + if (typeof value !== "object") { + return "structured object is not an object"; + } + for (const key in this.descriptor) { + if (!value.hasOwnProperty(key)) { + return "structured object is missing key " + key; + } + const subError = this.descriptor[key].verifySerializedValue(value[key]); + if (subError) { + return "structured object::" + subError; + } + } + } + + getCacheKey() { + let props = []; + for (const key in this.descriptor) { + props.push(key + "=" + this.descriptor[key].getCacheKey()); + } + return "structured[" + props.join(",") + "]"; + } +} diff --git a/src/js/savegame/serializer_internal.js b/src/js/savegame/serializer_internal.js index c75cebad..5cba58b2 100644 --- a/src/js/savegame/serializer_internal.js +++ b/src/js/savegame/serializer_internal.js @@ -1,97 +1,96 @@ -import { globalConfig } from "../core/config"; -import { createLogger } from "../core/logging"; -import { Vector } from "../core/vector"; -import { getBuildingDataFromCode } from "../game/building_codes"; -import { Entity } from "../game/entity"; -import { GameRoot } from "../game/root"; - -const logger = createLogger("serializer_internal"); - -// Internal serializer methods -export class SerializerInternal { - /** - * Serializes an array of entities - * @param {Array} array - */ - serializeEntityArray(array) { - const serialized = []; - for (let i = 0; i < array.length; ++i) { - const entity = array[i]; - if (!entity.queuedForDestroy && !entity.destroyed) { - serialized.push(entity.serialize()); - } - } - return serialized; - } - - /** - * - * @param {GameRoot} root - * @param {Array} array - * @returns {string|void} - */ - deserializeEntityArray(root, array) { - for (let i = 0; i < array.length; ++i) { - this.deserializeEntity(root, array[i]); - } - } - - /** - * - * @param {GameRoot} root - * @param {Entity} payload - */ - deserializeEntity(root, payload) { - const staticData = payload.components.StaticMapEntity; - assert(staticData, "entity has no static data"); - - const code = staticData.code; - const data = getBuildingDataFromCode(code); - - const metaBuilding = data.metaInstance; - - const entity = metaBuilding.createEntity({ - root, - origin: Vector.fromSerializedObject(staticData.origin), - rotation: staticData.rotation, - originalRotation: staticData.originalRotation, - rotationVariant: data.rotationVariant, - variant: data.variant, - }); - - entity.uid = payload.uid; - - this.deserializeComponents(root, entity, payload.components); - - root.entityMgr.registerEntity(entity, payload.uid); - root.map.placeStaticEntity(entity); - } - - /////// COMPONENTS //// - - /** - * Deserializes components of an entity - * @param {GameRoot} root - * @param {Entity} entity - * @param {Object.} data - * @returns {string|void} - */ - deserializeComponents(root, entity, data) { - for (const componentId in data) { - if (!entity.components[componentId]) { - if (G_IS_DEV && !globalConfig.debug.disableSlowAsserts) { - // @ts-ignore - if (++window.componentWarningsShown < 100) { - logger.warn("Entity no longer has component:", componentId); - } - } - continue; - } - - const errorStatus = entity.components[componentId].deserialize(data[componentId], root); - if (errorStatus) { - return errorStatus; - } - } - } -} +import { globalConfig } from "../core/config"; +import { createLogger } from "../core/logging"; +import { Vector } from "../core/vector"; +import { getBuildingDataFromCode } from "../game/building_codes"; +import { Entity } from "../game/entity"; +import { GameRoot } from "../game/root"; + +const logger = createLogger("serializer_internal"); + +// Internal serializer methods +export class SerializerInternal { + /** + * Serializes an array of entities + * @param {Map} map + */ + serializeEntityMap(map) { + const serialized = []; + for (const entity of map.values()) { + if (!entity.queuedForDestroy && !entity.destroyed) { + serialized.push(entity.serialize()); + } + } + return serialized; + } + + /** + * + * @param {GameRoot} root + * @param {Array} array + * @returns {string|void} + */ + deserializeEntityArray(root, array) { + for (let i = 0; i < array.length; ++i) { + this.deserializeEntity(root, array[i]); + } + } + + /** + * + * @param {GameRoot} root + * @param {Entity} payload + */ + deserializeEntity(root, payload) { + const staticData = payload.components.StaticMapEntity; + assert(staticData, "entity has no static data"); + + const code = staticData.code; + const data = getBuildingDataFromCode(code); + + const metaBuilding = data.metaInstance; + + const entity = metaBuilding.createEntity({ + root, + origin: Vector.fromSerializedObject(staticData.origin), + rotation: staticData.rotation, + originalRotation: staticData.originalRotation, + rotationVariant: data.rotationVariant, + variant: data.variant, + }); + + entity.uid = payload.uid; + + this.deserializeComponents(root, entity, payload.components); + + root.entityMgr.registerEntity(entity, payload.uid); + root.map.placeStaticEntity(entity); + } + + /////// COMPONENTS //// + + /** + * Deserializes components of an entity + * @param {GameRoot} root + * @param {Entity} entity + * @param {Object.} data + * @returns {string|void} + */ + deserializeComponents(root, entity, data) { + for (const componentId in data) { + if (!entity.components[componentId]) { + if (G_IS_DEV && !globalConfig.debug.disableSlowAsserts) { + // @ts-ignore + if (++window.componentWarningsShown < 100) { + logger.warn("Entity no longer has component:", componentId); + } + } + continue; + } + + const errorStatus = entity.components[componentId].deserialize(data[componentId], root); + if (errorStatus) { + return errorStatus; + } + } + } +} diff --git a/src/js/states/about.js b/src/js/states/about.js index 69f2e9b5..c36229e7 100644 --- a/src/js/states/about.js +++ b/src/js/states/about.js @@ -1,8 +1,6 @@ +import { THIRDPARTY_URLS } from "../core/config"; import { TextualGameState } from "../core/textual_game_state"; import { T } from "../translations"; -import { THIRDPARTY_URLS } from "../core/config"; -import { cachebust } from "../core/cachebust"; -import { getLogoSprite } from "../core/utils"; export class AboutState extends TextualGameState { constructor() { @@ -16,7 +14,7 @@ export class AboutState extends TextualGameState { getMainContentHTML() { return `
- shapez.io Logo + shapez.io Logo
${T.about.body diff --git a/src/js/states/changelog.js b/src/js/states/changelog.js index 4d2afa0f..683a4141 100644 --- a/src/js/states/changelog.js +++ b/src/js/states/changelog.js @@ -1,6 +1,6 @@ +import { CHANGELOG } from "../changelog"; import { TextualGameState } from "../core/textual_game_state"; import { T } from "../translations"; -import { CHANGELOG } from "../changelog"; export class ChangelogState extends TextualGameState { constructor() { @@ -19,7 +19,7 @@ export class ChangelogState extends TextualGameState { for (let i = 0; i < entries.length; ++i) { const entry = entries[i]; html += ` -
+
${entry.version} ${entry.date}
    diff --git a/src/js/states/ingame.js b/src/js/states/ingame.js index ec410b1a..34f4c1c9 100644 --- a/src/js/states/ingame.js +++ b/src/js/states/ingame.js @@ -1,518 +1,514 @@ -import { GameState } from "../core/game_state"; -import { logSection, createLogger } from "../core/logging"; -import { waitNextFrame } from "../core/utils"; -import { globalConfig } from "../core/config"; -import { GameLoadingOverlay } from "../game/game_loading_overlay"; -import { KeyActionMapper } from "../game/key_action_mapper"; -import { Savegame } from "../savegame/savegame"; -import { GameCore } from "../game/core"; -import { MUSIC } from "../platform/sound"; -import { enumGameModeIds } from "../game/game_mode"; -import { MOD_SIGNALS } from "../mods/mod_signals"; -import { HUDModalDialogs } from "../game/hud/parts/modal_dialogs"; -import { T } from "../translations"; - -const logger = createLogger("state/ingame"); - -// Different sub-states -export const GAME_LOADING_STATES = { - s3_createCore: "s3_createCore", - s4_A_initEmptyGame: "s4_A_initEmptyGame", - s4_B_resumeGame: "s4_B_resumeGame", - - s5_firstUpdate: "s5_firstUpdate", - s6_postLoadHook: "s6_postLoadHook", - s7_warmup: "s7_warmup", - - s10_gameRunning: "s10_gameRunning", - - leaving: "leaving", - destroyed: "destroyed", - initFailed: "initFailed", -}; - -export const gameCreationAction = { - new: "new-game", - resume: "resume-game", -}; - -// Typehints -export class GameCreationPayload { - constructor() { - /** @type {boolean|undefined} */ - this.fastEnter; - - /** @type {string} */ - this.gameModeId; - - /** @type {Savegame} */ - this.savegame; - - /** @type {object|undefined} */ - this.gameModeParameters; - } -} - -export class InGameState extends GameState { - constructor() { - super("InGameState"); - - /** @type {GameCreationPayload} */ - this.creationPayload = null; - - // Stores current stage - this.stage = ""; - - /** @type {GameCore} */ - this.core = null; - - /** @type {KeyActionMapper} */ - this.keyActionMapper = null; - - /** @type {GameLoadingOverlay} */ - this.loadingOverlay = null; - - /** @type {Savegame} */ - this.savegame = null; - - this.boundInputFilter = this.filterInput.bind(this); - - /** - * Whether we are currently saving the game - * @TODO: This doesn't realy fit here - */ - this.currentSavePromise = null; - } - - get dialogs() { - return this.core.root.hud.parts.dialogs; - } - - /** - * Switches the game into another sub-state - * @param {string} stage - */ - switchStage(stage) { - assert(stage, "Got empty stage"); - if (stage !== this.stage) { - this.stage = stage; - logger.log(this.stage); - MOD_SIGNALS.gameLoadingStageEntered.dispatch(this, stage); - return true; - } else { - // log(this, "Re entering", stage); - return false; - } - } - - // GameState implementation - getInnerHTML() { - return ""; - } - - onAppPause() { - // if (this.stage === stages.s10_gameRunning) { - // logger.log("Saving because app got paused"); - // this.doSave(); - // } - } - - getHasFadeIn() { - return false; - } - - getPauseOnFocusLost() { - return false; - } - - getHasUnloadConfirmation() { - return true; - } - - onLeave() { - if (this.core) { - this.stageDestroyed(); - } - this.app.inputMgr.dismountFilter(this.boundInputFilter); - } - - onResized(w, h) { - super.onResized(w, h); - if (this.stage === GAME_LOADING_STATES.s10_gameRunning) { - this.core.resize(w, h); - } - } - - // ---- End of GameState implementation - - /** - * Goes back to the menu state - */ - goBackToMenu() { - if ([enumGameModeIds.puzzleEdit, enumGameModeIds.puzzlePlay].includes(this.gameModeId)) { - this.saveThenGoToState("PuzzleMenuState"); - } else { - this.saveThenGoToState("MainMenuState"); - } - } - - /** - * Goes back to the settings state - */ - goToSettings() { - this.saveThenGoToState("SettingsState", { - backToStateId: this.key, - backToStatePayload: this.creationPayload, - }); - } - - /** - * Goes back to the settings state - */ - goToKeybindings() { - this.saveThenGoToState("KeybindingsState", { - backToStateId: this.key, - backToStatePayload: this.creationPayload, - }); - } - - /** - * Moves to a state outside of the game - * @param {string} stateId - * @param {any=} payload - */ - saveThenGoToState(stateId, payload) { - if (this.stage === GAME_LOADING_STATES.leaving || this.stage === GAME_LOADING_STATES.destroyed) { - logger.warn( - "Tried to leave game twice or during destroy:", - this.stage, - "(attempted to move to", - stateId, - ")" - ); - return; - } - this.stageLeavingGame(); - this.doSave().then(() => { - this.stageDestroyed(); - this.moveToState(stateId, payload); - }); - } - - onBackButton() { - // do nothing - } - - getIsIngame() { - return ( - this.stage === GAME_LOADING_STATES.s10_gameRunning && - this.core && - !this.core.root.hud.shouldPauseGame() - ); - } - - /** - * Called when the game somehow failed to initialize. Resets everything to basic state and - * then goes to the main menu, showing the error - * @param {string} err - */ - onInitializationFailure(err) { - if (this.switchStage(GAME_LOADING_STATES.initFailed)) { - logger.error("Init failure:", err); - this.stageDestroyed(); - this.moveToState("MainMenuState", { loadError: err }); - } - } - - // STAGES - - /** - * Creates the game core instance, and thus the root - */ - stage3CreateCore() { - if (this.switchStage(GAME_LOADING_STATES.s3_createCore)) { - logger.log("Waiting for resources to load"); - - this.app.backgroundResourceLoader.resourceStateChangedSignal.add(({ progress }) => { - this.loadingOverlay.loadingIndicator.innerText = T.global.loadingResources.replace( - "", - (progress * 100.0).toFixed(1) - ); - }); - - this.app.backgroundResourceLoader.getIngamePromise().then( - () => { - if ( - this.creationPayload.gameModeId && - this.creationPayload.gameModeId.includes("puzzle") - ) { - this.app.sound.playThemeMusic(MUSIC.puzzle); - } else { - this.app.sound.playThemeMusic(MUSIC.theme); - } - - this.loadingOverlay.loadingIndicator.innerText = ""; - this.app.backgroundResourceLoader.resourceStateChangedSignal.removeAll(); - - logger.log("Creating new game core"); - this.core = new GameCore(this.app); - - this.core.initializeRoot(this, this.savegame, this.gameModeId); - - if (this.savegame.hasGameDump()) { - this.stage4bResumeGame(); - } else { - this.app.gameAnalytics.handleGameStarted(); - this.stage4aInitEmptyGame(); - } - }, - err => { - logger.error("Failed to preload resources:", err); - const dialogs = new HUDModalDialogs(null, this.app); - const dialogsElement = document.createElement("div"); - dialogsElement.id = "ingame_HUD_ModalDialogs"; - dialogsElement.style.zIndex = "999999"; - document.body.appendChild(dialogsElement); - dialogs.initializeToElement(dialogsElement); - - this.app.backgroundResourceLoader.showLoaderError(dialogs, err); - } - ); - } - } - - /** - * Initializes a new empty game - */ - stage4aInitEmptyGame() { - if (this.switchStage(GAME_LOADING_STATES.s4_A_initEmptyGame)) { - this.core.initNewGame(); - this.stage5FirstUpdate(); - } - } - - /** - * Resumes an existing game - */ - stage4bResumeGame() { - if (this.switchStage(GAME_LOADING_STATES.s4_B_resumeGame)) { - if (!this.core.initExistingGame()) { - this.onInitializationFailure("Savegame is corrupt and can not be restored."); - return; - } - this.app.gameAnalytics.handleGameResumed(); - this.stage5FirstUpdate(); - } - } - - /** - * Performs the first game update on the game which initializes most caches - */ - stage5FirstUpdate() { - if (this.switchStage(GAME_LOADING_STATES.s5_firstUpdate)) { - this.core.root.logicInitialized = true; - this.core.updateLogic(); - this.stage6PostLoadHook(); - } - } - - /** - * Call the post load hook, this means that we have loaded the game, and all systems - * can operate and start to work now. - */ - stage6PostLoadHook() { - if (this.switchStage(GAME_LOADING_STATES.s6_postLoadHook)) { - logger.log("Post load hook"); - this.core.postLoadHook(); - this.stage7Warmup(); - } - } - - /** - * This makes the game idle and draw for a while, because we run most code this way - * the V8 engine can already start to optimize it. Also this makes sure the resources - * are in the VRAM and we have a smooth experience once we start. - */ - stage7Warmup() { - if (this.switchStage(GAME_LOADING_STATES.s7_warmup)) { - if (this.creationPayload.fastEnter) { - this.warmupTimeSeconds = globalConfig.warmupTimeSecondsFast; - } else { - this.warmupTimeSeconds = globalConfig.warmupTimeSecondsRegular; - } - } - } - - /** - * The final stage where this game is running and updating regulary. - */ - stage10GameRunning() { - if (this.switchStage(GAME_LOADING_STATES.s10_gameRunning)) { - this.core.root.signals.readyToRender.dispatch(); - - logSection("GAME STARTED", "#26a69a"); - - // Initial resize, might have changed during loading (this is possible) - this.core.resize(this.app.screenWidth, this.app.screenHeight); - - MOD_SIGNALS.gameStarted.dispatch(this.core.root); - } - } - - /** - * This stage destroys the whole game, used to cleanup - */ - stageDestroyed() { - if (this.switchStage(GAME_LOADING_STATES.destroyed)) { - // Cleanup all api calls - this.cancelAllAsyncOperations(); - - if (this.syncer) { - this.syncer.cancelSync(); - this.syncer = null; - } - - // Cleanup core - if (this.core) { - this.core.destruct(); - this.core = null; - } - } - } - - /** - * When leaving the game - */ - stageLeavingGame() { - if (this.switchStage(GAME_LOADING_STATES.leaving)) { - // ... - } - } - - // END STAGES - - /** - * Filters the input (keybindings) - */ - filterInput() { - return this.stage === GAME_LOADING_STATES.s10_gameRunning; - } - - /** - * @param {GameCreationPayload} payload - */ - onEnter(payload) { - this.app.inputMgr.installFilter(this.boundInputFilter); - - this.creationPayload = payload; - this.savegame = payload.savegame; - this.gameModeId = payload.gameModeId; - - this.loadingOverlay = new GameLoadingOverlay(this.app, this.getDivElement()); - this.loadingOverlay.showBasic(); - - // Remove unneded default element - document.body.querySelector(".modalDialogParent").remove(); - - this.asyncChannel - .watch(waitNextFrame()) - .then(() => this.stage3CreateCore()) - .catch(ex => { - logger.error(ex); - throw ex; - }); - } - - /** - * Render callback - * @param {number} dt - */ - onRender(dt) { - if (window.APP_ERROR_OCCURED) { - // Application somehow crashed, do not do anything - return; - } - - if (this.stage === GAME_LOADING_STATES.s7_warmup) { - this.core.draw(); - this.warmupTimeSeconds -= dt / 1000.0; - if (this.warmupTimeSeconds < 0) { - logger.log("Warmup completed"); - this.stage10GameRunning(); - } - } - - if (this.stage === GAME_LOADING_STATES.s10_gameRunning) { - this.core.tick(dt); - } - - // If the stage is still active (This might not be the case if tick() moved us to game over) - if (this.stage === GAME_LOADING_STATES.s10_gameRunning) { - // Only draw if page visible - if (this.app.pageVisible) { - this.core.draw(); - } - - this.loadingOverlay.removeIfAttached(); - } else { - if (!this.loadingOverlay.isAttached()) { - this.loadingOverlay.showBasic(); - } - } - } - - onBackgroundTick(dt) { - this.onRender(dt); - } - - /** - * Saves the game - */ - - doSave() { - if (!this.savegame || !this.savegame.isSaveable()) { - return Promise.resolve(); - } - - if (window.APP_ERROR_OCCURED) { - logger.warn("skipping save because application crashed"); - return Promise.resolve(); - } - - if ( - this.stage !== GAME_LOADING_STATES.s10_gameRunning && - this.stage !== GAME_LOADING_STATES.s7_warmup && - this.stage !== GAME_LOADING_STATES.leaving - ) { - logger.warn("Skipping save because game is not ready"); - return Promise.resolve(); - } - - if (this.currentSavePromise) { - logger.warn("Skipping double save and returning same promise"); - return this.currentSavePromise; - } - - if (!this.core.root.gameMode.getIsSaveable()) { - return Promise.resolve(); - } - - logger.log("Starting to save game ..."); - this.savegame.updateData(this.core.root); - - this.currentSavePromise = this.savegame - .writeSavegameAndMetadata() - .catch(err => { - // Catch errors - logger.warn("Failed to save:", err); - }) - .then(() => { - // Clear promise - logger.log("Saved!"); - this.core.root.signals.gameSaved.dispatch(); - this.currentSavePromise = null; - }); - - return this.currentSavePromise; - } -} +import { globalConfig } from "../core/config"; +import { GameState } from "../core/game_state"; +import { createLogger, logSection } from "../core/logging"; +import { waitNextFrame } from "../core/utils"; +import { GameCore } from "../game/core"; +import { GameLoadingOverlay } from "../game/game_loading_overlay"; +import { enumGameModeIds } from "../game/game_mode"; +import { HUDModalDialogs } from "../game/hud/parts/modal_dialogs"; +import { KeyActionMapper } from "../game/key_action_mapper"; +import { MOD_SIGNALS } from "../mods/mod_signals"; +import { MUSIC } from "../platform/sound"; +import { Savegame } from "../savegame/savegame"; +import { T } from "../translations"; + +const logger = createLogger("state/ingame"); + +// Different sub-states +export const GAME_LOADING_STATES = { + s3_createCore: "s3_createCore", + s4_A_initEmptyGame: "s4_A_initEmptyGame", + s4_B_resumeGame: "s4_B_resumeGame", + + s5_firstUpdate: "s5_firstUpdate", + s6_postLoadHook: "s6_postLoadHook", + s7_warmup: "s7_warmup", + + s10_gameRunning: "s10_gameRunning", + + leaving: "leaving", + destroyed: "destroyed", + initFailed: "initFailed", +}; + +export const gameCreationAction = { + new: "new-game", + resume: "resume-game", +}; + +// Typehints +export class GameCreationPayload { + constructor() { + /** @type {boolean|undefined} */ + this.fastEnter; + + /** @type {string} */ + this.gameModeId; + + /** @type {Savegame} */ + this.savegame; + + /** @type {object|undefined} */ + this.gameModeParameters; + } +} + +export class InGameState extends GameState { + constructor() { + super("InGameState"); + + /** @type {GameCreationPayload} */ + this.creationPayload = null; + + // Stores current stage + this.stage = ""; + + /** @type {GameCore} */ + this.core = null; + + /** @type {KeyActionMapper} */ + this.keyActionMapper = null; + + /** @type {GameLoadingOverlay} */ + this.loadingOverlay = null; + + /** @type {Savegame} */ + this.savegame = null; + + this.boundInputFilter = this.filterInput.bind(this); + + /** + * Whether we are currently saving the game + * @TODO: This doesn't realy fit here + */ + this.currentSavePromise = null; + } + + get dialogs() { + return this.core.root.hud.parts.dialogs; + } + + /** + * Switches the game into another sub-state + * @param {string} stage + */ + switchStage(stage) { + assert(stage, "Got empty stage"); + if (stage !== this.stage) { + this.stage = stage; + logger.log(this.stage); + MOD_SIGNALS.gameLoadingStageEntered.dispatch(this, stage); + return true; + } else { + // log(this, "Re entering", stage); + return false; + } + } + + // GameState implementation + getInnerHTML() { + return ""; + } + + onAppPause() { + // if (this.stage === stages.s10_gameRunning) { + // logger.log("Saving because app got paused"); + // this.doSave(); + // } + } + + getHasFadeIn() { + return false; + } + + getPauseOnFocusLost() { + return false; + } + + getHasUnloadConfirmation() { + return true; + } + + onLeave() { + if (this.core) { + this.stageDestroyed(); + } + this.app.inputMgr.dismountFilter(this.boundInputFilter); + } + + onResized(w, h) { + super.onResized(w, h); + if (this.stage === GAME_LOADING_STATES.s10_gameRunning) { + this.core.resize(w, h); + } + } + + // ---- End of GameState implementation + + /** + * Goes back to the menu state + */ + goBackToMenu() { + if ([enumGameModeIds.puzzleEdit, enumGameModeIds.puzzlePlay].includes(this.gameModeId)) { + this.saveThenGoToState("PuzzleMenuState"); + } else { + this.saveThenGoToState("MainMenuState"); + } + } + + /** + * Goes back to the settings state + */ + goToSettings() { + this.saveThenGoToState("SettingsState", { + backToStateId: this.key, + backToStatePayload: this.creationPayload, + }); + } + + /** + * Goes back to the settings state + */ + goToKeybindings() { + this.saveThenGoToState("KeybindingsState", { + backToStateId: this.key, + backToStatePayload: this.creationPayload, + }); + } + + /** + * Moves to a state outside of the game + * @param {string} stateId + * @param {any=} payload + */ + saveThenGoToState(stateId, payload) { + if (this.stage === GAME_LOADING_STATES.leaving || this.stage === GAME_LOADING_STATES.destroyed) { + logger.warn( + "Tried to leave game twice or during destroy:", + this.stage, + "(attempted to move to", + stateId, + ")" + ); + return; + } + this.stageLeavingGame(); + this.doSave().then(() => { + this.moveToState(stateId, payload); + }); + } + + onBackButton() { + // do nothing + } + + getIsIngame() { + return ( + this.stage === GAME_LOADING_STATES.s10_gameRunning && + this.core && + !this.core.root.hud.shouldPauseGame() + ); + } + + /** + * Called when the game somehow failed to initialize. Resets everything to basic state and + * then goes to the main menu, showing the error + * @param {string} err + */ + onInitializationFailure(err) { + if (this.switchStage(GAME_LOADING_STATES.initFailed)) { + logger.error("Init failure:", err); + this.moveToState("MainMenuState", { loadError: err }); + } + } + + // STAGES + + /** + * Creates the game core instance, and thus the root + */ + stage3CreateCore() { + if (this.switchStage(GAME_LOADING_STATES.s3_createCore)) { + logger.log("Waiting for resources to load"); + + this.app.backgroundResourceLoader.resourceStateChangedSignal.add(({ progress }) => { + this.loadingOverlay.loadingIndicator.innerText = T.global.loadingResources.replace( + "", + (progress * 100.0).toFixed(1) + ); + }); + + this.app.backgroundResourceLoader.getIngamePromise().then( + () => { + if ( + this.creationPayload.gameModeId && + this.creationPayload.gameModeId.includes("puzzle") + ) { + this.app.sound.playThemeMusic(MUSIC.puzzle); + } else { + this.app.sound.playThemeMusic(MUSIC.theme); + } + + this.loadingOverlay.loadingIndicator.innerText = ""; + this.app.backgroundResourceLoader.resourceStateChangedSignal.removeAll(); + + logger.log("Creating new game core"); + this.core = new GameCore(this.app); + + this.core.initializeRoot(this, this.savegame, this.gameModeId); + + if (this.savegame.hasGameDump()) { + this.stage4bResumeGame(); + } else { + this.stage4aInitEmptyGame(); + } + }, + err => { + logger.error("Failed to preload resources:", err); + const dialogs = new HUDModalDialogs(null, this.app); + const dialogsElement = document.createElement("div"); + dialogsElement.id = "ingame_HUD_ModalDialogs"; + dialogsElement.style.zIndex = "999999"; + document.body.appendChild(dialogsElement); + dialogs.initializeToElement(dialogsElement); + + this.app.backgroundResourceLoader.showLoaderError(dialogs, err); + } + ); + } + } + + /** + * Initializes a new empty game + */ + stage4aInitEmptyGame() { + if (this.switchStage(GAME_LOADING_STATES.s4_A_initEmptyGame)) { + this.core.initNewGame(); + this.stage5FirstUpdate(); + } + } + + /** + * Resumes an existing game + */ + stage4bResumeGame() { + if (this.switchStage(GAME_LOADING_STATES.s4_B_resumeGame)) { + if (!this.core.initExistingGame()) { + this.onInitializationFailure("Savegame is corrupt and can not be restored."); + return; + } + this.stage5FirstUpdate(); + } + } + + /** + * Performs the first game update on the game which initializes most caches + */ + stage5FirstUpdate() { + if (this.switchStage(GAME_LOADING_STATES.s5_firstUpdate)) { + this.core.root.logicInitialized = true; + this.core.updateLogic(); + this.stage6PostLoadHook(); + } + } + + /** + * Call the post load hook, this means that we have loaded the game, and all systems + * can operate and start to work now. + */ + stage6PostLoadHook() { + if (this.switchStage(GAME_LOADING_STATES.s6_postLoadHook)) { + logger.log("Post load hook"); + this.core.postLoadHook(); + this.stage7Warmup(); + } + } + + /** + * This makes the game idle and draw for a while, because we run most code this way + * the V8 engine can already start to optimize it. Also this makes sure the resources + * are in the VRAM and we have a smooth experience once we start. + */ + stage7Warmup() { + if (this.switchStage(GAME_LOADING_STATES.s7_warmup)) { + if (this.creationPayload.fastEnter) { + this.warmupTimeSeconds = globalConfig.warmupTimeSecondsFast; + } else { + this.warmupTimeSeconds = globalConfig.warmupTimeSecondsRegular; + } + } + } + + /** + * The final stage where this game is running and updating regulary. + */ + stage10GameRunning() { + if (this.switchStage(GAME_LOADING_STATES.s10_gameRunning)) { + this.core.root.signals.readyToRender.dispatch(); + + logSection("GAME STARTED", "#26a69a"); + + // Initial resize, might have changed during loading (this is possible) + this.core.resize(this.app.screenWidth, this.app.screenHeight); + + MOD_SIGNALS.gameStarted.dispatch(this.core.root); + } + } + + /** + * This stage destroys the whole game, used to cleanup + */ + stageDestroyed() { + if (this.switchStage(GAME_LOADING_STATES.destroyed)) { + // Cleanup all api calls + this.cancelAllAsyncOperations(); + + if (this.syncer) { + this.syncer.cancelSync(); + this.syncer = null; + } + + // Cleanup core + if (this.core) { + this.core.destruct(); + this.core = null; + } + } + } + + /** + * When leaving the game + */ + stageLeavingGame() { + if (this.switchStage(GAME_LOADING_STATES.leaving)) { + // ... + } + } + + // END STAGES + + /** + * Filters the input (keybindings) + */ + filterInput() { + return this.stage === GAME_LOADING_STATES.s10_gameRunning; + } + + /** + * @param {GameCreationPayload} payload + */ + onEnter(payload) { + this.app.inputMgr.installFilter(this.boundInputFilter); + + this.creationPayload = payload; + this.savegame = payload.savegame; + this.gameModeId = payload.gameModeId; + + this.loadingOverlay = new GameLoadingOverlay(this.app, this.getDivElement()); + this.loadingOverlay.showBasic(); + + // Remove unneded default element + document.body.querySelector(".modalDialogParent").remove(); + + this.asyncChannel + .watch(waitNextFrame()) + .then(() => this.stage3CreateCore()) + .catch(ex => { + logger.error(ex); + throw ex; + }); + } + + /** + * Render callback + * @param {number} dt + */ + onRender(dt) { + if (window.APP_ERROR_OCCURED) { + // Application somehow crashed, do not do anything + return; + } + + if (this.stage === GAME_LOADING_STATES.s7_warmup) { + this.core.draw(); + this.warmupTimeSeconds -= dt / 1000.0; + if (this.warmupTimeSeconds < 0) { + logger.log("Warmup completed"); + this.stage10GameRunning(); + } + } + + if (this.stage === GAME_LOADING_STATES.s10_gameRunning) { + this.core.tick(dt); + } + + // If the stage is still active (This might not be the case if tick() moved us to game over) + if (this.stage === GAME_LOADING_STATES.s10_gameRunning) { + // Only draw if page visible + if (this.app.pageVisible) { + this.core.draw(); + } + + this.loadingOverlay.removeIfAttached(); + } else { + if (!this.loadingOverlay.isAttached()) { + this.loadingOverlay.showBasic(); + } + } + } + + onBackgroundTick(dt) { + this.onRender(dt); + } + + /** + * Saves the game + */ + + doSave() { + if (!this.savegame || !this.savegame.isSaveable()) { + return Promise.resolve(); + } + + if (window.APP_ERROR_OCCURED) { + logger.warn("skipping save because application crashed"); + return Promise.resolve(); + } + + if ( + this.stage !== GAME_LOADING_STATES.s10_gameRunning && + this.stage !== GAME_LOADING_STATES.s7_warmup && + this.stage !== GAME_LOADING_STATES.leaving + ) { + logger.warn("Skipping save because game is not ready"); + return Promise.resolve(); + } + + if (this.currentSavePromise) { + logger.warn("Skipping double save and returning same promise"); + return this.currentSavePromise; + } + + if (!this.core.root.gameMode.getIsSaveable()) { + return Promise.resolve(); + } + + logger.log("Starting to save game ..."); + this.savegame.updateData(this.core.root); + + this.currentSavePromise = this.savegame + .writeSavegameAndMetadata() + .catch(err => { + // Catch errors + logger.warn("Failed to save:", err); + }) + .then(() => { + // Clear promise + logger.log("Saved!"); + this.core.root.signals.gameSaved.dispatch(); + this.currentSavePromise = null; + }); + + return this.currentSavePromise; + } +} diff --git a/src/js/states/keybindings.js b/src/js/states/keybindings.js index e6721bf8..bb1afe6c 100644 --- a/src/js/states/keybindings.js +++ b/src/js/states/keybindings.js @@ -1,189 +1,194 @@ -import { Dialog } from "../core/modal_dialog_elements"; -import { TextualGameState } from "../core/textual_game_state"; -import { getStringForKeyCode, KEYMAPPINGS } from "../game/key_action_mapper"; -import { SOUNDS } from "../platform/sound"; -import { T } from "../translations"; - -export class KeybindingsState extends TextualGameState { - constructor() { - super("KeybindingsState"); - } - - getStateHeaderTitle() { - return T.keybindings.title; - } - - getMainContentHTML() { - return ` - -
    - ${T.keybindings.hint} - - -
    - -
    - -
    - `; - } - - onEnter() { - const keybindingsElem = this.htmlElement.querySelector(".keybindings"); - - this.trackClicks(this.htmlElement.querySelector(".resetBindings"), this.resetBindings); - - for (const category in KEYMAPPINGS) { - if (Object.keys(KEYMAPPINGS[category]).length === 0) { - continue; - } - - const categoryDiv = document.createElement("div"); - categoryDiv.classList.add("category"); - keybindingsElem.appendChild(categoryDiv); - - const labelDiv = document.createElement("strong"); - labelDiv.innerText = T.keybindings.categoryLabels[category]; - labelDiv.classList.add("categoryLabel"); - categoryDiv.appendChild(labelDiv); - - for (const keybindingId in KEYMAPPINGS[category]) { - const mapped = KEYMAPPINGS[category][keybindingId]; - - const elem = document.createElement("div"); - elem.classList.add("entry"); - elem.setAttribute("data-keybinding", keybindingId); - categoryDiv.appendChild(elem); - - const title = document.createElement("span"); - title.classList.add("title"); - title.innerText = T.keybindings.mappings[keybindingId]; - elem.appendChild(title); - - const mappingDiv = document.createElement("span"); - mappingDiv.classList.add("mapping"); - elem.appendChild(mappingDiv); - - const editBtn = document.createElement("button"); - editBtn.classList.add("styledButton", "editKeybinding"); - - const resetBtn = document.createElement("button"); - resetBtn.classList.add("styledButton", "resetKeybinding"); - - if (mapped.builtin) { - editBtn.classList.add("disabled"); - resetBtn.classList.add("disabled"); - } else { - this.trackClicks(editBtn, () => this.editKeybinding(keybindingId)); - this.trackClicks(resetBtn, () => this.resetKeybinding(keybindingId)); - } - elem.appendChild(editBtn); - elem.appendChild(resetBtn); - } - } - this.updateKeybindings(); - } - - editKeybinding(id) { - const dialog = new Dialog({ - app: this.app, - title: T.dialogs.editKeybinding.title, - contentHTML: T.dialogs.editKeybinding.desc, - buttons: ["cancel:good"], - type: "info", - }); - - dialog.inputReciever.keydown.add(({ keyCode, shift, alt, event }) => { - if (keyCode === 27) { - this.dialogs.closeDialog(dialog); - return; - } - - if (event) { - event.preventDefault(); - } - - if (event.target && event.target.tagName === "BUTTON" && keyCode === 1) { - return; - } - - if ( - // Enter - keyCode === 13 - ) { - // Ignore builtins - return; - } - - this.app.settings.updateKeybindingOverride(id, keyCode); - - this.dialogs.closeDialog(dialog); - this.updateKeybindings(); - }); - - dialog.inputReciever.backButton.add(() => {}); - this.dialogs.internalShowDialog(dialog); - - this.app.sound.playUiSound(SOUNDS.dialogOk); - } - - updateKeybindings() { - const overrides = this.app.settings.getKeybindingOverrides(); - for (const category in KEYMAPPINGS) { - for (const keybindingId in KEYMAPPINGS[category]) { - const mapped = KEYMAPPINGS[category][keybindingId]; - - const container = this.htmlElement.querySelector("[data-keybinding='" + keybindingId + "']"); - assert(container, "Container for keybinding not found: " + keybindingId); - - let keyCode = mapped.keyCode; - if (overrides[keybindingId]) { - keyCode = overrides[keybindingId]; - } - - const mappingDiv = container.querySelector(".mapping"); - let modifiers = ""; - - if (mapped.modifiers && mapped.modifiers.shift) { - modifiers += "⇪ "; - } - if (mapped.modifiers && mapped.modifiers.alt) { - modifiers += T.global.keys.alt + " "; - } - if (mapped.modifiers && mapped.modifiers.ctrl) { - modifiers += T.global.keys.control + " "; - } - - mappingDiv.innerHTML = modifiers + getStringForKeyCode(keyCode); - mappingDiv.classList.toggle("changed", !!overrides[keybindingId]); - - const resetBtn = container.querySelector("button.resetKeybinding"); - resetBtn.classList.toggle("disabled", mapped.builtin || !overrides[keybindingId]); - } - } - } - - resetKeybinding(id) { - this.app.settings.resetKeybindingOverride(id); - this.updateKeybindings(); - } - - resetBindings() { - const { reset } = this.dialogs.showWarning( - T.dialogs.resetKeybindingsConfirmation.title, - T.dialogs.resetKeybindingsConfirmation.desc, - ["cancel:good", "reset:bad"] - ); - - reset.add(() => { - this.app.settings.resetKeybindingOverrides(); - this.updateKeybindings(); - - this.dialogs.showInfo(T.dialogs.keybindingsResetOk.title, T.dialogs.keybindingsResetOk.desc); - }); - } - - getDefaultPreviousState() { - return "SettingsState"; - } -} +import { getStringForKeyCode } from "@/core/keycodes"; +import { Dialog } from "../core/modal_dialog_elements"; +import { TextualGameState } from "../core/textual_game_state"; +import { KEYMAPPINGS } from "../game/key_action_mapper"; +import { SOUNDS } from "../platform/sound"; +import { T } from "../translations"; + +export class KeybindingsState extends TextualGameState { + constructor() { + super("KeybindingsState"); + } + + getStateHeaderTitle() { + return T.keybindings.title; + } + + getMainContentHTML() { + return ` + +
    + ${T.keybindings.hint} + + +
    + +
    + +
    + `; + } + + onEnter() { + const keybindingsElem = this.htmlElement.querySelector(".keybindings"); + + this.trackClicks(this.htmlElement.querySelector(".resetBindings"), this.resetBindings); + + for (const category in KEYMAPPINGS) { + if (Object.keys(KEYMAPPINGS[category]).length === 0) { + continue; + } + + const categoryDiv = document.createElement("div"); + categoryDiv.classList.add("category"); + keybindingsElem.appendChild(categoryDiv); + + const labelDiv = document.createElement("strong"); + labelDiv.innerText = T.keybindings.categoryLabels[category]; + labelDiv.classList.add("categoryLabel"); + categoryDiv.appendChild(labelDiv); + + for (const keybindingId in KEYMAPPINGS[category]) { + const mapped = KEYMAPPINGS[category][keybindingId]; + + const elem = document.createElement("div"); + elem.classList.add("entry"); + elem.setAttribute("data-keybinding", keybindingId); + categoryDiv.appendChild(elem); + + const title = document.createElement("span"); + title.classList.add("title"); + title.innerText = T.keybindings.mappings[keybindingId]; + elem.appendChild(title); + + const mappingDiv = document.createElement("span"); + mappingDiv.classList.add("mapping"); + elem.appendChild(mappingDiv); + + const editBtn = document.createElement("button"); + editBtn.classList.add("styledButton", "editKeybinding"); + + const resetBtn = document.createElement("button"); + resetBtn.classList.add("styledButton", "resetKeybinding"); + + if (mapped.builtin) { + editBtn.classList.add("disabled"); + resetBtn.classList.add("disabled"); + } else { + this.trackClicks(editBtn, () => this.editKeybinding(keybindingId)); + this.trackClicks(resetBtn, () => this.resetKeybinding(keybindingId)); + } + elem.appendChild(editBtn); + elem.appendChild(resetBtn); + } + } + this.updateKeybindings(); + } + + editKeybinding(id) { + const dialog = new Dialog({ + app: this.app, + title: T.dialogs.editKeybinding.title, + contentHTML: T.dialogs.editKeybinding.desc, + buttons: ["cancel:good"], + type: "info", + }); + + dialog.inputReceiver.keydown.add(({ keyCode, shift, alt, event }) => { + if (keyCode === 27) { + this.dialogs.closeDialog(dialog); + return; + } + + if (event) { + event.preventDefault(); + } + + if ( + event.target && + /** @type {HTMLElement} */ (event.target).tagName === "BUTTON" && + keyCode === 1 + ) { + return; + } + + if ( + // Enter + keyCode === 13 + ) { + // Ignore builtins + return; + } + + this.app.settings.updateKeybindingOverride(id, keyCode); + + this.dialogs.closeDialog(dialog); + this.updateKeybindings(); + }); + + dialog.inputReceiver.backButton.add(() => {}); + this.dialogs.internalShowDialog(dialog); + + this.app.sound.playUiSound(SOUNDS.dialogOk); + } + + updateKeybindings() { + const overrides = this.app.settings.getKeybindingOverrides(); + for (const category in KEYMAPPINGS) { + for (const keybindingId in KEYMAPPINGS[category]) { + const mapped = KEYMAPPINGS[category][keybindingId]; + + const container = this.htmlElement.querySelector("[data-keybinding='" + keybindingId + "']"); + assert(container, "Container for keybinding not found: " + keybindingId); + + let keyCode = mapped.keyCode; + if (overrides[keybindingId]) { + keyCode = overrides[keybindingId]; + } + + const mappingDiv = container.querySelector(".mapping"); + let modifiers = ""; + + if (mapped.modifiers && mapped.modifiers.shift) { + modifiers += "⇪ "; + } + if (mapped.modifiers && mapped.modifiers.alt) { + modifiers += T.global.keys.alt + " "; + } + if (mapped.modifiers && mapped.modifiers.ctrl) { + modifiers += T.global.keys.control + " "; + } + + mappingDiv.innerHTML = modifiers + getStringForKeyCode(keyCode); + mappingDiv.classList.toggle("changed", !!overrides[keybindingId]); + + const resetBtn = container.querySelector("button.resetKeybinding"); + resetBtn.classList.toggle("disabled", mapped.builtin || !overrides[keybindingId]); + } + } + } + + resetKeybinding(id) { + this.app.settings.resetKeybindingOverride(id); + this.updateKeybindings(); + } + + resetBindings() { + const { reset } = this.dialogs.showWarning( + T.dialogs.resetKeybindingsConfirmation.title, + T.dialogs.resetKeybindingsConfirmation.desc, + ["cancel:good", "reset:bad"] + ); + + reset.add(() => { + this.app.settings.resetKeybindingOverrides(); + this.updateKeybindings(); + + this.dialogs.showInfo(T.dialogs.keybindingsResetOk.title, T.dialogs.keybindingsResetOk.desc); + }); + } + + getDefaultPreviousState() { + return "SettingsState"; + } +} diff --git a/src/js/states/login.js b/src/js/states/login.js index cd8d8007..a529fe92 100644 --- a/src/js/states/login.js +++ b/src/js/states/login.js @@ -54,7 +54,9 @@ export class LoginState extends GameState { T.dialogs.offlineMode.desc, ["retry", "playOffline:bad"] ); - signals.retry.add(() => setTimeout(() => this.tryLogin(), 2000), this); + signals.retry.add(() => { + setTimeout(() => this.tryLogin(), 2000); + }, this); signals.playOffline.add(this.finishLoading, this); } else { this.finishLoading(); diff --git a/src/js/states/main_menu.js b/src/js/states/main_menu.js index 1987d0a2..9931bd67 100644 --- a/src/js/states/main_menu.js +++ b/src/js/states/main_menu.js @@ -1,987 +1,630 @@ -import { cachebust } from "../core/cachebust"; -import { globalConfig, openStandaloneLink, THIRDPARTY_URLS } from "../core/config"; -import { GameState } from "../core/game_state"; -import { DialogWithForm } from "../core/modal_dialog_elements"; -import { FormElementInput } from "../core/modal_dialog_forms"; -import { ReadWriteProxy } from "../core/read_write_proxy"; -import { STOP_PROPAGATION } from "../core/signal"; -import { WEB_STEAM_SSO_AUTHENTICATED } from "../core/steam_sso"; -import { - formatSecondsToTimeAgo, - generateFileDownload, - getLogoSprite, - makeButton, - makeDiv, - makeDivElement, - removeAllChildren, - startFileChoose, - waitNextFrame, -} from "../core/utils"; -import { HUDModalDialogs } from "../game/hud/parts/modal_dialogs"; -import { MODS } from "../mods/modloader"; -import { PlatformWrapperImplBrowser } from "../platform/browser/wrapper"; -import { PlatformWrapperImplElectron } from "../platform/electron/wrapper"; -import { Savegame } from "../savegame/savegame"; -import { T } from "../translations"; - -const trim = require("trim"); - -/** - * @typedef {import("../savegame/savegame_typedefs").SavegameMetadata} SavegameMetadata - * @typedef {import("../profile/setting_types").EnumSetting} EnumSetting - */ - -export class MainMenuState extends GameState { - constructor() { - super("MainMenuState"); - - this.refreshInterval = null; - } - - getInnerHTML() { - const showLanguageIcon = !G_CHINA_VERSION && !G_WEGAME_VERSION; - const showExitAppButton = G_IS_STANDALONE; - const showPuzzleDLC = - !G_WEGAME_VERSION && - (G_IS_STANDALONE || WEB_STEAM_SSO_AUTHENTICATED) && - !G_IS_STEAM_DEMO && - !G_GOG_VERSION; - const showWegameFooter = G_WEGAME_VERSION; - const hasMods = MODS.anyModsActive(); - const hasSteamBridge = !G_GOG_VERSION && !G_IS_STEAM_DEMO; - - let showExternalLinks = true; - - if (G_IS_STANDALONE) { - if (G_WEGAME_VERSION || G_CHINA_VERSION) { - showExternalLinks = false; - } - } else { - const wrapper = /** @type {PlatformWrapperImplBrowser} */ (this.app.platformWrapper); - if (!wrapper.embedProvider.externalLinks) { - showExternalLinks = false; - } - } - - let showDiscordLink = showExternalLinks; - if (G_CHINA_VERSION) { - showDiscordLink = true; - } - - const showDemoAdvertisement = - (showExternalLinks || G_CHINA_VERSION) && - this.app.restrictionMgr.getIsStandaloneMarketingActive(); - - const ownsPuzzleDLC = - WEB_STEAM_SSO_AUTHENTICATED || - (G_IS_STANDALONE && - !G_IS_STEAM_DEMO && - /** @type { PlatformWrapperImplElectron}*/ (this.app.platformWrapper).dlcs.puzzle); - - const showShapez2 = showExternalLinks && MODS.mods.length === 0; - - const bannerHtml = ` -

    ${T.demoBanners.titleV2}

    - - -
    - ${Array.from(Object.entries(T.ingame.standaloneAdvantages.points)) - .slice(0, 6) - .map( - ([key, trans]) => ` -
    - ${trans.title} -

    ${trans.desc}

    -
    ` - ) - .join("")} - -
    - - - - ${ - globalConfig.currentDiscount > 0 - ? `${T.global.discount.replace( - "", - String(globalConfig.currentDiscount) - )}` - : "" - } - Play shapez on Steam - - ${!G_IS_STEAM_DEMO ? `
    ` : ""} - - `; - - return ` -
    - ${ - showLanguageIcon - ? `` - : "" - } - - - ${showExitAppButton ? `` : ""} -
    - - - - - - -
    -
    -
    -
    - ${ - hasSteamBridge && (G_IS_STANDALONE || !WEB_STEAM_SSO_AUTHENTICATED) - ? `
    - ${ - G_IS_STANDALONE - ? T.mainMenu.playFullVersionStandalone - : T.mainMenu.playFullVersionV2 - } - Sign in -
    ` - : "" - } - ${ - hasSteamBridge && WEB_STEAM_SSO_AUTHENTICATED - ? ` -
    - ${T.mainMenu.playingFullVersion} - ${T.mainMenu.logout} - -
    - ` - : "" - } - - - -
    - -
    - ${showDemoAdvertisement ? `
    ${bannerHtml}
    ` : ""} - - ${ - showShapez2 - ? `
    -
    We are currently prototyping Shapez 2!
    - -
    ` - : "" - } - - ${ - showPuzzleDLC - ? ` - - ${ - ownsPuzzleDLC && !hasMods - ? ` -
    - -
    ` - : "" - } - - ${ - !ownsPuzzleDLC && !hasMods - ? ` -
    -

    ${T.mainMenu.puzzleDlcText}

    - -
    ` - : "" - } - - - - ` - : "" - } - - - ${ - hasMods - ? ` - -
    -
    -

    ${T.mods.title}

    - -
    -
    - ${MODS.mods - .map(mod => { - return ` -
    -
    ${mod.metadata.name}
    -
    by ${mod.metadata.author}
    -
    - `; - }) - .join("")} -
    - -
    - ${T.mainMenu.mods.warningPuzzleDLC} -
    - - -
    - ` - : "" - } - -
    - - -
    - - ${ - showWegameFooter - ? ` - - ` - : ` - - - - ` - } - `; - } - - /** - * Asks the user to import a savegame - */ - requestImportSavegame() { - if ( - this.app.savegameMgr.getSavegamesMetaData().length > 0 && - !this.app.restrictionMgr.getHasUnlimitedSavegames() - ) { - this.showSavegameSlotLimit(); - return; - } - - this.app.gameAnalytics.note("startimport"); - - // Create a 'fake' file-input to accept savegames - startFileChoose(".bin").then(file => { - if (file) { - const closeLoader = this.dialogs.showLoadingDialog(); - waitNextFrame().then(() => { - const reader = new FileReader(); - reader.addEventListener("load", event => { - const contents = event.target.result; - let realContent; - - try { - realContent = ReadWriteProxy.deserializeObject(contents); - } catch (err) { - closeLoader(); - this.dialogs.showWarning( - T.dialogs.importSavegameError.title, - T.dialogs.importSavegameError.text + "

    " + err - ); - return; - } - - this.app.savegameMgr.importSavegame(realContent).then( - () => { - closeLoader(); - this.dialogs.showWarning( - T.dialogs.importSavegameSuccess.title, - T.dialogs.importSavegameSuccess.text - ); - - this.renderMainMenu(); - this.renderSavegames(); - }, - err => { - closeLoader(); - this.dialogs.showWarning( - T.dialogs.importSavegameError.title, - T.dialogs.importSavegameError.text + ":

    " + err - ); - } - ); - }); - reader.addEventListener("error", error => { - this.dialogs.showWarning( - T.dialogs.importSavegameError.title, - T.dialogs.importSavegameError.text + ":

    " + error - ); - }); - reader.readAsText(file, "utf-8"); - }); - } - }); - } - - onBackButton() { - this.app.platformWrapper.exitApp(); - } - - onEnter(payload) { - // Start loading already - const app = this.app; - setTimeout(() => app.backgroundResourceLoader.getIngamePromise(), 10); - - this.dialogs = new HUDModalDialogs(null, this.app); - const dialogsElement = document.body.querySelector(".modalDialogParent"); - this.dialogs.initializeToElement(dialogsElement); - - if (payload.loadError) { - this.dialogs.showWarning( - T.dialogs.gameLoadFailure.title, - T.dialogs.gameLoadFailure.text + "

    " + payload.loadError - ); - } - - if (G_IS_DEV && globalConfig.debug.testPuzzleMode) { - this.onPuzzleModeButtonClicked(true); - return; - } - - if (G_IS_DEV && globalConfig.debug.fastGameEnter) { - const games = this.app.savegameMgr.getSavegamesMetaData(); - if (games.length > 0 && globalConfig.debug.resumeGameOnFastEnter) { - this.resumeGame(games[0]); - } else { - this.onPlayButtonClicked(); - } - } - - // Initialize video - this.videoElement = this.htmlElement.querySelector("video"); - this.videoElement.playbackRate = 0.9; - this.videoElement.addEventListener("canplay", () => { - if (this.videoElement) { - this.videoElement.classList.add("loaded"); - } - }); - - const clickHandling = { - ".settingsButton": this.onSettingsButtonClicked, - ".languageChoose": this.onLanguageChooseClicked, - ".redditLink": this.onRedditClicked, - ".twitterLink": this.onTwitterLinkClicked, - ".patreonLink": this.onPatreonLinkClicked, - ".changelog": this.onChangelogClicked, - ".helpTranslate": this.onTranslationHelpLinkClicked, - ".exitAppButton": this.onExitAppButtonClicked, - ".steamLink": this.onSteamLinkClicked, - ".steamLinkSocial": this.onSteamLinkClickedSocial, - ".shapez2": this.onShapez2Clicked, - ".discordLink": () => { - this.app.platformWrapper.openExternalLink(THIRDPARTY_URLS.discord); - }, - ".githubLink": () => { - this.app.platformWrapper.openExternalLink(THIRDPARTY_URLS.github); - }, - ".puzzleDlcPlayButton": this.onPuzzleModeButtonClicked, - ".puzzleDlcGetButton": this.onPuzzleWishlistButtonClicked, - ".wegameDisclaimer > .rating": this.onWegameRatingClicked, - ".editMods": this.onModsClicked, - }; - - for (const key in clickHandling) { - const handler = clickHandling[key]; - const element = this.htmlElement.querySelector(key); - if (element) { - this.trackClicks(element, handler, { preventClick: true }); - } - } - - this.renderMainMenu(); - this.renderSavegames(); - this.fetchPlayerCount(); - - this.refreshInterval = setInterval(() => this.fetchPlayerCount(), 10000); - - this.app.gameAnalytics.noteMinor("menu.enter"); - } - - renderMainMenu() { - const buttonContainer = this.htmlElement.querySelector(".mainContainer .buttons"); - removeAllChildren(buttonContainer); - - const outerDiv = makeDivElement(null, ["outer"], null); - - // Import button - this.trackClicks( - makeButton(outerDiv, ["importButton", "styledButton"], T.mainMenu.importSavegame), - this.requestImportSavegame - ); - - if (this.savedGames.length > 0) { - // Continue game - this.trackClicks( - makeButton(buttonContainer, ["continueButton", "styledButton"], T.mainMenu.continue), - this.onContinueButtonClicked - ); - - // New game - this.trackClicks( - makeButton(outerDiv, ["newGameButton", "styledButton"], T.mainMenu.newGame), - this.onPlayButtonClicked - ); - } else { - // New game - this.trackClicks( - makeButton(buttonContainer, ["playButton", "styledButton"], T.mainMenu.play), - this.onPlayButtonClicked - ); - } - - this.htmlElement - .querySelector(".mainContainer") - .setAttribute("data-savegames", String(this.savedGames.length)); - - // Mods - this.trackClicks( - makeButton(outerDiv, ["modsButton", "styledButton"], T.mods.title), - this.onModsClicked - ); - - buttonContainer.appendChild(outerDiv); - } - - fetchPlayerCount() { - const element = this.htmlElement.querySelector(".onlinePlayerCount"); - if (!element) { - return; - } - fetch("https://analytics.shapez.io/v1/player-count", { - cache: "no-cache", - }) - .then(res => res.json()) - .then( - count => { - element.innerText = T.demoBanners.playerCount.replace("", String(count)); - }, - ex => { - console.warn("Failed to get player count:", ex); - } - ); - } - - onPuzzleModeButtonClicked(force = false) { - const hasUnlockedBlueprints = this.app.savegameMgr.getSavegamesMetaData().some(s => s.level >= 12); - if (!force && !hasUnlockedBlueprints) { - const { ok } = this.dialogs.showWarning( - T.dialogs.puzzlePlayRegularRecommendation.title, - T.dialogs.puzzlePlayRegularRecommendation.desc, - ["cancel:good", "ok:bad:timeout"] - ); - ok.add(() => this.onPuzzleModeButtonClicked(true)); - return; - } - - this.moveToState("LoginState", { - nextStateId: "PuzzleMenuState", - }); - } - - onPuzzleWishlistButtonClicked() { - this.app.platformWrapper.openExternalLink(THIRDPARTY_URLS.puzzleDlcStorePage); - } - - onShapez2Clicked() { - this.app.platformWrapper.openExternalLink("https://tobspr.io/shapez-2?utm_medium=shapez"); - } - - onBackButtonClicked() { - this.renderMainMenu(); - this.renderSavegames(); - } - - onSteamLinkClicked() { - openStandaloneLink(this.app, "shapez_mainmenu"); - return false; - } - - onSteamLinkClickedSocial() { - openStandaloneLink(this.app, "shapez_mainmenu_social"); - return false; - } - - onExitAppButtonClicked() { - this.app.platformWrapper.exitApp(); - } - - onChangelogClicked() { - this.moveToState("ChangelogState"); - } - - onRedditClicked() { - this.app.platformWrapper.openExternalLink(THIRDPARTY_URLS.reddit); - } - - onTwitterLinkClicked() { - this.app.platformWrapper.openExternalLink(THIRDPARTY_URLS.twitter); - } - - onPatreonLinkClicked() { - this.app.platformWrapper.openExternalLink(THIRDPARTY_URLS.patreon); - } - - onLanguageChooseClicked() { - const setting = /** @type {EnumSetting} */ (this.app.settings.getSettingHandleById("language")); - - const { optionSelected } = this.dialogs.showOptionChooser(T.settings.labels.language.title, { - active: this.app.settings.getLanguage(), - options: setting.options.map(option => ({ - value: setting.valueGetter(option), - text: setting.textGetter(option), - desc: setting.descGetter(option), - iconPrefix: setting.iconPrefix, - })), - }); - - optionSelected.add(value => { - this.app.settings.updateLanguage(value).then(() => { - if (setting.restartRequired) { - if (this.app.platformWrapper.getSupportsRestart()) { - this.app.platformWrapper.performRestart(); - } else { - this.dialogs.showInfo( - T.dialogs.restartRequired.title, - T.dialogs.restartRequired.text, - ["ok:good"] - ); - } - } - - if (setting.changeCb) { - setting.changeCb(this.app, value); - } - }); - - // Update current icon - this.htmlElement.querySelector("button.languageChoose").setAttribute("data-languageIcon", value); - }, this); - } - - get savedGames() { - return this.app.savegameMgr.getSavegamesMetaData(); - } - - renderSavegames() { - const oldContainer = this.htmlElement.querySelector(".mainContainer .savegames"); - if (oldContainer) { - oldContainer.remove(); - } - const games = this.savedGames; - if (games.length > 0) { - const parent = makeDiv(this.htmlElement.querySelector(".mainContainer .savegamesMount"), null, [ - "savegames", - ]); - - for (let i = 0; i < games.length; ++i) { - const elem = makeDiv(parent, null, ["savegame"]); - - makeDiv( - elem, - null, - ["playtime"], - formatSecondsToTimeAgo((new Date().getTime() - games[i].lastUpdate) / 1000.0) - ); - - makeDiv( - elem, - null, - ["level"], - games[i].level - ? T.mainMenu.savegameLevel.replace("", "" + games[i].level) - : T.mainMenu.savegameLevelUnknown - ); - - const name = makeDiv( - elem, - null, - ["name"], - "" + (games[i].name ? games[i].name : T.mainMenu.savegameUnnamed) + "" - ); - - const deleteButton = document.createElement("button"); - deleteButton.classList.add("styledButton", "deleteGame"); - deleteButton.setAttribute("aria-label", "Delete"); - elem.appendChild(deleteButton); - - const downloadButton = document.createElement("button"); - downloadButton.classList.add("styledButton", "downloadGame"); - downloadButton.setAttribute("aria-label", "Download"); - elem.appendChild(downloadButton); - - if (!G_WEGAME_VERSION) { - const renameButton = document.createElement("button"); - renameButton.classList.add("styledButton", "renameGame"); - renameButton.setAttribute("aria-label", "Rename Savegame"); - name.appendChild(renameButton); - this.trackClicks(renameButton, () => this.requestRenameSavegame(games[i])); - } - - const resumeButton = document.createElement("button"); - resumeButton.classList.add("styledButton", "resumeGame"); - resumeButton.setAttribute("aria-label", "Resumee"); - elem.appendChild(resumeButton); - - this.trackClicks(deleteButton, () => this.deleteGame(games[i])); - this.trackClicks(downloadButton, () => this.downloadGame(games[i])); - this.trackClicks(resumeButton, () => this.resumeGame(games[i])); - } - } else { - const parent = makeDiv( - this.htmlElement.querySelector(".mainContainer .savegamesMount"), - null, - ["savegamesNone"], - T.mainMenu.noActiveSavegames - ); - } - } - - /** - * @param {SavegameMetadata} game - */ - requestRenameSavegame(game) { - const regex = /^[a-zA-Z0-9_\- ]{1,20}$/; - - const nameInput = new FormElementInput({ - id: "nameInput", - label: null, - placeholder: "", - defaultValue: game.name || "", - validator: val => val.match(regex) && trim(val).length > 0, - }); - const dialog = new DialogWithForm({ - app: this.app, - title: T.dialogs.renameSavegame.title, - desc: T.dialogs.renameSavegame.desc, - formElements: [nameInput], - buttons: ["cancel:bad:escape", "ok:good:enter"], - }); - this.dialogs.internalShowDialog(dialog); - - // When confirmed, save the name - dialog.buttonSignals.ok.add(() => { - game.name = trim(nameInput.getValue()); - this.app.savegameMgr.writeAsync(); - this.renderSavegames(); - }); - } - - /** - * @param {SavegameMetadata} game - */ - resumeGame(game) { - this.app.adProvider.showVideoAd().then(() => { - const savegame = this.app.savegameMgr.getSavegameById(game.internalId); - savegame - .readAsync() - .then(() => this.checkForModDifferences(savegame)) - .then(() => { - this.moveToState("InGameState", { - savegame, - }); - }) - - .catch(err => { - this.dialogs.showWarning( - T.dialogs.gameLoadFailure.title, - T.dialogs.gameLoadFailure.text + "

    " + err - ); - }); - }); - } - - /** - * @param {Savegame} savegame - */ - checkForModDifferences(savegame) { - const difference = MODS.computeModDifference(savegame.currentData.mods); - - if (difference.missing.length === 0 && difference.extra.length === 0) { - return Promise.resolve(); - } - - let dialogHtml = T.dialogs.modsDifference.desc; - - /** - * - * @param {import("../savegame/savegame_typedefs").SavegameStoredMods[0]} mod - */ - function formatMod(mod) { - return ` -
    -
    ${mod.name}
    -
    ${T.mods.version} ${mod.version}
    - - -
    - `; - } - - if (difference.missing.length > 0) { - dialogHtml += "

    " + T.dialogs.modsDifference.missingMods + "

    "; - dialogHtml += difference.missing.map(formatMod).join("
    "); - } - - if (difference.extra.length > 0) { - dialogHtml += "

    " + T.dialogs.modsDifference.newMods + "

    "; - dialogHtml += difference.extra.map(formatMod).join("
    "); - } - - const signals = this.dialogs.showWarning(T.dialogs.modsDifference.title, dialogHtml, [ - "cancel:good", - "continue:bad", - ]); - - return new Promise(resolve => { - signals.continue.add(resolve); - }); - } - - /** - * @param {SavegameMetadata} game - */ - deleteGame(game) { - const signals = this.dialogs.showWarning( - T.dialogs.confirmSavegameDelete.title, - T.dialogs.confirmSavegameDelete.text - .replace("", game.name || T.mainMenu.savegameUnnamed) - .replace("", String(game.level)), - ["cancel:good", "delete:bad:timeout"] - ); - - signals.delete.add(() => { - this.app.savegameMgr.deleteSavegame(game).then( - () => { - this.renderSavegames(); - if (this.savedGames.length <= 0) this.renderMainMenu(); - }, - err => { - this.dialogs.showWarning( - T.dialogs.savegameDeletionError.title, - T.dialogs.savegameDeletionError.text + "

    " + err - ); - } - ); - }); - } - - /** - * @param {SavegameMetadata} game - */ - downloadGame(game) { - const savegame = this.app.savegameMgr.getSavegameById(game.internalId); - savegame.readAsync().then(() => { - const data = ReadWriteProxy.serializeObject(savegame.currentData); - const filename = (game.name || "unnamed") + ".bin"; - generateFileDownload(filename, data); - }); - } - - /** - * Shows a hint that the slot limit has been reached - */ - showSavegameSlotLimit() { - const { getStandalone } = this.dialogs.showWarning( - T.dialogs.oneSavegameLimit.title, - T.dialogs.oneSavegameLimit.desc, - ["cancel:bad", "getStandalone:good"] - ); - getStandalone.add(() => { - openStandaloneLink(this.app, "shapez_slotlimit"); - }); - - this.app.gameAnalytics.note("slotlimit"); - } - - onSettingsButtonClicked() { - this.moveToState("SettingsState"); - } - - onTranslationHelpLinkClicked() { - this.app.platformWrapper.openExternalLink( - "https://github.com/tobspr-games/shapez.io/blob/master/translations" - ); - } - - onPlayButtonClicked() { - if ( - this.app.savegameMgr.getSavegamesMetaData().length > 0 && - !this.app.restrictionMgr.getHasUnlimitedSavegames() - ) { - this.app.gameAnalytics.noteMinor("menu.slotlimit"); - this.showSavegameSlotLimit(); - return; - } - - this.app.adProvider.showVideoAd().then(() => { - this.app.gameAnalytics.noteMinor("menu.play"); - const savegame = this.app.savegameMgr.createNewSavegame(); - - this.moveToState("InGameState", { - savegame, - }); - }); - } - - onWegameRatingClicked() { - this.dialogs.showInfo( - "提示说明:", - ` - 1)本游戏是一款休闲建造类单机游戏,画面简洁而乐趣充足。适用于年满8周岁及以上的用户,建议未成年人在家长监护下使用游戏产品。
    - 2)本游戏模拟简单的生产流水线,剧情简单且积极向上,没有基于真实历史和现实事件的改编内容。游戏玩法为摆放简单的部件,完成生产目标。游戏为单机作品,没有基于文字和语音的陌生人社交系统。
    - 3)本游戏中有用户实名认证系统,认证为未成年人的用户将接受以下管理:未满8周岁的用户不能付费;8周岁以上未满16周岁的未成年人用户,单次充值金额不得超过50元人民币,每月充值金额累计不得超过200元人民币;16周岁以上的未成年人用户,单次充值金额不得超过100元人民币,每月充值金额累计不得超过400元人民币。未成年玩家,仅可在周五、周六、周日和法定节假日每日20时至21时进行游戏。
    - 4)游戏功能说明:一款关于传送带自动化生产特定形状产品的工厂流水线模拟游戏,画面简洁而乐趣充足,可以让玩家在轻松愉快的氛围下获得各种游戏乐趣,体验完成目标的成就感。游戏没有失败功能,自动存档,不存在较强的挫折体验。 - ` - ); - } - - onModsClicked() { - this.app.gameAnalytics.noteMinor("menu.mods"); - this.moveToState("ModsState", { - backToStateId: "MainMenuState", - }); - } - - onContinueButtonClicked() { - let latestLastUpdate = 0; - let latestInternalId; - this.app.savegameMgr.currentData.savegames.forEach(saveGame => { - if (saveGame.lastUpdate > latestLastUpdate) { - latestLastUpdate = saveGame.lastUpdate; - latestInternalId = saveGame.internalId; - } - }); - - const savegame = this.app.savegameMgr.getSavegameById(latestInternalId); - if (!savegame) { - console.warn("No savegame to continue found:", this.app.savegameMgr.currentData.savegames); - return; - } - - this.app.gameAnalytics.noteMinor("menu.continue"); - savegame - .readAsync() - .then(() => this.app.adProvider.showVideoAd()) - .then(() => this.checkForModDifferences(savegame)) - .then(() => { - this.moveToState("InGameState", { - savegame, - }); - }); - } - - onLeave() { - this.dialogs.cleanup(); - clearInterval(this.refreshInterval); - } -} +import { globalConfig, THIRDPARTY_URLS } from "../core/config"; +import { GameState } from "../core/game_state"; +import { DialogWithForm } from "../core/modal_dialog_elements"; +import { FormElementInput } from "../core/modal_dialog_forms"; +import { + formatSecondsToTimeAgo, + makeButton, + makeDiv, + makeDivElement, + removeAllChildren, + waitNextFrame, +} from "../core/utils"; +import { HUDModalDialogs } from "../game/hud/parts/modal_dialogs"; +import { MODS } from "../mods/modloader"; +import { Savegame } from "../savegame/savegame"; +import { T } from "../translations"; + +/** + * @typedef {import("../savegame/savegame_typedefs").SavegameMetadata} SavegameMetadata + * @typedef {import("../profile/setting_types").EnumSetting} EnumSetting + */ + +export class MainMenuState extends GameState { + constructor() { + super("MainMenuState"); + } + + getInnerHTML() { + const hasMods = MODS.allMods.length > 0; + + return ` +
    + + + + +
    + + + + + +
    +
    +
    +
    +
    + +
    + ${ + !hasMods + ? ` +
    + +
    ` + : "" + } + + ${ + hasMods + ? ` +
    +
    +

    ${T.mods.title}

    + +
    +
    +
    +
    Mod support in progress
    +
    Not implemented yet
    +
    +
    + +
    + ${T.mainMenu.mods.warningPuzzleDLC} +
    +
    + ` + : "" + } +
    +
    + + + `; + } + + /** + * Asks the user to import a savegame + */ + async requestImportSavegame() { + const closeLoader = this.dialogs.showLoadingDialog(); + await waitNextFrame(); + + try { + const data = await this.app.storage.requestOpenFile("bin"); + if (data === undefined) { + // User canceled the request + closeLoader(); + return; + } + + await this.app.savegameMgr.importSavegame(data); + closeLoader(); + this.dialogs.showWarning( + T.dialogs.importSavegameSuccess.title, + T.dialogs.importSavegameSuccess.text + ); + + this.renderMainMenu(); + this.renderSavegames(); + } catch (err) { + closeLoader(); + this.dialogs.showWarning( + T.dialogs.importSavegameError.title, + T.dialogs.importSavegameError.text + ":

    " + err + ); + } + } + + onBackButton() { + this.app.platformWrapper.exitApp(); + } + + onEnter(payload) { + // Start loading already + const app = this.app; + setTimeout(() => app.backgroundResourceLoader.getIngamePromise(), 10); + + this.dialogs = new HUDModalDialogs(null, this.app); + const dialogsElement = document.body.querySelector(".modalDialogParent"); + this.dialogs.initializeToElement(dialogsElement); + + if (payload.loadError) { + this.dialogs.showWarning( + T.dialogs.gameLoadFailure.title, + T.dialogs.gameLoadFailure.text + "

    " + payload.loadError + ); + } + + if (G_IS_DEV && globalConfig.debug.testPuzzleMode) { + this.onPuzzleModeButtonClicked(true); + return; + } + + if (G_IS_DEV && globalConfig.debug.fastGameEnter) { + const games = this.app.savegameMgr.getSavegamesMetaData(); + if (games.length > 0 && globalConfig.debug.resumeGameOnFastEnter) { + this.resumeGame(games[0]); + } else { + this.onPlayButtonClicked(); + } + } + + // Initialize video + this.videoElement = this.htmlElement.querySelector("video"); + this.videoElement.playbackRate = 0.9; + this.videoElement.addEventListener("canplay", () => { + if (this.videoElement) { + this.videoElement.classList.add("loaded"); + } + }); + + const clickHandling = { + ".settingsButton": this.onSettingsButtonClicked, + ".languageChoose": this.onLanguageChooseClicked, + ".redditLink": this.onRedditClicked, + ".patreonLink": this.onPatreonLinkClicked, + ".changelog": this.onChangelogClicked, + ".helpTranslate": this.onTranslationHelpLinkClicked, + ".exitAppButton": this.onExitAppButtonClicked, + ".discordLink": () => { + this.app.platformWrapper.openExternalLink(THIRDPARTY_URLS.discord); + }, + ".githubLink": () => { + this.app.platformWrapper.openExternalLink(THIRDPARTY_URLS.github); + }, + ".puzzleDlcPlayButton": this.onPuzzleModeButtonClicked, + ".editMods": this.onModsClicked, + }; + + for (const key in clickHandling) { + const handler = clickHandling[key]; + const element = this.htmlElement.querySelector(key); + if (element) { + this.trackClicks(element, handler, { preventClick: true }); + } + } + + this.renderMainMenu(); + this.renderSavegames(); + } + + renderMainMenu() { + const buttonContainer = this.htmlElement.querySelector(".mainContainer .buttons"); + removeAllChildren(buttonContainer); + + const outerDiv = makeDivElement(null, ["outer"], null); + + // Import button + this.trackClicks( + makeButton(outerDiv, ["importButton", "styledButton"], T.mainMenu.importSavegame), + this.requestImportSavegame + ); + + if (this.savedGames.length > 0) { + // Continue game + this.trackClicks( + makeButton(buttonContainer, ["continueButton", "styledButton"], T.mainMenu.continue), + this.onContinueButtonClicked + ); + + // New game + this.trackClicks( + makeButton(outerDiv, ["newGameButton", "styledButton"], T.mainMenu.newGame), + this.onPlayButtonClicked + ); + } else { + // New game + this.trackClicks( + makeButton(buttonContainer, ["playButton", "styledButton"], T.mainMenu.play), + this.onPlayButtonClicked + ); + } + + this.htmlElement + .querySelector(".mainContainer") + .setAttribute("data-savegames", String(this.savedGames.length)); + + // Mods + this.trackClicks( + makeButton(outerDiv, ["modsButton", "styledButton"], T.mods.title), + this.onModsClicked + ); + + buttonContainer.appendChild(outerDiv); + } + + onPuzzleModeButtonClicked(force = false) { + const hasUnlockedBlueprints = this.app.savegameMgr.getSavegamesMetaData().some(s => s.level >= 12); + if (!force && !hasUnlockedBlueprints) { + const { ok } = this.dialogs.showWarning( + T.dialogs.puzzlePlayRegularRecommendation.title, + T.dialogs.puzzlePlayRegularRecommendation.desc, + ["cancel:good", "ok:bad:timeout"] + ); + ok.add(() => this.onPuzzleModeButtonClicked(true)); + return; + } + + this.moveToState("LoginState", { + nextStateId: "PuzzleMenuState", + }); + } + + onBackButtonClicked() { + this.renderMainMenu(); + this.renderSavegames(); + } + + onExitAppButtonClicked() { + this.app.platformWrapper.exitApp(); + } + + onChangelogClicked() { + this.moveToState("ChangelogState"); + } + + onRedditClicked() { + this.app.platformWrapper.openExternalLink(THIRDPARTY_URLS.reddit); + } + + onPatreonLinkClicked() { + this.app.platformWrapper.openExternalLink(THIRDPARTY_URLS.patreon); + } + + onLanguageChooseClicked() { + const setting = /** @type {EnumSetting} */ (this.app.settings.getSettingHandleById("language")); + + const { optionSelected } = this.dialogs.showOptionChooser(T.settings.labels.language.title, { + active: this.app.settings.getLanguage(), + options: setting.options.map(option => ({ + value: setting.valueGetter(option), + text: setting.textGetter(option), + desc: setting.descGetter(option), + iconPrefix: setting.iconPrefix, + })), + }); + + optionSelected.add(value => { + this.app.settings.updateLanguage(value).then(() => { + if (setting.restartRequired) { + if (this.app.platformWrapper.getSupportsRestart()) { + this.app.platformWrapper.performRestart(); + } else { + this.dialogs.showInfo( + T.dialogs.restartRequired.title, + T.dialogs.restartRequired.text, + ["ok:good"] + ); + } + } + + if (setting.changeCb) { + setting.changeCb(this.app, value); + } + }); + + // Update current icon + this.htmlElement.querySelector("button.languageChoose").setAttribute("data-languageIcon", value); + }, this); + } + + get savedGames() { + return this.app.savegameMgr.getSavegamesMetaData(); + } + + renderSavegames() { + const oldContainer = this.htmlElement.querySelector(".mainContainer .savegames"); + if (oldContainer) { + oldContainer.remove(); + } + const games = this.savedGames; + if (games.length > 0) { + const parent = makeDiv(this.htmlElement.querySelector(".mainContainer .savegamesMount"), null, [ + "savegames", + ]); + + for (let i = 0; i < games.length; ++i) { + const elem = makeDiv(parent, null, ["savegame"]); + + makeDiv( + elem, + null, + ["playtime"], + formatSecondsToTimeAgo((new Date().getTime() - games[i].lastUpdate) / 1000.0) + ); + + makeDiv( + elem, + null, + ["level"], + games[i].level + ? T.mainMenu.savegameLevel.replace("", "" + games[i].level) + : T.mainMenu.savegameLevelUnknown + ); + + const name = makeDiv( + elem, + null, + ["name"], + "" + (games[i].name ? games[i].name : T.mainMenu.savegameUnnamed) + "" + ); + + const deleteButton = document.createElement("button"); + deleteButton.classList.add("styledButton", "deleteGame"); + deleteButton.setAttribute("aria-label", "Delete"); + elem.appendChild(deleteButton); + + const downloadButton = document.createElement("button"); + downloadButton.classList.add("styledButton", "downloadGame"); + downloadButton.setAttribute("aria-label", "Download"); + elem.appendChild(downloadButton); + + const renameButton = document.createElement("button"); + renameButton.classList.add("styledButton", "renameGame"); + renameButton.setAttribute("aria-label", "Rename Savegame"); + name.appendChild(renameButton); + this.trackClicks(renameButton, () => this.requestRenameSavegame(games[i])); + + const resumeButton = document.createElement("button"); + resumeButton.classList.add("styledButton", "resumeGame"); + resumeButton.setAttribute("aria-label", "Resumee"); + elem.appendChild(resumeButton); + + this.trackClicks(deleteButton, () => this.deleteGame(games[i])); + this.trackClicks(downloadButton, () => this.downloadGame(games[i])); + this.trackClicks(resumeButton, () => this.resumeGame(games[i])); + } + } else { + const parent = makeDiv( + this.htmlElement.querySelector(".mainContainer .savegamesMount"), + null, + ["savegamesNone"], + T.mainMenu.noActiveSavegames + ); + } + } + + /** + * @param {SavegameMetadata} game + */ + requestRenameSavegame(game) { + const regex = /^[a-zA-Z0-9_\- ]{1,20}$/; + + const nameInput = new FormElementInput({ + id: "nameInput", + label: null, + placeholder: "", + defaultValue: game.name || "", + validator: val => val.match(regex) && val.trim().length > 0, + }); + const dialog = new DialogWithForm({ + app: this.app, + title: T.dialogs.renameSavegame.title, + desc: T.dialogs.renameSavegame.desc, + formElements: [nameInput], + buttons: ["cancel:bad:escape", "ok:good:enter"], + }); + + this.dialogs.internalShowDialog(dialog); + + // When confirmed, save the name + dialog.buttonSignals.ok.add(() => { + game.name = nameInput.getValue().trim(); + this.app.savegameMgr.writeAsync(); + this.renderSavegames(); + }); + } + + /** + * @param {SavegameMetadata} game + */ + resumeGame(game) { + const savegame = this.app.savegameMgr.getSavegameById(game.internalId); + savegame + .readAsync() + .then(() => this.checkForModDifferences(savegame)) + .then(() => { + this.moveToState("InGameState", { + savegame, + }); + }) + + .catch(err => { + this.dialogs.showWarning( + T.dialogs.gameLoadFailure.title, + T.dialogs.gameLoadFailure.text + "

    " + err + ); + }); + } + + /** + * @param {Savegame} savegame + */ + checkForModDifferences(savegame) { + const difference = MODS.computeModDifference(savegame.currentData.mods); + + if (difference.missing.length === 0 && difference.extra.length === 0) { + return Promise.resolve(); + } + + let dialogHtml = T.dialogs.modsDifference.desc; + + /** + * + * @param {import("../savegame/savegame_typedefs").SavegameStoredMods[0]} mod + */ + function formatMod(mod) { + return ` +
    +
    ${mod.name}
    +
    ${T.mods.version} ${mod.version}
    + + +
    + `; + } + + if (difference.missing.length > 0) { + dialogHtml += "

    " + T.dialogs.modsDifference.missingMods + "

    "; + dialogHtml += difference.missing.map(formatMod).join("
    "); + } + + if (difference.extra.length > 0) { + dialogHtml += "

    " + T.dialogs.modsDifference.newMods + "

    "; + dialogHtml += difference.extra.map(formatMod).join("
    "); + } + + const signals = this.dialogs.showWarning(T.dialogs.modsDifference.title, dialogHtml, [ + "cancel:good", + "continue:bad", + ]); + + return new /** @type {typeof Promise} */ (Promise)(resolve => { + signals.continue.add(resolve); + }); + } + + /** + * @param {SavegameMetadata} game + */ + deleteGame(game) { + const signals = this.dialogs.showWarning( + T.dialogs.confirmSavegameDelete.title, + T.dialogs.confirmSavegameDelete.text + .replace("", game.name || T.mainMenu.savegameUnnamed) + .replace("", String(game.level)), + ["cancel:good", "delete:bad:timeout"] + ); + + signals.delete.add(() => { + this.app.savegameMgr.deleteSavegame(game).then( + () => { + this.renderSavegames(); + if (this.savedGames.length <= 0) this.renderMainMenu(); + }, + err => { + this.dialogs.showWarning( + T.dialogs.savegameDeletionError.title, + T.dialogs.savegameDeletionError.text + "

    " + err + ); + } + ); + }); + } + + /** + * @param {SavegameMetadata} game + */ + downloadGame(game) { + const savegame = this.app.savegameMgr.getSavegameById(game.internalId); + savegame.readAsync().then(() => { + const filename = (game.name || "unnamed") + ".bin"; + savegame.storage.requestSaveFile(filename, savegame.currentData); + }); + } + + onSettingsButtonClicked() { + this.moveToState("SettingsState"); + } + + onTranslationHelpLinkClicked() { + this.app.platformWrapper.openExternalLink( + "https://github.com/tobspr-games/shapez.io/blob/master/translations" + ); + } + + onPlayButtonClicked() { + const savegame = this.app.savegameMgr.createNewSavegame(); + + this.moveToState("InGameState", { + savegame, + }); + } + + onModsClicked() { + this.moveToState("ModsState", { + backToStateId: "MainMenuState", + }); + } + + onContinueButtonClicked() { + let latestLastUpdate = 0; + let latestInternalId; + this.app.savegameMgr.currentData.savegames.forEach(saveGame => { + if (saveGame.lastUpdate > latestLastUpdate) { + latestLastUpdate = saveGame.lastUpdate; + latestInternalId = saveGame.internalId; + } + }); + + const savegame = this.app.savegameMgr.getSavegameById(latestInternalId); + if (!savegame) { + console.warn("No savegame to continue found:", this.app.savegameMgr.currentData.savegames); + return; + } + + savegame + .readAsync() + .then(() => this.checkForModDifferences(savegame)) + .then(() => { + this.moveToState("InGameState", { + savegame, + }); + }); + } + + onLeave() { + this.dialogs.cleanup(); + } +} diff --git a/src/js/states/mobile_warning.js b/src/js/states/mobile_warning.js deleted file mode 100644 index 07aa347a..00000000 --- a/src/js/states/mobile_warning.js +++ /dev/null @@ -1,45 +0,0 @@ -import { cachebust } from "../core/cachebust"; -import { GameState } from "../core/game_state"; - -export class MobileWarningState extends GameState { - constructor() { - super("MobileWarningState"); - } - - getInnerHTML() { - return ` - - - -

    I'm sorry, but shapez.io is not available on mobile devices yet!

    -

    If you have a desktop device, you can get shapez on Steam:

    - - - Play on Steam! - `; - } - - getThemeMusic() { - return null; - } - - getHasFadeIn() { - return false; - } - - onEnter() { - try { - if (window.gtag) { - window.gtag("event", "click", { - event_category: "ui", - event_label: "mobile_warning", - }); - } - } catch (ex) { - console.warn("Failed to track mobile click:", ex); - } - } - onLeave() { - // this.dialogs.cleanup(); - } -} diff --git a/src/js/states/mods.js b/src/js/states/mods.js deleted file mode 100644 index 07e47bb4..00000000 --- a/src/js/states/mods.js +++ /dev/null @@ -1,154 +0,0 @@ -import { openStandaloneLink, THIRDPARTY_URLS } from "../core/config"; -import { WEB_STEAM_SSO_AUTHENTICATED } from "../core/steam_sso"; -import { TextualGameState } from "../core/textual_game_state"; -import { MODS } from "../mods/modloader"; -import { T } from "../translations"; - -export class ModsState extends TextualGameState { - constructor() { - super("ModsState"); - } - - getStateHeaderTitle() { - return T.mods.title; - } - - get modsSupported() { - return ( - !WEB_STEAM_SSO_AUTHENTICATED && - !G_IS_STEAM_DEMO && - (G_IS_STANDALONE || (G_IS_DEV && !window.location.href.includes("demo"))) - ); - } - - internalGetFullHtml() { - let headerHtml = ` -
    -

    ${this.getStateHeaderTitle()}

    - -
    - ${ - this.modsSupported && MODS.mods.length > 0 - ? `` - : "" - } - ${ - this.modsSupported - ? `` - : "" - } -
    - -
    `; - - return ` - ${headerHtml} -
    - ${this.getInnerHTML()} -
    - `; - } - - getMainContentHTML() { - if (!this.modsSupported) { - return ` -
    - -

    ${WEB_STEAM_SSO_AUTHENTICATED ? T.mods.browserNoSupport : T.mods.noModSupport}

    -
    - - Get on Steam! - - -
    - `; - } - - if (MODS.mods.length === 0) { - return ` - -
    - ${T.mods.modsInfo} - - -
    - - `; - } - - let modsHtml = ``; - - MODS.mods.forEach(mod => { - modsHtml += ` -
    -
    - ${mod.metadata.name} - ${mod.metadata.description} - ${T.mods.modWebsite} -
    - ${T.mods.version}${mod.metadata.version} - ${T.mods.author}${mod.metadata.author} -
    - -
    - -
    - `; - }); - return ` - -
    - ${T.mods.modsInfo} -
    - -
    - ${modsHtml} -
    - `; - } - - onEnter() { - const steamLink = this.htmlElement.querySelector(".steamLink"); - if (steamLink) { - this.trackClicks(steamLink, this.onSteamLinkClicked); - } - const openModsFolder = this.htmlElement.querySelector(".openModsFolder"); - if (openModsFolder) { - this.trackClicks(openModsFolder, this.openModsFolder); - } - const browseMods = this.htmlElement.querySelector(".browseMods"); - if (browseMods) { - this.trackClicks(browseMods, this.openBrowseMods); - } - - const checkboxes = this.htmlElement.querySelectorAll(".checkbox"); - Array.from(checkboxes).forEach(checkbox => { - this.trackClicks(checkbox, this.showModTogglingComingSoon); - }); - } - - showModTogglingComingSoon() { - this.dialogs.showWarning(T.mods.togglingComingSoon.title, T.mods.togglingComingSoon.description); - } - - openModsFolder() { - if (!G_IS_STANDALONE) { - this.dialogs.showWarning(T.global.error, T.mods.folderOnlyStandalone); - return; - } - ipcRenderer.invoke("open-mods-folder"); - } - - openBrowseMods() { - this.app.platformWrapper.openExternalLink(THIRDPARTY_URLS.modBrowser); - } - - onSteamLinkClicked() { - openStandaloneLink(this.app, "shapez_modsettings"); - return false; - } - - getDefaultPreviousState() { - return "SettingsState"; - } -} diff --git a/src/js/states/mods.tsx b/src/js/states/mods.tsx new file mode 100644 index 00000000..68dc9795 --- /dev/null +++ b/src/js/states/mods.tsx @@ -0,0 +1,54 @@ +import { Mod } from "@/mods/mod"; +import { ModAuthor } from "@/mods/mod_metadata"; +import { MODS } from "@/mods/modloader"; +import { TextualGameState } from "../core/textual_game_state"; +import { T } from "../translations"; + +export class ModsState extends TextualGameState { + constructor() { + super("ModsState"); + } + + getStateHeaderTitle() { + return T.mods.title; + } + + protected getInitialContent() { + // TODO: implement proper UI for disabled, errored mods etc. + const modElements = MODS.allMods.map(info => this.getModElement(info.mod)); + const hasMods = modElements.length > 0; + + if (!hasMods) { + modElements.push(this.getNoModsMessage()); + } + + return
    {modElements}
    ; + } + + private getModElement(mod: Mod): HTMLElement { + // TODO: Ensure proper design and localization once mods are reworked + return ( +
    +
    + {mod.metadata.name} by {this.formatAuthors(mod.metadata.authors)} +
    +
    {mod.metadata.description}
    +
    + {mod.metadata.id} @ {mod.metadata.version} +
    +
    + ); + } + + private formatAuthors(authors: readonly ModAuthor[]): string { + return authors.map(author => author.name).join(", "); + } + + private getNoModsMessage(): HTMLElement { + return
    No mods are currently installed.
    ; + } + + getDefaultPreviousState() { + return "SettingsState"; + } +} diff --git a/src/js/states/preload.js b/src/js/states/preload.js index 9a4b1075..8ff9d98e 100644 --- a/src/js/states/preload.js +++ b/src/js/states/preload.js @@ -1,390 +1,276 @@ -import { CHANGELOG } from "../changelog"; -import { cachebust } from "../core/cachebust"; -import { globalConfig, THIRDPARTY_URLS } from "../core/config"; -import { GameState } from "../core/game_state"; -import { createLogger } from "../core/logging"; -import { queryParamOptions } from "../core/query_parameters"; -import { authorizeViaSSOToken } from "../core/steam_sso"; -import { getLogoSprite, timeoutPromise } from "../core/utils"; -import { getRandomHint } from "../game/hints"; -import { HUDModalDialogs } from "../game/hud/parts/modal_dialogs"; -import { PlatformWrapperImplBrowser } from "../platform/browser/wrapper"; -import { autoDetectLanguageId, T, updateApplicationLanguage } from "../translations"; - -const logger = createLogger("state/preload"); - -export class PreloadState extends GameState { - constructor() { - super("PreloadState"); - } - - getThemeMusic() { - return null; - } - - getHasFadeIn() { - return false; - } - - getRemovePreviousContent() { - return false; - } - - onEnter() { - this.dialogs = new HUDModalDialogs(null, this.app); - const dialogsElement = document.body.querySelector(".modalDialogParent"); - this.dialogs.initializeToElement(dialogsElement); - - /** @type {HTMLElement} */ - this.hintsText = this.htmlElement.querySelector("#preload_ll_text"); - this.lastHintShown = -1000; - this.nextHintDuration = 0; - - this.statusText = this.htmlElement.querySelector("#ll_preload_status"); - this.progressElement = this.htmlElement.querySelector("#ll_progressbar span"); - - this.startLoading(); - } - - async fetchDiscounts() { - await timeoutPromise( - fetch("https://analytics.shapez.io/v1/discounts") - .then(res => res.json()) - .then(data => { - globalConfig.currentDiscount = Number( - data["1318690"].data.price_overview.discount_percent - ); - logger.log("Fetched current discount:", globalConfig.currentDiscount); - }), - 2000 - ).catch(err => { - logger.warn("Failed to fetch current discount:", err); - }); - } - - async sendBeacon() { - if (G_IS_STANDALONE && !G_IS_STEAM_DEMO) { - return; - } - if (queryParamOptions.campaign) { - fetch( - "https://analytics.shapez.io/campaign/" + - queryParamOptions.campaign + - "?lpurl=nocontent&fbclid=" + - (queryParamOptions.fbclid || "") + - "&gclid=" + - (queryParamOptions.gclid || "") - ).catch(err => { - console.warn("Failed to send beacon:", err); - }); - } - if (queryParamOptions.embedProvider) { - fetch( - "https://analytics.shapez.io/campaign/embed_" + - queryParamOptions.embedProvider + - "?lpurl=nocontent" - ).catch(err => { - console.warn("Failed to send beacon:", err); - }); - } - } - - onLeave() { - // this.dialogs.cleanup(); - } - - startLoading() { - this.setStatus("Booting") - .then(() => { - try { - window.localStorage.setItem("local_storage_feature_detection", "1"); - } catch (ex) { - throw new Error( - "Could not access local storage. Make sure you are not playing in incognito mode and allow thirdparty cookies!" - ); - } - }) - .then(() => this.setStatus("Creating platform wrapper", 3)) - - .then(() => this.sendBeacon()) - .then(() => authorizeViaSSOToken(this.app, this.dialogs)) - - .then(() => this.app.platformWrapper.initialize()) - - .then(() => this.setStatus("Initializing local storage", 6)) - .then(() => { - const wrapper = this.app.platformWrapper; - if (wrapper instanceof PlatformWrapperImplBrowser) { - try { - window.localStorage.setItem("local_storage_test", "1"); - window.localStorage.removeItem("local_storage_test"); - } catch (ex) { - logger.error("Failed to read/write local storage:", ex); - return new Promise(() => { - alert( - "Your brower does not support thirdparty cookies or you have disabled it in your security settings.\n\n" + - "In Chrome this setting is called 'Block third-party cookies and site data'.\n\n" + - "Please allow third party cookies and then reload the page." - ); - // Never return - }); - } - } - }) - - .then(() => this.setStatus("Creating storage", 9)) - .then(() => { - return this.app.storage.initialize(); - }) - - .then(() => this.setStatus("Initializing libraries", 12)) - .then(() => this.app.analytics.initialize()) - .then(() => this.app.gameAnalytics.initialize()) - - .then(() => this.setStatus("Connecting to api", 15)) - .then(() => this.fetchDiscounts()) - - .then(() => this.setStatus("Initializing settings", 20)) - .then(() => { - return this.app.settings.initialize(); - }) - - .then(() => { - // Initialize fullscreen - if (this.app.platformWrapper.getSupportsFullscreen()) { - this.app.platformWrapper.setFullscreen(this.app.settings.getIsFullScreen()); - } - }) - - .then(() => this.setStatus("Initializing language", 25)) - .then(() => { - if (G_CHINA_VERSION || G_WEGAME_VERSION) { - return this.app.settings.updateLanguage("zh-CN"); - } - - if (this.app.settings.getLanguage() === "auto-detect") { - const language = autoDetectLanguageId(); - logger.log("Setting language to", language); - return this.app.settings.updateLanguage(language); - } - }) - .then(() => { - document.documentElement.setAttribute("lang", this.app.settings.getLanguage()); - }) - - .then(() => { - const language = this.app.settings.getLanguage(); - updateApplicationLanguage(language); - }) - - .then(() => this.setStatus("Initializing sounds", 30)) - .then(() => { - return this.app.sound.initialize(); - }) - - .then(() => this.setStatus("Initializing restrictions", 34)) - .then(() => { - return this.app.restrictionMgr.initialize(); - }) - - .then(() => this.setStatus("Initializing savegames", 38)) - .then(() => { - return this.app.savegameMgr.initialize().catch(err => { - logger.error("Failed to initialize savegames:", err); - alert( - "Your savegames failed to load, it seems your data files got corrupted. I'm so sorry!\n\n(This can happen if your pc crashed while a game was saved).\n\nYou can try re-importing your savegames." - ); - return this.app.savegameMgr.writeAsync(); - }); - }) - - .then(() => this.setStatus("Downloading resources", 40)) - .then(() => { - this.app.backgroundResourceLoader.resourceStateChangedSignal.add(({ progress }) => { - this.setStatus( - "Downloading resources (" + (progress * 100.0).toFixed(1) + " %)", - 40 + progress * 50 - ); - }); - return this.app.backgroundResourceLoader.getMainMenuPromise().catch(err => { - logger.error("Failed to load resources:", err); - this.app.backgroundResourceLoader.showLoaderError(this.dialogs, err); - return new Promise(() => null); - }); - }) - .then(() => { - this.app.backgroundResourceLoader.resourceStateChangedSignal.removeAll(); - }) - - .then(() => this.setStatus("Checking changelog", 95)) - .then(() => { - if (G_IS_DEV && globalConfig.debug.disableUpgradeNotification) { - return; - } - - if (G_CHINA_VERSION || G_WEGAME_VERSION) { - return; - } - - if (G_IS_STEAM_DEMO || !G_IS_STANDALONE) { - return; - } - - return this.app.storage - .readFileAsync("lastversion.bin") - .catch(err => { - logger.warn("Failed to read lastversion:", err); - return G_BUILD_VERSION; - }) - .then(version => { - logger.log("Last version:", version, "App version:", G_BUILD_VERSION); - this.app.storage.writeFileAsync("lastversion.bin", G_BUILD_VERSION); - return version; - }) - .then(version => { - let changelogEntries = []; - logger.log("Last seen version:", version); - - for (let i = 0; i < CHANGELOG.length; ++i) { - if (CHANGELOG[i].version === version) { - break; - } - changelogEntries.push(CHANGELOG[i]); - } - if (changelogEntries.length === 0) { - return; - } - - let dialogHtml = T.dialogs.updateSummary.desc; - for (let i = 0; i < changelogEntries.length; ++i) { - const entry = changelogEntries[i]; - dialogHtml += ` -
    - ${entry.version} - ${entry.date} -
      - ${entry.entries.map(text => `
    • ${text}
    • `).join("")} -
    -
    - `; - } - - return new Promise(resolve => { - this.dialogs.showInfo(T.dialogs.updateSummary.title, dialogHtml).ok.add(resolve); - }); - }); - }) - - .then(() => this.setStatus("Launching", 99)) - .then( - () => { - this.moveToState("MainMenuState"); - }, - err => { - this.showFailMessage(err); - } - ); - } - - update() { - if (G_CHINA_VERSION || G_WEGAME_VERSION) { - return; - } - const now = performance.now(); - if (now - this.lastHintShown > this.nextHintDuration) { - this.lastHintShown = now; - const hintText = getRandomHint(); - - this.hintsText.innerHTML = hintText; - - /** - * Compute how long the user will need to read the hint. - * We calculate with 130 words per minute, with an average of 5 chars - * that is 650 characters / minute - */ - this.nextHintDuration = Math.max(2500, (hintText.length / 650) * 60 * 1000); - } - } - - onRender() { - this.update(); - } - - onBackgroundTick() { - this.update(); - } - - /** - * - * @param {string} text - */ - setStatus(text, progress) { - logger.log("✅ " + text); - - if (G_CHINA_VERSION || G_WEGAME_VERSION) { - return Promise.resolve(); - } - this.currentStatus = text; - this.statusText.innerText = text; - this.progressElement.style.width = 80 + (progress / 100) * 20 + "%"; - return Promise.resolve(); - } - - showFailMessage(text) { - logger.error("App init failed:", text); - - const email = "bugs@shapez.io"; - - const subElement = document.createElement("div"); - subElement.classList.add("failureBox"); - - subElement.innerHTML = ` - -
    -
    - Failed to initialize application! -
    -
    - ${this.currentStatus} failed:
    - ${text} -
    -
    - - Build ${G_BUILD_VERSION} @ ${G_BUILD_COMMIT_HASH} -
    -
    - `; - - this.htmlElement.classList.add("failure"); - this.htmlElement.appendChild(subElement); - - const resetBtn = subElement.querySelector("button.resetApp"); - this.trackClicks(resetBtn, this.showResetConfirm); - - this.hintsText.remove(); - } - - showResetConfirm() { - if (confirm("Are you sure you want to reset the app? This will delete all your savegames!")) { - this.resetApp(); - } - } - - resetApp() { - this.app.settings - .resetEverythingAsync() - .then(() => { - this.app.savegameMgr.resetEverythingAsync(); - }) - .then(() => { - this.app.settings.resetEverythingAsync(); - }) - .then(() => { - window.location.reload(); - }); - } -} +import { CHANGELOG } from "../changelog"; +import { globalConfig } from "../core/config"; +import { GameState } from "../core/game_state"; +import { createLogger } from "../core/logging"; +import { getRandomHint } from "../game/hints"; +import { HUDModalDialogs } from "../game/hud/parts/modal_dialogs"; +import { T, autoDetectLanguageId, updateApplicationLanguage } from "../translations"; + +const logger = createLogger("state/preload"); + +export class PreloadState extends GameState { + constructor() { + super("PreloadState"); + } + + getThemeMusic() { + return null; + } + + getHasFadeIn() { + return false; + } + + getRemovePreviousContent() { + return false; + } + + onEnter() { + this.dialogs = new HUDModalDialogs(null, this.app); + const dialogsElement = document.body.querySelector(".modalDialogParent"); + this.dialogs.initializeToElement(dialogsElement); + + /** @type {HTMLElement} */ + this.hintsText = this.htmlElement.querySelector("#preload_ll_text"); + this.lastHintShown = -1000; + this.nextHintDuration = 0; + + /** @type {HTMLElement} */ + this.statusText = this.htmlElement.querySelector("#ll_preload_status"); + /** @type {HTMLElement} */ + this.progressElement = this.htmlElement.querySelector("#ll_progressbar span"); + + this.startLoading(); + } + + onLeave() { + // this.dialogs.cleanup(); + } + + startLoading() { + this.setStatus("Booting") + .then(() => this.setStatus("Creating platform wrapper", 3)) + .then(() => this.app.platformWrapper.initialize()) + + .then(() => this.setStatus("Creating storage", 9)) + .then(() => { + return this.app.storage.initialize(); + }) + + .then(() => this.setStatus("Initializing settings", 20)) + .then(() => { + return this.app.settings.initialize(); + }) + + .then(() => { + // Initialize fullscreen + if (this.app.platformWrapper.getSupportsFullscreen()) { + this.app.platformWrapper.setFullscreen(this.app.settings.getIsFullScreen()); + } + }) + + .then(() => this.setStatus("Initializing language", 25)) + .then(() => { + if (this.app.settings.getLanguage() === "auto-detect") { + const language = autoDetectLanguageId(); + logger.log("Setting language to", language); + return this.app.settings.updateLanguage(language); + } + }) + .then(() => { + document.documentElement.setAttribute("lang", this.app.settings.getLanguage()); + }) + + .then(() => { + const language = this.app.settings.getLanguage(); + return updateApplicationLanguage(language); + }) + + .then(() => this.setStatus("Initializing sounds", 30)) + .then(() => { + return this.app.sound.initialize(); + }) + + .then(() => this.setStatus("Initializing savegames", 38)) + .then(() => { + return this.app.savegameMgr.initialize().catch(err => { + logger.error("Failed to initialize savegames:", err); + alert( + "Your savegames failed to load, it seems your data files got corrupted. I'm so sorry!\n\n(This can happen if your pc crashed while a game was saved).\n\nYou can try re-importing your savegames." + ); + return this.app.savegameMgr.writeAsync(); + }); + }) + + .then(() => this.setStatus("Downloading resources", 40)) + .then(() => { + this.app.backgroundResourceLoader.resourceStateChangedSignal.add(({ progress }) => { + this.setStatus( + "Downloading resources (" + (progress * 100.0).toFixed(1) + " %)", + 40 + progress * 50 + ); + }); + return this.app.backgroundResourceLoader.getMainMenuPromise().catch(err => { + logger.error("Failed to load resources:", err); + this.app.backgroundResourceLoader.showLoaderError(this.dialogs, err); + return new Promise(() => null); + }); + }) + .then(() => { + this.app.backgroundResourceLoader.resourceStateChangedSignal.removeAll(); + }) + + .then(() => this.setStatus("Checking changelog", 95)) + .then(() => { + if (G_IS_DEV && globalConfig.debug.disableUpgradeNotification) { + return; + } + + return this.app.storage + .readFileAsync("lastversion.bin") + .catch(err => { + logger.warn("Failed to read lastversion:", err); + return G_BUILD_VERSION; + }) + .then(version => { + logger.log("Last version:", version, "App version:", G_BUILD_VERSION); + this.app.storage.writeFileAsync("lastversion.bin", G_BUILD_VERSION); + return version; + }) + .then(version => { + let changelogEntries = []; + logger.log("Last seen version:", version); + + for (let i = 0; i < CHANGELOG.length; ++i) { + if (CHANGELOG[i].version === version) { + break; + } + changelogEntries.push(CHANGELOG[i]); + } + if (changelogEntries.length === 0) { + return; + } + + let dialogHtml = T.dialogs.updateSummary.desc; + for (let i = 0; i < changelogEntries.length; ++i) { + const entry = changelogEntries[i]; + dialogHtml += ` +
    + ${entry.version} + ${entry.date} +
      + ${entry.entries.map(text => `
    • ${text}
    • `).join("")} +
    +
    + `; + } + + return new /** @type {typeof Promise} */ (Promise)(resolve => { + this.dialogs.showInfo(T.dialogs.updateSummary.title, dialogHtml).ok.add(resolve); + }); + }); + }) + + .then(() => this.setStatus("Launching", 99)) + .then( + () => { + this.moveToState("MainMenuState"); + }, + err => { + this.showFailMessage(err); + } + ); + } + + update() { + const now = performance.now(); + if (now - this.lastHintShown > this.nextHintDuration) { + this.lastHintShown = now; + const hintText = getRandomHint(); + + this.hintsText.innerHTML = hintText; + + /** + * Compute how long the user will need to read the hint. + * We calculate with 130 words per minute, with an average of 5 chars + * that is 650 characters / minute + */ + this.nextHintDuration = Math.max(2500, (hintText.length / 650) * 60 * 1000); + } + } + + onRender() { + this.update(); + } + + onBackgroundTick() { + this.update(); + } + + /** + * + * @param {string} text + */ + setStatus(text, progress) { + logger.log("✅ " + text); + + this.currentStatus = text; + this.statusText.innerText = text; + this.progressElement.style.width = 80 + (progress / 100) * 20 + "%"; + return Promise.resolve(); + } + + showFailMessage(text) { + logger.error("App init failed:", text); + + const subElement = document.createElement("div"); + subElement.classList.add("failureBox"); + + subElement.innerHTML = ` + +
    +
    + Failed to initialize application! +
    +
    + ${this.currentStatus} failed:
    + ${text} +
    +
    + + Build ${G_BUILD_VERSION} @ ${G_BUILD_COMMIT_HASH} +
    +
    + `; + + this.htmlElement.classList.add("failure"); + this.htmlElement.appendChild(subElement); + + const resetBtn = subElement.querySelector("button.resetApp"); + this.trackClicks(resetBtn, this.showResetConfirm); + + this.hintsText.remove(); + } + + showResetConfirm() { + if (confirm("Are you sure you want to reset the app? This will delete all your savegames!")) { + this.resetApp(); + } + } + + resetApp() { + this.app.settings + .resetEverythingAsync() + .then(() => { + this.app.savegameMgr.resetEverythingAsync(); + }) + .then(() => { + this.app.settings.resetEverythingAsync(); + }) + .then(() => { + window.location.reload(); + }); + } +} diff --git a/src/js/states/settings.js b/src/js/states/settings.js index 0927fbf9..65d086c1 100644 --- a/src/js/states/settings.js +++ b/src/js/states/settings.js @@ -1,219 +1,204 @@ -import { THIRDPARTY_URLS } from "../core/config"; -import { TextualGameState } from "../core/textual_game_state"; -import { formatSecondsToTimeAgo } from "../core/utils"; -import { enumCategories } from "../profile/application_settings"; -import { T } from "../translations"; - -export class SettingsState extends TextualGameState { - constructor() { - super("SettingsState"); - } - - getStateHeaderTitle() { - return T.settings.title; - } - - getMainContentHTML() { - return ` - - - -
    - ${this.getSettingsHtml()} -
    - - `; - } - - getCategoryButtonsHtml() { - return Object.keys(enumCategories) - .map(key => enumCategories[key]) - .map( - category => - ` - - ` - ) - .join(""); - } - - getSettingsHtml() { - const categoriesHTML = {}; - - Object.keys(enumCategories).forEach(key => { - const catName = enumCategories[key]; - categoriesHTML[catName] = `
    `; - }); - - for (let i = 0; i < this.app.settings.settingHandles.length; ++i) { - const setting = this.app.settings.settingHandles[i]; - if (!setting.categoryId) { - continue; - } - - categoriesHTML[setting.categoryId] += setting.getHtml(this.app); - } - - return Object.keys(categoriesHTML) - .map(k => categoriesHTML[k] + "
    ") - .join(""); - } - - renderBuildText() { - const labelVersion = this.htmlElement.querySelector(".buildVersion"); - if (!labelVersion) { - return; - } - const lastBuildMs = new Date().getTime() - G_BUILD_TIME; - const lastBuildText = formatSecondsToTimeAgo(lastBuildMs / 1000.0); - - const version = T.settings.versionBadges[G_APP_ENVIRONMENT]; - - labelVersion.innerHTML = ` - - ${G_BUILD_VERSION} @ ${version} @ ${G_BUILD_COMMIT_HASH} - - - ${T.settings.buildDate.replace("", lastBuildText)}
    -
    `; - } - - onEnter(payload) { - this.renderBuildText(); - - if (!G_CHINA_VERSION && !G_WEGAME_VERSION) { - this.trackClicks(this.htmlElement.querySelector(".about"), this.onAboutClicked, { - preventDefault: false, - }); - this.trackClicks(this.htmlElement.querySelector(".privacy"), this.onPrivacyClicked, { - preventDefault: false, - }); - } - - const keybindingsButton = this.htmlElement.querySelector(".editKeybindings"); - - if (keybindingsButton) { - this.trackClicks(keybindingsButton, this.onKeybindingsClicked, { preventDefault: false }); - } - - this.initSettings(); - this.initCategoryButtons(); - - this.htmlElement.querySelector(".category").classList.add("active"); - this.htmlElement.querySelector(".categoryButton").classList.add("active"); - - const modsButton = this.htmlElement.querySelector(".manageMods"); - if (modsButton) { - this.trackClicks(modsButton, this.onModsClicked, { preventDefault: false }); - } - } - - setActiveCategory(category) { - const previousCategory = this.htmlElement.querySelector(".category.active"); - const previousCategoryButton = this.htmlElement.querySelector(".categoryButton.active"); - - if (previousCategory.getAttribute("data-category") == category) { - return; - } - - previousCategory.classList.remove("active"); - previousCategoryButton.classList.remove("active"); - - const newCategory = this.htmlElement.querySelector("[data-category='" + category + "']"); - const newCategoryButton = this.htmlElement.querySelector("[data-category-btn='" + category + "']"); - - newCategory.classList.add("active"); - newCategoryButton.classList.add("active"); - } - - initSettings() { - this.app.settings.settingHandles.forEach(setting => { - if (!setting.categoryId) { - return; - } - - /** @type {HTMLElement} */ - const element = this.htmlElement.querySelector("[data-setting='" + setting.id + "']"); - setting.bind(this.app, element, this.dialogs); - setting.syncValueToElement(); - this.trackClicks( - element, - () => { - setting.modify(); - }, - { preventDefault: false } - ); - }); - } - - initCategoryButtons() { - Object.keys(enumCategories).forEach(key => { - const category = enumCategories[key]; - const button = this.htmlElement.querySelector("[data-category-btn='" + category + "']"); - this.trackClicks( - button, - () => { - this.setActiveCategory(category); - }, - { preventDefault: false } - ); - }); - } - - onAboutClicked() { - this.moveToStateAddGoBack("AboutState"); - } - - onPrivacyClicked() { - this.app.platformWrapper.openExternalLink(THIRDPARTY_URLS.privacyPolicy); - } - - onKeybindingsClicked() { - this.moveToStateAddGoBack("KeybindingsState"); - } - - onModsClicked() { - this.moveToStateAddGoBack("ModsState"); - } -} +import { THIRDPARTY_URLS } from "../core/config"; +import { TextualGameState } from "../core/textual_game_state"; +import { formatSecondsToTimeAgo } from "../core/utils"; +import { enumCategories } from "../profile/application_settings"; +import { T } from "../translations"; + +export class SettingsState extends TextualGameState { + constructor() { + super("SettingsState"); + } + + getStateHeaderTitle() { + return T.settings.title; + } + + getMainContentHTML() { + return ` + + + +
    + ${this.getSettingsHtml()} +
    + + `; + } + + getCategoryButtonsHtml() { + return Object.keys(enumCategories) + .map(key => enumCategories[key]) + .map( + category => + ` + + ` + ) + .join(""); + } + + getSettingsHtml() { + const categoriesHTML = {}; + + Object.keys(enumCategories).forEach(key => { + const catName = enumCategories[key]; + categoriesHTML[catName] = `
    `; + }); + + for (let i = 0; i < this.app.settings.settingHandles.length; ++i) { + const setting = this.app.settings.settingHandles[i]; + if (!setting.categoryId) { + continue; + } + + categoriesHTML[setting.categoryId] += setting.getHtml(this.app); + } + + return Object.keys(categoriesHTML) + .map(k => categoriesHTML[k] + "
    ") + .join(""); + } + + renderBuildText() { + const labelVersion = this.htmlElement.querySelector(".buildVersion"); + if (!labelVersion) { + return; + } + const lastBuildMs = new Date().getTime() - G_BUILD_TIME; + const lastBuildText = formatSecondsToTimeAgo(lastBuildMs / 1000.0); + + const version = T.settings.versionBadges[G_APP_ENVIRONMENT]; + + labelVersion.innerHTML = ` + + ${G_BUILD_VERSION} @ ${version} @ ${G_BUILD_COMMIT_HASH} + + + ${T.settings.buildDate.replace("", lastBuildText)}
    +
    `; + } + + onEnter(payload) { + this.renderBuildText(); + + this.trackClicks(this.htmlElement.querySelector(".about"), this.onAboutClicked, { + preventDefault: false, + }); + this.trackClicks(this.htmlElement.querySelector(".privacy"), this.onPrivacyClicked, { + preventDefault: false, + }); + + const keybindingsButton = this.htmlElement.querySelector(".editKeybindings"); + + if (keybindingsButton) { + this.trackClicks(keybindingsButton, this.onKeybindingsClicked, { preventDefault: false }); + } + + this.initSettings(); + this.initCategoryButtons(); + + this.htmlElement.querySelector(".category").classList.add("active"); + this.htmlElement.querySelector(".categoryButton").classList.add("active"); + + const modsButton = this.htmlElement.querySelector(".manageMods"); + if (modsButton) { + this.trackClicks(modsButton, this.onModsClicked, { preventDefault: false }); + } + } + + setActiveCategory(category) { + const previousCategory = this.htmlElement.querySelector(".category.active"); + const previousCategoryButton = this.htmlElement.querySelector(".categoryButton.active"); + + if (previousCategory.getAttribute("data-category") == category) { + return; + } + + previousCategory.classList.remove("active"); + previousCategoryButton.classList.remove("active"); + + const newCategory = this.htmlElement.querySelector("[data-category='" + category + "']"); + const newCategoryButton = this.htmlElement.querySelector("[data-category-btn='" + category + "']"); + + newCategory.classList.add("active"); + newCategoryButton.classList.add("active"); + } + + initSettings() { + this.app.settings.settingHandles.forEach(setting => { + if (!setting.categoryId) { + return; + } + + /** @type {HTMLElement} */ + const element = this.htmlElement.querySelector("[data-setting='" + setting.id + "']"); + setting.bind(this.app, element, this.dialogs); + setting.syncValueToElement(); + this.trackClicks( + element, + () => { + setting.modify(); + }, + { preventDefault: false } + ); + }); + } + + initCategoryButtons() { + Object.keys(enumCategories).forEach(key => { + const category = enumCategories[key]; + const button = this.htmlElement.querySelector("[data-category-btn='" + category + "']"); + this.trackClicks( + button, + () => { + this.setActiveCategory(category); + }, + { preventDefault: false } + ); + }); + } + + onAboutClicked() { + this.moveToStateAddGoBack("AboutState"); + } + + onPrivacyClicked() { + this.app.platformWrapper.openExternalLink(THIRDPARTY_URLS.privacyPolicy); + } + + onKeybindingsClicked() { + this.moveToStateAddGoBack("KeybindingsState"); + } + + onModsClicked() { + this.moveToStateAddGoBack("ModsState"); + } +} diff --git a/src/js/states/wegame_splash.js b/src/js/states/wegame_splash.js deleted file mode 100644 index a5483112..00000000 --- a/src/js/states/wegame_splash.js +++ /dev/null @@ -1,27 +0,0 @@ -import { GameState } from "../core/game_state"; - -export class WegameSplashState extends GameState { - constructor() { - super("WegameSplashState"); - } - - getInnerHTML() { - return ` -
    - 健康游戏忠告 -
    抵制不良游戏,拒绝盗版游戏。
    -
    注意自我保护,谨防受骗上当。
    -
    适度游戏益脑,沉迷游戏伤身。
    -
    合理安排时间,享受健康生活。
    -
    -`; - } - onEnter() { - setTimeout( - () => { - this.app.stateMgr.moveToState("PreloadState"); - }, - G_IS_DEV ? 1 : 6000 - ); - } -} diff --git a/src/js/translations.js b/src/js/translations.js index 9d976a41..9599665e 100644 --- a/src/js/translations.js +++ b/src/js/translations.js @@ -5,9 +5,9 @@ import { LANGUAGES } from "./languages"; const logger = createLogger("translations"); // @ts-ignore -const baseTranslations = require("./built-temp/base-en.json"); +import baseTranslations from "./built-temp/base-en.json"; -export let T = baseTranslations; +export const T = baseTranslations; if (G_IS_DEV && globalConfig.debug.testTranslations) { // Replaces all translations by fake translations to see whats translated and what not @@ -67,18 +67,11 @@ function mapLanguageCodeToId(languageKey) { * @returns {string} */ export function autoDetectLanguageId() { - let languages = []; - if (navigator.languages) { - languages = navigator.languages.slice(); - } else if (navigator.language) { - languages = [navigator.language]; - } else { - logger.warn("Navigator has no languages prop"); - } + const languages = navigator.languages; - for (let i = 0; i < languages.length; ++i) { - logger.log("Trying to find language target for", languages[i]); - const trans = mapLanguageCodeToId(languages[i]); + for (const language of languages) { + logger.log("Trying to find language target for", language); + const trans = mapLanguageCodeToId(language); if (trans) { return trans; } @@ -88,6 +81,16 @@ export function autoDetectLanguageId() { return "en"; } +/** + * Loads translation data for the specified language + * @param {string} code + * @param {string | ""} region + */ +export async function loadTranslationData(code, region) { + const locale = code + (region === "" ? "" : `-${region}`); + return (await import(`./built-temp/base-${locale}.json`)).default; +} + export function matchDataRecursive(dest, src, addNewKeys = false) { if (typeof dest !== "object" || typeof src !== "object") { return; @@ -120,7 +123,7 @@ export function matchDataRecursive(dest, src, addNewKeys = false) { } } -export function updateApplicationLanguage(id) { +export async function updateApplicationLanguage(id) { logger.log("Setting application language:", id); const data = LANGUAGES[id]; @@ -130,8 +133,13 @@ export function updateApplicationLanguage(id) { return; } - if (data.data) { + if (id !== "en") { logger.log("Applying translations ..."); - matchDataRecursive(T, data.data); + const translations = await loadTranslationData(data.code, data.region); + + matchDataRecursive(T, translations); } + + // Translation overrides are used by mods + matchDataRecursive(T, data.overrides, true); } diff --git a/src/js/tsconfig.json b/src/js/tsconfig.json deleted file mode 100644 index 7ecc605a..00000000 --- a/src/js/tsconfig.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "compilerOptions": { - /* Basic Options */ - "target": "es6" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */, - "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */, - "lib": ["DOM", "ES2018"] /* Specify library files to be included in the compilation. */, - "allowJs": true /* Allow javascript files to be compiled. */, - "checkJs": true /* Report errors in .js files. */, - // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ - // "declaration": true, /* Generates corresponding '.d.ts' file. */ - // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ - // "sourceMap": true, /* Generates corresponding '.map' file. */ - // "outFile": "./typedefs_gen", /* Concatenate and emit output to single file. */ - // "outDir": "./typedefs_gen", /* Redirect output structure to the directory. */ - // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ - // "composite": true, /* Enable project compilation */ - // "incremental": true, /* Enable incremental compilation */ - // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ - // "removeComments": true, /* Do not emit comments to output. */ - "noEmit": true /* Do not emit outputs. */, - // "importHelpers": true, /* Import emit helpers from 'tslib'. */ - // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ - // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ - /* Strict Type-Checking Options */ - // "strict": true, /* Enable all strict type-checking options. */ - // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ - // "strictNullChecks": true, /* Enable strict null checks. */ - "strictFunctionTypes": true /* Enable strict checking of function types. */, - "strictBindCallApply": true /* Enable strict 'bind', 'call', and 'apply' methods on functions. */, - // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ - "noImplicitThis": true /* Raise error on 'this' expressions with an implied 'any' type. */, - "alwaysStrict": true /* Parse in strict mode and emit "use strict" for each source file. */, - /* Additional Checks */ - // "noUnusedLocals": true, /* Report errors on unused locals. */ - // "noUnusedParameters": true, /* Report errors on unused parameters. */ - // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ - "noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */, - /* Module Resolution Options */ - "moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */, - // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ - // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ - // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ - // "typeRoots": [], /* List of folders to include type definitions from. */ - // "types": [], /* Type declaration files to be included in compilation. */ - // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ - "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, - // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ - /* Source Map Options */ - // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ - // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ - /* Experimental Options */ - // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ - // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ - "resolveJsonModule": true - }, - "exclude": ["webworkers"] -} diff --git a/src/js/tslint.json b/src/js/tslint.json deleted file mode 100644 index c89e7770..00000000 --- a/src/js/tslint.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "defaultSeverity": "error", - "extends": ["tslint:recommended"], - "jsRules": { - "trailing-comma": false, - "comma-dangle": ["error", "never"], - "object-literal-sort-keys": false, - "member-ordering": false, - "max-line-length": false, - "no-console": false, - "forin": false, - "no-empty": false, - "space-before-function-paren": ["always"] - }, - "rulesDirectory": [] -} diff --git a/src/js/webworkers/background_animation_frame_emittter.worker.js b/src/js/webworkers/background_animation_frame_emittter.js similarity index 90% rename from src/js/webworkers/background_animation_frame_emittter.worker.js rename to src/js/webworkers/background_animation_frame_emittter.js index 3941e0dd..d409c590 100644 --- a/src/js/webworkers/background_animation_frame_emittter.worker.js +++ b/src/js/webworkers/background_animation_frame_emittter.js @@ -1,3 +1,5 @@ +/// + // We clamp high deltas so 30 fps is fairly ok const bgFps = 30; const desiredMsDelay = 1000 / bgFps; @@ -9,7 +11,6 @@ function tick() { const delta = now - lastTick; lastTick = now; - // @ts-ignore self.postMessage({ delta }); } diff --git a/src/js/webworkers/compression.ts b/src/js/webworkers/compression.ts new file mode 100644 index 00000000..2d548fb4 --- /dev/null +++ b/src/js/webworkers/compression.ts @@ -0,0 +1,20 @@ +/// + +import { encode } from "@msgpack/msgpack"; + +async function compress(data: unknown): Promise { + const input = new Blob([encode(data)]).stream(); + const gzip = new CompressionStream("gzip"); + const response = new Response(input.pipeThrough(gzip)); + + return new Uint8Array(await response.arrayBuffer()); +} + +self.addEventListener("message", async ev => { + try { + const result = await compress(ev.data); + self.postMessage({ result, error: null }, [result.buffer]); + } catch (err) { + self.postMessage({ result: null, error: err }); + } +}); diff --git a/src/js/webworkers/compression.worker.js b/src/js/webworkers/compression.worker.js deleted file mode 100644 index 1e567c05..00000000 --- a/src/js/webworkers/compression.worker.js +++ /dev/null @@ -1,41 +0,0 @@ -import { globalConfig } from "../core/config"; -import { compressX64 } from "../core/lzstring"; -import { computeCrc } from "../core/sensitive_utils.encrypt"; -import { compressObject } from "../savegame/savegame_compressor"; - -function accessNestedPropertyReverse(obj, keys) { - let result = obj; - for (let i = keys.length - 1; i >= 0; --i) { - result = result[keys[i]]; - } - return result; -} - -const salt = accessNestedPropertyReverse(globalConfig, ["file", "info"]); - -self.addEventListener("message", event => { - // @ts-ignore - const { jobId, job, data } = event.data; - const result = performJob(job, data); - - // @ts-ignore - self.postMessage({ jobId, result }); -}); - -function performJob(job, data) { - switch (job) { - case "compressX64": { - return compressX64(data); - } - - case "compressObject": { - const optimized = compressObject(data.obj); - const stringified = JSON.stringify(optimized); - - const checksum = computeCrc(stringified + salt); - return data.compressionPrefix + compressX64(checksum + stringified); - } - default: - throw new Error("Webworker: Unknown job: " + job); - } -} diff --git a/src/js/webworkers/decompression.ts b/src/js/webworkers/decompression.ts new file mode 100644 index 00000000..c9dde14e --- /dev/null +++ b/src/js/webworkers/decompression.ts @@ -0,0 +1,24 @@ +/// + +import { decodeAsync } from "@msgpack/msgpack"; + +async function decompress(data: Uint8Array): Promise { + const input = new Blob([data]).stream(); + const gunzip = new DecompressionStream("gzip"); + + return await decodeAsync(input.pipeThrough(gunzip)); +} + +self.addEventListener("message", async ev => { + if (!(ev.data instanceof Uint8Array)) { + self.postMessage({ result: null, error: new Error("Incoming data must be of type Uint8Array") }); + return; + } + + try { + const result = await decompress(ev.data); + self.postMessage({ result, error: null }); + } catch (err) { + self.postMessage({ result: null, error: err }); + } +}); diff --git a/src/js/webworkers/tsconfig.json b/src/js/webworkers/tsconfig.json deleted file mode 100644 index dce06856..00000000 --- a/src/js/webworkers/tsconfig.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "compilerOptions": { - "lib": ["ES2018","WebWorker"] - }, - "exclude": [], - "extends": "../tsconfig", - "include": ["*.worker.js"] -} diff --git a/src/tsconfig.json b/src/tsconfig.json new file mode 100644 index 00000000..c8aa9318 --- /dev/null +++ b/src/tsconfig.json @@ -0,0 +1,28 @@ +{ + "extends": ["@tsconfig/strictest/tsconfig"], + "include": ["./js/**/*"], + "compilerOptions": { + "allowJs": true, + "module": "es2022", + "moduleResolution": "bundler", + "noEmit": true, + "target": "ES2024", + /* JSX Compilation */ + "paths": { + "@/*": ["./js/*"] + }, + "jsx": "react-jsx", + "jsxImportSource": "@", + "types": [], + // remove when comfortable + "exactOptionalPropertyTypes": false, + "noImplicitAny": false, + "noImplicitOverride": false, + "noImplicitReturns": false, + "noPropertyAccessFromIndexSignature": false, + "noUncheckedIndexedAccess": false, + "strictNullChecks": false, + // eslint warns for this + "noUnusedLocals": true + } +} diff --git a/sync-translations.js b/sync-translations.js index f4944045..71ba8bb2 100644 --- a/sync-translations.js +++ b/sync-translations.js @@ -1,81 +1,80 @@ -// Synchronizes all translations - -const fs = require("fs"); -const matchAll = require("match-all"); -const path = require("path"); -const YAML = require("yaml"); - -const files = fs - .readdirSync(path.join(__dirname, "translations")) - .filter(x => x.endsWith(".yaml")) - .filter(x => x.indexOf("base-en") < 0); - -const originalContents = fs - .readFileSync(path.join(__dirname, "translations", "base-en.yaml")) - .toString("utf-8"); - -const original = YAML.parse(originalContents); - -const placeholderRegexp = /[[<]([a-zA-Z_0-9/-_]+?)[\]>]/gi; - -function match(originalObj, translatedObj, path = "/", ignorePlaceholderMismatch = false) { - for (const key in originalObj) { - if (!translatedObj.hasOwnProperty(key)) { - console.warn(" | Missing key", path + key); - translatedObj[key] = originalObj[key]; - continue; - } - const valueOriginal = originalObj[key]; - const valueMatching = translatedObj[key]; - if (typeof valueOriginal !== typeof valueMatching) { - console.warn(" | MISMATCHING type (obj|non-obj) in", path + key); - translatedObj[key] = originalObj[key]; - continue; - } - - if (typeof valueOriginal === "object") { - match(valueOriginal, valueMatching, path + key + "/", ignorePlaceholderMismatch); - } else if (typeof valueOriginal === "string") { - const originalPlaceholders = matchAll(valueOriginal, placeholderRegexp).toArray(); - const translatedPlaceholders = matchAll(valueMatching, placeholderRegexp).toArray(); - - if (!ignorePlaceholderMismatch && originalPlaceholders.length !== translatedPlaceholders.length) { - console.warn( - " | Mismatching placeholders in", - path + key, - "->", - originalPlaceholders, - "vs", - translatedPlaceholders - ); - translatedObj[key] = originalObj[key]; - continue; - } - } else { - console.warn(" | Unknown type: ", typeof valueOriginal); - } - } - - for (const key in translatedObj) { - if (!originalObj.hasOwnProperty(key)) { - console.warn(" | Obsolete key", path + key); - delete translatedObj[key]; - } - } -} - -for (let i = 0; i < files.length; ++i) { - const filename = files[i]; - const filePath = path.join(__dirname, "translations", filename); - console.log("Processing", filename); - const translatedContents = fs.readFileSync(filePath).toString("utf-8"); - - const json = YAML.parse(translatedContents); - match(original, json, "/", filename.toLowerCase().includes("zh-cn")); - - const stringified = YAML.stringify(json, { - indent: 4, - simpleKeys: true, - }); - fs.writeFileSync(filePath, stringified, "utf-8"); -} +// Synchronizes all translations + +const fs = require("fs"); +const path = require("path"); +const YAML = require("yaml"); + +const files = fs + .readdirSync(path.join(__dirname, "translations")) + .filter(x => x.endsWith(".yaml")) + .filter(x => x.indexOf("base-en") < 0); + +const originalContents = fs + .readFileSync(path.join(__dirname, "translations", "base-en.yaml")) + .toString("utf-8"); + +const original = YAML.parse(originalContents); + +const placeholderRegexp = /[[<]([a-zA-Z_0-9/-_]+?)[\]>]/gi; + +function match(originalObj, translatedObj, path = "/", ignorePlaceholderMismatch = false) { + for (const key in originalObj) { + if (!translatedObj.hasOwnProperty(key)) { + console.warn(" | Missing key", path + key); + translatedObj[key] = originalObj[key]; + continue; + } + const valueOriginal = originalObj[key]; + const valueMatching = translatedObj[key]; + if (typeof valueOriginal !== typeof valueMatching) { + console.warn(" | MISMATCHING type (obj|non-obj) in", path + key); + translatedObj[key] = originalObj[key]; + continue; + } + + if (typeof valueOriginal === "object") { + match(valueOriginal, valueMatching, path + key + "/", ignorePlaceholderMismatch); + } else if (typeof valueOriginal === "string") { + const originalPlaceholders = [...valueOriginal.matchAll(placeholderRegexp)]; + const translatedPlaceholders = [...valueMatching.matchAll(placeholderRegexp)]; + + if (!ignorePlaceholderMismatch && originalPlaceholders.length !== translatedPlaceholders.length) { + console.warn( + " | Mismatching placeholders in", + path + key, + "->", + originalPlaceholders, + "vs", + translatedPlaceholders + ); + translatedObj[key] = originalObj[key]; + continue; + } + } else { + console.warn(" | Unknown type: ", typeof valueOriginal); + } + } + + for (const key in translatedObj) { + if (!originalObj.hasOwnProperty(key)) { + console.warn(" | Obsolete key", path + key); + delete translatedObj[key]; + } + } +} + +for (let i = 0; i < files.length; ++i) { + const filename = files[i]; + const filePath = path.join(__dirname, "translations", filename); + console.log("Processing", filename); + const translatedContents = fs.readFileSync(filePath).toString("utf-8"); + + const json = YAML.parse(translatedContents); + match(original, json, "/", filename.toLowerCase().includes("zh-cn")); + + const stringified = YAML.stringify(json, { + indent: 4, + simpleKeys: true, + }); + fs.writeFileSync(filePath, stringified, "utf-8"); +} diff --git a/translations/.gitignore b/translations/.gitignore deleted file mode 100644 index 6f7f420d..00000000 --- a/translations/.gitignore +++ /dev/null @@ -1 +0,0 @@ -tmp diff --git a/translations/README.md b/translations/README.md index 020c7ca6..9e3fd533 100644 --- a/translations/README.md +++ b/translations/README.md @@ -1,83 +1,83 @@ -# Translations - -The base language is English and can be found [here](base-en.yaml). - -## Languages - -- [German](base-de.yaml) -- [French](base-fr.yaml) -- [Korean](base-kor.yaml) -- [Dutch](base-nl.yaml) -- [Polish](base-pl.yaml) -- [Portuguese (Brazil)](base-pt-BR.yaml) -- [Portuguese (Portugal)](base-pt-PT.yaml) -- [Russian](base-ru.yaml) -- [Greek](base-el.yaml) -- [Italian](base-it.yaml) -- [Romanian](base-ro.yaml) -- [Swedish](base-sv.yaml) -- [Chinese (Simplified)](base-zh-CN.yaml) -- [Chinese (Traditional)](base-zh-TW.yaml) -- [Spanish](base-es.yaml) -- [Hungarian](base-hu.yaml) -- [Turkish](base-tr.yaml) -- [Japanese](base-ja.yaml) -- [Lithuanian](base-lt.yaml) -- [Arabic](base-ar.yaml) -- [Norwegian](base-no.yaml) -- [Kroatian](base-hr.yaml) -- [Danish](base-da.yaml) -- [Finnish](base-fi.yaml) -- [Catalan](base-cat.yaml) -- [Slovenian](base-sl.yaml) -- [Ukrainian](base-uk.yaml) -- [Indonesian](base-ind.yaml) -- [Serbian](base-sr.yaml) -- [Czech](base-cz.yaml) - -(If you want to translate into a new language, see below!) - -## Editing existing translations - -If you want to edit an existing translation (Fixing typos, updating it to a newer version, etc), you can just use the github file editor to edit the file. - -- Click the language you want to edit from the list above -- Click the small "edit" symbol on the top right - -edit symbol - -- Do the changes you wish to do (Be sure **not** to translate placeholders! For example, ` minutes` should get ` Minuten` and **not** ` Minuten`!) - -- Click "Propose Changes" - -propose changes - -- Click "Create pull request" - -create pull request - -- I will review your changes and make comments, and eventually merge them so they will be in the next release! Be sure to regulary check the created pull request for comments. - -## Adding a new language - -Please DM me on Discord (tobspr#5407), so I can add the language template for you. - -**Important: I am currently not accepting new languages until the wires update is out!** - -Please use the following template: - -``` -Hey, could you add a new translation? - -Language: -Short code: -Local Name: -``` - -You can find the short code [here](https://www.science.co.il/language/Codes.php) (In column `Code 2`). - -PS: I'm super busy, but I'll give my best to do it quickly! - -## Updating a language to the latest version - -Run `yarn syncTranslations` in the root directory to synchronize all translations to the latest version! This will remove obsolete keys and add newly added keys. (Run `yarn` before to install packages). +# Translations + +The base language is English and can be found [here](base-en.yaml). + +## Languages + +- [German](base-de.yaml) +- [French](base-fr.yaml) +- [Korean](base-kor.yaml) +- [Dutch](base-nl.yaml) +- [Polish](base-pl.yaml) +- [Portuguese (Brazil)](base-pt-BR.yaml) +- [Portuguese (Portugal)](base-pt-PT.yaml) +- [Russian](base-ru.yaml) +- [Greek](base-el.yaml) +- [Italian](base-it.yaml) +- [Romanian](base-ro.yaml) +- [Swedish](base-sv.yaml) +- [Chinese (Simplified)](base-zh-CN.yaml) +- [Chinese (Traditional)](base-zh-TW.yaml) +- [Spanish](base-es.yaml) +- [Hungarian](base-hu.yaml) +- [Turkish](base-tr.yaml) +- [Japanese](base-ja.yaml) +- [Lithuanian](base-lt.yaml) +- [Arabic](base-ar.yaml) +- [Norwegian](base-no.yaml) +- [Kroatian](base-hr.yaml) +- [Danish](base-da.yaml) +- [Finnish](base-fi.yaml) +- [Catalan](base-cat.yaml) +- [Slovenian](base-sl.yaml) +- [Ukrainian](base-uk.yaml) +- [Indonesian](base-ind.yaml) +- [Serbian](base-sr.yaml) +- [Czech](base-cz.yaml) + +(If you want to translate into a new language, see below!) + +## Editing existing translations + +If you want to edit an existing translation (Fixing typos, updating it to a newer version, etc), you can just use the github file editor to edit the file. + +- Click the language you want to edit from the list above +- Click the small "edit" symbol on the top right + +edit symbol + +- Do the changes you wish to do (Be sure **not** to translate placeholders! For example, ` minutes` should get ` Minuten` and **not** ` Minuten`!) + +- Click "Propose Changes" + +propose changes + +- Click "Create pull request" + +create pull request + +- I will review your changes and make comments, and eventually merge them so they will be in the next release! Be sure to regulary check the created pull request for comments. + +## Adding a new language + +Please DM me on Discord (tobspr#5407), so I can add the language template for you. + +**Important: I am currently not accepting new languages until the wires update is out!** + +Please use the following template: + +``` +Hey, could you add a new translation? + +Language: +Short code: +Local Name: +``` + +You can find the short code [here](https://www.science.co.il/language/Codes.php) (In column `Code 2`). + +PS: I'm super busy, but I'll give my best to do it quickly! + +## Updating a language to the latest version + +Run `yarn syncTranslations` in the root directory to synchronize all translations to the latest version! This will remove obsolete keys and add newly added keys. (Run `yarn` before to install packages). diff --git a/translations/base-ar.yaml b/translations/base-ar.yaml index 08f440e3..e12985b8 100644 --- a/translations/base-ar.yaml +++ b/translations/base-ar.yaml @@ -1,27 +1,6 @@ -steamPage: - shortText: لعبة شيبز (أشكال) هي لعبة تدور حول بناء مصانع وتوصيلها حتى تقوم بشكل - .آلي بصناعة أشكال مختلفة تزداد تعقيدا في خريطة لانهائية. - discordLinkShort: Official Discord - intro: >- - لعبة شيبز (أشكال) هي لعبة مريحة تقوم فيها ببناء مصانع ووتشغيلها آليا - لصناعة أشكال هندسية. - - مع التقدم في المستوى، تزداد الأشكال تعقيداً، فيتوجب عليك التوسع في الخريطة اللانهائية، وذلك ليس كافياً للتقدم في مستوى اللعبة حيث عليك صناعة المزيد بأضعاف مضاعفة لتلبية الطلب، الشيء الوحيد الذي يمكنه مساعدتك هو التوسع. - - بينما في البداية تقوم بصناعة أشكال مختلفة، تتطلب منك المراحل المتقدمة تلوين هذه الأشكال، حيث يتوجب عليك استخراج وخلط الألوان. - - عند شراءك اللعبة على ستيم (Steam) تحصل على الإصدار الكامل للعبة، ولكن يمكن أيضاً لعبة نسخة تجريبية على موقع shapez.io ثم يمكنك القرار لاحقا - what_others_say: What people say about shapez.io - nothernlion_comment: This game is great - I'm having a wonderful time playing, - and time has flown by. - notch_comment: Oh crap. I really should sleep, but I think I just figured out - how to make a computer in shapez.io - steam_review_comment: This game has stolen my life and I don't want it back. - Very chill factory game that won't let me stop making my lines more - efficient. global: - loading: Loading - error: Error + loading: جاري التحميل + error: خطأ thousandsDivider: "," decimalSeparator: . suffix: @@ -34,11 +13,11 @@ global: oneSecondAgo: قبل ثانية واحدة xSecondsAgo: قبل ثانية oneMinuteAgo: قبل دقيقة واحدة - xMinutesAgo: minutes ago - oneHourAgo: one hour ago - xHoursAgo: hours ago - oneDayAgo: one day ago - xDaysAgo: days ago + xMinutesAgo: قبل دقيقة + oneHourAgo: قبل ساعة واحدة + xHoursAgo: قبل ساعة + oneDayAgo: قبل يوم واحد + xDaysAgo: قبل يوم secondsShort: s minutesAndSecondsShort: m s hoursAndMinutesShort: h m @@ -50,1296 +29,1138 @@ global: escape: ESC shift: SHIFT space: SPACE - loggingIn: Logging in - loadingResources: Downloading additional resources ( %) - discount: -% - discountSummerSale: SPECIAL PROMOTION! Offer ends 7 July -demoBanners: - title: Demo Version - intro: |- - Get the full game now to unlock:
      -
    • All 26 levels + infinite Freeplay
    • -
    • 22 new buildings
    • -
    • Mod support
    • -
    • Achievements
    • -
    • Dark Mode
    • -
    • ... and a lot more!
    • -
    - playtimeDisclaimer: The full version contains more than 24 hours of content. - playerCount: players like you are currently playing shapez on Steam - untilEndOfDemo: Until end of demo - playtimeDisclaimerDownload: You can continue your savegame in the full version! - Click here to download your savegame. - titleV2: "Play the full version now for:" + loggingIn: جاري تسجيل الدخول + loadingResources: يتم تحميل الملفات ( %) mainMenu: - play: Play - continue: Continue - newGame: New Game - changelog: Changelog - subreddit: Reddit - importSavegame: Import - openSourceHint: This game is open source! - discordLink: Official Discord Server - helpTranslate: Help translate! - madeBy: Made by - browserWarning: Sorry, but the game is known to run slow on your browser! Get - the standalone version or download chrome for the full experience. - savegameLevel: Level - savegameLevelUnknown: Unknown Level - savegameUnnamed: Unnamed - puzzleMode: Puzzle Mode - back: Back - puzzleDlcText: Do you enjoy compacting and optimizing factories? Get the Puzzle - DLC now on Steam for even more fun! - puzzleDlcWishlist: Wishlist now! - puzzleDlcViewNow: View Dlc + play: العب + continue: اكمل + newGame: لعبة جديدة + importSavegame: استيراد حفظ اللعبة + helpTranslate: ساعدنا في الترجمة! + savegameLevel: مرحلة رقم + savegameLevelUnknown: مرحلة غير معروفة + savegameUnnamed: بلا اسم mods: - title: Active Mods - warningPuzzleDLC: Playing the Puzzle DLC is not possible with mods. Please - disable all mods to play the DLC. - playingFullVersion: You are now playing the full version! - logout: Logout - noActiveSavegames: No active savegames found - Click play to start a new game! - playFullVersionV2: Bough shapez on Steam? Play the full version in your Browser! - playFullVersionStandalone: You can now also play the full version in your Browser! + warningPuzzleDLC: لا يمكنك لعب اضافة الالغاز مع المودات, يرجي ايقاف جميع المودات اولًا. + noActiveSavegames: لم يتم العثور على حفظ اللعبة النشطة - انقر على اللعب لبدء لعبة جديدة! dialogs: buttons: - ok: OK - delete: Delete - cancel: Cancel - later: Later - restart: Restart - reset: Reset - getStandalone: Get Standalone - deleteGame: I know what I am doing - viewUpdate: View Update - showUpgrades: Show Upgrades - showKeybindings: Show Keybindings - retry: Retry - continue: Continue - playOffline: Play Offline + ok: حسنًا + delete: حذف + cancel: إلغاء + later: لاحقًا + restart: إعادة تشغيل + reset: إعادة تعيين + deleteGame: أنا أعلم ما أفعله + viewUpdate: عرض التحديث + showUpgrades: عرض الترقيات + showKeybindings: عرض اختصارات المفاتيح + retry: إعادة المحاولة + continue: متابعة + playOffline: اللعب في وضع عدم الاتصال importSavegameError: - title: Import Error - text: "Failed to import your savegame:" + title: خطأ في الاستيراد + text: "فشل في استيراد ملف الحفظ الخاص بك:" importSavegameSuccess: - title: Savegame Imported - text: Your savegame has been successfully imported. + title: تم استيراد ملف الحفظ + text: تم استيراد ملف الحفظ الخاص بك بنجاح. gameLoadFailure: - title: Game is broken - text: "Failed to load your savegame:" + title: اللعبة معطلة + text: "فشل في تحميل ملف الحفظ الخاص بك:" confirmSavegameDelete: - title: Confirm deletion - text: Are you sure you want to delete the following game?

    - '' at level

    This can not be - undone! + title: تأكيد الحذف + text: هل أنت متأكد أنك تريد حذف اللعبة التالية؟

    '' في + المستوى

    لا يمكن التراجع عن هذا! savegameDeletionError: - title: Failed to delete - text: "Failed to delete the savegame:" + title: فشل في الحذف + text: "فشل في حذف ملف الحفظ:" restartRequired: - title: Restart required - text: You need to restart the game to apply the settings. + title: إعادة تشغيل مطلوبة + text: تحتاج إلى إعادة تشغيل اللعبة لتطبيق الإعدادات. editKeybinding: - title: Change Keybinding - desc: Press the key or mouse button you want to assign, or escape to cancel. + title: تغيير اختصار المفتاح + desc: اضغط على المفتاح أو زر الماوس الذي تريد تعيينه، أو اضغط على "إلغاء" + للإلغاء. resetKeybindingsConfirmation: - title: Reset keybindings - desc: This will reset all keybindings to their default values. Please confirm. + title: إعادة تعيين اختصارات المفاتيح + desc: سيتم إعادة تعيين جميع اختصارات المفاتيح إلى قيمها الافتراضية. يرجى + التأكيد. keybindingsResetOk: - title: Keybindings reset - desc: The keybindings have been reset to their respective defaults! - featureRestriction: - title: Demo Version - desc: You tried to access a feature () which is not available in the - demo. Consider getting the standalone version for the full - experience! - oneSavegameLimit: - title: Limited savegames - desc: You can only have one savegame at a time in the demo version. Please - remove the existing one or get the standalone version! + title: إعادة تعيين اختصارات المفاتيح + desc: تم إعادة تعيين جميع اختصارات المفاتيح إلى قيمها الافتراضية! updateSummary: - title: New update! - desc: "Here are the changes since you last played:" + title: تحديث جديد! + desc: "إليك التغييرات منذ آخر مرة لعبت فيها:" upgradesIntroduction: - title: Unlock Upgrades - desc: All shapes you produce can be used to unlock upgrades - Don't - destroy your old factories! The upgrades tab can be found - on the top right corner of the screen. + title: فتح الترقيات + desc: يمكن استخدام جميع الأشكال التي تنتجها لفتح الترقيات - لا تدمر + مصانعك القديمة! يمكن العثور على علامة التبويب الخاصة + بالترقيات في الزاوية اليمنى العلوية من الشاشة. massDeleteConfirm: - title: Confirm delete - desc: You are deleting a lot of buildings ( to be exact)! Are you sure - you want to do this? + title: تأكيد الحذف + desc: أنت تحذف الكثير من المباني ( على وجه الدقة)! هل أنت متأكد أنك تريد + القيام بذلك؟ massCutConfirm: - title: Confirm cut - desc: You are cutting a lot of buildings ( to be exact)! Are you sure you - want to do this? + title: تأكيد القص + desc: أنت تقص الكثير من المباني ( على وجه الدقة)! هل أنت متأكد أنك تريد + القيام بذلك؟ massCutInsufficientConfirm: - title: Confirm cut - desc: You can not afford to paste this area! Are you sure you want to cut it? + title: تأكيد القص + desc: لا يمكنك تحمل لصق هذه المنطقة! هل أنت متأكد أنك تريد قصها؟ blueprintsNotUnlocked: - title: Not unlocked yet - desc: Complete level 12 to unlock Blueprints! + title: لم يتم فتحه بعد + desc: أكمل المستوى 12 لفتح المخططات! keybindingsIntroduction: - title: Useful keybindings - desc: "This game has a lot of keybindings which make it easier to build big - factories. Here are a few, but be sure to check out the - keybindings!

    CTRL + - Drag: Select an area.
    SHIFT: - Hold to place multiple of one building.
    ALT: Invert orientation of placed - belts.
    " + title: اختصارات مفيدة + desc: "تحتوي هذه اللعبة على الكثير من اختصارات المفاتيح التي تجعل بناء المصانع + الكبيرة أسهل. إليك بعض منها، لكن تأكد من التحقق من اختصارات + المفاتيح!

    CTRL + + سحب: تحديد منطقة.
    SHIFT: اضغط + للاستمرار في وضع عدة مباني.
    ALT: + عكس اتجاه الأحزمة الموضوعة.
    " createMarker: - title: New Marker - titleEdit: Edit Marker - desc: Give it a meaningful name, you can also include a short - key of a shape (Which you can generate here) - markerDemoLimit: - desc: You can only create two custom markers in the demo. Get the standalone for - unlimited markers! - exportScreenshotWarning: - title: Export screenshot - desc: You requested to export your base as a screenshot. Please note that this - can be quite slow for a big base and even crash your game! + title: علامة جديدة + titleEdit: تحرير العلامة + desc: أعطها اسمًا ذا معنى، يمكنك أيضًا تضمين اختصار قصير لشكل + (يمكنك إنشاؤه هنا) editSignal: - title: Set Signal - descItems: "Choose a pre-defined item:" - descShortKey: ... or enter the short key of a shape (Which you - can generate here) - renameSavegame: - title: Rename Savegame - desc: You can rename your savegame here. - tutorialVideoAvailable: - title: Tutorial Available - desc: There is a tutorial video available for this level! Would you like to - watch it? - tutorialVideoAvailableForeignLanguage: - title: Tutorial Available - desc: There is a tutorial video available for this level, but it is only - available in English. Would you like to watch it? + title: تعيين إشارة + descItems: "اختر عنصرًا محددًا مسبقًا:" + descShortKey: ... أو أدخل الاختصار القصير لشكل (يمكنك إنشاؤه + هنا) editConstantProducer: - title: Set Item + title: تعيين عنصر + exportScreenshotWarning: + title: تصدير لقطة شاشة + desc: طلبت تصدير قاعدتك كلقطة شاشة. يرجى ملاحظة أن هذا سيكون بطيئًا جدًا لقاعدة + أكبر وقد يؤدي إلى تعطل اللعبة! + renameSavegame: + title: إعادة تسمية ملف الحفظ + desc: يمكنك إعادة تسمية ملف الحفظ الخاص بك هنا. + tutorialVideoAvailable: + title: البرنامج التعليمي متاح + desc: يوجد فيديو تعليمي متاح لهذا المستوى! هل ترغب في مشاهدته؟ + tutorialVideoAvailableForeignLanguage: + title: البرنامج التعليمي متاح + desc: يوجد فيديو تعليمي متاح لهذا المستوى، ولكنه متوفر فقط باللغة الإنجليزية. هل + ترغب في مشاهدته؟ puzzleLoadFailed: - title: Puzzles failed to load - desc: "Unfortunately the puzzles could not be loaded:" + title: فشل تحميل الألغاز + desc: "للأسف، لم يتم تحميل الألغاز:" submitPuzzle: - title: Submit Puzzle - descName: "Give your puzzle a name:" - descIcon: "Please enter a unique short key, which will be shown as the icon of - your puzzle (You can generate them here, or choose one - of the randomly suggested shapes below):" - placeholderName: Puzzle Title + title: تقديم اللغز + descName: "أعطِ لغزك اسمًا:" + descIcon: "يرجى إدخال مفتاح قصير فريد، والذي سيتم عرضه كأيقونة للغز الخاص بك + (يمكنك إنشاؤها هنا، أو اختر واحدة من الأشكال المقترحة + عشوائيًا أدناه):" + placeholderName: عنوان اللغز puzzleResizeBadBuildings: - title: Resize not possible - desc: You can't make the zone any smaller, because then some buildings would be - outside the zone. + title: تغيير الحجم غير ممكن + desc: لا يمكنك جعل المنطقة أصغر، لأنه بعد ذلك ستخرج بعض المباني خارج المنطقة. puzzleLoadError: - title: Bad Puzzle - desc: "The puzzle failed to load:" + title: لغز غير صالح + desc: "فشل اللغز في التحميل:" offlineMode: - title: Offline Mode - desc: We couldn't reach the servers, so the game has to run in offline mode. - Please make sure you have an active internet connection. + title: وضع عدم الاتصال + desc: لم نتمكن من الوصول إلى الخوادم، لذا يجب أن تعمل اللعبة في وضع عدم الاتصال. + يرجى التأكد من أن لديك اتصال إنترنت نشط. puzzleDownloadError: - title: Download Error - desc: "Failed to download the puzzle:" + title: خطأ في التنزيل + desc: "فشل في تنزيل اللغز:" puzzleSubmitError: - title: Submission Error - desc: "Failed to submit your puzzle:" + title: خطأ في التقديم + desc: "فشل في تقديم اللغز الخاص بك:" puzzleSubmitOk: - title: Puzzle Published - desc: Congratulations! Your puzzle has been published and can now be played by - others. You can now find it in the "My puzzles" section. + title: تم نشر اللغز + desc: تهانينا! لقد تم نشر اللغز الخاص بك ويمكن الآن للآخرين اللعب به. يمكنك الآن + العثور عليه في قسم "ألغزي". puzzleCreateOffline: - title: Offline Mode - desc: Since you are offline, you will not be able to save and/or publish your - puzzle. Would you still like to continue? + title: وضع عدم الاتصال + desc: نظرًا لأنك غير متصل، فلن تتمكن من حفظ و/أو نشر اللغز الخاص بك. هل لا تزال + ترغب في المتابعة؟ puzzlePlayRegularRecommendation: - title: Recommendation - desc: I strongly recommend playing the normal game to level 12 - before attempting the puzzle DLC, otherwise you may encounter - mechanics not yet introduced. Do you still want to continue? + title: توصية + desc: أوصي بشدة بلعب اللعبة العادية حتى المستوى 12 قبل محاولة + لغز DLC، وإلا قد تواجه آليات لم يتم تقديمها بعد. هل لا تزال ترغب في + المتابعة؟ puzzleShare: - title: Short Key Copied - desc: The short key of the puzzle () has been copied to your clipboard! It - can be entered in the puzzle menu to access the puzzle. + title: تم نسخ المفتاح القصير + desc: تم نسخ المفتاح القصير للغز () إلى حافظة جهازك! يمكن إدخاله في قائمة + الألغاز للوصول إلى اللغز. puzzleReport: - title: Report Puzzle + title: الإبلاغ عن اللغز options: - profane: Profane - unsolvable: Not solvable - trolling: Trolling + profane: بذيء + unsolvable: غير قابل للحل + trolling: مزاح puzzleReportComplete: - title: Thank you for your feedback! - desc: The puzzle has been flagged. + title: شكرًا على ملاحظاتك! + desc: تم الإبلاغ عن اللغز. puzzleReportError: - title: Failed to report - desc: "Your report could not get processed:" + title: فشل في الإبلاغ + desc: "لم يتمكن تقريرك من المعالجة:" puzzleLoadShortKey: - title: Enter short key - desc: Enter the short key of the puzzle to load it. + title: أدخل المفتاح القصير + desc: أدخل المفتاح القصير للغز لتحميله. puzzleDelete: - title: Delete Puzzle? - desc: Are you sure you want to delete ''? This can not be undone! + title: حذف اللغز؟ + desc: هل أنت متأكد أنك تريد حذف '<title>'؟ لا يمكن التراجع عن ذلك! modsDifference: - title: Mod Warning - desc: The currently installed mods differ from the mods the savegame was created - with. This might cause the savegame to break or not load at all. Are - you sure you want to continue? - missingMods: Missing Mods - newMods: Newly installed Mods + title: تحذير التعديل + desc: تختلف التعديلات المثبتة حاليًا عن التعديلات التي تم إنشاء ملف الحفظ بها. + قد يؤدي ذلك إلى تعطل ملف الحفظ أو عدم تحميله على الإطلاق. هل أنت + متأكد أنك تريد المتابعة؟ + missingMods: التعديلات المفقودة + newMods: التعديلات المثبتة حديثًا resourceLoadFailed: - title: Resources failed to load - demoLinkText: shapez demo on Steam - descWeb: "One ore more resources could not be loaded. Make sure you have a - stable internet connection and try again. If this still doesn't - work, make sure to also disable any browser extensions (including - adblockers).<br><br> As an alternative, you can also play the - <demoOnSteamLinkText>. <br><br> Error Message:" - descSteamDemo: "One ore more resources could not be loaded. Try restarting the - game - If that does not help, try reinstalling and verifying the - game files via Steam. <br><br> Error Message:" - steamSsoError: - title: Full Version Logout - desc: You have been logged out from the Full Browser Version since either your - network connection is unstable or you are playing on another - device.<br><br> Please make sure you don't have shapez open in any - other browser tab or another computer with the same Steam - account.<br><br> You can login again in the main menu. - steamSsoNoOwnership: - title: Full Edition not owned - desc: In order to play the Full Edition in your Browser, you need to own both - the base game and the Puzzle DLC on your Steam account.<br><br> - Please make sure you own both, signed in with the correct Steam - account and then try again. + title: فشل تحميل الموارد + descSteamDemo: "لم يتمكن واحد أو أكثر من الموارد من التحميل. حاول إعادة تشغيل + اللعبة - إذا لم يساعد ذلك، جرب إعادة تثبيت والتحقق من ملفات اللعبة + عبر Steam. <br><br> رسالة الخطأ:" ingame: keybindingsOverlay: - moveMap: Move - selectBuildings: Select area - stopPlacement: Stop placement - rotateBuilding: Rotate building - placeMultiple: Place multiple - reverseOrientation: Reverse orientation - disableAutoOrientation: Disable auto-orientation - toggleHud: Toggle HUD - placeBuilding: Place building - createMarker: Create marker - delete: Delete - pasteLastBlueprint: Paste last blueprint - lockBeltDirection: Enable belt planner - plannerSwitchSide: Flip planner side - cutSelection: Cut - copySelection: Copy - clearSelection: Clear selection - pipette: Pipette - switchLayers: Switch layers - clearBelts: Clear belts + moveMap: تحريك الخريطة + selectBuildings: تحديد المنطقة + stopPlacement: إيقاف التثبيت + rotateBuilding: تدوير المبنى + placeMultiple: وضع عدة مبانٍ + reverseOrientation: عكس الاتجاه + disableAutoOrientation: تعطيل التوجيه التلقائي + toggleHud: تبديل واجهة المستخدم + placeBuilding: وضع المبنى + createMarker: إنشاء علامة + delete: حذف + pasteLastBlueprint: لصق المخطط الأخير + lockBeltDirection: تفعيل مخطط السير + plannerSwitchSide: قلب جانب المخطط + cutSelection: قص + copySelection: نسخ + clearSelection: مسح التحديد + pipette: ماصة + switchLayers: تبديل الطبقات + clearBelts: مسح السير colors: - red: Red - green: Green - blue: Blue - yellow: Yellow - purple: Purple - cyan: Cyan - white: White - black: Black - uncolored: Gray + red: أحمر + green: أخضر + blue: أزرق + yellow: أصفر + purple: بنفسجي + cyan: سماوي + white: أبيض + black: أسود + uncolored: رمادي buildingPlacement: - cycleBuildingVariants: Press <key> to cycle variants. - hotkeyLabel: "Hotkey: <key>" + cycleBuildingVariants: اضغط على <key> لتبديل النماذج. + hotkeyLabel: "المفتاح المختصر: <key>" infoTexts: - speed: Speed - range: Range - storage: Storage - oneItemPerSecond: 1 item / second - itemsPerSecond: <x> items / s + speed: السرعة + range: المدى + storage: التخزين + oneItemPerSecond: عنصر واحد / ثانية + itemsPerSecond: <x> عناصر / ثانية itemsPerSecondDouble: (x2) - tiles: <x> tiles + tiles: <x> بلاطات levelCompleteNotification: - levelTitle: Level <level> - completed: Completed - unlockText: Unlocked <reward>! - buttonNextLevel: Next Level + levelTitle: المستوى <level> + completed: تم + unlockText: تم فتح <reward>! + buttonNextLevel: المستوى التالي notifications: - newUpgrade: A new upgrade is available! - gameSaved: Your game has been saved. - freeplayLevelComplete: Level <level> has been completed! + newUpgrade: ترقية جديدة متاحة! + gameSaved: تم حفظ اللعبة. + freeplayLevelComplete: تم إكمال المستوى <level>! shop: - title: Upgrades - buttonUnlock: Upgrade - tier: Tier <x> - maximumLevel: MAXIMUM LEVEL (Speed x<currentMult>) + title: الترقيات + buttonUnlock: ترقية + tier: المستوى <x> + maximumLevel: الحد الأقصى للمستوى (السرعة x<currentMult>) statistics: - title: Statistics + title: الإحصائيات dataSources: stored: - title: Stored - description: Displaying amount of stored shapes in your central building. + title: المخزن + description: عرض كمية الأشكال المخزنة في المبنى المركزي. produced: - title: Produced - description: Displaying all shapes your whole factory produces, including - intermediate products. + title: الإنتاج + description: عرض جميع الأشكال التي تنتجها المصنع بالكامل، بما في ذلك المنتجات + الوسيطة. delivered: - title: Delivered - description: Displaying shapes which are delivered to your central building. - noShapesProduced: No shapes have been produced so far. + title: التسليم + description: عرض الأشكال التي تم تسليمها إلى المبنى المركزي. + noShapesProduced: لم يتم إنتاج أي أشكال حتى الآن. shapesDisplayUnits: - second: <shapes> / s - minute: <shapes> / m - hour: <shapes> / h + second: <shapes> / ثانية + minute: <shapes> / دقيقة + hour: <shapes> / ساعة settingsMenu: - playtime: Playtime - buildingsPlaced: Buildings - beltsPlaced: Belts + playtime: وقت اللعب + buildingsPlaced: المباني + beltsPlaced: السير tutorialHints: - title: Need help? - showHint: Show hint - hideHint: Close + title: تحتاج إلى مساعدة؟ + showHint: عرض التلميح + hideHint: إغلاق blueprintPlacer: - cost: Cost + cost: التكلفة waypoints: - waypoints: Markers - hub: HUB - description: Left-click a marker to jump to it, right-click to delete - it.<br><br>Press <keybinding> to create a marker from the current - view, or <strong>right-click</strong> to create a marker at the - selected location. - creationSuccessNotification: Marker has been created. + waypoints: العلامات + hub: المحور + description: انقر بالزر الأيسر على العلامة للانتقال إليها، انقر بالزر الأيمن + لحذفها .<br><br>اضغط على <keybinding> لإنشاء علامة من العرض الحالي، + أو <strong>انقر بالزر الأيمن</strong> لإنشاء علامة في الموقع المحدد. + creationSuccessNotification: تم إنشاء العلامة. shapeViewer: - title: Layers - empty: Empty - copyKey: Copy Key + title: الطبقات + empty: فارغ + copyKey: نسخ المفتاح interactiveTutorial: - title: Tutorial + title: البرنامج التعليمي hints: - 1_1_extractor: Place an <strong>extractor</strong> on top of a <strong>circle - shape</strong> to extract it! - 1_2_conveyor: "Connect the extractor with a <strong>conveyor belt</strong> to - your hub!<br><br>Tip: <strong>Click and drag</strong> the belt - with your mouse!" - 1_3_expand: "This is <strong>NOT</strong> an idle game! Build more extractors - and belts to finish the goal quicker.<br><br>Tip: Hold - <strong>SHIFT</strong> to place multiple extractors, and use - <strong>R</strong> to rotate them." - 2_1_place_cutter: "Now place a <strong>Cutter</strong> to cut the circles in two - halves!<br><br> PS: The cutter always cuts from <strong>top to - bottom</strong> regardless of its orientation." - 2_2_place_trash: The cutter can <strong>clog and stall</strong>!<br><br> Use a - <strong>trash</strong> to get rid of the currently (!) not - needed waste. - 2_3_more_cutters: "Good job! Now place <strong>2 more cutters</strong> to speed - up this slow process!<br><br> PS: Use the <strong>0-9 - hotkeys</strong> to access buildings faster!" - 3_1_rectangles: "Now let's extract some rectangles! <strong>Build 4 - extractors</strong> and connect them to the hub.<br><br> PS: - Hold <strong>SHIFT</strong> while dragging a belt to activate - the belt planner!" - 21_1_place_quad_painter: Place the <strong>quad painter</strong> and get some - <strong>circles</strong>, <strong>white</strong> and - <strong>red</strong> color! - 21_2_switch_to_wires: Switch to the wires layer by pressing - <strong>E</strong>!<br><br> Then <strong>connect all four - inputs</strong> of the painter with cables! - 21_3_place_button: Awesome! Now place a <strong>Switch</strong> and connect it - with wires! - 21_4_press_button: "Press the switch to make it <strong>emit a truthy - signal</strong> and thus activate the painter.<br><br> PS: You - don't have to connect all inputs! Try wiring only two." - 1_2_hold_and_drag: Hold and drag + 1_1_extractor: ضع <strong>مستخرجًا</strong> على شكل <strong>دائرة</strong> + لاستخراجها! + 1_2_conveyor: "قم بتوصيل المستخرج بـ <strong>حزام ناقل</strong> إلى المحور الخاص + بك! <br><br>نصيحة: <strong>انقر واسحب</strong> الحزام بالفأرة!" + 1_3_expand: "هذا ليس <strong>لعبة خمول</strong>! قم ببناء المزيد من المستخرجات + والأحزمة لإنهاء الهدف بشكل أسرع.<br><br>نصيحة: اضغط مع الاستمرار + على <strong>SHIFT</strong> لوضع عدة مستخرجات، واستخدم + <strong>R</strong> لتدويرها." + 2_1_place_cutter: "الآن ضع <strong>قاطعًا</strong> لقطع الدوائر إلى نصفين! + <br><br> ملاحظة: القاطع دائمًا يقطع من <strong>الأعلى إلى + الأسفل</strong> بغض النظر عن اتجاهه." + 2_2_place_trash: قد يتعطل <strong>القاطع</strong> ويتوقف!<br><br> استخدم + <strong>سلة المهملات</strong> للتخلص من النفايات غير المطلوبة + حاليًا. + 2_3_more_cutters: "عمل رائع! الآن ضع <strong>2 المزيد من القواطع</strong> لتسريع + هذه العملية البطيئة!<br><br> ملاحظة: استخدم <strong>المفاتيح + السريعة 0-9</strong> للوصول إلى المباني بشكل أسرع!" + 3_1_rectangles: "الآن لنستخرج بعض المستطيلات! <strong>قم ببناء 4 + مستخرجات</strong> وقم بتوصيلها إلى المحور.<br><br> ملاحظة: اضغط + مع الاستمرار على <strong>SHIFT</strong> أثناء سحب الحزام لتفعيل + مخطط السير!" + 21_1_place_quad_painter: ضع <strong>الرسام الرباعي</strong> واحصل على بعض + <strong>الدوائر</strong>، <strong>اللون الأبيض</strong> و + <strong>اللون الأحمر</strong>! + 21_2_switch_to_wires: انتقل إلى طبقة الأسلاك بالضغط على + <strong>E</strong>!<br><br> ثم <strong>قم بتوصيل جميع المدخلات + الأربعة</strong> للرسام بالكابلات! + 21_3_place_button: رائع! الآن ضع <strong>مفتاحًا</strong> وقم بتوصيله بالأسلاك! + 21_4_press_button: "اضغط على المفتاح ليقوم بإرسال <strong>إشارة صحيحة</strong> + ومن ثم تفعيل الرسام.<br><br> ملاحظة: لست بحاجة إلى توصيل جميع + المدخلات! جرب توصيل اثنين فقط." + 1_2_hold_and_drag: امسك واسحب connectedMiners: - one_miner: 1 Miner - n_miners: <amount> Miners - limited_items: Limited to <max_throughput> - watermark: - title: Demo version - desc: Click here to see the Steam version advantages! - get_on_steam: Get on steam - standaloneAdvantages: - no_thanks: No, thanks! - points: - levels: - title: 12 New Levels - desc: For a total of 26 levels! - buildings: - title: 22 New Buildings - desc: Fully automate your factory! - markers: - title: ∞ Markers - desc: Never get lost in your factory! - wires: - title: Wires - desc: An entirely new dimension! - darkmode: - title: Dark Mode - desc: Stop hurting your eyes! - support: - title: Support me - desc: I develop it in my spare time! - achievements: - title: Achievements - desc: Hunt them all! - mods: - title: Modding support! - desc: Over 80 mods available! - titleV2: "Get the full version now on Steam to unlock:" - titleExpiredV2: Demo completed! - titleEnjoyingDemo: Enjoy the demo? + one_miner: عامل منجم واحد + n_miners: <amount> من عمال المناجم + limited_items: محدود بـ <max_throughput> puzzleEditorSettings: - zoneTitle: Zone - zoneWidth: Width - zoneHeight: Height - trimZone: Trim - clearItems: Clear Items - share: Share - report: Report - clearBuildings: Clear Buildings - resetPuzzle: Reset Puzzle + zoneTitle: المنطقة + zoneWidth: العرض + zoneHeight: الارتفاع + trimZone: تقليم المنطقة + clearItems: مسح العناصر + share: مشاركة + report: بلاغ + clearBuildings: مسح المباني + resetPuzzle: إعادة ضبط اللغز puzzleEditorControls: - title: Puzzle Creator + title: منشئ الألغاز instructions: - - 1. Place <strong>Constant Producers</strong> to provide shapes and - colors to the player - - 2. Build one or more shapes you want the player to build later and - deliver it to one or more <strong>Goal Acceptors</strong> - - 3. Once a Goal Acceptor receives a shape for a certain amount of - time, it <strong>saves it as a goal</strong> that the player must - produce later (Indicated by the <strong>green badge</strong>). - - 4. Click the <strong>lock button</strong> on a building to disable - it. - - 5. Once you click review, your puzzle will be validated and you - can publish it. - - 6. Upon release, <strong>all buildings will be removed</strong> - except for the Producers and Goal Acceptors - That's the part that - the player is supposed to figure out for themselves, after all :) + - 1. ضع <strong>منتجين ثابتين</strong> لتوفير الأشكال والألوان للاعب + - 2. قم ببناء شكل أو أكثر تريد أن يقوم اللاعب ببنائه لاحقًا وتسليمه + إلى واحد أو أكثر من <strong>مستقبلي الأهداف</strong> + - 3. بمجرد أن يتلقى مستقبِل الهدف شكلًا لفترة معينة من الوقت، + <strong>يتم حفظه كهدف</strong> يجب على اللاعب إنتاجه لاحقًا (يُشار + إليه بـ<strong>الشارة الخضراء</strong>). + - 4. انقر على <strong>زر القفل</strong> على المبنى لتعطيله. + - 5. بمجرد النقر على المراجعة، سيتم التحقق من اللغز ويمكنك نشره. + - 6. عند الإطلاق، <strong>سيتم إزالة جميع المباني</strong> باستثناء + المنتجين ومستقبلي الأهداف - هذا هو الجزء الذي من المفترض أن يكتشفه + اللاعب بمفرده بعد كل شيء :) puzzleCompletion: - title: Puzzle Completed! - titleLike: "Click the heart if you liked the puzzle:" - titleRating: How difficult did you find the puzzle? - titleRatingDesc: Your rating will help me to make you better suggestions in the future - continueBtn: Keep Playing - menuBtn: Menu - nextPuzzle: Next Puzzle + title: اللغز مكتمل! + titleLike: "انقر على القلب إذا أعجبك اللغز:" + titleRating: ما مدى صعوبة اللغز بالنسبة لك؟ + titleRatingDesc: تقييمك سيساعدني في تقديم اقتراحات أفضل لك في المستقبل + continueBtn: استمر في اللعب + menuBtn: القائمة + nextPuzzle: اللغز التالي puzzleMetadata: - author: Author - shortKey: Short Key - rating: Difficulty score - averageDuration: Avg. Duration - completionRate: Completion rate + author: المؤلف + shortKey: المفتاح القصير + rating: درجة الصعوبة + averageDuration: متوسط المدة + completionRate: نسبة الإكمال shopUpgrades: belt: - name: Belts, Distributor & Tunnels - description: Speed x<currentMult> → x<newMult> + name: الأحزمة، الموزعون والأنفاق + description: السرعة x<currentMult> → x<newMult> miner: - name: Extraction - description: Speed x<currentMult> → x<newMult> + name: الاستخراج + description: السرعة x<currentMult> → x<newMult> processors: - name: Cutting, Rotating & Stacking - description: Speed x<currentMult> → x<newMult> + name: القطع، التدوير والتكديس + description: السرعة x<currentMult> → x<newMult> painting: - name: Mixing & Painting - description: Speed x<currentMult> → x<newMult> + name: الخلط والطلاء + description: السرعة x<currentMult> → x<newMult> buildings: hub: - deliver: Deliver - toUnlock: to unlock + deliver: تسليم + toUnlock: لفتح levelShortcut: LVL - endOfDemo: End of Demo + endOfDemo: نهاية العرض التجريبي belt: default: - name: Conveyor Belt - description: Transports items, hold and drag to place multiple. + name: حزام ناقل + description: ينقل العناصر، امسك واسحب لوضع عدة عناصر. wire: default: - name: Energy Wire - description: Allows you to transport energy. + name: سلك الطاقة + description: يسمح لك بنقل الطاقة. second: - name: Wire - description: Transfers signals, which can be items, colors or booleans (1 / 0). - Different colored wires do not connect. + name: سلك + description: ينقل الإشارات، والتي يمكن أن تكون عناصر أو ألوان أو قيم بوليانية (1 + / 0). الأسلاك الملونة المختلفة لا تتصل. miner: default: - name: Extractor - description: Place over a shape or color to extract it. + name: مستخرج + description: ضع فوق شكل أو لون لاستخراجه. chainable: - name: Extractor (Chain) - description: Place over a shape or color to extract it. Can be chained. + name: مستخرج (سلسلة) + description: ضع فوق شكل أو لون لاستخراجه. يمكن ربطه بسلاسل. underground_belt: default: - name: Tunnel - description: Allows you to tunnel resources under buildings and belts. + name: نفق + description: يسمح لك بحفر الموارد تحت المباني والأحزمة. tier2: - name: Tunnel Tier II - description: Allows you to tunnel resources under buildings and belts. + name: نفق من الدرجة الثانية + description: يسمح لك بحفر الموارد تحت المباني والأحزمة. cutter: default: - name: Cutter - description: Cuts shapes from top to bottom and outputs both halves. <strong>If - you use only one part, be sure to destroy the other part or it - will stall!</strong> + name: قاطع + description: يقطع الأشكال من الأعلى إلى الأسفل ويخرج نصفين. <strong>إذا استخدمت + جزءًا واحدًا فقط، تأكد من تدمير الجزء الآخر أو سيتوقف!</strong> quad: - name: Cutter (Quad) - description: Cuts shapes into four parts. <strong>If you use only one part, be - sure to destroy the other parts or it will stall!</strong> - rotater: + name: قاطع (رباعي) + description: يقطع الأشكال إلى أربعة أجزاء. <strong>إذا استخدمت جزءًا واحدًا فقط، + تأكد من تدمير الأجزاء الأخرى أو سيتوقف!</strong> + rotator: default: - name: Rotate - description: Rotates shapes clockwise by 90 degrees. + name: دوران + description: يدور الأشكال بعكس عقارب الساعة بزاوية 90 درجة. ccw: - name: Rotate (CCW) - description: Rotates shapes counter-clockwise by 90 degrees. + name: دوران (عكس عقارب الساعة) + description: يدور الأشكال بعكس عقارب الساعة بزاوية 90 درجة. rotate180: - name: Rotate (180) - description: Rotates shapes by 180 degrees. + name: دوران (180) + description: يدور الأشكال بزاوية 180 درجة. stacker: default: - name: Stacker - description: Stacks both items. If they can not be merged, the right item is - placed above the left item. + name: مكدس + description: يكدس كلاً من العناصر. إذا لم يكن بالإمكان دمجها، يتم وضع العنصر + الأيمن فوق العنصر الأيسر. mixer: default: - name: Color Mixer - description: Mixes two colors using additive blending. + name: خلاط الألوان + description: يمزج لونين باستخدام الخلط الإضافي. painter: default: - name: Painter - description: Colors the whole shape on the left input with the color from the - top input. + name: دهان + description: يلون الشكل بالكامل في المدخل الأيسر باللون من المدخل العلوي. mirrored: - name: Painter - description: Colors the whole shape on the left input with the color from the - top input. + name: دهان + description: يلون الشكل بالكامل في المدخل الأيسر باللون من المدخل العلوي. double: - name: Painter (Double) - description: Colors the shapes on the left inputs with the color from the top - input. + name: دهان (مزدوج) + description: يلون الأشكال في المدخلين الأيسر باللون من المدخل العلوي. quad: - name: Painter (Quad) - description: Allows you to color each quadrant of the shape individually. Only - slots with a <strong>truthy signal</strong> on the wires layer - will be painted! + name: دهان (رباعي) + description: يتيح لك تلوين كل ربع من الشكل بشكل فردي. سيتم تلوين الفتحات فقط + التي تحتوي على <strong>إشارة صحيحة</strong> في طبقة الأسلاك! trash: default: - name: Trash - description: Accepts inputs from all sides and destroys them. Forever. + name: نفايات + description: يقبل المدخلات من جميع الاتجاهات ويدمرها. إلى الأبد. balancer: default: - name: Balancer - description: Multifunctional - Evenly distributes all inputs onto all outputs. + name: موازن + description: متعدد الاستخدامات - يوزع المدخلات بشكل متساوٍ على جميع المخرجات. merger: - name: Merger (compact) - description: Merges two conveyor belts into one. + name: دمج (مضغوط) + description: يدمج حزامين ناقلين في واحد. merger-inverse: - name: Merger (compact) - description: Merges two conveyor belts into one. + name: دمج (مضغوط) + description: يدمج حزامين ناقلين في واحد. splitter: - name: Splitter (compact) - description: Splits one conveyor belt into two. + name: مقسم (مضغوط) + description: يقسم حزامًا ناقلًا واحدًا إلى اثنين. splitter-inverse: - name: Splitter (compact) - description: Splits one conveyor belt into two. + name: مقسم (مضغوط) + description: يقسم حزامًا ناقلًا واحدًا إلى اثنين. storage: default: - name: Storage - description: Stores excess items, up to a given capacity. Prioritizes the left - output and can be used as an overflow gate. + name: تخزين + description: يخزن العناصر الزائدة، حتى سعة معينة. يفضل المخرج الأيسر ويمكن + استخدامه كبوابة فائضة. wire_tunnel: default: - name: Wire Crossing - description: Allows to cross two wires without connecting them. + name: عبور السلك + description: يسمح بعبور سلكين دون ربطهما. constant_signal: default: - name: Constant Signal - description: Emits a constant signal, which can be either a shape, color or - boolean (1 / 0). + name: إشارة ثابتة + description: يصدر إشارة ثابتة، والتي يمكن أن تكون شكلًا أو لونًا أو قيمة + بوليانية (1 / 0). lever: default: - name: Switch - description: Can be toggled to emit a boolean signal (1 / 0) on the wires layer, - which can then be used to control for example an item filter. + name: مفتاح + description: يمكن toggled لإصدار إشارة بوليانية (1 / 0) في طبقة الأسلاك، والتي + يمكن استخدامها للتحكم على سبيل المثال في فلتر العناصر. logic_gate: default: - name: AND Gate - description: Emits a boolean "1" if both inputs are truthy. (Truthy means shape, - color or boolean "1") + name: بوابة AND + description: تصدر "1" بوليانية إذا كانت المدخلات كلاهما صحيحتين. (الصحيحة تعني + شكل، لون أو بوليانية "1") not: - name: NOT Gate - description: Emits a boolean "1" if the input is not truthy. (Truthy means - shape, color or boolean "1") + name: بوابة NOT + description: تصدر "1" بوليانية إذا لم تكن المدخلات صحيحة. (الصحيحة تعني شكل، لون + أو بوليانية "1") xor: - name: XOR Gate - description: Emits a boolean "1" if one of the inputs is truthy, but not both. - (Truthy means shape, color or boolean "1") + name: بوابة XOR + description: تصدر "1" بوليانية إذا كانت واحدة من المدخلات صحيحة، ولكن ليس + كلاهما. (الصحيحة تعني شكل، لون أو بوليانية "1") or: - name: OR Gate - description: Emits a boolean "1" if one of the inputs is truthy. (Truthy means - shape, color or boolean "1") + name: بوابة OR + description: تصدر "1" بوليانية إذا كانت واحدة من المدخلات صحيحة. (الصحيحة تعني + شكل، لون أو بوليانية "1") transistor: default: - name: Transistor - description: Forwards the bottom input if the side input is truthy (a shape, - color or "1"). + name: ترانزستور + description: ينقل المدخل السفلي إذا كانت المدخل الجانبي صحيحة (شكل، لون أو "1"). mirrored: - name: Transistor - description: Forwards the bottom input if the side input is truthy (a shape, - color or "1"). + name: ترانزستور + description: ينقل المدخل السفلي إذا كانت المدخل الجانبي صحيحة (شكل، لون أو "1"). filter: default: - name: Filter - description: Connect a signal to route all matching items to the top and the - remaining to the right. Can be controlled with boolean signals - too. + name: فلتر + description: قم بتوصيل إشارة لتوجيه جميع العناصر المطابقة إلى الأعلى والباقي إلى + اليمين. يمكن التحكم فيه باستخدام إشارات بوليانية أيضًا. display: default: - name: Display - description: Connect a signal to show it on the display - It can be a shape, - color or boolean. + name: شاشة عرض + description: قم بتوصيل إشارة لإظهارها على الشاشة - يمكن أن تكون شكلًا أو لونًا + أو بوليانية. reader: default: - name: Belt Reader - description: Allows to measure the average belt throughput. Outputs the last - read item on the wires layer (once unlocked). + name: قارئ الحزام + description: يسمح بقياس متوسط إنتاج الحزام. يصدر العنصر الأخير الذي تم قراءته في + طبقة الأسلاك (عند الفتح). analyzer: default: - name: Shape Analyzer - description: Analyzes the top right quadrant of the lowest layer of the shape - and returns its shape and color. + name: محلل الشكل + description: يحلل الربع العلوي الأيمن من أدنى طبقة الشكل ويعيد شكلها ولونها. comparator: default: - name: Compare - description: Returns boolean "1" if both signals are exactly equal. Can compare - shapes, items and booleans. + name: مقارنة + description: يعيد "1" بوليانية إذا كانت كلا الإشارتين متساويتين تمامًا. يمكن + مقارنة الأشكال، العناصر والبوليانات. virtual_processor: default: - name: Virtual Cutter - description: Virtually cuts the shape into two halves. - rotater: - name: Virtual Rotater - description: Virtually rotates the shape, both clockwise and counter-clockwise. + name: قاطع افتراضي + description: يقطع الشكل افتراضيًا إلى نصفين. + rotator: + name: دوار افتراضي + description: يدور الشكل افتراضيًا، سواء بعقارب الساعة أو عكسها. unstacker: - name: Virtual Unstacker - description: Virtually extracts the topmost layer to the right output and the - remaining ones to the left. + name: مفكك افتراضي + description: يستخرج الطبقة العليا إلى المخرج الأيمن والطبقات المتبقية إلى + اليسار. stacker: - name: Virtual Stacker - description: Virtually stacks the right shape onto the left. + name: مكدس افتراضي + description: يكدس الشكل الأيمن على الأيسر افتراضيًا. painter: - name: Virtual Painter - description: Virtually paints the shape from the bottom input with the shape on - the right input. + name: دهان افتراضي + description: يلون الشكل من المدخل السفلي بالشكل الموجود في المدخل الأيمن. item_producer: default: - name: Item Producer - description: Available in sandbox mode only, outputs the given signal from the - wires layer on the regular layer. + name: منتج العناصر + description: متاح فقط في وضع الصندوق الرمل، يصدر الإشارة المعطاة من طبقة الأسلاك + إلى الطبقة العادية. constant_producer: default: - name: Constant Producer - description: Constantly outputs a specified shape or color. + name: منتج ثابت + description: يصدر بشكل مستمر شكلًا أو لونًا محددًا. goal_acceptor: default: - name: Goal Acceptor - description: Deliver shapes to the goal acceptor to set them as a goal. + name: مستقبل الأهداف + description: تسليم الأشكال إلى مستقبل الأهداف لتحديدها كهدف. block: default: - name: Block - description: Allows you to block a tile. + name: كتلة + description: يسمح لك بحجب بلاطة. storyRewards: reward_cutter_and_trash: - title: Cutting Shapes - desc: You just unlocked the <strong>cutter</strong>, which cuts shapes in half - from top to bottom <strong>regardless of its - orientation</strong>!<br><br>Be sure to get rid of the waste, or - otherwise <strong>it will clog and stall</strong> - For this purpose - I have given you the <strong>trash</strong>, which destroys - everything you put into it! - reward_rotater: - title: Rotating - desc: The <strong>rotater</strong> has been unlocked! It rotates shapes - clockwise by 90 degrees. + title: قطع الأشكال + desc: لقد قمت بفتح <strong>القاطع</strong>، الذي يقوم بقطع الأشكال إلى نصفين من + الأعلى إلى الأسفل <strong>بغض النظر عن اتجاهه</strong>!<br><br>تأكد + من التخلص من النفايات، وإلا <strong>ستسد وتوقف</strong> - لهذا الغرض + أعطيتك <strong>سلة المهملات</strong>، التي تدمر كل ما تضعه فيها! + reward_rotator: + title: التدوير + desc: <strong>الدوران</strong> قد تم فتحه! يقوم بتدوير الأشكال باتجاه عقارب + الساعة بمقدار 90 درجة. reward_painter: - title: Painting - desc: "The <strong>painter</strong> has been unlocked - Extract some color veins - (just as you do with shapes) and combine it with a shape in the - painter to color them!<br><br>PS: If you are colorblind, there is a - <strong>colorblind mode</strong> in the settings!" + title: الطلاء + desc: "لقد تم فتح <strong>الدهان</strong> - استخرج بعض خطوط الألوان (تمامًا كما + تفعل مع الأشكال) وادمجها مع شكل في الدهان لتلوينه!<br><br>ملاحظة: + إذا كنت تعاني من عمى الألوان، هناك <strong>وضع لعمى الألوان</strong> + في الإعدادات!" reward_mixer: - title: Color Mixing - desc: The <strong>mixer</strong> has been unlocked - Combine two colors using - <strong>additive blending</strong> with this building! + title: خلط الألوان + desc: <strong>الخلاط</strong> قد تم فتحه - يقوم بخلط لونين باستخدام + <strong>الخلط الإضافي</strong>! reward_stacker: - title: Combiner - desc: You can now combine shapes with the <strong>combiner</strong>! Both inputs - are combined, and if they can be put next to each other, they will - be <strong>fused</strong>. If not, the right input is - <strong>stacked on top</strong> of the left input! - reward_splitter: - title: Splitter/Merger - desc: You have unlocked a <strong>splitter</strong> variant of the - <strong>balancer</strong> - It accepts one input and splits them - into two! - reward_tunnel: - title: Tunnel - desc: The <strong>tunnel</strong> has been unlocked - You can now tunnel items - through belts and buildings with it! - reward_rotater_ccw: - title: CCW Rotating - desc: You have unlocked a variant of the <strong>rotater</strong> - It allows - you to rotate shapes counter-clockwise! To build it, select the - rotater and <strong>press 'T' to cycle through its - variants</strong>! - reward_miner_chainable: - title: Chaining Extractor - desc: "You have unlocked the <strong>chained extractor</strong>! It can - <strong>forward its resources</strong> to other extractors so you - can more efficiently extract resources!<br><br> PS: The old - extractor has been replaced in your toolbar now!" - reward_underground_belt_tier_2: - title: Tunnel Tier II - desc: You have unlocked a new variant of the <strong>tunnel</strong> - It has a - <strong>bigger range</strong>, and you can also mix-n-match those - tunnels now! - reward_cutter_quad: - title: Quad Cutting - desc: You have unlocked a variant of the <strong>cutter</strong> - It allows you - to cut shapes in <strong>four parts</strong> instead of just two! - reward_painter_double: - title: Double Painting - desc: You have unlocked a variant of the <strong>painter</strong> - It works as - the regular painter but processes <strong>two shapes at - once</strong> consuming just one color instead of two! - reward_storage: - title: Storage Buffer - desc: You have unlocked the <strong>storage</strong> building - It allows you to - store items up to a given capacity!<br><br> It priorities the left - output, so you can also use it as an <strong>overflow gate</strong>! - reward_freeplay: - title: Freeplay - desc: You did it! You unlocked the <strong>free-play mode</strong>! This means - that shapes are now <strong>randomly</strong> generated!<br><br> - Since the hub will require a <strong>throughput</strong> from now - on, I highly recommend to build a machine which automatically - delivers the requested shape!<br><br> The HUB outputs the requested - shape on the wires layer, so all you have to do is to analyze it and - automatically configure your factory based on that. - reward_blueprints: - title: Blueprints - desc: You can now <strong>copy and paste</strong> parts of your factory! Select - an area (Hold CTRL, then drag with your mouse), and press 'C' to - copy it.<br><br>Pasting it is <strong>not free</strong>, you need to - produce <strong>blueprint shapes</strong> to afford it! (Those you - just delivered). - no_reward: - title: Next level - desc: "This level gave you no reward, but the next one will! <br><br> PS: Better - don't destroy your existing factory - You need <strong>all</strong> - those shapes later again to <strong>unlock upgrades</strong>!" - no_reward_freeplay: - title: Next level - desc: Congratulations! By the way, more content is planned for the standalone! + title: المكدس + desc: يمكنك الآن دمج الأشكال باستخدام <strong>المكدس</strong>! يتم دمج كلا + المدخلين، وإذا كان يمكن وضعهما بجانب بعضهما البعض، سيتم + <strong>دمجهما</strong>. إذا لم يكن كذلك، يتم <strong>تكديس المدخل + الأيمن فوق</strong> المدخل الأيسر! reward_balancer: - title: Balancer - desc: The multifunctional <strong>balancer</strong> has been unlocked - It can - be used to build bigger factories by <strong>splitting and merging - items</strong> onto multiple belts! + title: الموازن + desc: <strong>الموازن</strong> متعدد الوظائف قد تم فتحه - يمكن استخدامه لبناء + مصانع أكبر عن طريق <strong>تقسيم ودمج العناصر</strong> على عدة + أحزمة! + reward_tunnel: + title: النفق + desc: <strong>النفق</strong> قد تم فتحه - يمكنك الآن حفر العناصر تحت الأحزمة + والمباني باستخدامه! + reward_rotator_ccw: + title: التدوير عكس عقارب الساعة + desc: لقد قمت بفتح نسخة من <strong>الدوران</strong> - تسمح لك بتدوير الأشكال عكس + عقارب الساعة! لبنائه، حدد الدوار و<strong>اضغط على 'T' للدوران بين + أنواعه</strong>! + reward_miner_chainable: + title: مستخرج سلسلة + desc: "لقد قمت بفتح <strong>المستخرج المتسلسل</strong>! يمكنه <strong>إرسال + موارده</strong> إلى مستخرجين آخرين حتى تتمكن من استخراج الموارد بشكل + أكثر كفاءة!<br><br>ملاحظة: تم استبدال المستخرج القديم في شريط + الأدوات الآن!" + reward_underground_belt_tier_2: + title: النفق المستوى الثاني + desc: لقد قمت بفتح نوع جديد من <strong>النفق</strong> - لديه <strong>نطاق + أكبر</strong>، ويمكنك أيضًا مزج وتطابق تلك الأنفاق الآن! reward_merger: - title: Compact Merger - desc: You have unlocked a <strong>merger</strong> variant of the - <strong>balancer</strong> - It accepts two inputs and merges them - into one belt! + title: دمج مدمج + desc: لقد قمت بفتح <strong>نوع دمج</strong> من <strong>الموازن</strong> - يقبل + مدخلين ويجمعهما في حزام واحد! + reward_splitter: + title: قواطع مدمجة + desc: لقد قمت بفتح <strong>نوع قاطع</strong> من <strong>الموازن</strong> - يقبل + مدخل واحد ويقسمه إلى اثنين! reward_belt_reader: - title: Belt reader - desc: You have now unlocked the <strong>belt reader</strong>! It allows you to - measure the throughput of a belt.<br><br>And wait until you unlock - wires - then it gets really useful! - reward_rotater_180: - title: Rotater (180 degrees) - desc: You just unlocked the 180 degrees <strong>rotater</strong>! - It allows - you to rotate a shape by 180 degrees (Surprise! :D) - reward_display: - title: Display - desc: "You have unlocked the <strong>Display</strong> - Connect a signal on the - wires layer to visualize it!<br><br> PS: Did you notice the belt - reader and storage output their last read item? Try showing it on a - display!" - reward_constant_signal: - title: Constant Signal - desc: You unlocked the <strong>constant signal</strong> building on the wires - layer! This is useful to connect it to <strong>item filters</strong> - for example.<br><br> The constant signal can emit a - <strong>shape</strong>, <strong>color</strong> or - <strong>boolean</strong> (1 / 0). - reward_logic_gates: - title: Logic Gates - desc: You unlocked <strong>logic gates</strong>! You don't have to be excited - about this, but it's actually super cool!<br><br> With those gates - you can now compute AND, OR, XOR and NOT operations.<br><br> As a - bonus on top I also just gave you a <strong>transistor</strong>! - reward_virtual_processing: - title: Virtual Processing - desc: I just gave a whole bunch of new buildings which allow you to - <strong>simulate the processing of shapes</strong>!<br><br> You can - now simulate a cutter, rotater, stacker and more on the wires layer! - With this you now have three options to continue the game:<br><br> - - Build an <strong>automated machine</strong> to create any possible - shape requested by the HUB (I recommend to try it!).<br><br> - Build - something cool with wires.<br><br> - Continue to play - regulary.<br><br> Whatever you choose, remember to have fun! + title: قارئ الحزام + desc: لقد قمت بفتح <strong>قارئ الحزام</strong>! يسمح لك بقياس الإنتاجية + لحزام.<br><br>انتظر حتى تفتح الأسلاك - ستحصل على استخدام حقيقي! + reward_cutter_quad: + title: قاطع رباعي + desc: لقد قمت بفتح نوع من <strong>القاطع</strong> - يسمح لك بقطع الأشكال إلى + <strong>أربعة أجزاء</strong> بدلاً من مجرد اثنين! + reward_painter_double: + title: طلاء مزدوج + desc: لقد قمت بفتح نوع من <strong>الدهان</strong> - يعمل بشكل مشابه للدهان + العادي ولكن يعالج <strong>شكلين في وقت واحد</strong>، مستهلكًا لونًا + واحدًا بدلاً من اثنين! + reward_storage: + title: التخزين + desc: لقد قمت بفتح <strong>بناية التخزين</strong> - يسمح لك بتخزين العناصر حتى + سعة معينة!<br><br> يعطي الأولوية للمخرج الأيسر، لذا يمكنك أيضًا + استخدامه كبوابة <strong>تدفق زائد</strong>! + reward_blueprints: + title: المخططات + desc: يمكنك الآن <strong>نسخ ولصق</strong> أجزاء من مصنعك! حدد منطقة (اضغط على + CTRL، ثم اسحب بالفأرة)، واضغط على 'C' لنسخها.<br><br>لصقها + <strong>ليس مجانيًا</strong>، تحتاج إلى إنتاج <strong>أشكال + المخططات</strong> لتحصل عليها! (التي قمت بتسليمها للتو). + reward_rotator_180: + title: دوار (180°) + desc: لقد قمت بفتح <strong>الدوران بزاوية 180 درجة</strong>! - يسمح لك بتدوير + شكل بزاوية 180 درجة (مفاجأة! :D) reward_wires_painter_and_levers: - title: Wires & Quad Painter - desc: "You just unlocked the <strong>Wires Layer</strong>: It is a separate - layer on top of the regular layer and introduces a lot of new - mechanics!<br><br> For the beginning I unlocked you the <strong>Quad - Painter</strong> - Connect the slots you would like to paint with on - the wires layer!<br><br> To switch to the wires layer, press - <strong>E</strong>. <br><br> PS: <strong>Enable hints</strong> in - the settings to activate the wires tutorial!" + title: الأسلاك ودهان رباعي + desc: "لقد قمت بفتح <strong>طبقة الأسلاك</strong>: إنها طبقة منفصلة فوق الطبقة + العادية وتقدم الكثير من الآليات الجديدة!<br><br> في البداية، قمت + بفتح <strong>دهان رباعي</strong> - اربط الفتحات التي ترغب في تلوينها + مع طبقة الأسلاك!<br><br> للتبديل إلى طبقة الأسلاك، اضغط على + <strong>E</strong>. <br><br> ملاحظة: <strong>قم بتمكين + التلميحات</strong> في الإعدادات لتفعيل درس الأسلاك!" reward_filter: - title: Item Filter - desc: You unlocked the <strong>Item Filter</strong>! It will route items either - to the top or the right output depending on whether they match the - signal from the wires layer or not.<br><br> You can also pass in a - boolean signal (1 / 0) to entirely activate or disable it. + title: فلتر العناصر + desc: لقد قمت بفتح <strong>فلتر العناصر</strong>! سيوجه العناصر إما إلى المخرج + العلوي أو الأيمن اعتمادًا على ما إذا كانت تتطابق مع الإشارة من طبقة + الأسلاك أم لا.<br><br> يمكنك أيضًا تمرير إشارة منطقية (1 أو 0) + لتفعيلها أو تعطيلها بالكامل. + reward_display: + title: العرض + desc: "لقد قمت بفتح <strong>العرض</strong> - اربط إشارة على طبقة الأسلاك + لرؤيتها!<br><br> ملاحظة: هل لاحظت أن قارئ الحزام والتخزين يخرجان آخر + عنصر تم قراءته؟ حاول عرضه على شاشة!" + reward_constant_signal: + title: إشارة ثابتة + desc: لقد قمت بفتح <strong>بناية الإشارة الثابتة</strong> على طبقة الأسلاك! هذا + مفيد للربط مع <strong>فلاتر العناصر</strong> على سبيل + المثال.<br><br> يمكن للإشارة الثابتة أن تصدر <strong>شكلًا</strong> + أو <strong>لونًا</strong> أو <strong>منطقًا</strong> (1 أو 0). + reward_logic_gates: + title: بوابات منطقية + desc: لقد قمت بفتح <strong>البوابات المنطقية</strong>! لا داعي لأن تكون متحمسًا + لذلك، لكنه في الواقع رائع جدًا!<br><br> مع البوابات المنطقية يمكنك + الآن حساب عمليات AND وOR وXOR وNOT.<br><br> كمكافأة على ذلك، لقد + أعطيتك أيضًا <strong>ترانزستورًا</strong>! + reward_virtual_processing: + title: المعالجة الافتراضية + desc: لقد أعطيت مجموعة كاملة من المباني التي تسمح لك <strong>بمحاكاة معالجة + الأشكال</strong>!<br><br> يمكنك الآن محاكاة قاطع، دوار، مكدس والمزيد + على طبقة الأسلاك! مع هذا لديك الآن ثلاث خيارات لمتابعة + اللعبة:<br><br> - بناء <strong>آلة تلقائية</strong> لإنشاء أي شكل + ممكن يُطلب من قبل المركز (أوصيك بتجربتها!).<br><br> - بناء شيء رائع + باستخدام الأسلاك.<br><br> - متابعة اللعب بشكل طبيعي.<br><br> مهما + اخترت، تذكر أن تستمتع! + no_reward: + title: المستوى التالي + desc: "هذا المستوى لم يمنحك أي مكافأة، لكن المستوى التالي سيفعل! <br><br> + ملاحظة: من الأفضل عدم تدمير مصنعك الحالي - ستحتاج إلى + <strong>جميع</strong> تلك الأشكال لاحقًا لـ <strong>فتح + التحديثات</strong>!" + no_reward_freeplay: + title: المستوى التالي + desc: تهانينا! + reward_freeplay: + title: اللعب الحر + desc: لقد فعلت ذلك! لقد فتحت <strong>وضع اللعب الحر</strong>! هذا يعني أن + الأشكال تُولد الآن <strong>عشوائيًا</strong>!<br><br> نظرًا لأن + المركز سيطلب <strong>إنتاجية</strong> من الآن فصاعدًا، أوصي بشدة + ببناء آلة تقوم تلقائيًا بتوصيل الشكل المطلوب!<br><br> المركز يخرج + الشكل المطلوب على طبقة الأسلاك، لذا كل ما عليك فعله هو تحليله وتكوين + مصنعك تلقائيًا بناءً على ذلك. reward_demo_end: - title: End of Demo - desc: You have reached the end of the demo version! + title: نهاية العرض + desc: لقد وصلت إلى نهاية النسخة التجريبية! settings: - title: Settings + title: الإعدادات categories: - general: General - userInterface: User Interface - advanced: Advanced - performance: Performance + general: عام + userInterface: واجهة المستخدم + advanced: متقدم + performance: الأداء versionBadges: - dev: Development - staging: Staging - prod: Production - buildDate: Built <at-date> + dev: تطوير + staging: تجريبي + prod: إنتاج + buildDate: تم البناء في <at-date> labels: uiScale: - title: Interface scale - description: Changes the size of the user interface. The interface will still - scale based on your device's resolution, but this setting - controls the amount of scaling. + title: مقياس الواجهة + description: يغير حجم واجهة المستخدم. ستظل الواجهة تتناسب بناءً على دقة جهازك، + لكن هذا الإعداد يتحكم في مقدار التناسب. scales: - super_small: Super small - small: Small - regular: Regular - large: Large - huge: Huge + super_small: صغير جدًا + small: صغير + regular: عادي + large: كبير + huge: ضخم autosaveInterval: - title: Autosave Interval - description: Controls how often the game saves automatically. You can also - disable it entirely here. + title: فترة الحفظ التلقائي + description: يتحكم في مدى تكرار حفظ اللعبة تلقائيًا. يمكنك أيضًا تعطيله تمامًا + هنا. intervals: - one_minute: 1 Minute - two_minutes: 2 Minutes - five_minutes: 5 Minutes - ten_minutes: 10 Minutes - twenty_minutes: 20 Minutes - disabled: Disabled + one_minute: 1 دقيقة + two_minutes: 2 دقيقة + five_minutes: 5 دقائق + ten_minutes: 10 دقائق + twenty_minutes: 20 دقيقة + disabled: معطل scrollWheelSensitivity: - title: Zoom sensitivity - description: Changes how sensitive the zoom is (Either mouse wheel or trackpad). + title: حساسية الزوم + description: يغير مدى حساسية الزوم (سواء من خلال عجلة الماوس أو لوحة اللمس). sensitivity: - super_slow: Super slow - slow: Slow - regular: Regular - fast: Fast - super_fast: Super fast + super_slow: بطيء جدًا + slow: بطيء + regular: عادي + fast: سريع + super_fast: سريع جدًا movementSpeed: - title: Movement speed - description: Changes how fast the view moves when using the keyboard. + title: سرعة الحركة + description: يغير مدى سرعة حركة العرض عند استخدام لوحة المفاتيح. speeds: - super_slow: Super slow - slow: Slow - regular: Regular - fast: Fast - super_fast: Super Fast - extremely_fast: Extremely Fast + super_slow: بطيء جدًا + slow: بطيء + regular: عادي + fast: سريع + super_fast: سريع جدًا + extremely_fast: سريع للغاية language: - title: Language - description: Change the language. All translations are user-contributed and - might be incomplete! + title: اللغة + description: تغيير اللغة. جميع الترجمات مقدمة من المستخدمين وقد تكون غير مكتملة! enableColorBlindHelper: - title: Color Blind Mode - description: Enables various tools which allow you to play the game if you are - color blind. + title: وضع عمى الألوان + description: يمكنك من استخدام أدوات مختلفة تسمح لك بلعب اللعبة إذا كنت تعاني من + عمى الألوان. fullscreen: - title: Fullscreen - description: It is recommended to play the game in fullscreen to get the best - experience. Only available in the standalone. + title: ملء الشاشة + description: يوصى بلعب اللعبة في وضع ملء الشاشة للحصول على أفضل تجربة. متاح فقط + في النسخة المستقلة. soundsMuted: - title: Mute Sounds - description: If enabled, mutes all sound effects. + title: كتم الصوت + description: إذا تم تفعيله، سيتم كتم جميع المؤثرات الصوتية. musicMuted: - title: Mute Music - description: If enabled, mutes all music. + title: كتم الموسيقى + description: إذا تم تفعيله، سيتم كتم جميع الموسيقى. theme: - title: Game theme - description: Choose the game theme (light / dark). + title: سمة اللعبة + description: اختر سمة اللعبة (فاتح / داكن). themes: - dark: Dark - light: Light + dark: داكن + light: فاتح refreshRate: - title: Simulation Target - description: If you have a 144hz monitor, change the refresh rate here so the - game will properly simulate at higher refresh rates. This might - actually decrease the FPS if your computer is too slow. + title: معدل التحديث + description: إذا كان لديك شاشة بمعدل 144 هرتز، غير معدل التحديث هنا حتى تتمكن + اللعبة من المحاكاة بشكل صحيح بمعدلات تحديث أعلى. قد يؤدي ذلك إلى + تقليل معدل الإطارات إذا كان جهاز الكمبيوتر بطيئًا جدًا. alwaysMultiplace: - title: Multiplace - description: If enabled, all buildings will stay selected after placement until - you cancel it. This is equivalent to holding SHIFT permanently. + title: وضع التعدد + description: إذا تم تفعيله، ستبقى جميع المباني مختارة بعد التثبيت حتى تلغي ذلك. + هذا يعادل الضغط على SHIFT بشكل دائم. offerHints: - title: Hints & Tutorials - description: Whether to offer hints and tutorials while playing. Also hides - certain UI elements up to a given level to make it easier to get - into the game. + title: تلميحات ودروس + description: سواء لتقديم التلميحات والدروس أثناء اللعب. كما يخفي بعض عناصر واجهة + المستخدم حتى مستوى معين لتسهيل الدخول إلى اللعبة. enableTunnelSmartplace: - title: Smart Tunnels - description: When enabled, placing tunnels will automatically remove unnecessary - belts. This also enables you to drag tunnels and excess tunnels - will get removed. + title: الأنفاق الذكية + description: عند تفعيلها، ستقوم بوضع الأنفاق تلقائيًا بإزالة الأحزمة غير + الضرورية. يتيح لك ذلك أيضًا سحب الأنفاق وسيتم إزالة الأنفاق + الزائدة. vignette: - title: Vignette - description: Enables the vignette, which darkens the screen corners and makes - text easier to read. + title: تأثير الإضاءة المحيطية + description: يمكنك من تفعيل التأثير الذي يظلم زوايا الشاشة مما يجعل النص أسهل في + القراءة. rotationByBuilding: - title: Rotation by building type - description: Each building type remembers the rotation you last set it to - individually. This may be more comfortable if you frequently - switch between placing different building types. + title: التدوير حسب نوع المبنى + description: يتذكر كل نوع من المباني التدوير الذي قمت بضبطه آخر مرة بشكل فردي. + قد يكون ذلك أكثر راحة إذا كنت تتنقل بشكل متكرر بين أنواع المباني + المختلفة. compactBuildingInfo: - title: Compact Building Infos - description: Shortens info boxes for buildings by only showing their ratios. - Otherwise a description and image is shown. + title: معلومات المباني المضغوطة + description: يقصر صناديق المعلومات للمباني من خلال عرض نسبها فقط. بخلاف ذلك، + يظهر وصف وصورة. disableCutDeleteWarnings: - title: Disable Cut/Delete Warnings - description: Disables the warning dialogs brought up when cutting/deleting more - than 100 entities. + title: تعطيل تحذيرات القطع / الحذف + description: يعطل رسائل التحذير التي تظهر عند قطع / حذف أكثر من 100 كيان. soundVolume: - title: Sound Volume - description: Set the volume for sound effects + title: مستوى صوت المؤثرات الصوتية + description: ضبط مستوى الصوت للمؤثرات الصوتية. musicVolume: - title: Music Volume - description: Set the volume for music + title: مستوى صوت الموسيقى + description: ضبط مستوى الصوت للموسيقى. lowQualityMapResources: - title: Low Quality Map Resources - description: Simplifies the rendering of resources on the map when zoomed in to - improve performance. It even looks cleaner, so be sure to try it - out! + title: موارد خريطة ذات جودة منخفضة + description: تبسط من عرض الموارد على الخريطة عند التكبير لتحسين الأداء. تبدو + أكثر نظافة، لذا تأكد من تجربتها! disableTileGrid: - title: Disable Grid - description: Disabling the tile grid can help with the performance. This also - makes the game look cleaner! + title: تعطيل الشبكة + description: قد يساعد تعطيل الشبكة في الأداء. هذا يجعل اللعبة تبدو أنظف أيضًا! clearCursorOnDeleteWhilePlacing: - title: Clear Cursor on Right Click - description: Enabled by default, clears the cursor whenever you right click - while you have a building selected for placement. If disabled, - you can delete buildings by right-clicking while placing a - building. + title: مسح المؤشر عند الحذف بالزر الأيمن + description: مفعل بشكل افتراضي، يقوم بمسح المؤشر كلما قمت بالنقر بالزر الأيمن + بينما لديك مبنى محدد للتثبيت. إذا تم تعطيله، يمكنك حذف المباني + من خلال النقر بالزر الأيمن أثناء وضع مبنى. lowQualityTextures: - title: Low quality textures (Ugly) - description: Uses low quality textures to save performance. This will make the - game look very ugly! + title: قوام منخفض الجودة (قبيح) + description: يستخدم قوامًا منخفض الجودة لتوفير الأداء. سيجعل هذا اللعبة تبدو + قبيحة جدًا! displayChunkBorders: - title: Display Chunk Borders - description: The game is divided into chunks of 16x16 tiles, if this setting is - enabled the borders of each chunk are displayed. + title: عرض حدود الكتل + description: يتم تقسيم اللعبة إلى كتل من 16x16 بلاطة، إذا تم تفعيل هذا الإعداد، + يتم عرض حدود كل كتلة. pickMinerOnPatch: - title: Pick miner on resource patch - description: Enabled by default, selects the miner if you use the pipette when - hovering a resource patch. + title: اختر المنقب على رقعة الموارد + description: مفعل بشكل افتراضي، يختار المنقب إذا استخدمت pipette عند التحويم فوق + رقعة الموارد. simplifiedBelts: - title: Simplified Belts (Ugly) - description: Does not render belt items except when hovering the belt to save - performance. I do not recommend to play with this setting if you - do not absolutely need the performance. + title: أحزمة مبسطة (قبيحة) + description: لا يقوم بعرض عناصر الحزام إلا عند التحويم فوق الحزام لتوفير الأداء. + لا أوصي باللعب بهذا الإعداد إذا لم تكن بحاجة ماسة للأداء. enableMousePan: - title: Enable Mouse Pan - description: Allows to move the map by moving the cursor to the edges of the - screen. The speed depends on the Movement Speed setting. + title: تفعيل الحركة بالماوس + description: يتيح لك نقل الخريطة عن طريق تحريك المؤشر إلى حواف الشاشة. السرعة + تعتمد على إعداد سرعة الحركة. zoomToCursor: - title: Zoom towards Cursor - description: If activated the zoom will happen in the direction of your mouse - position, otherwise in the middle of the screen. + title: الزوم نحو المؤشر + description: إذا تم تفعيله، سيكون الزوم في اتجاه موضع الماوس، بخلاف ذلك يكون في + منتصف الشاشة. mapResourcesScale: - title: Map Resources Size - description: Controls the size of the shapes on the map overview (when zooming - out). + title: حجم موارد الخريطة + description: يتحكم في حجم الأشكال على نظرة الخريطة (عند التكبير). shapeTooltipAlwaysOn: - title: Shape Tooltip - Show Always - description: Whether to always show the shape tooltip when hovering buildings, - instead of having to hold 'ALT'. + title: نص الأداة - عرض دائم + description: سواء لإظهار نص الأداة للأشكال عند التحويم فوق المباني، بدلاً من + الحاجة إلى الضغط على 'ALT'. rangeSliderPercentage: <amount> % - tickrateHz: <amount> Hz - newBadge: New! + tickrateHz: <amount> هرتز + newBadge: جديد! keybindings: - title: Keybindings - hint: "Tip: Be sure to make use of CTRL, SHIFT and ALT! They enable different - placement options." - resetKeybindings: Reset Keybindings + title: اختصارات المفاتيح + hint: "نصيحة: تأكد من استخدام CTRL، SHIFT و ALT! فهي توفر خيارات وضع مختلفة." + resetKeybindings: إعادة تعيين اختصارات المفاتيح categoryLabels: - general: Application - ingame: Game - navigation: Navigating - placement: Placement - massSelect: Mass Select - buildings: Building Shortcuts - placementModifiers: Placement Modifiers - mods: Provided by Mods + general: التطبيق + ingame: اللعبة + navigation: التنقل + placement: وضع الأشياء + massSelect: تحديد جماعي + buildings: اختصارات المباني + placementModifiers: تعديلات الوضع + mods: مقدمة من الإضافات mappings: - confirm: Confirm - back: Back - mapMoveUp: Move Up - mapMoveRight: Move Right - mapMoveDown: Move Down - mapMoveLeft: Move Left - mapMoveFaster: Move Faster - centerMap: Center Map - mapZoomIn: Zoom in - mapZoomOut: Zoom out - createMarker: Create Marker - menuOpenShop: Upgrades - menuOpenStats: Statistics - menuClose: Close Menu - toggleHud: Toggle HUD - toggleFPSInfo: Toggle FPS and Debug Info - switchLayers: Switch layers - exportScreenshot: Export whole Base as Image - belt: Conveyor Belt - underground_belt: Tunnel - miner: Extractor - cutter: Cutter - rotater: Rotate - stacker: Stacker - mixer: Color Mixer - painter: Painter - trash: Trash - wire: Energy Wire - pipette: Pipette - rotateWhilePlacing: Rotate - rotateInverseModifier: "Modifier: Rotate CCW instead" - cycleBuildingVariants: Cycle Variants - confirmMassDelete: Delete area - pasteLastBlueprint: Paste last blueprint - cycleBuildings: Cycle Buildings - lockBeltDirection: Enable belt planner - switchDirectionLockSide: "Planner: Switch side" - massSelectStart: Hold and drag to start - massSelectSelectMultiple: Select multiple areas - massSelectCopy: Copy area - massSelectCut: Cut area - placementDisableAutoOrientation: Disable automatic orientation - placeMultiple: Stay in placement mode - placeInverse: Invert automatic belt orientation - balancer: Balancer - storage: Storage - constant_signal: Constant Signal - logic_gate: Logic Gate - lever: Switch (regular) - filter: Filter - wire_tunnel: Wire Crossing - display: Display - reader: Belt Reader - virtual_processor: Virtual Cutter - transistor: Transistor - analyzer: Shape Analyzer - comparator: Compare - item_producer: Item Producer (Sandbox) - copyWireValue: "Wires: Copy value below cursor" - rotateToUp: "Rotate: Point Up" - rotateToDown: "Rotate: Point Down" - rotateToRight: "Rotate: Point Right" - rotateToLeft: "Rotate: Point Left" - constant_producer: Constant Producer - goal_acceptor: Goal Acceptor - block: Block - massSelectClear: Clear belts - showShapeTooltip: Show shape output tooltip + confirm: تأكيد + back: رجوع + mapMoveUp: التحرك للأعلى + mapMoveRight: التحرك لليمين + mapMoveDown: التحرك للأسفل + mapMoveLeft: التحرك لليسار + mapMoveFaster: التحرك أسرع + centerMap: تمركز الخريطة + mapZoomIn: تكبير + mapZoomOut: تصغير + createMarker: إنشاء علامة + menuOpenShop: الترقيات + menuOpenStats: الإحصائيات + menuClose: إغلاق القائمة + toggleHud: تبديل واجهة المستخدم + toggleFPSInfo: تبديل معلومات FPS و Debug + switchLayers: تبديل الطبقات + exportScreenshot: تصدير القاعدة كصورة + belt: سير ناقل + underground_belt: نفق + miner: مستخرج + cutter: قاطع + rotator: تدوير + stacker: مكدس + mixer: خلاط ألوان + painter: رسام + trash: سلة المهملات + wire: سلك الطاقة + pipette: ماصة + rotateWhilePlacing: تدوير أثناء الوضع + rotateInverseModifier: "تعديل: تدوير عكس عقارب الساعة" + cycleBuildingVariants: تبديل المتغيرات + confirmMassDelete: حذف منطقة + pasteLastBlueprint: لصق المخطط الأخير + cycleBuildings: تبديل المباني + lockBeltDirection: تفعيل مخطط السير الناقل + switchDirectionLockSide: "مخطط: تبديل الجانب" + massSelectStart: اضغط واسحب لبدء التحديد + massSelectSelectMultiple: تحديد عدة مناطق + massSelectCopy: نسخ المنطقة + massSelectCut: قطع المنطقة + placementDisableAutoOrientation: تعطيل التوجه التلقائي + placeMultiple: البقاء في وضع الوضع + placeInverse: عكس توجه السير التلقائي + balancer: موزع + storage: تخزين + constant_signal: إشارة ثابتة + logic_gate: بوابة منطقية + lever: مفتاح (عادي) + filter: فلتر + wire_tunnel: عبور الأسلاك + display: شاشة عرض + reader: قارئ السير الناقل + virtual_processor: قاطع افتراضي + transistor: ترانزستور + analyzer: محلل الأشكال + comparator: مقارنة + item_producer: منتج العناصر (صندوق الرمل) + copyWireValue: "أسلاك: نسخ القيمة تحت المؤشر" + rotateToUp: "تدوير: الاتجاه للأعلى" + rotateToDown: "تدوير: الاتجاه للأسفل" + rotateToRight: "تدوير: الاتجاه لليمين" + rotateToLeft: "تدوير: الاتجاه لليسار" + constant_producer: منتج ثابت + goal_acceptor: مستلم الهدف + block: كتلة + massSelectClear: مسح الأحزمة + showShapeTooltip: إظهار نص الأدوات لشكل الإخراج about: - title: About this Game + title: حول هذه اللعبة body: >- - This game is open source and developed by <a - href="https://github.com/tobspr" target="_blank">Tobias Springer</a> - (this is me).<br><br> + هذه اللعبة مفتوحة المصدر وتم تطويرها بواسطة <a + href="https://github.com/tobspr" target="_blank">توبياس سبرينغر</a> (هذا + أنا).<br><br> - If you want to contribute, check out <a href="<githublink>" target="_blank">shapez.io on GitHub</a>.<br><br> + إذا كنت ترغب في المساهمة، تحقق من <a href="<githublink>" target="_blank">shapez.io على GitHub</a>.<br><br> - This game wouldn't have been possible without the great Discord community around my games - You should really join the <a href="<discordlink>" target="_blank">Discord server</a>!<br><br> + لم تكن هذه اللعبة ممكنة بدون مجتمع Discord الرائع حول ألعابي - يجب عليك حقًا الانضمام إلى <a href="<discordlink>" target="_blank">خادم Discord</a>!<br><br> - The soundtrack was made by <a href="https://soundcloud.com/pettersumelius" target="_blank">Peppsen</a> - He's awesome.<br><br> + الموسيقى التصويرية تم إعدادها بواسطة <a href="https://soundcloud.com/pettersumelius" target="_blank">بيبسن</a> - إنه رائع.<br><br> - Finally, huge thanks to my best friend <a href="https://github.com/niklas-dahl" target="_blank">Niklas</a> - Without our Factorio sessions, this game would never have existed. + وأخيرًا، شكر كبير لأفضل أصدقائي <a href="https://github.com/niklas-dahl" target="_blank">نيكلاس</a> - بدون جلساتنا في Factorio، لما كانت هذه اللعبة موجودة أبدًا. changelog: - title: Changelog + title: سجل التغييرات demo: - features: - restoringGames: Restoring savegames - importingGames: Importing savegames - oneGameLimit: Limited to one savegame - customizeKeybindings: Customizing Keybindings - exportingBase: Exporting whole Base as Image - settingNotAvailable: Not available in the demo. + settingNotAvailable: غير متاح في العرض التجريبي. tips: - - The hub accepts input of any kind, not just the current shape! - - Make sure your factories are modular - it will pay out! - - Don't build too close to the hub, or it will be a huge chaos! - - If stacking does not work, try switching the inputs. - - You can toggle the belt planner direction by pressing <b>R</b>. - - Holding <b>CTRL</b> allows dragging of belts without auto-orientation. - - Ratios stay the same, as long as all upgrades are on the same Tier. - - Serial execution is more efficient than parallel. - - You will unlock more variants of buildings later in the game! - - You can use <b>T</b> to switch between different variants. - - Symmetry is key! - - You can weave different tiers of tunnels. - - Try to build compact factories - it will pay out! - - The painter has a mirrored variant which you can select with <b>T</b> - - Having the right building ratios will maximize efficiency. - - At maximum level, 5 extractors will fill a single belt. - - Don't forget about tunnels! - - You don't need to divide up items evenly for full efficiency. - - Holding <b>SHIFT</b> will activate the belt planner, letting you place - long lines of belts easily. - - Cutters always cut vertically, regardless of their orientation. - - To get white mix all three colors. - - The storage buffer priorities the first output. - - Invest time to build repeatable designs - it's worth it! - - Holding <b>CTRL</b> allows to place multiple buildings. - - You can hold <b>ALT</b> to invert the direction of placed belts. - - Efficiency is key! - - Shape patches that are further away from the hub are more complex. - - Machines have a limited speed, divide them up for maximum efficiency. - - Use balancers to maximize your efficiency. - - Organization is important. Try not to cross conveyors too much. - - Plan in advance, or it will be a huge chaos! - - Don't remove your old factories! You'll need them to unlock upgrades. - - Try beating level 20 on your own before seeking for help! - - Don't complicate things, try to stay simple and you'll go far. - - You may need to re-use factories later in the game. Plan your factories to - be re-usable. - - Sometimes, you can find a needed shape in the map without creating it with - stackers. - - Full windmills / pinwheels can never spawn naturally. - - Color your shapes before cutting for maximum efficiency. - - With modules, space is merely a perception; a concern for mortal men. - - Make a separate blueprint factory. They're important for modules. - - Have a closer look on the color mixer, and your questions will be answered. - - Use <b>CTRL</b> + Click to select an area. - - Building too close to the hub can get in the way of later projects. - - The pin icon next to each shape in the upgrade list pins it to the screen. - - Mix all primary colors together to make white! - - You have an infinite map, don't cramp your factory, expand! - - Also try Factorio! It's my favorite game. - - The quad cutter cuts clockwise starting from the top right! - - You can download your savegames in the main menu! - - This game has a lot of useful keybindings! Be sure to check out the - settings page. - - This game has a lot of settings, be sure to check them out! - - The marker to your hub has a small compass to indicate its direction! - - To clear belts, cut the area and then paste it at the same location. - - Press F4 to show your FPS and Tick Rate. - - Press F4 twice to show the tile of your mouse and camera. - - You can click a pinned shape on the left side to unpin it. + - يقبل المركز أي مدخلات، وليس فقط الشكل الحالي! + - تأكد من أن مصانعك معيارية - سيؤتي ذلك ثماره! + - لا تبنِ بالقرب من المركز كثيرًا، أو سيكون هناك فوضى عارمة! + - إذا لم تعمل عملية التكديس، جرّب تبديل المدخلات. + - يمكنك تبديل اتجاه تخطيط الحزام بالضغط على <b>R</b>. + - الضغط مع الاستمرار على <b>CTRL</b> يسمح بسحب الأحزمة بدون توجيه تلقائي. + - تبقى النسب كما هي، طالما أن جميع الترقيات في نفس المستوى. + - التنفيذ التسلسلي أكثر كفاءة من التنفيذ المتوازي. + - ستفتح المزيد من أنواع المباني لاحقًا في اللعبة! + - يمكنك استخدام <b>T</b> للتبديل بين الأنواع المختلفة. + - التناسق هو الأساس! + - يمكنك دمج مستويات مختلفة من الأنفاق. + - حاول بناء مصانع مضغوطة - سيؤتي ذلك ثماره! + - للرسام نسخة معكوسة يمكنك اختيارها باستخدام <b>T</b>. + - وجود نسب المباني الصحيحة سيزيد من الكفاءة. + - عند المستوى الأقصى، يمكن لخمس مستخرجين ملء حزام واحد. + - لا تنس الأنفاق! + - لا تحتاج إلى تقسيم العناصر بالتساوي لتحقيق أقصى كفاءة. + - الضغط مع الاستمرار على <b>SHIFT</b> سيفعل مخطط الحزام، مما يتيح لك وضع + خطوط طويلة من الأحزمة بسهولة. + - القواطع دائمًا تقطع عموديًا، بغض النظر عن اتجاهها. + - للحصول على اللون الأبيض، اخلط بين جميع الألوان الثلاثة. + - مخزن التخزين يعطي الأولوية للمخرج الأول. + - استثمر وقتًا لبناء تصميمات قابلة للتكرار - يستحق ذلك العناء! + - الضغط مع الاستمرار على <b>CTRL</b> يتيح لك وضع مبانٍ متعددة. + - يمكنك الضغط مع الاستمرار على <b>ALT</b> لعكس اتجاه الأحزمة التي يتم وضعها. + - الكفاءة هي الأساس! + - تكون بقع الأشكال البعيدة عن المركز أكثر تعقيدًا. + - للآلات سرعة محدودة، قسمها لتحقيق أقصى كفاءة. + - استخدم الموازنات لتحقيق أقصى كفاءة. + - التنظيم مهم. حاول ألا تتقاطع الأحزمة كثيرًا. + - خطط مسبقًا، أو ستكون هناك فوضى عارمة! + - لا تقم بإزالة المصانع القديمة! ستحتاجها لفتح الترقيات. + - حاول تجاوز المستوى 20 بنفسك قبل طلب المساعدة! + - لا تعقد الأمور، حاول أن تكون بسيطًا وستذهب بعيدًا. + - قد تحتاج إلى إعادة استخدام المصانع لاحقًا في اللعبة. خطط لجعل مصانعك قابلة + لإعادة الاستخدام. + - أحيانًا يمكنك العثور على الشكل المطلوب في الخريطة دون إنشائه باستخدام + المكدسات. + - لا يمكن أن تظهر الطواحين الهوائية الكاملة بشكل طبيعي. + - قم بتلوين أشكالك قبل القطع لتحقيق أقصى كفاءة. + - مع الوحدات، المساحة مجرد تصور؛ قلق بالنسبة للبشر العاديين. + - أنشئ مصنعًا منفصلًا للمخططات. إنها مهمة للوحدات. + - ألقِ نظرة عن كثب على خالط الألوان، وستجد إجابات لأسئلتك. + - استخدم <b>CTRL</b> + انقر لتحديد منطقة. + - البناء بالقرب من المركز قد يعوق المشاريع المستقبلية. + - يتيح لك رمز التثبيت بجانب كل شكل في قائمة الترقية تثبيته على الشاشة. + - اخلط بين جميع الألوان الأساسية معًا للحصول على الأبيض! + - لديك خريطة لا نهائية، لا تكدس مصنعك، توسع! + - جرب أيضًا لعبة Factorio! إنها لعبتي المفضلة. + - قاطع الأشكال الرباعي يقطع مع اتجاه عقارب الساعة بدءًا من الزاوية العلوية + اليمنى! + - يمكنك تحميل ملفات حفظ اللعبة من القائمة الرئيسية! + - تحتوي هذه اللعبة على العديد من اختصارات المفاتيح المفيدة! تأكد من مراجعة + صفحة الإعدادات. + - تحتوي هذه اللعبة على العديد من الإعدادات، تأكد من الاطلاع عليها! + - يحتوي المؤشر الموجود على مركزك على بوصلة صغيرة تشير إلى اتجاهه! + - لمسح الأحزمة، قم بقص المنطقة ثم لصقها في نفس المكان. + - اضغط على F4 لعرض معدل الإطارات (FPS) ومعدل التحديث. + - اضغط على F4 مرتين لعرض المربعات الخاصة بمؤشر الماوس والكاميرا. + - يمكنك النقر فوق الشكل المثبت على الجانب الأيسر لإلغاء تثبيته. puzzleMenu: - play: Play - edit: Edit - title: Puzzle Mode - createPuzzle: Create Puzzle - loadPuzzle: Load - reviewPuzzle: Review & Publish - validatingPuzzle: Validating Puzzle - submittingPuzzle: Submitting Puzzle - noPuzzles: There are currently no puzzles in this section. + play: لعب + edit: تحرير + title: وضع الألغاز + createPuzzle: إنشاء لغز + loadPuzzle: تحميل + reviewPuzzle: مراجعة ونشر + validatingPuzzle: التحقق من صحة اللغز + submittingPuzzle: إرسال اللغز + noPuzzles: لا توجد ألغاز حاليًا في هذا القسم. categories: - levels: Levels - new: New - top-rated: Top Rated - mine: My Puzzles - easy: Easy - hard: Hard - completed: Completed - medium: Medium - official: Official - trending: Trending today - trending-weekly: Trending weekly - categories: Categories - difficulties: By Difficulty - account: My Puzzles - search: Search + levels: المستويات + new: جديد + top-rated: الأعلى تقييمًا + mine: ألغازي + easy: سهل + hard: صعب + completed: مكتمل + medium: متوسط + official: رسمي + trending: الأكثر رواجًا اليوم + trending-weekly: الأكثر رواجًا هذا الأسبوع + categories: الفئات + difficulties: حسب الصعوبة + account: ألغازي + search: بحث validation: - title: Invalid Puzzle - noProducers: Please place a Constant Producer! - noGoalAcceptors: Please place a Goal Acceptor! - goalAcceptorNoItem: One or more Goal Acceptors have not yet assigned an item. - Deliver a shape to them to set a goal. - goalAcceptorRateNotMet: One or more Goal Acceptors are not getting enough items. - Make sure that the indicators are green for all acceptors. - buildingOutOfBounds: One or more buildings are outside of the buildable area. - Either increase the area or remove them. - autoComplete: Your puzzle autocompletes itself! Please make sure your constant - producers are not directly delivering to your goal acceptors. + title: لغز غير صالح + noProducers: الرجاء وضع منتج ثابت! + noGoalAcceptors: الرجاء وضع مستلم هدف! + goalAcceptorNoItem: يوجد مستلم أو أكثر لم يتم تعيين عنصر له بعد. قدم له شكلًا لتحديد هدف. + goalAcceptorRateNotMet: المستلمون لا يحصلون على ما يكفي من العناصر. تأكد من أن + جميع المؤشرات خضراء لجميع المستلمين. + buildingOutOfBounds: هناك مبنى أو أكثر خارج المنطقة القابلة للبناء. إما أن تزيد + المنطقة أو تزيل المباني. + autoComplete: اللغز يحل نفسه تلقائيًا! تأكد من أن المنتجين الثابتين لا يسلمون + مباشرة إلى مستلمي الهدف. difficulties: - easy: Easy - medium: Medium - hard: Hard - unknown: Unrated - dlcHint: Purchased the DLC already? Make sure it is activated by right clicking - shapez.io in your library, selecting Properties > DLCs. + easy: سهل + medium: متوسط + hard: صعب + unknown: غير مُصنف + dlcHint: اشتريت المحتوى القابل للتنزيل (DLC) بالفعل؟ تأكد من تفعيله بالنقر بزر + الماوس الأيمن على shapez.io في مكتبتك، ثم اختر الخصائص > المحتويات + القابلة للتنزيل. search: - action: Search - placeholder: Enter a puzzle or author name - includeCompleted: Include Completed + action: بحث + placeholder: أدخل اسم لغز أو اسم مؤلف + includeCompleted: تضمين المكتمل difficulties: - any: Any Difficulty - easy: Easy - medium: Medium - hard: Hard + any: أي صعوبة + easy: سهل + medium: متوسط + hard: صعب durations: - any: Any Duration - short: Short (< 2 min) - medium: Normal - long: Long (> 10 min) + any: أي مدة + short: قصيرة (< 2 دقيقة) + medium: عادية + long: طويلة (> 10 دقائق) backendErrors: - ratelimit: You are performing your actions too frequent. Please wait a bit. - invalid-api-key: Failed to communicate with the backend, please try to - update/restart the game (Invalid Api Key). - unauthorized: Failed to communicate with the backend, please try to - update/restart the game (Unauthorized). - bad-token: Failed to communicate with the backend, please try to update/restart - the game (Bad Token). - bad-id: Invalid puzzle identifier. - not-found: The given puzzle could not be found. - bad-category: The given category could not be found. - bad-short-key: The given short key is invalid. - profane-title: Your puzzle title contains profane words. - bad-title-too-many-spaces: Your puzzle title is too short. - bad-shape-key-in-emitter: A constant producer has an invalid item. - bad-shape-key-in-goal: A goal acceptor has an invalid item. - no-emitters: Your puzzle does not contain any constant producers. - no-goals: Your puzzle does not contain any goal acceptors. - short-key-already-taken: This short key is already taken, please use another one. - can-not-report-your-own-puzzle: You can not report your own puzzle. - bad-payload: The request contains invalid data. - bad-building-placement: Your puzzle contains invalid placed buildings. - timeout: The request timed out. - too-many-likes-already: The puzzle alreay got too many likes. If you still want - to remove it, please contact support@shapez.io! - no-permission: You do not have the permission to perform this action. + ratelimit: أنت تقوم بتنفيذ أفعالك بشكل متكرر جدًا. الرجاء الانتظار قليلًا. + invalid-api-key: فشل الاتصال بالخادم الخلفي، يرجى محاولة تحديث/إعادة تشغيل + اللعبة (مفتاح API غير صالح). + unauthorized: فشل الاتصال بالخادم الخلفي، يرجى محاولة تحديث/إعادة تشغيل اللعبة + (غير مصرح). + bad-token: فشل الاتصال بالخادم الخلفي، يرجى محاولة تحديث/إعادة تشغيل اللعبة (رمز + غير صالح). + bad-id: معرف اللغز غير صالح. + not-found: لم يتم العثور على اللغز المعطى. + bad-category: لم يتم العثور على الفئة المعطاة. + bad-short-key: المفتاح القصير المعطى غير صالح. + profane-title: عنوان اللغز الخاص بك يحتوي على كلمات بذيئة. + bad-title-too-many-spaces: عنوان اللغز الخاص بك قصير جدًا. + bad-shape-key-in-emitter: المنتج الثابت يحتوي على عنصر غير صالح. + bad-shape-key-in-goal: مستلم الهدف يحتوي على عنصر غير صالح. + no-emitters: لغزك لا يحتوي على أي منتجات ثابتة. + no-goals: لغزك لا يحتوي على أي مستلمي أهداف. + short-key-already-taken: المفتاح القصير هذا مأخوذ بالفعل، يرجى استخدام آخر. + can-not-report-your-own-puzzle: لا يمكنك الإبلاغ عن لغزك الخاص. + bad-payload: الطلب يحتوي على بيانات غير صالحة. + bad-building-placement: لغزك يحتوي على مباني تم وضعها بشكل غير صالح. + timeout: انتهت مهلة الطلب. + too-many-likes-already: اللغز حصل بالفعل على عدد كبير جدًا من الإعجابات. إذا كنت + لا تزال تريد إزالته، يرجى الاتصال بـ support@shapez.io! + no-permission: ليس لديك الصلاحية لتنفيذ هذا الإجراء. mods: title: Mods author: Author version: Version modWebsite: Website openFolder: Open Mods Folder - folderOnlyStandalone: Opening the mod folder is only possible when running the standalone. browseMods: Browse Mods modsInfo: To install and manage mods, copy them to the mods folder within the game directory. You can also use the 'Open Mods Folder' button on the top right. - noModSupport: You need the standalone version on Steam to install mods. - togglingComingSoon: - title: Coming Soon - description: Enabling or disabling mods is currently only possible by copying - the mod file from or to the mods/ folder. However, being able to - toggle them here is planned for a future update! - browserNoSupport: Due to browser restrictions it is currently only possible to - install mods in the Steam version - Sorry! diff --git a/translations/base-cat.yaml b/translations/base-cat.yaml index 1c8b3584..76418435 100644 --- a/translations/base-cat.yaml +++ b/translations/base-cat.yaml @@ -1,27 +1,3 @@ -steamPage: - shortText: shapez.io és un joc que té com a objectiu construir i automatitzar - fàbriques per tal de produir figures cada cop més complexes en un mapa - infinit. - discordLinkShort: Official Discord - intro: >- - Shapez.io es un joc relaxant en el qual has de construir fàbriques per a - la producció automàtica de formes geomètriques. - - A mesura que el nivell augmenta, les formes esdevenen més complexes, i has d'explorar el mapa infinit. - - Per si això no era suficient, la demanda de formes creixerà exponencialment, pel que hauràs d'escalar les teves fàbriques! - - Mentre que al principi només processes formes, més envant les hauràs de colorejar, pel que necessitaràs extreure y mesclar colors! - - Si compres el joc a Steam tendràs accés al joc complet, però també pots jugar a la demo a shapez.io primer i decidir-te més tard! - what_others_say: What people say about shapez.io - nothernlion_comment: This game is great - I'm having a wonderful time playing, - and time has flown by. - notch_comment: Oh crap. I really should sleep, but I think I just figured out - how to make a computer in shapez.io - steam_review_comment: This game has stolen my life and I don't want it back. - Very chill factory game that won't let me stop making my lines more - efficient. global: loading: Carregant error: Error @@ -55,57 +31,19 @@ global: space: ESPAI loggingIn: Logging in loadingResources: Downloading additional resources (<percentage> %) - discount: -<percentage>% - discountSummerSale: SPECIAL PROMOTION! Offer ends 7 July -demoBanners: - title: Demo - Versió de prova - intro: |- - Get the full game <strong>now</strong> to unlock:<ul> - <li>All 26 levels + infinite Freeplay</li> - <li>22 new buildings</li> - <li>Mod support</li> - <li>Achievements</li> - <li>Dark Mode</li> - <li>... and a lot more!</li> - </ul> - playtimeDisclaimer: The full version contains more than <strong>20 hours of content</strong>. - playerCount: <playerCount> players like you are currently playing shapez on Steam - untilEndOfDemo: Until end of demo - playtimeDisclaimerDownload: You can continue your savegame in the full version! - Click <strong>here</strong> to download your savegame. - titleV2: "Play the full version now for:" mainMenu: play: Jugar continue: Continuar newGame: Nou joc - changelog: Historial de canvis - subreddit: Reddit importSavegame: Importar - openSourceHint: Aquest joc es de codi obert! - discordLink: Servidor Discord oficial helpTranslate: Ajuda a traduir-lo! - madeBy: Creat per <author-link> - browserWarning: Disculpa, però el joc funcionarà lent al teu navegador! - Aconsegueix el joc complet o descarrega't chrome per una millor - experiència. savegameLevel: Nivell <x> savegameLevelUnknown: Nivell desconegut savegameUnnamed: Unnamed - puzzleMode: Puzzle Mode - back: Back - puzzleDlcText: Do you enjoy compacting and optimizing factories? Get the Puzzle - DLC now on Steam for even more fun! - puzzleDlcWishlist: Wishlist now! - puzzleDlcViewNow: View Dlc mods: - title: Active Mods warningPuzzleDLC: Playing the Puzzle DLC is not possible with mods. Please disable all mods to play the DLC. - playingFullVersion: You are now playing the full version! - logout: Logout noActiveSavegames: No active savegames found - Click play to start a new game! - playFullVersionV2: Bough shapez on Steam? Play the full version in your Browser! - playFullVersionStandalone: You can now also play the full version in your Browser! dialogs: buttons: ok: OK @@ -114,7 +52,6 @@ dialogs: later: Més tard restart: Tornar a començar reset: Reiniciar - getStandalone: Obtenir versió completa deleteGame: Sé el que faig viewUpdate: Veure actualitzacions showUpgrades: Mostrar millores @@ -152,16 +89,6 @@ dialogs: keybindingsResetOk: title: Cambiar dreceres de teclat desc: Les dreceres han tornat en el seu estat predeterminat! - featureRestriction: - title: Demo - Versió de prova - desc: Has intentat accedir a una característica (<feature>) que no està - disponible en la demo. Considera obtenir el joc complet per a una - experiència completa! - oneSavegameLimit: - title: Partides guardades limitades - desc: Només pots tenir una sola partida guardada a la versió de demostració. Si - vols, elimina la ja existent o fes-te amb la versió completa del - joc! updateSummary: title: Nova actualització! desc: "Aquí tens els canvis des de l'últim cop que vas jugar:" @@ -199,9 +126,6 @@ dialogs: titleEdit: Editar Marcador desc: Give it a meaningful name, you can also include a <strong>short key</strong> of a shape (Which you can generate <link>here</link>) - markerDemoLimit: - desc: En la Demo només pots crear dos marcadors, aconsegueix la versió completa - per gaudir de l'experiència completa! exportScreenshotWarning: title: Exportar Captura de Pantalla desc: Has demanat exportar la teva/teua base com una captura de pantalla. Tin en @@ -296,28 +220,9 @@ dialogs: newMods: Newly installed Mods resourceLoadFailed: title: Resources failed to load - demoLinkText: shapez demo on Steam - descWeb: "One ore more resources could not be loaded. Make sure you have a - stable internet connection and try again. If this still doesn't - work, make sure to also disable any browser extensions (including - adblockers).<br><br> As an alternative, you can also play the - <demoOnSteamLinkText>. <br><br> Error Message:" descSteamDemo: "One ore more resources could not be loaded. Try restarting the game - If that does not help, try reinstalling and verifying the game files via Steam. <br><br> Error Message:" - steamSsoError: - title: Full Version Logout - desc: You have been logged out from the Full Browser Version since either your - network connection is unstable or you are playing on another - device.<br><br> Please make sure you don't have shapez open in any - other browser tab or another computer with the same Steam - account.<br><br> You can login again in the main menu. - steamSsoNoOwnership: - title: Full Edition not owned - desc: In order to play the Full Edition in your Browser, you need to own both - the base game and the Puzzle DLC on your Steam account.<br><br> - Please make sure you own both, signed in with the correct Steam - account and then try again. ingame: keybindingsOverlay: moveMap: Moure @@ -458,40 +363,6 @@ ingame: one_miner: 1 Miner n_miners: <amount> Miners limited_items: Limitat a <max_throughput> - watermark: - title: Versió demo - desc: Premeu aquí per veure les avantatges d'Steam! - get_on_steam: Comprar a Steam - standaloneAdvantages: - no_thanks: No, gràcies! - points: - levels: - title: 12 Nivells now - desc: Per a un total de 26 nivells! - buildings: - title: 22 Nous edificis - desc: Automatitza la teva fàbrica completament! - markers: - title: ∞ Marcadors - desc: Mai et perdis per el mapa! - wires: - title: Cables - desc: Una nova dimensió! - darkmode: - title: Mode Oscur - desc: Deixa de fer-te mal als ulls! - support: - title: Dona'm suport - desc: EL desenvolupo en el meu temps lliure! - achievements: - title: Achievements - desc: Hunt them all! - mods: - title: Modding support! - desc: Over 80 mods available! - titleV2: "Get the full version now on Steam to unlock:" - titleExpiredV2: Demo completed! - titleEnjoyingDemo: Enjoy the demo? puzzleEditorSettings: zoneTitle: Zone zoneWidth: Width @@ -590,7 +461,7 @@ buildings: name: Tallador (Quàdruple) description: Talla figures en quatre parts. <strong>Si no utilitzes totes les parts, assegura't de destruir les altres o es pararà!</strong> - rotater: + rotator: default: name: Rotador description: Rota formes en sentit horari 90 graus. @@ -726,7 +597,7 @@ buildings: default: name: Tallador virtual description: Talla la forma virtual en dues parts. - rotater: + rotator: name: Rotador Virtual description: Rota la forma virtual, tant en sentit horari com antihorari. unstacker: @@ -766,7 +637,7 @@ storyRewards: otherwise <strong>it will clog and stall</strong> - For this purpose I have given you the <strong>trash</strong>, which destroys everything you put into it! - reward_rotater: + reward_rotator: title: Rotar desc: El <strong>Rotador</strong> s'ha desbloquejat! Rota formes en sentit horari 90 graus. @@ -796,7 +667,7 @@ storyRewards: title: Túnel desc: El <strong>túnel</strong> s'ha desbloquejat - Ara pots passar objectes a través d'edificis i cintes transportadores! - reward_rotater_ccw: + reward_rotator_ccw: title: Rotació antihorària desc: Has desbloquejat una variant del <strong>rotador</strong> - Et permet rotar en sentit antihorari! Per tal de construir-lo, selecciona el @@ -869,7 +740,7 @@ storyRewards: desc: You have now unlocked the <strong>belt reader</strong>! It allows you to measure the throughput of a belt.<br><br>And wait until you unlock wires - then it gets really useful! - reward_rotater_180: + reward_rotator_180: title: Rotador (180 graus) desc: Acabes de desbloquejar un altre <strong>rotador</strong>!. Et permet rotar una forma 180 graus. @@ -1135,7 +1006,7 @@ keybindings: underground_belt: Túnel miner: Extractor cutter: Tallador - rotater: Rotador + rotator: Rotador stacker: Apilador mixer: Mesclador de colors painter: Pintor @@ -1198,12 +1069,6 @@ about: changelog: title: Registre de Canvis demo: - features: - restoringGames: Restaurar partides guardats - importingGames: Importar partides guardats - oneGameLimit: Limitat a una partida guardada. - customizeKeybindings: Personalitzar teclats - exportingBase: Exportar la base com a Imatge settingNotAvailable: No disponible en la versió de demostració. tips: - El NEXE accepta qualsevol tipus d’entrada, no només la forma actual. @@ -1372,16 +1237,7 @@ mods: version: Version modWebsite: Website openFolder: Open Mods Folder - folderOnlyStandalone: Opening the mod folder is only possible when running the standalone. browseMods: Browse Mods modsInfo: To install and manage mods, copy them to the mods folder within the game directory. You can also use the 'Open Mods Folder' button on the top right. - noModSupport: You need the standalone version on Steam to install mods. - togglingComingSoon: - title: Coming Soon - description: Enabling or disabling mods is currently only possible by copying - the mod file from or to the mods/ folder. However, being able to - toggle them here is planned for a future update! - browserNoSupport: Due to browser restrictions it is currently only possible to - install mods in the Steam version - Sorry! diff --git a/translations/base-cz.yaml b/translations/base-cz.yaml index 3dac1670..51eadbaf 100644 --- a/translations/base-cz.yaml +++ b/translations/base-cz.yaml @@ -1,23 +1,3 @@ -steamPage: - shortText: shapez.io je hra o stavbě továren na automatizaci výroby a - kombinování čím dál složitějších tvarů na nekonečné mapě. - discordLinkShort: Oficiální Discord - intro: >- - Máte rádi automatizaci? Tak to jste na správném místě! - - shapez.io je relaxační hra, ve které musíte stavět továrny na automatizaci výroby geometrických tvarů. Jak se zvyšuje úroveň, tvary se stávají stále složitějšími a vy se musíte rozšířit po nekonečné mapě. - - A jako by to nestačilo, musíte také produkovat exponenciálně více, abyste uspokojili požadavky - jediná věc, která pomáhá, je škálování! Zatímco na začátku tvary pouze zpracováváte, později je musíte obarvit - těžbou a mícháním barev! - - Koupením hry na platformě Steam získáte přístup k plné verzi hry, ale také můžete nejdříve hrát demo verzi na shapez.io a až potom se rozhodnout! - what_others_say: Co o shapez.io říkají lidé - nothernlion_comment: Tato hra je úžasná - Užívám si čas strávený hraním této - hry, jen strašně rychle utekl. - notch_comment: Sakra. Opravdu bych měl jít spát, ale myslím si, že jsem zrovna - přišel na to, jak v shapez.io vytvořit počítač. - steam_review_comment: Tato hra mi ukradla život a já ho nechci zpět. Odpočinková - factory hra, která mi nedovolí přestat dělat mé výrobní linky více - efektivní. global: loading: Načítání error: Chyba @@ -51,57 +31,19 @@ global: space: SPACE loggingIn: Přihlašuji loadingResources: Stahování dalších zdrojů (<percentage> %) - discount: -<percentage>% - discountSummerSale: SPECIAL PROMOTION! Offer ends 7 July -demoBanners: - title: Demo verze - intro: |- - Získejte plnou hru <strong>teď</strong> a odemkněte ji:<ul> - <li>Všech 26 úrovní + nekonečná hra zdarma</li> - <li>22 nových budov</li> - <li>Podpora modifikací</li> - <li>Úspěchy</li> - <li>Tmavý režim</li> - <li>... a mnoho dalšího!</li> - </ul> - playtimeDisclaimer: Plná verze obsahuje více než <strong>20 hodin obsahu</strong>. - playerCount: <playerCount> hráči jako vy právě hrají shapez ve službě Steam - untilEndOfDemo: do konce ukázky - playtimeDisclaimerDownload: Ve hře můžete pokračovat v plné verzi! Klikněte - <strong>sem</strong> a stáhněte si uloženou hru. - titleV2: "Přehrajte si nyní plnou verzi pro:" mainMenu: play: Hrát continue: Pokračovat newGame: Nová hra - changelog: Seznam změn - subreddit: Reddit importSavegame: Importovat - openSourceHint: Tato hra je open source! - discordLink: Oficiální Discord Server helpTranslate: Pomozte přeložit hru! - madeBy: Vytvořil <author-link> - browserWarning: Promiňte, ale víme, že hra poběží pomalu ve vašem prohlížeči! - Pořiďte si samostatnou verzi nebo si stáhněte Google Chrome pro - plnohodnotný zážitek. savegameLevel: Úroveň <x> savegameLevelUnknown: Neznámá úroveň savegameUnnamed: Nepojmenovaný - puzzleMode: Puzzle mód - back: Zpět - puzzleDlcText: Baví vás zmenšování a optimalizace továren? Pořiďte si nyní - Puzzle DLC na Steamu pro ještě více zábavy! - puzzleDlcWishlist: Přidejte si nyní na seznam přání! - puzzleDlcViewNow: Zobrazit DLC mods: - title: Aktivní módy warningPuzzleDLC: Nelze hrát Puzzle DLC společně s módy. Prosím deaktivujte všechny módy, abyste mohli hrát DLC. - playingFullVersion: You are now playing the full version! - logout: Logout noActiveSavegames: No active savegames found - Click play to start a new game! - playFullVersionV2: Bough shapez on Steam? Play the full version in your Browser! - playFullVersionStandalone: You can now also play the full version in your Browser! dialogs: buttons: ok: OK @@ -110,7 +52,6 @@ dialogs: later: Později restart: Restart reset: Reset - getStandalone: Získejte plnou verzi deleteGame: Vím, co dělám viewUpdate: Zobrazit aktualizaci showUpgrades: Zobrazit vylepšení @@ -149,14 +90,6 @@ dialogs: keybindingsResetOk: title: Reset klávesových zkratek desc: Všechny klávesové zkratky byly vráceny do původního nastavení! - featureRestriction: - title: Demo verze - desc: Zkoušíte použít funkci (<feature>), která není v demo verzi. Zvažte - pořízení plné verze pro kompletní zážitek! - oneSavegameLimit: - title: Omezené ukládání - desc: Ve demo verzi můžete mít pouze jednu uloženou hru. Odstraňte stávající - uloženou hru nebo si pořiďte plnou verzi! updateSummary: title: Nová aktualizace! desc: "Zde jsou změny od doby, kdy jste naposledy hráli:" @@ -196,9 +129,6 @@ dialogs: descItems: "Vyberte předdefinovanou položku:" descShortKey: ... nebo zadejte <strong>krátký klíč</strong> tvaru (Ten můžete vygenerovat <link>zde</link>) - markerDemoLimit: - desc: V demo verzi můžete vytvořit pouze dvě značky. Pořiďte si plnou verzi pro - neomezený počet značek! exportScreenshotWarning: title: Exportuj snímek obrazovky desc: Chcete exportovat svou základnu jako snímek obrazovky. Mějte prosím na @@ -287,28 +217,9 @@ dialogs: newMods: Nově nainstalované módy resourceLoadFailed: title: Resources failed to load - demoLinkText: shapez demo on Steam - descWeb: "One ore more resources could not be loaded. Make sure you have a - stable internet connection and try again. If this still doesn't - work, make sure to also disable any browser extensions (including - adblockers).<br><br> As an alternative, you can also play the - <demoOnSteamLinkText>. <br><br> Error Message:" descSteamDemo: "One ore more resources could not be loaded. Try restarting the game - If that does not help, try reinstalling and verifying the game files via Steam. <br><br> Error Message:" - steamSsoError: - title: Full Version Logout - desc: You have been logged out from the Full Browser Version since either your - network connection is unstable or you are playing on another - device.<br><br> Please make sure you don't have shapez open in any - other browser tab or another computer with the same Steam - account.<br><br> You can login again in the main menu. - steamSsoNoOwnership: - title: Full Edition not owned - desc: In order to play the Full Edition in your Browser, you need to own both - the base game and the Puzzle DLC on your Steam account.<br><br> - Please make sure you own both, signed in with the correct Steam - account and then try again. ingame: keybindingsOverlay: moveMap: Posun mapy @@ -449,40 +360,6 @@ ingame: one_miner: 1 Extraktor n_miners: <amount> Extraktorů limited_items: Limit je <max_throughput> - watermark: - title: Demo verze - desc: Kliknutím sem zobrazíte výhody Steam verze! - get_on_steam: Získejte na steamu - standaloneAdvantages: - no_thanks: Ne, děkuji! - points: - levels: - title: 12 Nových úrovní - desc: Celkem 26 úrovní! - buildings: - title: 22 Nových budov - desc: Plně automatizujte svou továrnu! - markers: - title: ∞ Značek - desc: Nikdy se neztraťte ve své továrně! - wires: - title: Kabely - desc: Zcela nový rozměr! - darkmode: - title: Tmavý mód - desc: Přestanou vás bolet oči! - support: - title: Podpořte mě - desc: Vyvíjím to ve svém volném čase! - achievements: - title: Achievements - desc: Získejte je všechny! - mods: - title: Modding support! - desc: Over 80 mods available! - titleV2: "Get the full version now on Steam to unlock:" - titleExpiredV2: Demo completed! - titleEnjoyingDemo: Enjoy the demo? puzzleEditorSettings: zoneTitle: Zóna zoneWidth: Šířka @@ -574,7 +451,7 @@ buildings: description: Rozebere tvar na čtyři části. <strong>Pokud použijete jen některé části, nezapomeňte ostatní smazat, jinak se vám produkce zasekne!</strong> - rotater: + rotator: default: name: Rotor description: Otáčí tvary o 90 stupňů po směru hodinových ručiček. @@ -703,7 +580,7 @@ buildings: default: name: Virtuální pila description: Virtuálně rozřízne tvar svisle na dvě části. - rotater: + rotator: name: Virtuální rotor description: Virtuálně otáčí tvary o 90 stupňů po směru hodinových ručiček. unstacker: @@ -742,7 +619,7 @@ storyRewards: orientaci</strong>!<br><br>Nezapomeňte se zbavit zbytku tvarů, jinak se <strong>vám produkce zasekne</strong> - za tímto účelem jsem vám dal <strong>koš</strong>, který smaže vše, co do něj vložíte! - reward_rotater: + reward_rotator: title: Otáčení desc: <strong>Rotor</strong> byl právě odemčen! Otáčí tvary po směru hodinových ručiček o 90 stupňů. @@ -770,7 +647,7 @@ storyRewards: title: Tunel desc: <strong>Tunel</strong> byl právě odemčen - Umožňuje vézt suroviny pod budovami a pásy. - reward_rotater_ccw: + reward_rotator_ccw: title: Otáčení II desc: Odemkli jste variantu <strong>rotoru</strong> - Umožňuje vám otáčet proti směru hodinových ručiček. Vyberte rotor a <strong>zmáčkněte 'T' pro @@ -840,7 +717,7 @@ storyRewards: desc: Právě jste odemkli <strong>čtečku pásů</strong>! Umožňuje vám změřit propustnost pásu.<br><br>A počkejte na odemčení kabelů - později to bude velmi užitečné! - reward_rotater_180: + reward_rotator_180: title: Rotor (180°) desc: Právě jste odemkli 180 stupňoví <strong>rotor</strong>! - Umožňuje vám otáčet tvar o 180 stupňů! @@ -1096,7 +973,7 @@ keybindings: underground_belt: Tunel miner: Extraktor cutter: Pila - rotater: Rotor + rotator: Rotor stacker: Kombinátor mixer: Mixér na barvy painter: Barvič @@ -1162,12 +1039,6 @@ about: changelog: title: Seznam změn demo: - features: - restoringGames: Nahrávání uložených her - importingGames: Importování uložených her - oneGameLimit: Omezeno pouze na jednu uloženou hru - customizeKeybindings: Změna klávesových zkratek - exportingBase: Exportovat celou základnu jako obrázek settingNotAvailable: Nedostupné v demo verzi. tips: - Hub přijímá vstup jakéhokoliv tvaru, nejen právě požadovaný tvar! @@ -1332,16 +1203,7 @@ mods: version: Verze modWebsite: Webová stránka openFolder: Otevřít složku s módy - folderOnlyStandalone: Otevření složky s módy je možné pouze v samostatné verzi hry. browseMods: Procházet módy modsInfo: Chcete-li nainstalovat a spravovat módy, zkopírujte je do složky mods v adresáři hry. Můžete také použít tlačítko 'Otevřít složku s módy' vpravo nahoře. - noModSupport: K instalaci módů potřebujete samostatnou verzi hry na Steamu. - togglingComingSoon: - title: Již brzy - description: Aktivace či deaktivace módů je v současné době možná pouze - kopírováním souboru módu z nebo do mods/ složky. Nicméně, možnost - správy módů přímo ze hry je plánována pro budoucí aktualizaci! - browserNoSupport: Due to browser restrictions it is currently only possible to - install mods in the Steam version - Sorry! diff --git a/translations/base-da.yaml b/translations/base-da.yaml index 6a510d60..e820266f 100644 --- a/translations/base-da.yaml +++ b/translations/base-da.yaml @@ -1,25 +1,3 @@ -steamPage: - shortText: Shapez.io handler om at bygge fabrikker på en grænseløs spilleflade - for automatisk at skabe og kombinere figurer, der i stigende grad bliver - mere komplicerede. - discordLinkShort: Official Discord - intro: >- - Shapez.io er et afslappet spil hvor du skal bygge fabrikker for at - automatisere productionen af geometriske figurer. - - Jo længere du når, jo mere kompliceret bliver figurerne, og du bliver nødt til at sprede dig ud på den grænseløse spilleflade. - - Og, hvis det ikke var nok, så skal du også producere eksponentielt flere figurer for at møde behovene spillet giver dig - det eneste der virker er skalering! Mens du i starten kun laver former, skal du senere farvelægge dem - for at gøre dette skal du udvinde og blande farver! - - Køb spillet på Steam, det giver dig adgang til det fulde spil, men du kan også spille en demoversion på shapez.io og vælge senere! - what_others_say: Hvad andre siger om shapez.io - nothernlion_comment: This game is great - I'm having a wonderful time playing, - and time has flown by. - notch_comment: Oh crap. I really should sleep, but I think I just figured out - how to make a computer in shapez.io - steam_review_comment: This game has stolen my life and I don't want it back. - Very chill factory game that won't let me stop making my lines more - efficient. global: loading: Indlæser error: Fejl @@ -53,56 +31,19 @@ global: space: MELLEMRUM loggingIn: Logging in loadingResources: Downloader yderligere ressourcer (<percentage> %) - discount: -<percentage>% - discountSummerSale: SPECIAL PROMOTION! Offer ends 7 July -demoBanners: - title: Demoversion - intro: |- - Få det fulde spil <strong>nu</strong> for at låse op:<ul> - <li>Alle 26 niveauer + uendeligt freeplay</li> - <li>22 nye bygninger</li> - <li>Mod-understøttelse</li> - <li>Mod-understøttelse</li> - <li>Mørk tilstand</li> - <li>... og meget mere!</li> - </ul> - playtimeDisclaimer: Den fulde version indeholder mere end <strong>20 timers indhold</strong>. - playerCount: <playerCount> spillere som dig spiller lige nu shapez på Steam - untilEndOfDemo: Indtil afslutningen af demoen - playtimeDisclaimerDownload: Du kan fortsætte dit savegame i den fulde version! - Klik <strong>her</strong> for at downloade dit savegame. - titleV2: "Spil den fulde version nu for:" mainMenu: play: Spil continue: Fortsæt newGame: Nyt Spil - changelog: Ændringsliste - subreddit: Reddit importSavegame: Importér - openSourceHint: Dette spil er open source! - discordLink: Officiel Discordserver helpTranslate: Hjælp med at oversætte! - madeBy: Lavet af <author-link> - browserWarning: Undskyld, men spillet er kendt for at køre langsomt på denne - browser! Køb spillet eller download Chrome for den fulde oplevelse. savegameLevel: Niveau <x> savegameLevelUnknown: Ukendt Niveau savegameUnnamed: Uden Navn - puzzleMode: Puzzle-tilstand - back: Tilbage - puzzleDlcText: Synes du det er det sjovt at gøre maskinerne mindre og mere - optimale? Få fat i DLC på Steam nu for mere sjov! - puzzleDlcWishlist: Wishlist den nu! - puzzleDlcViewNow: Se DLC mods: - title: Active Mods warningPuzzleDLC: Det er ikke muligt at spille Puzzle DLC med mods. Slå alle mods fra for at spille DLC. - playingFullVersion: You are now playing the full version! - logout: Logout noActiveSavegames: No active savegames found - Click play to start a new game! - playFullVersionV2: Bough shapez on Steam? Play the full version in your Browser! - playFullVersionStandalone: You can now also play the full version in your Browser! dialogs: buttons: ok: OK @@ -111,7 +52,6 @@ dialogs: later: Senere restart: Genstart reset: Nulstil - getStandalone: Køb spillet deleteGame: Jeg ved hvad jeg laver viewUpdate: Se opdatering showUpgrades: Vis opgraderinger @@ -148,14 +88,6 @@ dialogs: keybindingsResetOk: title: Tastefunktioner nulstillet desc: Tastefunktioner er nulstillet til deres standarder! - featureRestriction: - title: Demoversion - desc: Du prøvede at bruge en funktion (<feature>) der ikke er tilgængelig i - demoen. Overvej at købe spillet for den fulde oplevelse! - oneSavegameLimit: - title: Begrænset mængde gemte spil - desc: Du kan kun have et gemt spil ad gangen in demoversionen. Vær sød at slette - det nuværende gemte spil eller at købe spillet! updateSummary: title: Ny opdatering! desc: "Dette har ændret sig siden sidst du spillede:" @@ -189,9 +121,6 @@ dialogs: titleEdit: Rediger Markør desc: Giv det et meningsfyldt navn. Du kan også inkludere en <strong> kort nøgle</strong> af en figur (som du kan lave <link>her</link>) - markerDemoLimit: - desc: Du kan kun lave to markører i demoen. Køb spillet for uendeligt mange - markører! exportScreenshotWarning: title: Eksporter skærmbillede desc: Du bad om at eksportere din fabrik som et skærmbillede. Bemærk at dette @@ -289,28 +218,9 @@ dialogs: newMods: Newly installed Mods resourceLoadFailed: title: Resources failed to load - demoLinkText: shapez demo on Steam - descWeb: "One ore more resources could not be loaded. Make sure you have a - stable internet connection and try again. If this still doesn't - work, make sure to also disable any browser extensions (including - adblockers).<br><br> As an alternative, you can also play the - <demoOnSteamLinkText>. <br><br> Error Message:" descSteamDemo: "One ore more resources could not be loaded. Try restarting the game - If that does not help, try reinstalling and verifying the game files via Steam. <br><br> Error Message:" - steamSsoError: - title: Full Version Logout - desc: You have been logged out from the Full Browser Version since either your - network connection is unstable or you are playing on another - device.<br><br> Please make sure you don't have shapez open in any - other browser tab or another computer with the same Steam - account.<br><br> You can login again in the main menu. - steamSsoNoOwnership: - title: Full Edition not owned - desc: In order to play the Full Edition in your Browser, you need to own both - the base game and the Puzzle DLC on your Steam account.<br><br> - Please make sure you own both, signed in with the correct Steam - account and then try again. ingame: keybindingsOverlay: moveMap: Bevæg dig @@ -450,40 +360,6 @@ ingame: one_miner: 1 Miner n_miners: <amount> Miners limited_items: Limited to <max_throughput> - watermark: - title: Demo version - desc: Click here to see the Steam version advantages! - get_on_steam: Få på steam - standaloneAdvantages: - no_thanks: Nej tak! - points: - levels: - title: 12 Nye Levels - desc: For et total af 26 levels! - buildings: - title: 22 Nye Bygninger - desc: Fully automate your factory! - markers: - title: ∞ Markers - desc: Never get lost in your factory! - wires: - title: Ledninger - desc: En helt ny dimension! - darkmode: - title: Dark Mode - desc: Stop hurting your eyes! - support: - title: Støt mig - desc: Jeg arbejder på det i min fritid! - achievements: - title: Achievements - desc: Hunt them all! - mods: - title: Modding support! - desc: Over 80 mods available! - titleV2: "Get the full version now on Steam to unlock:" - titleExpiredV2: Demo completed! - titleEnjoyingDemo: Enjoy the demo? puzzleEditorSettings: zoneTitle: Zone zoneWidth: Width @@ -573,7 +449,7 @@ buildings: description: Klipper figurer om til fire dele. <strong>Hvis du kun bruger nogle af dem så husk at ødelægge de andre dele, ellers går maskinen i stå!</strong> - rotater: + rotator: default: name: Drejer description: Drejer figurer 90 grader med uret. @@ -708,8 +584,8 @@ buildings: default: name: Virtual Cutter description: Virtually cuts the shape into two halves. - rotater: - name: Virtual Rotater + rotator: + name: Virtual Rotator description: Virtually rotates the shape, both clockwise and counter-clockwise. unstacker: name: Virtual Unstacker @@ -748,7 +624,7 @@ storyRewards: anden side, ellers vil den <strong>tilstoppe og gå i stå</strong> - Derfor har du nu også fået <strong>skraldespanden</strong>, som fjerner alt der kommer i den. - reward_rotater: + reward_rotator: title: Drejning desc: <strong>Drejeren</strong> er nu tilgængelig! Den drejer figurer 90 grader med uret. @@ -778,7 +654,7 @@ storyRewards: title: Tunnel desc: <strong>Tunnellen</strong> er nu tilgængelig - Du kan nu lave tuneller under bånd og bygninger! - reward_rotater_ccw: + reward_rotator_ccw: title: Rotation mod uret desc: Du har fået adgang til en variant af <strong>drejeren</strong> - Den lader dig dreje ting mod uret! For at bygge den skal du vælge drejeren og @@ -849,7 +725,7 @@ storyRewards: desc: Du har nu åbnet op for <strong>Båndlæseren</strong>. Den måler gennemstrømning på et bånd. <br><br>Bare vent til du har Ledninger - så bliver den meget nyttig! - reward_rotater_180: + reward_rotator_180: title: Drejer (180 grader) desc: Du har nu åbnet op for <strong>Drejeren med 180°</strong>! - Nu kan du dreje dine figurer med 180 grader (Overraskelse! :D) @@ -877,7 +753,7 @@ storyRewards: title: Virtual Processing desc: I just gave a whole bunch of new buildings which allow you to <strong>simulate the processing of shapes</strong>!<br><br> You can - now simulate a cutter, rotater, stacker and more on the wires layer! + now simulate a cutter, rotator, stacker and more on the wires layer! With this you now have three options to continue the game:<br><br> - Build an <strong>automated machine</strong> to create any possible shape requested by the HUB (I recommend to try it!).<br><br> - Build @@ -1109,7 +985,7 @@ keybindings: underground_belt: Tunnel miner: Udvinder cutter: Klipper - rotater: Drejer + rotator: Drejer stacker: Stabler mixer: Farveblander painter: Maler @@ -1173,12 +1049,6 @@ about: changelog: title: Changelog demo: - features: - restoringGames: Genopretter gemte spil - importingGames: Importerer gemte spil - oneGameLimit: Afgrænset til et gem - customizeKeybindings: Tilpasse Tastaturfunktioner - exportingBase: Eksporterer hele basen som et billede settingNotAvailable: Ikke tilgængelig i demoen. tips: - Det central NAV modtager alle figurer, ikke kun den aktuelle figur! @@ -1334,16 +1204,7 @@ mods: version: Version modWebsite: Website openFolder: Open Mods Folder - folderOnlyStandalone: Opening the mod folder is only possible when running the standalone. browseMods: Browse Mods modsInfo: To install and manage mods, copy them to the mods folder within the game directory. You can also use the 'Open Mods Folder' button on the top right. - noModSupport: You need the standalone version on Steam to install mods. - togglingComingSoon: - title: Coming Soon - description: Enabling or disabling mods is currently only possible by copying - the mod file from or to the mods/ folder. However, being able to - toggle them here is planned for a future update! - browserNoSupport: Due to browser restrictions it is currently only possible to - install mods in the Steam version - Sorry! diff --git a/translations/base-de.yaml b/translations/base-de.yaml index f944cb11..38d4eab4 100644 --- a/translations/base-de.yaml +++ b/translations/base-de.yaml @@ -1,27 +1,3 @@ -steamPage: - shortText: In shapez.io nutzt du die vorhandenen Ressourcen, um mit deinen - Maschinen durch Kombination immer komplexere Formen zu erschaffen. - discordLinkShort: Offizieller Discord - intro: >- - Du magst Automatisierungsspiele? Dann bist du hier genau richtig! - - Shapez.io ist ein entspanntes Spiel, in dem du Fabriken zur automatisierten Produktion von geometrischen Formen bauen musst. - - Mit steigendem Level werden die Formen immer komplexer und du musst dich auf der unendlich großen Karte ausbreiten. - - Das ist noch nicht alles, denn du musst exponentiell mehr produzieren, um die Anforderungen zu erfüllen - Da hilft nur skalieren! - - Während du am Anfang nur Formen verarbeitest, müssen diese später auch eingefärbt werden. Die benötigten Farben können extrahiert und gemischt werden. - - Der Kauf des Spiels auf Steam gibt dir Zugriff auf die Vollversion, aber du kannst die Demo auf shapez.io auch zuerst ausprobieren und dich später entscheiden! - what_others_say: Was andere über shapez.io sagen - nothernlion_comment: This game is great - I'm having a wonderful time playing, - and time has flown by. - notch_comment: Oh crap. I really should sleep, but I think I just figured out - how to make a computer in shapez.io - steam_review_comment: This game has stolen my life and I don't want it back. - Very chill factory game that won't let me stop making my lines more - efficient. global: loading: Laden error: Fehler @@ -55,59 +31,20 @@ global: shift: UMSCH space: LEER loadingResources: Lade zusätzliche Ressourcen (<percentage> %) - discount: -<percentage>% - discountSummerSale: SONDERANGEBOT! Angebot endet am 7. Juli -demoBanners: - title: Demoversion - titleV2: "Spiele jetzt die Vollversion für:" - intro: |- - Kaufe die Vollversion <strong>jetzt</strong> für:<ul> - <li>Alle 26 Level + unendlich Freeplay</li> - <li>22 weitere Gebäude</li> - <li>Mod Support</li> - <li>Errungenschaften</li> - <li>Dunkler-Modus</li> - <li>... und noch viel mehr!</li> - </ul> - playtimeDisclaimer: Die Vollversion bietet mehr als <strong>20 Stunden Spielspaß</strong>. - playerCount: <playerCount> Spieler wie du spielen shapez gerade auf Steam - untilEndOfDemo: Bis zum Ende der Demo - playtimeDisclaimerDownload: Du kannst deinen Speicherstand in der Vollversion - fortsetzen! Klicke <strong>hier</strong> um ihn herunterzuladen. mainMenu: play: Spielen continue: Fortsetzen newGame: Neues Spiel - changelog: Änderungshistorie - subreddit: Reddit importSavegame: Import - openSourceHint: Dieses Spiel ist quelloffen! - discordLink: Offizieller Discord Server helpTranslate: Hilf beim Übersetzen! - madeBy: Ein Spiel von <author-link> - browserWarning: Sorry, aber das Spiel wird in deinem Browser langsamer laufen! - Kaufe die Vollversion oder verwende Google Chrome für die beste - Erfahrung. savegameLevel: Level <x> savegameLevelUnknown: Unbekanntes Level savegameUnnamed: Unbenannt - puzzleMode: Puzzlemodus - back: Zurück - puzzleDlcText: Du hast Spaß daran, deine Fabriken zu optimieren und effizienter - zu machen? Hol dir das Puzzle-DLC auf Steam für noch mehr Spaß! - puzzleDlcWishlist: Jetzt zur Wunschliste hinzufügen! - puzzleDlcViewNow: DLC anzeigen mods: - title: Aktive Mods warningPuzzleDLC: Das Puzzle-DLC und Mods sind nicht kompatibel. Bitte deaktiviere zuerst alle Mods, um das DLC zu spielen. - playFullVersionV2: Du hast shapez schon auf Steam gekauft? Spiel jetzt die - Vollversion im Browser! - playingFullVersion: Du spielst jetzt die Vollversion! - logout: Ausloggen noActiveSavegames: Keine Speicherstände gefunden - Klicke Spielen um ein neues Spiel zu starten! - playFullVersionStandalone: You can now also play the full version in your Browser! puzzleMenu: play: Spielen edit: Bearbeiten @@ -177,7 +114,6 @@ dialogs: later: Später restart: Neustart reset: Zurücksetzen - getStandalone: Zur Vollversion deleteGame: Ich weiß, was ich tue viewUpdate: Update anzeigen showUpgrades: Upgrades anzeigen @@ -215,14 +151,6 @@ dialogs: keybindingsResetOk: title: Tastenbelegung zurückgesetzt desc: Die Tastenbelegung wurde auf den Standard zurückgesetzt! - featureRestriction: - title: Demoversion - desc: Du hast ein Feature benutzt (<feature>), welches nicht in der Demo - enthalten ist. Erwerbe die Vollversion für das ganze Erlebnis! - oneSavegameLimit: - title: Begrenzte Speicherstände - desc: Du kannst in der Demo nur einen Speicherstand haben. Bitte lösche den - existierenden oder hole dir die Vollversion! updateSummary: title: Neues Update! desc: "Hier sind die Änderungen, seitdem du das letzte Mal gespielt hast:" @@ -269,9 +197,6 @@ dialogs: (<link>Hier</link> geht es zum Generator). editConstantProducer: title: Item wählen - markerDemoLimit: - desc: Du kannst nur 2 Markierungen in der Demo benutzen. Hole dir die - Vollversion, um unbegrenzt viele Markierungen zu setzen! exportScreenshotWarning: title: Bildschirmfoto exportieren desc: Hier kannst du ein Bildschirmfoto von deiner ganzen Fabrik erstellen. Für @@ -363,29 +288,9 @@ dialogs: newMods: Neu installierte Mods resourceLoadFailed: title: Resources failed to load - demoLinkText: shapez demo on Steam - descWeb: "One ore more resources could not be loaded. Make sure you have a - stable internet connection and try again. If this still doesn't - work, make sure to also disable any browser extensions (including - adblockers).<br><br> As an alternative, you can also play the - <demoOnSteamLinkText>. <br><br> Error Message:" descSteamDemo: "One ore more resources could not be loaded. Try restarting the game - If that does not help, try reinstalling and verifying the game files via Steam. <br><br> Error Message:" - steamSsoError: - title: Ausgeloggt aus Vollversion - desc: Du wurdest aus der Browser-Vollversion ausgeloggt da entweder deine - Internetverbindung instabil ist, oder du auf einem anderen Gerät - spielst.<br><br> Bitte stelle sicher dass du shapez nicht in einem - anderen Browser-Tab offen hast oder auf einem weiteren Computer mit - demselben Steam-Account. <br><br> Du kannst dich dann erneut im - Hauptmenü einloggen. - steamSsoNoOwnership: - title: Vollversion nötig - desc: Um die Vollversion im Browser zu spielen brauchst du sowohl das Basispiel - als auch das Puzzle DLC auf deinem Steam Account.<br><br> Bitte - stelle sicher, dass du beide gekauft hast und den richtigen Steam - Account verwendest. Danach kannst du es erneut versuchen. ingame: keybindingsOverlay: moveMap: Bewegen @@ -526,40 +431,6 @@ ingame: one_miner: Ein Extraktor n_miners: <amount> Extraktoren limited_items: Begrenzt auf <max_throughput> - watermark: - title: Demoversion - desc: Klicke hier, um die Vorteile der Vollversion zu sehen! - get_on_steam: Zur Vollversion - standaloneAdvantages: - no_thanks: Nein, danke! - points: - levels: - title: 19 Neue Level - desc: Über 20 Stunden weitere Inhalte! - wires: - title: Wires-Ebene - desc: Eine ganz neue Dimension! - darkmode: - title: Dark-Mode - desc: Gönn deinen Augen eine Auszeit! - buildings: - title: 22 Neue Gebäude - desc: Automatisiere deine Fabrik! - achievements: - title: 45 Errungenschaften - desc: Hol sie dir alle! - markers: - title: ∞ Markierungen - desc: Verliere nie den Überblick! - support: - title: Unterstütze Mich - desc: Ich entwickle das Spiel in meiner Freizeit! - mods: - title: Mod-Support! - desc: Über 80 Mods verfügbar! - titleV2: "Hol dir jetzt die Vollversion auf Steam für:" - titleExpiredV2: Demo abgeschlossen! - titleEnjoyingDemo: Gefällt dir die Demo? puzzleEditorSettings: zoneTitle: Zone zoneWidth: Breite @@ -668,7 +539,7 @@ buildings: name: Schneider (vierfach) description: Zerschneidet Formen in vier Teile. <strong>Benutze oder zerstöre alle Viertel, sonst verstopft die Maschine!</strong> - rotater: + rotator: default: name: Rotierer (90°) description: Rotiert Formen im Uhrzeigersinn um 90 Grad. @@ -799,7 +670,7 @@ buildings: default: name: Virtueller Schneider description: Schneidet die Form virtuell in zwei Hälften. - rotater: + rotator: name: Virtueller Rotierer description: Dreht die Form virtuell im Uhrzeigersinn. unstacker: @@ -839,7 +710,7 @@ storyRewards: sonst <b>verstopft und blockiert er</b> - Zu diesem Zweck habe ich dir den <strong>Mülleimer</strong> gegeben. Er entsorgt alles, was du ihm fütterst! - reward_rotater: + reward_rotator: title: Rotieren desc: Der <strong>Rotierer</strong> wurde freigeschaltet! Er rotiert Formen im Uhrzeigersinn um 90 Grad. @@ -869,7 +740,7 @@ storyRewards: title: Tunnel desc: Der <strong>Tunnel</strong> wurde freigeschaltet! Du kannst Items nun unter Gebäuden oder Fließbändern hindurchleiten. - reward_rotater_ccw: + reward_rotator_ccw: title: Gegen den UZS rotieren desc: Du hast eine zweite Variante des <strong>Rotierers</strong> freigeschaltet! Damit können Items gegen den Uhrzeigersinn gedreht @@ -926,7 +797,7 @@ storyRewards: <strong>nicht kostenlos</strong>! Du musst <strong>Blaupausenformen</strong> produzieren, um die Kopierkosten zu decken (Welche du gerade produziert hast). - reward_rotater_180: + reward_rotator_180: title: Rotierer (180°) desc: Du hast eine weitere Variante des <strong>Rotierers</strong> freigeschaltet! Mit ihm kannst du Formen um 180° drehen @@ -1012,20 +883,10 @@ mods: version: Version modWebsite: Webseite openFolder: Modordner öffnen - folderOnlyStandalone: Du kannst den Modordner nur in der Vollversion öffnen. browseMods: Mods durchsuchen modsInfo: Um Mods zu installieren und zu verwalten, kopiere sie in den Modordner im Spielverzeichnis. Diesen kannst du auch mit dem 'Modordner öffnen'-Knopf oben rechts aufrufen. - noModSupport: Du brauchst die Steam-Vollversion, um Mods zu installieren. - togglingComingSoon: - title: Demnächst - description: Du kannst Mods aktuell nur (de-)aktivieren, indem du die - entsprechende Datei in den Modordner einfügst, oder löschst. Das - (De-)Aktivieren der Mods in diesem Menü ist allerdings schon für ein - späteres Update geplant! - browserNoSupport: Aufgrund von Browser-Einschränkungen kannst du aktuell nur in - der Steam-Version Mods installieren - Sorry! settings: title: Einstellungen categories: @@ -1246,7 +1107,7 @@ keybindings: underground_belt: Tunnel miner: Extraktor cutter: Schneider - rotater: Rotierer (90°) + rotator: Rotierer (90°) stacker: Stapler mixer: Farbmischer painter: Färber @@ -1309,12 +1170,6 @@ about: changelog: title: Änderungshistorie demo: - features: - restoringGames: Spielstände wiederherstellen - importingGames: Spielstände importieren - oneGameLimit: Beschränkt auf einen Spielstand - customizeKeybindings: Tastenbelegung anpassen - exportingBase: Ganze Fabrik als Foto exportieren settingNotAvailable: Nicht verfügbar in der Demo. backendErrors: ratelimit: Du führst deine Aktionen zu schnell aus. Bitte warte kurz. diff --git a/translations/base-el.yaml b/translations/base-el.yaml index 8e1c4d51..f792d8bc 100644 --- a/translations/base-el.yaml +++ b/translations/base-el.yaml @@ -1,27 +1,3 @@ -steamPage: - shortText: Στο shapez.io χτήζεις εργοστάσια για να αυτοματοποιήσεις την - δημιουργία και τον συνδιασμό σχημάτων αυξανόμενης πολυπλοκότητας σε έναν - ατέλειωτο χάρτη. - discordLinkShort: Official Discord - intro: >- - Shapez.io is a relaxed game in which you have to build factories for the - automated production of geometric shapes. - - As the level increases, the shapes become more and more complex, and you have to spread out on the infinite map. - - And as if that wasn't enough, you also have to produce exponentially more to satisfy the demands - the only thing that helps is scaling! - - While you only process shapes at the beginning, you have to color them later - for this you have to extract and mix colors! - - Buying the game on Steam gives you access to the full version, but you can also play a demo on shapez.io first and decide later! - what_others_say: What people say about shapez.io - nothernlion_comment: This game is great - I'm having a wonderful time playing, - and time has flown by. - notch_comment: Oh crap. I really should sleep, but I think I just figured out - how to make a computer in shapez.io - steam_review_comment: This game has stolen my life and I don't want it back. - Very chill factory game that won't let me stop making my lines more - efficient. global: loading: Φόρτωση error: Σφάλμα @@ -55,57 +31,19 @@ global: space: SPACE loggingIn: Logging in loadingResources: Downloading additional resources (<percentage> %) - discount: -<percentage>% - discountSummerSale: SPECIAL PROMOTION! Offer ends 7 July -demoBanners: - title: Demo Version - intro: |- - Get the full game <strong>now</strong> to unlock:<ul> - <li>All 26 levels + infinite Freeplay</li> - <li>22 new buildings</li> - <li>Mod support</li> - <li>Achievements</li> - <li>Dark Mode</li> - <li>... and a lot more!</li> - </ul> - playtimeDisclaimer: The full version contains more than <strong>20 hours of content</strong>. - playerCount: <playerCount> players like you are currently playing shapez on Steam - untilEndOfDemo: Until end of demo - playtimeDisclaimerDownload: You can continue your savegame in the full version! - Click <strong>here</strong> to download your savegame. - titleV2: "Play the full version now for:" mainMenu: play: Παίξε - changelog: Changelog importSavegame: Εισαγωγή αποθηκευμένου παιχνιδιού - openSourceHint: Αυτό το παιχνίδι είναι ανοιχτού κώδικα! - discordLink: Επίσημο Discord Server helpTranslate: Βοήθησε με μεταφράσεις! - browserWarning: Δυστυχώς, το παιχνίδι τρέχει αργά στο πρόγραμμα περιήγησής σας! - Αποκτήστε την αυτόνομη έκδοση ή κατεβάστε το chrome για την πλήρη - εμπειρία. savegameLevel: Επίπεδο <x> savegameLevelUnknown: Άγνωστο Επίπεδο continue: Συνέχεια newGame: Καινούριο παιχνίδι - madeBy: Κατασκευασμένο απο <author-link> - subreddit: Reddit savegameUnnamed: Unnamed - puzzleMode: Λειτουργία παζλ - back: Πίσω - puzzleDlcText: Σε αρέσει να συμπάγης και να βελτιστοποίεις εργοστάσια; Πάρε την - λειτουργεία παζλ στο Steam για ακόμη περισσότερη πλάκα! - puzzleDlcWishlist: Βαλτε το στην λίστα επιθυμιών τώρα! - puzzleDlcViewNow: View Dlc mods: - title: Active Mods warningPuzzleDLC: Playing the Puzzle DLC is not possible with mods. Please disable all mods to play the DLC. - playingFullVersion: You are now playing the full version! - logout: Logout noActiveSavegames: No active savegames found - Click play to start a new game! - playFullVersionV2: Bough shapez on Steam? Play the full version in your Browser! - playFullVersionStandalone: You can now also play the full version in your Browser! dialogs: buttons: ok: OK @@ -114,7 +52,6 @@ dialogs: later: Αργότερα restart: Επανεκκίνηση reset: Επαναφορά - getStandalone: Απόκτησε την Αυτόνομη έκδοση deleteGame: Ξέρω τί κάνω viewUpdate: Προβολή ενημέρωσης showUpgrades: Εμφάνιση αναβαθμίσεων @@ -153,16 +90,6 @@ dialogs: keybindingsResetOk: title: Συνδιασμοί πλήκτρων επαναφέρθηκαν desc: Οι συνδιασμών πλήκτρων επαναφέρθηκαν στις προεπιλεγμένες τιμές τους! - featureRestriction: - title: Έκδοση Demo - desc: You tried to access a feature (<feature>) which is not available in the - demo. Consider getting the standalone version for the full - experience! - oneSavegameLimit: - title: Περιορισμένα αποθηκευμένα παιχνίδια - desc: Στην demo έκδοση μπορείς να έχεις μόνο ένα αποθηκευμένο παιχνίδι. Παρακαλώ - διάγραψε το υπάρχον αποθηκευμένο παιχνίδι ή απόκτησε την αθτόνομη - έκδοση! updateSummary: title: Νέα αναβάθμιση! desc: "Αυτές είναι οι αλλαγές από την τελευταία φορά που έπαιξες:" @@ -195,10 +122,6 @@ dialogs: desc: Give it a meaningful name, you can also include a <strong>short key</strong> of a shape (Which you can generate <link>here</link>) titleEdit: Επεξεργασία Σημαδιού - markerDemoLimit: - desc: Στην έκδωση demo μπορείς να βάλεις μέχρι δύο σημάδια στον χάρτη. - Αποκτήστε την αυτόνομη έκδοση για να μπορείς να βάλεις απεριόριστα - σημάδια στον χάρτη! massCutConfirm: title: Επιβεβαίωση αποκοπής desc: Ετοιμάζεσαι να αποκόψεις πολλά κτήρια (<count> φια την ακρίβεια)! Είσαι @@ -301,28 +224,9 @@ dialogs: newMods: Newly installed Mods resourceLoadFailed: title: Resources failed to load - demoLinkText: shapez demo on Steam - descWeb: "One ore more resources could not be loaded. Make sure you have a - stable internet connection and try again. If this still doesn't - work, make sure to also disable any browser extensions (including - adblockers).<br><br> As an alternative, you can also play the - <demoOnSteamLinkText>. <br><br> Error Message:" descSteamDemo: "One ore more resources could not be loaded. Try restarting the game - If that does not help, try reinstalling and verifying the game files via Steam. <br><br> Error Message:" - steamSsoError: - title: Full Version Logout - desc: You have been logged out from the Full Browser Version since either your - network connection is unstable or you are playing on another - device.<br><br> Please make sure you don't have shapez open in any - other browser tab or another computer with the same Steam - account.<br><br> You can login again in the main menu. - steamSsoNoOwnership: - title: Full Edition not owned - desc: In order to play the Full Edition in your Browser, you need to own both - the base game and the Puzzle DLC on your Steam account.<br><br> - Please make sure you own both, signed in with the correct Steam - account and then try again. ingame: keybindingsOverlay: moveMap: Κίνηση @@ -464,40 +368,6 @@ ingame: one_miner: 1 Miner n_miners: <amount> Miners limited_items: Limited to <max_throughput> - watermark: - title: Demo version - desc: Click here to see the Steam version advantages! - get_on_steam: Get on steam - standaloneAdvantages: - no_thanks: No, thanks! - points: - levels: - title: 12 New Levels - desc: For a total of 26 levels! - buildings: - title: 22 New Buildings - desc: Fully automate your factory! - markers: - title: ∞ Markers - desc: Never get lost in your factory! - wires: - title: Wires - desc: An entirely new dimension! - darkmode: - title: Dark Mode - desc: Stop hurting your eyes! - support: - title: Support me - desc: I develop it in my spare time! - achievements: - title: Achievements - desc: Hunt them all! - mods: - title: Modding support! - desc: Over 80 mods available! - titleV2: "Get the full version now on Steam to unlock:" - titleExpiredV2: Demo completed! - titleEnjoyingDemo: Enjoy the demo? puzzleEditorSettings: zoneTitle: Zone zoneWidth: Width @@ -588,7 +458,7 @@ buildings: description: Κόβει σχήματα σε τέσσερα κομμάτια. <strong>Εάν χρησιμοποιείς μόνο το ένα κομμάτι, φρόντισε να καταστρέψεις τα άλλα κομμάτια, διαφορετικά η λειτουργία θα σταματήσει!</strong> - rotater: + rotator: default: name: Περιστροφέας description: Περιστρέφει τα σχήματα δεξιόστροφα κατά 90 μοίρες. @@ -733,8 +603,8 @@ buildings: default: name: Virtual Cutter description: Virtually cuts the shape into two halves. - rotater: - name: Virtual Rotater + rotator: + name: Virtual Rotator description: Virtually rotates the shape, both clockwise and counter-clockwise. unstacker: name: Virtual Unstacker @@ -773,7 +643,7 @@ storyRewards: otherwise <strong>it will clog and stall</strong> - For this purpose I have given you the <strong>trash</strong>, which destroys everything you put into it! - reward_rotater: + reward_rotator: title: Περιστροφή desc: Ο <strong>Περιστροφέας</strong> ξεκλειδώθηκε! Το κτήριο αυτό περιστρέφει τα σχήματα δεξιόστροφα κατά 90 μοίρες. @@ -805,7 +675,7 @@ storyRewards: title: Σήραγγα desc: Το <strong>Σήραγγα</strong> είναι πλέον διαθέσιμο - Τώρα μπορείς να διοχετεύσεις είδη κάτω από ιμάντες και κτήρια! - reward_rotater_ccw: + reward_rotator_ccw: title: Περιστροφή (Αρστ.) desc: Ξεκλείδωσες μια παραλλαγή του <strong>Περιστροφέα</strong> - Επιτρέπει αριστερόστροφη περιστροφή! Για να τον τοποθετήσεις, επίλεξε τον @@ -879,9 +749,9 @@ storyRewards: desc: You have now unlocked the <strong>belt reader</strong>! It allows you to measure the throughput of a belt.<br><br>And wait until you unlock wires - then it gets really useful! - reward_rotater_180: - title: Rotater (180 degrees) - desc: You just unlocked the 180 degrees <strong>rotater</strong>! - It allows + reward_rotator_180: + title: Rotator (180 degrees) + desc: You just unlocked the 180 degrees <strong>rotator</strong>! - It allows you to rotate a shape by 180 degrees (Surprise! :D) reward_display: title: Display @@ -906,7 +776,7 @@ storyRewards: title: Virtual Processing desc: I just gave a whole bunch of new buildings which allow you to <strong>simulate the processing of shapes</strong>!<br><br> You can - now simulate a cutter, rotater, stacker and more on the wires layer! + now simulate a cutter, rotator, stacker and more on the wires layer! With this you now have three options to continue the game:<br><br> - Build an <strong>automated machine</strong> to create any possible shape requested by the HUB (I recommend to try it!).<br><br> - Build @@ -1139,7 +1009,7 @@ keybindings: underground_belt: Σήραγγα miner: Αποσπαστής cutter: Κόπτης - rotater: Περιστροφέας + rotator: Περιστροφέας stacker: Στοίβαχτής mixer: Αναμείκτης Χρωμάτων painter: Βαφέας @@ -1213,12 +1083,6 @@ about: changelog: title: Changelog demo: - features: - restoringGames: Επαναφορά αποθηκευμένων παιχνιδιών - importingGames: Εισαγωγή αποθηκευμένων παιχνιδιών - oneGameLimit: Περιορίζεται σε ένα αποθηκευμένο παιχνίδι - customizeKeybindings: Προσαρμογή συνδιασμών πλήκτρων - exportingBase: Εξαγωγή ολόκληρης της βάσης ως εικόνα settingNotAvailable: Δεν είναι διαθέσιμο στο demo. tips: - The hub accepts input of any kind, not just the current shape! @@ -1372,16 +1236,7 @@ mods: version: Version modWebsite: Website openFolder: Open Mods Folder - folderOnlyStandalone: Opening the mod folder is only possible when running the standalone. browseMods: Browse Mods modsInfo: To install and manage mods, copy them to the mods folder within the game directory. You can also use the 'Open Mods Folder' button on the top right. - noModSupport: You need the standalone version on Steam to install mods. - togglingComingSoon: - title: Coming Soon - description: Enabling or disabling mods is currently only possible by copying - the mod file from or to the mods/ folder. However, being able to - toggle them here is planned for a future update! - browserNoSupport: Due to browser restrictions it is currently only possible to - install mods in the Steam version - Sorry! diff --git a/translations/base-en.yaml b/translations/base-en.yaml index b2dbd736..30d9c228 100644 --- a/translations/base-en.yaml +++ b/translations/base-en.yaml @@ -20,31 +20,6 @@ # --- -steamPage: - # This is the short text appearing on the steam page - shortText: shapez is a game about building factories to automate the creation and processing of increasingly complex shapes across an infinitely expanding map. - - # This is the text shown above the Discord link - discordLinkShort: Official Discord - - intro: >- - Do you like automation games? Then you are in the right place! - - shapez is a relaxed game in which you have to build factories for the automated production of geometric shapes. As the level increases, the shapes become more and more complex, and you have to spread out on the infinite map. - - And as if that wasn't enough, you also have to produce exponentially more to satisfy the demands - the only thing that helps is scaling! While you only have to process shapes at the beginning, you will later have to color them - by extracting and mixing colors! - - Buying the game on Steam gives you access to the full version, but you can also play a demo at shapez first and decide later! - - what_others_say: What people say about shapez - - nothernlion_comment: >- - This game is great - I'm having a wonderful time playing, and time has flown by. - notch_comment: >- - Oh crap. I really should sleep, but I think I just figured out how to make a computer in shapez - steam_review_comment: >- - This game has stolen my life and I don't want it back. Very chill factory game that won't let me stop making my lines more efficient. - global: loading: Loading error: Error @@ -58,9 +33,6 @@ global: # What symbol to use to separate the integer part from the fractional part of a number, e.g. "0.4" decimalSeparator: "." - discount: -<percentage>% - discountSummerSale: "SPECIAL PROMOTION! Offer ends 7 July" - # The suffix for large numbers, e.g. 1.3k, 400.2M, etc. suffix: thousands: k @@ -97,67 +69,19 @@ global: shift: SHIFT space: SPACE -demoBanners: - # This is the "advertisement" shown in the main menu and other various places - title: Demo - titleV2: >- - Play the full version now for: - - intro: >- - Get the full game <strong>now</strong> to unlock:<ul> - <li>All 26 levels + infinite Freeplay</li> - <li>22 new buildings</li> - <li>Mod support</li> - <li>Achievements</li> - <li>Dark Mode</li> - <li>... and a lot more!</li> - </ul> - playtimeDisclaimer: >- - The full version contains more than <strong>20 hours of additional content</strong>! - - playtimeDisclaimerDownload: >- - You can continue your savegame in the full version! Click <strong>here</strong> to download your savegame. - - playerCount: >- - <playerCount> players like you are playing shapez on Steam right now - untilEndOfDemo: Until end of demo - mainMenu: play: Play continue: Continue newGame: New Game - changelog: Changelog - subreddit: Reddit importSavegame: Import - openSourceHint: This game is open source! - discordLink: Official Discord Server helpTranslate: Help translate! - madeBy: Made by <author-link> noActiveSavegames: No active savegames found - Click play to start a new game! - playFullVersionV2: Bought shapez on Steam? Play the full version in your Browser! - playFullVersionStandalone: You can now also play the full version in your Browser! - playingFullVersion: You are now playing the full version! - logout: Logout - - # This is shown when using firefox and other browsers which are not supported. - browserWarning: >- - Sorry, but the game is known to run slowly on your browser! Get the full version or download Google Chrome for the full experience. - savegameLevel: Level <x> savegameLevelUnknown: Unknown Level savegameUnnamed: Unnamed - puzzleMode: Puzzle Mode - back: Back - - puzzleDlcText: >- - Do you enjoy compacting and optimizing factories? - Get the Puzzle DLC now on Steam for even more fun! - puzzleDlcWishlist: Wishlist now! - puzzleDlcViewNow: View Dlc mods: - title: Active Mods warningPuzzleDLC: >- Playing the Puzzle DLC is not possible with mods. Please disable all mods to play the DLC. @@ -234,7 +158,6 @@ dialogs: later: Later restart: Restart reset: Reset - getStandalone: Get on Steam deleteGame: I know what I am doing viewUpdate: View Update showUpgrades: Show Upgrades @@ -287,14 +210,6 @@ dialogs: title: Keybindings reset desc: All keybindings have been reset to their defaults values! - featureRestriction: - title: Demo Version - desc: You tried to access a feature (<feature>) which is not available in the demo. Consider getting the full version on Steam for the best experience! - - oneSavegameLimit: - title: Limited savegames - desc: You can only have one savegame at a time in the demo version. Please remove the existing one or get the full version on Steam! - updateSummary: title: New update! desc: >- @@ -349,9 +264,6 @@ dialogs: editConstantProducer: title: Set Item - markerDemoLimit: - desc: You can only create two custom markers in the demo. Get the full version on Steam for unlimited markers! - exportScreenshotWarning: title: Export screenshot desc: You requested to export your base as a screenshot. Please note that this will be quite slow for a bigger base and could potentially crash your game! @@ -464,32 +376,11 @@ dialogs: resourceLoadFailed: title: Resources failed to load - demoLinkText: shapez demo on Steam - descWeb: >- - One ore more resources could not be loaded. Make sure you have a stable internet connection and try again. - If this still doesn't work, make sure to also disable any browser extensions (including adblockers).<br><br> - As an alternative, you can also play the <demoOnSteamLinkText>. - <br><br> - Error Message: - descSteamDemo: >- One ore more resources could not be loaded. Try restarting the game - If that does not help, try reinstalling and verifying the game files via Steam. <br><br> Error Message: - steamSsoError: - title: Full Version Logout - desc: >- - You have been logged out from the Full Browser Version since either your network connection is unstable or you are playing on another device.<br><br> - Please make sure you don't have shapez open in any other browser tab or another computer with the same Steam account.<br><br> - You can login again in the main menu. - - steamSsoNoOwnership: - title: Full Edition not owned - desc: >- - In order to play the Full Edition in your Browser, you need to own both the base game and the Puzzle DLC on your Steam account.<br><br> - Please make sure you own both, signed in with the correct Steam account and then try again. - ingame: # This is shown in the top left corner and displays useful keybindings in # every situation @@ -674,53 +565,6 @@ ingame: n_miners: <amount> Extractors limited_items: Limited to <max_throughput> - # Pops up in the demo every few minutes - watermark: - title: Demo version - desc: Click here to see the advantages of the full version! - get_on_steam: Get on Steam - - standaloneAdvantages: - titleV2: >- - Play the full version now on Steam to unlock: - titleExpiredV2: Demo completed! - titleEnjoyingDemo: Enjoying the demo? - - no_thanks: No, thanks! - - points: - levels: - title: 19 New Levels - desc: >- - Over 20 hours of additional content! - wires: - title: Wires - desc: An entirely new dimension! - - darkmode: - title: Dark Mode - desc: Stop hurting your eyes! - - buildings: - title: 22 New Buildings - desc: Fully automate your factory! - - achievements: - title: 45 Achievements - desc: Hunt them all! - - mods: - title: Modding support! - desc: Over 80 mods available! - - markers: - title: ∞ Markers - desc: Never get lost in your factory! - - support: - title: Support me - desc: I develop shapez in my spare time! - # puzzle mode puzzleEditorSettings: zoneTitle: Zone @@ -840,9 +684,9 @@ buildings: name: Cutter (Quad) description: Cuts shapes into four parts. <strong>If you use only one part, be sure to destroy the other parts or it will clog and stall!</strong> - rotater: + rotator: default: - name: &rotater Rotator + name: &rotator Rotator description: Rotates shapes clockwise by 90 degrees. ccw: name: Rotator (CCW) @@ -965,7 +809,7 @@ buildings: name: &virtual_processor Virtual Cutter description: Virtually cuts the shape into two halves. - rotater: + rotator: name: Virtual Rotator description: Virtually rotates the shape clockwise. @@ -1009,7 +853,7 @@ storyRewards: You just unlocked the <strong>cutter</strong>, which cuts shapes in half from top to bottom <strong>regardless of its orientation</strong>!<br><br>Be sure to get rid of the waste, or otherwise <strong>it will clog and stall</strong> - For this purpose I have given you the <strong>trash</strong>, which destroys everything you put into it! - reward_rotater: + reward_rotator: title: Rotating desc: The <strong>rotator</strong> has been unlocked! It rotates shapes clockwise by 90 degrees. @@ -1037,7 +881,7 @@ storyRewards: title: Tunnel desc: The <strong>tunnel</strong> has been unlocked - You can now tunnel items below belts and buildings with it! - reward_rotater_ccw: + reward_rotator_ccw: title: CCW Rotating desc: >- You have unlocked a variant of the <strong>rotator</strong> - It allows you to rotate shapes counter-clockwise! To build it, select the rotator and @@ -1090,7 +934,7 @@ storyRewards: You can now <strong>copy and paste</strong> parts of your factory! Select an area (Hold CTRL, then drag with your mouse), and press 'C' to copy it.<br><br>Pasting it is <strong>not free</strong>, you need to produce <strong>blueprint shapes</strong> to afford it! (Those you just delivered). - reward_rotater_180: + reward_rotator_180: title: Rotator (180°) desc: You just unlocked the 180 degrees <strong>rotator</strong>! - It allows you to rotate a shape by 180 degrees (Surprise! :D) @@ -1171,17 +1015,10 @@ mods: version: Version modWebsite: Website openFolder: Open Mods Folder - folderOnlyStandalone: Opening the mod folder is only possible when running the full version on Steam. browseMods: Browse Mods modsInfo: >- To install and manage mods, copy them to the mods folder (use the 'Open Mods Folder' button). Be sure to restart the game afterwards, otherwise the mods will not show up. - noModSupport: Get the full version on Steam to install mods! - browserNoSupport: Due to browser restrictions it is currently only possible to install mods in the Steam version - Sorry! - - togglingComingSoon: - title: Coming Soon - description: Enabling or disabling mods is currently only possible by copying the mod file from or to the mods/ folder. However, being able to toggle them here is planned for a future update! settings: title: Settings @@ -1435,7 +1272,7 @@ keybindings: underground_belt: *underground_belt miner: *miner cutter: *cutter - rotater: *rotater + rotator: *rotator stacker: *stacker mixer: *mixer painter: *painter @@ -1505,13 +1342,6 @@ changelog: title: Changelog demo: - features: - restoringGames: Restoring savegames - importingGames: Importing savegames - oneGameLimit: Limited to one savegame - customizeKeybindings: Customizing Keybindings - exportingBase: Exporting whole Base as Image - settingNotAvailable: Not available in the demo. backendErrors: @@ -1594,3 +1424,13 @@ tips: - Press F4 to show your FPS and Tick Rate. - Press F4 twice to show the tile of your mouse and camera. - You can click a pinned shape on the left side to unpin it. + +errorHandler: + title: Unhandled Error! + labels: + buildInformation: "Build Information:" + loadedMods: "Loaded Mods:" + actions: + copy: Copy + copyDone: Copied! + restart: Restart diff --git a/translations/base-es.yaml b/translations/base-es.yaml index 5dbea916..fb88f458 100644 --- a/translations/base-es.yaml +++ b/translations/base-es.yaml @@ -1,27 +1,3 @@ -steamPage: - shortText: shapez.io es un juego sobre construir fábricas para automatizar la - creación y combinación de figuras cada vez más complejas en un mapa - infinito. - discordLinkShort: Discord oficial - intro: >- - Shapez.io es un juego tranquilo en el que tienes que construir fábricas - para la producción automatizada de formas geométricas. - - A medida que el nivel aumenta, las formas se vuelven más y más complejas, y tienes que extenderte en un mapa infinito. - - Y por si fuera poco, también tienes que producir exponencialmente más para satisfacer las demandas - ¡lo único que ayuda es ampliar! - - Mientras que sólo procesas formas al principio, tienes que colorearlas después - ¡para ello tienes que extraer y mezclar colores! - - Comprando el juego en Steam tienes acceso a la versión completa, ¡pero también puedes jugar una demo en shapez.io primero y decidir después! - what_others_say: Lo que otras personas dicen sobre shapez.io - nothernlion_comment: Este juego es estupendo - Estoy teniendo un tiempo - maravolloso jugano, y el tiempo ha pasado volando. - notch_comment: Miércoles. Verdaderamente debería dormir, pero creo que acabo de - descubrir como hacer un ordenador en shapez.io - steam_review_comment: Este juego ha robado mi vida y no la quiero de vuelta. Muy - relajante juego de fábrica que no me dejará hacer mis lineas más - eficientes. global: loading: Cargando error: Error @@ -55,62 +31,20 @@ global: space: ESPACIO loggingIn: Iniciando sesión loadingResources: Descargando recursos adicionales (<percentage> %) - discount: -<percentage>% - discountSummerSale: SPECIAL PROMOTION! Offer ends 7 July -demoBanners: - title: Versión de prueba - intro: >- - Consigue el juego completo <strong>ahora</strong> para - desbloquearlo:<ul> - <li>Los 26 niveles + juego libre infinito</li> - <li>22 edificios nuevos</li> - <li>Apoyo a los mods</li> - <li>Logros</li> - <li>Modo oscuro</li> - <li>... ¡y mucho más!</li> - </ul> - playtimeDisclaimer: La versión completa contiene más de <strong>20 horas de - contenido</strong>. - playerCount: <playerCount> jugadores como tú están jugando actualmente a shapez - en Steam - untilEndOfDemo: Hasta el final de la demo - playtimeDisclaimerDownload: ¡Puedes continuar con tu partida guardada en la - versión completa! Haz clic <strong>aquí</strong> para descargar tu - partida guardada. - titleV2: "Juega la versión completa para:" mainMenu: play: Jugar continue: Continuar newGame: Nuevo juego - changelog: Historial de cambios - subreddit: Reddit importSavegame: Importar - openSourceHint: ¡Este juego es de código abierto! - discordLink: Servidor de Discord oficial helpTranslate: ¡Ayuda a traducirlo! - madeBy: Desarrollado por <author-link> - browserWarning: ¡Lo siento, pero el juego funcionará lento en tu navegador! - Obtén el juego completo o descarga Chrome para la experiencia completa. savegameLevel: Nivel <x> savegameLevelUnknown: Nivel desconocido savegameUnnamed: Sin nombre - puzzleMode: Modo Puzle - back: Atrás - puzzleDlcText: ¿Disfrutas compactando y optimizando fábricas? ¡Consigue ahora el - DLC de Puzles en Steam para aún más diversión! - puzzleDlcWishlist: ¡Añádelo ahora a tu lista de deseos! - puzzleDlcViewNow: Ver DLC mods: - title: Mods Activos warningPuzzleDLC: No es posible jugar el DLC Puzzle con mods. Por favor desactiva todos los mods para jugar el DLC. - playingFullVersion: ¡Estás jugando la versión completa! - logout: Cerrar Sesión noActiveSavegames: No se encontraron partidas guardadas activas - ¡Haz click en Jugar para empezar una nueva partida! - playFullVersionV2: Has comprado shapez en Steam? ¡Juega la versión completa - desde tu navegador! - playFullVersionStandalone: ¡Ahora puedes jugar la versión completa desde tu navegador! dialogs: buttons: ok: OK @@ -119,7 +53,6 @@ dialogs: later: Más tarde restart: Volver a empezar reset: Reiniciar - getStandalone: Obtener juego completo deleteGame: Sé lo que hago viewUpdate: Ver actualización showUpgrades: Ver mejoras @@ -158,15 +91,6 @@ dialogs: keybindingsResetOk: title: Atajos de teclado reiniciados desc: ¡Los atajos de teclado han sito reiniciados a los valores por defecto! - featureRestriction: - title: Versión de prueba - desc: Has intentado acceder a una característica (<feature>) que no está - disponible en la versión de prueba. ¡Considera obtener el juego - completo para la experiencia completa! - oneSavegameLimit: - title: Partidas guardadas limitadas - desc: Solo puedes tener una partida guardada a la vez en la versión de prueba. - ¡Por favor, elimina la ya existente u obtén el juego completo! updateSummary: title: ¡Nueva actualización! desc: "Estos son los cambios desde la última vez que jugaste:" @@ -206,9 +130,6 @@ dialogs: desc: Dale un nombre significativo, tambien puedes incluir la <strong>clave</strong> de una forma (La cual puedes generar <link>aquí</link>) - markerDemoLimit: - desc: Solo puedes crear dos marcadores en la versión de prueba. ¡Obtén el juego - completo para marcadores ilimitados! exportScreenshotWarning: title: Exportar captura de pantalla desc: Has solicitado una captura de pantalla de tu base. Por favor, ten en @@ -305,29 +226,9 @@ dialogs: newMods: Mods Nuevos Instalados resourceLoadFailed: title: Recursos fallaron en cargar - demoLinkText: Demo de shapez en Steam - descWeb: "Uno o más recrusos no pudieron cargar. Asegurate de tener una conexión - estable de internet y vuelve a intentar. Si todavía no funciona, - asegurate de tambien desactivar extensiones del navegador - (incluyendo adblockers).<br><br> Como alternativa, puedes tambien - jugar el <demoOnSteamLinkText>. <br><br> Mensaje de Error:" descSteamDemo: "Uno o más recursos no puedieron cargar. Intenta reiniciar el juego - Si eso no ayuda, intenta reinstalar y verificar los archivos del juego en Steam. <br><br> Mensaje de Error:" - steamSsoError: - title: Cierre de sesión de versión completa - desc: Se te ha cerrado sesión desde la versión completa por navegaador debido a - que tu conexión de internet es inestable o estás jugando desde otro - dispositivo.<br><br> Por favor asegurate de que no tienes shapez - abierto en otra pestaña del navegador o en otra computadora con la - misma cuenta de Steam.<br><br> Puedes iniciar sesión nuevamente - desde el menú principal. - steamSsoNoOwnership: - title: No eres dueño de la Edición completa - desc: En orden de poder jugar la versión completa en tu nevagador, debes comprar - el juego base y el DLC Puzzle desde tu cuenta de Steam.<br><br> Por - favor asegurate de tener ambos, iniciendo sesión con la cuenta - correcta de Steam y vuelve a intentar. ingame: keybindingsOverlay: moveMap: Mover @@ -470,40 +371,6 @@ ingame: one_miner: 1 Minero n_miners: <amount> Mineros limited_items: Limitado a <max_throughput> - watermark: - title: Versión demo - desc: Presiona aquí para ver que tiene la versión de Steam! - get_on_steam: Consiguelo en Steam - standaloneAdvantages: - no_thanks: ¡No gracias! - points: - levels: - title: 12 nuevos niveles - desc: ¡Para un total de 26 niveles! - buildings: - title: 22 nuevos edificios - desc: ¡Automatiza completamente tu fabrica! - markers: - title: Marcadores infinitos - desc: ¡Nunca te pierdas en tu propia fabrica! - wires: - title: Cables - desc: ¡Una dimension completamente nueva! - darkmode: - title: Modo oscuro - desc: ¡Deja de lastimar tus ojos! - support: - title: Apoyame - desc: ¡Desarrollo este juego en mi tiempo libre! - achievements: - title: Logros - desc: Atrapalos a todos! - mods: - title: ¡Soporte de Modificaciones! - desc: ¡Más de 80 modificaciones disponibles! - titleV2: "Obten la versión completa en Steam para desbloquear:" - titleExpiredV2: ¡Demo completada! - titleEnjoyingDemo: ¿Estás disfrutando la demo? puzzleEditorSettings: zoneTitle: Área zoneWidth: Anchura @@ -607,7 +474,7 @@ buildings: name: Cortador (Cuádruple) description: Corta figuras en cuatro partes. <strong> ¡Si solo usas una parte, asegúrate de destruir las otras partes o se parará!</strong> - rotater: + rotator: default: name: Rotador description: Rota las figuras en sentido horario 90 grados. @@ -744,7 +611,7 @@ buildings: default: name: Cortador virtual description: Corta virtualmente la forma en dos. - rotater: + rotator: name: Rotador virtual description: Rota virtualmente la forma, tanto en sentido del horario como sentido anti-horario. @@ -786,7 +653,7 @@ storyRewards: sino <strong>se trabará y parará</strong> - Por este proposite Te he dado el <strong>basurero</strong>, el cual destruye todo lo que pongas dentro de él! - reward_rotater: + reward_rotator: title: Rotador desc: ¡El <strong>rotador</strong> se ha desbloqueado! Rota figuras en sentido horario 90 grados. @@ -816,7 +683,7 @@ storyRewards: title: Túnel desc: El <strong>túnel</strong> se ha desbloqueado - ¡Ahora puedes transportar elementos por debajo de edificios u otras cintas! - reward_rotater_ccw: + reward_rotator_ccw: title: Rotador inverso desc: Has desbloqueado una variante del <strong>rotador</strong> - ¡Te permite rotar en sentido antihorario! Para construirlo selecciona el rotador @@ -891,7 +758,7 @@ storyRewards: desc: Has desbloqueado el <strong>Lector de cinta</strong>! Este te permite medir el rendimiento de una cinta. <br><br>Y espera a desbloquear los cables... Se vuelve súper útil! - reward_rotater_180: + reward_rotator_180: title: Rotador (180 grados) desc: ¡Has desbloqueado el <strong>rotador</strong> de 180 grados! - Te permite rotar una forma en 180 grados (¡Sorpresa! :D) @@ -1159,7 +1026,7 @@ keybindings: underground_belt: Túnel miner: Extractor cutter: Cortador - rotater: Rotador + rotator: Rotador stacker: Apilador mixer: Mezclador de colores painter: Pintor @@ -1222,12 +1089,6 @@ about: changelog: title: Historial de cambios demo: - features: - restoringGames: Recuperando partidas guardadas - importingGames: Importando partidas guardadas - oneGameLimit: Limitado a una partida guardada - customizeKeybindings: Personalizando atajos de teclado - exportingBase: Exportando la base completa como imagen settingNotAvailable: No disponible en la versión de prueba. tips: - El Centro acepta entradas de cualquier tipo ¡No solo la forma actual! @@ -1402,18 +1263,7 @@ mods: version: Versión modWebsite: Pagina Web openFolder: Abrir Carpeta de Mods - folderOnlyStandalone: Solo es posible abrir la carpeta de mods corriendo la - versión de escritorio. browseMods: Examinar Mods modsInfo: Para instalar y manejar mods, copialos en la carpeta de mods dentro del directorio del juego. Tambien puedes usar el boton 'Abrir Carpeta de Mods' en la esquina superior derecha. - noModSupport: Necesitas la versión de escritorio de Steam para instalar mods. - togglingComingSoon: - title: Proximamente - description: Solo es posible activar o desactivar mods copiando o removiendo el - archivo del mod desde o en la carpeta mods/. Sin embargo, la - posibilidad de activarlos o desactivarlos aquí está planeada para - una actualización futura! - browserNoSupport: Debido a restricciones del navegador, solo es posible instalar - mods en la versión de Steam - Disculpas! diff --git a/translations/base-fi.yaml b/translations/base-fi.yaml index 71847829..eb633ab5 100644 --- a/translations/base-fi.yaml +++ b/translations/base-fi.yaml @@ -1,27 +1,3 @@ -steamPage: - shortText: shapez.io on peli tehtaiden rakentamisesta, joiden avulla - automatisoidaan yhä monimutkaisempien muotojen luonti ja yhdisteleminen - loputtomassa maailmassa. - discordLinkShort: Virallinen Discord - intro: >- - Shapez.io on rento peli, jossa sinun täytyy rakentaa tehtaita - geometristen muotojen automatisoituun tuotantoon. - - Kun taso nousee, muodot tulevat entistä vaikeammiksi ja sinun täytyy laajentua loputtomassa kartassa. - - Ja jos tämä ei ollut tarpeeksi, niin sinun täytyy tuottaa eksponentiaalisesti enemmän täyttääksesi tarpeet - ainut asia mikä auttaa on skaalautuminen! - - Vaikka alussa vain prosessoit muotoja, myöhemmin niitä pitää myös maalata - tätä varten sinun täytyy kerätä ja sekoittaa värejä - - Pelin ostaminen Steamista antaa sinulle pääsyn pelin täysversioon, mutta voit myös pelata kokeiluversiota ensin sivuillamme shapez.io ja päättää myöhemmin! - what_others_say: Muiden mielipiteitä shapez.io:sta - nothernlion_comment: This game is great - I'm having a wonderful time playing, - and time has flown by. - notch_comment: Oh crap. I really should sleep, but I think I just figured out - how to make a computer in shapez.io - steam_review_comment: This game has stolen my life and I don't want it back. - Very chill factory game that won't let me stop making my lines more - efficient. global: loading: Ladataan error: Virhe @@ -55,58 +31,20 @@ global: space: VÄLILYÖNTI loggingIn: Logging in loadingResources: Downloading additional resources (<percentage> %) - discount: -<percentage>% - discountSummerSale: SPECIAL PROMOTION! Offer ends 7 July -demoBanners: - title: Demoversio - intro: |- - Hanki koko peli <strong>jatkossa</strong> avataksesi sen:<ul> - <li>Kaikki 26 tasoa + ääretön ilmaispeli</li> - <li>22 uutta rakennusta</li> - <li>Mod-tuki</li> - <li>Saavutukset</li> - <li>Tumma tila</li> - <li>... ja paljon muuta!</li> - </ul> - playtimeDisclaimer: Täysversio sisältää yli <strong>20 tuntia sisältöä</strong>. - playerCount: <playerCount> kaltaisesi pelaajat pelaavat parhaillaan shapezia - Steamissa. - untilEndOfDemo: Demon loppuun asti - playtimeDisclaimerDownload: Voit jatkaa tallentamistasi täysversiossa! Lataa - tallennettava peli napsauttamalla <strong>tätä</strong>. - titleV2: "Pelaa täysversiota nyt:" mainMenu: play: Pelaa continue: Jatka newGame: Uusi Peli - changelog: Muutosloki - subreddit: Reddit importSavegame: Tuo peli - openSourceHint: Tämä on avoimen lähdekoodin peli! - discordLink: Virallinen Discord -palvelin helpTranslate: Auta kääntämään! - madeBy: Pelin on tehnyt <author-link> - browserWarning: Anteeksi, mutta pelin tiedetään toimivan huonosti selaimellasi! - Hanki pelin täysversio tai lataa Google Chrome täyttä tukea varten. savegameLevel: Taso <x> savegameLevelUnknown: Tuntematon taso savegameUnnamed: Nimetön - puzzleMode: Pulmatila - back: Takaisin - puzzleDlcText: Nautitko tehtaiden pienentämisestä ja optimoinnista? Hanki pulma - DLC nyt Steamista lisähuviksi! - puzzleDlcWishlist: Lisää nyt toivelistallesi! - puzzleDlcViewNow: Katso DLC mods: - title: Aktiiviset modit warningPuzzleDLC: Pulma DLC:n pelaaminen ei ole mahdollista modien kanssa. kytke kaikki modit pois päältä pelataksesi DLC:tä. - playingFullVersion: Pelaat nyt täyttä versiota! - logout: Kirjaudu ulos noActiveSavegames: Aktiivisia pelitallennuksia ei löytynyt - Klikkaa pelaa aloittaaksesi uuden pelin! - playFullVersionV2: Ostitko Shapezin Steamista? Pelaa täyttä versiota selaimellasi! - playFullVersionStandalone: Voit nyt myös pelatatäyttä versiota selaimellasi! dialogs: buttons: ok: OK @@ -115,7 +53,6 @@ dialogs: later: Myöhemmin restart: Käynnistä uudelleen reset: Nollaa - getStandalone: Hanki täysversio deleteGame: Tiedän mitä olen tekemässä viewUpdate: Näytä päivitys showUpgrades: Näytä päivitykset @@ -152,14 +89,6 @@ dialogs: keybindingsResetOk: title: Pikanäppäimet nollattiin desc: Pikanäppäimet nollattiin oletusarvoihin! - featureRestriction: - title: Demoversio - desc: Yritit käyttää ominaisuutta (<feature>), joka ei ole saatavilla - demoversiossa. Hanki täysversio avataksesi kaikki ominaisuudet! - oneSavegameLimit: - title: Rajoitetut tallennukset - desc: Sinulla voi olla demoversiossa vain yksi tallennus kerrallaan. Poista - vanha tallennus tai hanki täysversio pelistä! updateSummary: title: Uusi päivitys! desc: "Tässä uudet muutokset edellisen pelikertasi jälkeen:" @@ -193,9 +122,6 @@ dialogs: desc: Anna merkille kuvaava nimi. Voit myös liittää <strong>lyhyen koodin</strong> muodosta. (Jonka voit luoda <link>täällä</link>.) titleEdit: Muokkaa merkkiä - markerDemoLimit: - desc: Voit tehdä vain kaksi mukautettua merkkiä demoversiossa. Hanki täysversio - saadaksesi loputtoman määrän merkkejä! exportScreenshotWarning: title: Vie kuvakaappaus desc: Pyysit tukikohtasi viemistä kuvakaappauksena. Huomaa, että tämä voi olla @@ -291,28 +217,9 @@ dialogs: newMods: Newly installed Mods resourceLoadFailed: title: Resources failed to load - demoLinkText: shapez demo on Steam - descWeb: "One ore more resources could not be loaded. Make sure you have a - stable internet connection and try again. If this still doesn't - work, make sure to also disable any browser extensions (including - adblockers).<br><br> As an alternative, you can also play the - <demoOnSteamLinkText>. <br><br> Error Message:" descSteamDemo: "One ore more resources could not be loaded. Try restarting the game - If that does not help, try reinstalling and verifying the game files via Steam. <br><br> Error Message:" - steamSsoError: - title: Full Version Logout - desc: You have been logged out from the Full Browser Version since either your - network connection is unstable or you are playing on another - device.<br><br> Please make sure you don't have shapez open in any - other browser tab or another computer with the same Steam - account.<br><br> You can login again in the main menu. - steamSsoNoOwnership: - title: Full Edition not owned - desc: In order to play the Full Edition in your Browser, you need to own both - the base game and the Puzzle DLC on your Steam account.<br><br> - Please make sure you own both, signed in with the correct Steam - account and then try again. ingame: keybindingsOverlay: moveMap: Liiku @@ -452,40 +359,6 @@ ingame: one_miner: 1 poimija n_miners: <amount> poimijaa limited_items: Rajoitettu <max_throughput> - watermark: - title: Kokeiluversio - desc: Napsauta tästä nähdäksesi Steam version edut! - get_on_steam: Hanki Steamista - standaloneAdvantages: - no_thanks: Ei kiitos! - points: - levels: - title: 12 Uutta tasoa - desc: Yhteensä 26 tasoa! - buildings: - title: 22 Uutta laitetta - desc: Automatisoi tehtaasi täysin! - markers: - title: ∞ Merkit - desc: Älä koskaan eksy tehtaassasi! - wires: - title: Johdot - desc: Täysin uusi ulottuvuus! - darkmode: - title: Tumma teema - desc: Jotta silmiisi ei sattuisi! - support: - title: Tue minua - desc: Kehitän peliä vapaa-ajallani! - achievements: - title: Saavutukset - desc: Metsästä kaikki! - mods: - title: Modding support! - desc: Over 80 mods available! - titleV2: "Get the full version now on Steam to unlock:" - titleExpiredV2: Demo completed! - titleEnjoyingDemo: Enjoy the demo? puzzleEditorSettings: zoneTitle: Zone zoneWidth: Width @@ -586,7 +459,7 @@ buildings: description: Leikkaa muotoja ylhäältä alaspäin ja tuottaa muodon molemmat puoliskot. <strong>Jos käytät vain yhden puoliskon, tuhoa toinen puolisko tai se jumittaa leikkurin!</strong> - rotater: + rotator: default: name: Kääntäjä description: Kääntää muotoja 90 astetta myötäpäivään. @@ -718,7 +591,7 @@ buildings: default: name: Virtuaalileikkuri description: Leikkaa tavara virtuaalisesti kahteen puoliskoon. - rotater: + rotator: name: Virtuaalikääntäjä description: Käännä tavara virtuaalisesti, sekä myötäpäivään että vastapäivään. unstacker: @@ -757,7 +630,7 @@ storyRewards: hankkiutua eroon jätteestä, tai muuten <strong>se tukkii ja pysäyttää leikkurin</strong> - Siksi olen antanut sinulle <strong>roskiksen</strong>, joka tuhoaa kaiken sinne laitetun! - reward_rotater: + reward_rotator: title: Kääntö desc: Avasit <strong>Kääntäjän</strong>! Se kääntää muotoja myötäpäivään 90 astetta. @@ -785,7 +658,7 @@ storyRewards: title: Tunneli desc: Avasit <strong>Tunnelin</strong> - Nyt voit kuljettaa tavaroita kuljettimilla laitteiden ja toisten kuljettimien alta! - reward_rotater_ccw: + reward_rotator_ccw: title: Kääntö vastapäivään desc: Avasit uuden version <strong>Kääntäjästä</strong> - Se sallii kääntämisen vastapäivään! Sen voi rakentaa valitsemalla kääntäjän ja @@ -855,7 +728,7 @@ storyRewards: desc: Olet avannut <strong>Kuljetinanturin</strong>! Voit mitata kuljettimen tehokkuutta.<br><br>Ja odotahan vain, kunhan saat Johdot auki. Sitten tämä on kätevä! - reward_rotater_180: + reward_rotator_180: title: Kääntäjä (180°) desc: Avasit juuri 180-asteen <strong>Kääntäjän</strong>! - Sillä voit kääntää muotoa 180 astetta (Ylläripylläri! :D) @@ -1110,7 +983,7 @@ keybindings: underground_belt: Tunneli miner: Poimija cutter: Leikkuri - rotater: Kääntäjä + rotator: Kääntäjä stacker: Pinoaja mixer: Värinsekoittaja painter: Maalain @@ -1173,12 +1046,6 @@ about: changelog: title: Muutosloki demo: - features: - restoringGames: Tallennusten palautus - importingGames: Tallennettujen pelien tuonti - oneGameLimit: Rajoitettu yhteen tallennukseen - customizeKeybindings: Pikanäppäinten mukautus - exportingBase: Viedään koko tukikohta kuvana settingNotAvailable: Ei saatavilla demoversiossa. tips: - Keskusrakennus huolii minkä tahansa muodon, ei ainoastaan tarvittua muotoa! @@ -1334,16 +1201,7 @@ mods: version: Version modWebsite: Website openFolder: Open Mods Folder - folderOnlyStandalone: Opening the mod folder is only possible when running the standalone. browseMods: Browse Mods modsInfo: To install and manage mods, copy them to the mods folder within the game directory. You can also use the 'Open Mods Folder' button on the top right. - noModSupport: You need the standalone version on Steam to install mods. - togglingComingSoon: - title: Coming Soon - description: Enabling or disabling mods is currently only possible by copying - the mod file from or to the mods/ folder. However, being able to - toggle them here is planned for a future update! - browserNoSupport: Due to browser restrictions it is currently only possible to - install mods in the Steam version - Sorry! diff --git a/translations/base-fr.yaml b/translations/base-fr.yaml index 169e3cfe..95a4f290 100644 --- a/translations/base-fr.yaml +++ b/translations/base-fr.yaml @@ -1,24 +1,3 @@ -steamPage: - shortText: shapez.io est un jeu qui consiste à construire des usines pour - automatiser la création et la combinaison de formes de plus en plus - complexes sur une carte infinie. - discordLinkShort: Discord officiel - intro: >- - Vous aimez les jeux d’automatisation ? Ce jeu est pour vous ! - - shapez.io est un jeu calme où vous devrez construire des usines pour produire automatiquement des formes géométriques. À mesure que le niveau augmente, les formes deviennent de plus en plus complexes, et vous devrez vous étendre sur la carte infinie. - - Et en plus, vous devrez aussi produire de plus en plus pour satisfaire la demande. La seule solution est de construire en plus grand ! Au début vous ne ferez que découper les formes, mais plus tard vous devrez les peindre — et pour ça vous devrez extraire et mélanger des couleurs ! - - En achetant le jeu sur Steam, vous aurez accès à la version complète, mais vous pouvez aussi jouer à une démo sur shapez.io et vous décider ensuite ! - what_others_say: Ce que les gens pensent de Shapez.io - nothernlion_comment: Ce jeu est génial - Je passe un merveilleux moment à jouer, - et le temps s'est envolé. - notch_comment: Mince ! Je devrais vraiment me coucher, mais je crois que j'ai - trouvé comment faire un ordinateur dans Shapez.io - steam_review_comment: Ce jeu a volé ma vie et je ne veux pas la récupérer. Jeu - d'usine très cool qui ne me laissera pas arrêter de rendre mes lignes - plus efficaces. global: loading: Chargement error: Erreur @@ -52,59 +31,19 @@ global: space: ESPACE loggingIn: Se connecter loadingResources: Chargement de ressources supplémentaires (<percentage> %) - discount: -<percentage>% - discountSummerSale: SPECIAL PROMOTION! Offer ends 7 July -demoBanners: - title: Version de démo - intro: >- - Achetez la version complète <strong>maintenant</strong> et - débloquez:<ul> - <li>Les 26 niveaux + jeu libre infini</li> - <li>22 nouveaux bâtiments</li> - <li>Mods autorisés</li> - <li>Achievements</li> - <li>Mode sombre</li> - <li>... et bien plus encore !</li> - </ul> - playtimeDisclaimer: La version complète offre plus de <strong>20 heures de contenu</strong>. - playerCount: <playerCount> joueurs comme vous jouent actuellement à shapez sur Steam - untilEndOfDemo: Jusqu'à la fin de la démo - playtimeDisclaimerDownload: Vous pouvez continuer votre sauvegarde dans la - version complète ! Cliquez <strong>ici</strong> pour télécharger votre - sauvegarde. - titleV2: "Jouez la version complète pour :" mainMenu: play: Jouer continue: Continuer newGame: Nouvelle partie - changelog: Historique - subreddit: Reddit importSavegame: Importer - openSourceHint: Ce jeu est open source ! - discordLink: Serveur Discord officiel helpTranslate: Contribuez à la traduction ! - madeBy: Créé par <author-link> - browserWarning: Désolé, ce jeu sera lent sur votre navigateur web ! - Procurez-vous la version complète ou téléchargez Chrome pour une - meilleure expérience. savegameLevel: Niveau <x> savegameLevelUnknown: Niveau inconnu savegameUnnamed: Sans titre - puzzleMode: Mode Puzzle - back: Retour - puzzleDlcText: Vous aimez compacter et optimiser vos usines ? Achetez le DLC sur - Steam dès maintenant pour encore plus d'amusement! - puzzleDlcWishlist: Ajoute à ta liste de souhaits maintenant ! - puzzleDlcViewNow: Voir le DLC mods: - title: Mods Actifs warningPuzzleDLC: Jouer au DLC Puzzle n'est pas possible avec des mods. Désactivez-les pour jouer au DLC. - playingFullVersion: You are now playing the full version! - logout: Logout noActiveSavegames: No active savegames found - Click play to start a new game! - playFullVersionV2: Bough shapez on Steam? Play the full version in your Browser! - playFullVersionStandalone: You can now also play the full version in your Browser! dialogs: buttons: ok: OK @@ -113,7 +52,6 @@ dialogs: later: Plus tard restart: Relancer reset: Réinitialiser - getStandalone: Obtenir la version complète deleteGame: Je sais ce que je fais viewUpdate: Voir les mises à jour showUpgrades: Montrer les améliorations @@ -150,16 +88,6 @@ dialogs: keybindingsResetOk: title: Réinitialisation des contrôles desc: Les contrôles ont été remis à défaut ! - featureRestriction: - title: Version de démo - desc: Vous avez essayé d’accéder à la fonction « <feature> » qui n’est pas - disponible dans la démo. Pensez à acheter la version complète pour - une expérience optimale ! - oneSavegameLimit: - title: Sauvegardes limitées - desc: Vous ne pouvez avoir qu’une seule sauvegarde en même temps dans la version - démo. Merci d’effacer celle en cours ou bien de vous procurer la - version complète ! updateSummary: title: Nouvelle mise à jour ! desc: "Voici les changements depuis votre dernière session de jeu :" @@ -204,9 +132,6 @@ dialogs: descItems: "Choisissez un objet prédéfini :" descShortKey: …ou entrez le <strong>raccourci</strong> d’une forme (que vous pouvez générer <link>ici</link>) - markerDemoLimit: - desc: Vous ne pouvez créer que deux balises dans la démo. Achetez la version - complète pour en placer autant que vous voulez ! exportScreenshotWarning: title: Exporter une capture d’écran desc: Vous avez demandé à exporter une capture d’écran de votre base. Soyez @@ -298,28 +223,9 @@ dialogs: newMods: Newly installed Mods resourceLoadFailed: title: Resources failed to load - demoLinkText: shapez demo on Steam - descWeb: "One ore more resources could not be loaded. Make sure you have a - stable internet connection and try again. If this still doesn't - work, make sure to also disable any browser extensions (including - adblockers).<br><br> As an alternative, you can also play the - <demoOnSteamLinkText>. <br><br> Error Message:" descSteamDemo: "One ore more resources could not be loaded. Try restarting the game - If that does not help, try reinstalling and verifying the game files via Steam. <br><br> Error Message:" - steamSsoError: - title: Full Version Logout - desc: You have been logged out from the Full Browser Version since either your - network connection is unstable or you are playing on another - device.<br><br> Please make sure you don't have shapez open in any - other browser tab or another computer with the same Steam - account.<br><br> You can login again in the main menu. - steamSsoNoOwnership: - title: Full Edition not owned - desc: In order to play the Full Edition in your Browser, you need to own both - the base game and the Puzzle DLC on your Steam account.<br><br> - Please make sure you own both, signed in with the correct Steam - account and then try again. ingame: keybindingsOverlay: moveMap: Déplacer @@ -464,40 +370,6 @@ ingame: one_miner: 1 extracteur n_miners: <amount> extracteurs limited_items: Limité à <max_throughput> - watermark: - title: Version de démo - desc: Cliquez ici pour voir les avantages de la version Steam ! - get_on_steam: Acheter sur Steam - standaloneAdvantages: - no_thanks: Non merci ! - points: - levels: - title: 12 nouveaux niveaux - desc: Pour un total de 26 niveaux ! - buildings: - title: 22 nouveaux bâtiments - desc: Automatisez entièrement votre usine ! - markers: - title: Balises ∞ - desc: Ne vous perdez plus jamais dans votre usine ! - wires: - title: Câbles - desc: Une toute nouvelle dimension ! - darkmode: - title: Mode sombre - desc: Plus jamais mal aux yeux ! - support: - title: Me soutenir - desc: Je le développe pendant mon temps libre ! - achievements: - title: Succès - desc: Débloquez-les tous ! - mods: - title: Modding support! - desc: Over 80 mods available! - titleV2: "Get the full version now on Steam to unlock:" - titleExpiredV2: Demo completed! - titleEnjoyingDemo: Enjoy the demo? puzzleEditorSettings: zoneTitle: Zone zoneWidth: Largeur @@ -610,7 +482,7 @@ buildings: description: Coupe une forme en quatre parties. <strong>Si vous n’utilisez pas toutes les parties, assurez-vous de détruire les autres ou sinon, gare au blocage !</strong> - rotater: + rotator: default: name: Pivoteur description: Fait pivoter une forme de 90 degrés vers la droite. @@ -741,7 +613,7 @@ buildings: default: name: Découpeur virtuel description: Découpe virtuellement une forme. - rotater: + rotator: name: Pivoteur virtuel description: Fait pivoter virtuellement la forme de 90 degrés vers la droite. unstacker: @@ -781,7 +653,7 @@ storyRewards: déchets, ou sinon <strong>il se bouchera et se bloquera</strong> - À cet effet, je vous ai donné la <strong>poubelle</strong>, qui détruit tout ce que vous mettez dedans ! - reward_rotater: + reward_rotator: title: Rotation desc: Le <strong>pivoteur</strong> a été débloqué ! Il pivote les formes de 90 degrés vers la droite. @@ -814,7 +686,7 @@ storyRewards: title: Tunnel desc: Le <strong>tunnel</strong> a été débloqué. Vous pouvez maintenant faire passer des formes sous les convoyeurs et les bâtiments ! - reward_rotater_ccw: + reward_rotator_ccw: title: Pivoteur inversé desc: Vous avez débloqué une variante du <strong>pivoteur</strong>. Elle permet de faire pivoter vers la gauche ! Pour le construire, sélectionnez @@ -871,7 +743,7 @@ storyRewards: copier.<br><br> Coller n’est <strong>pas gratuit</strong>, vous devez produire <strong>des formes de plans</strong> pour vous le payer (les mêmes que celles que vous venez de livrer). - reward_rotater_180: + reward_rotator_180: title: Retourneur desc: Vous avez débloqué le <strong>retourneur</strong> ! Il permet de faire pivoter une forme de 180 degrés (Surprise ! :D) @@ -1169,7 +1041,7 @@ keybindings: underground_belt: Tunnel miner: Extracteur cutter: Découpeur - rotater: Pivoteur + rotator: Pivoteur stacker: Assembleur mixer: Mélangeur de couleur painter: Station de peinture @@ -1231,12 +1103,6 @@ about: changelog: title: Historique demo: - features: - restoringGames: Charger des sauvegardes - importingGames: Importer des sauvegardes - oneGameLimit: Limité à une sauvegarde - customizeKeybindings: Personnalisation des contrôles - exportingBase: Exporter une image de toute la base settingNotAvailable: Indisponible dans la démo. tips: - Le centre accepte n’importe quelle forme, pas seulement la forme actuelle ! @@ -1410,19 +1276,7 @@ mods: version: Version modWebsite: Page web openFolder: Ouvrir le dossier des mods - folderOnlyStandalone: Ouvrir le dossier des mods est uniquement possible avec la - version complète browseMods: Chercher les Mods modsInfo: Pour installer et gérer les mods, copier-les dans le dossier Mods dans le répertoire du jeu. Vous pouvez aussi utiliser le bouton 'Ouvrir le dossier des Mods' en haut à droite - noModSupport: Vous avez besoin de la version complète sur Steam pour installer - des mods. - togglingComingSoon: - title: Bientôt disponible - description: Activer ou désactiver un mod est pour le moment possible qu'en - copiant le fichier du mod depuis ou vers le dossier des mods. - Cependant, activer ou désactiver un mod est prévu pour une mise à - jour future ! - browserNoSupport: Due to browser restrictions it is currently only possible to - install mods in the Steam version - Sorry! diff --git a/translations/base-he.yaml b/translations/base-he.yaml index c0b61ef4..9e375496 100644 --- a/translations/base-he.yaml +++ b/translations/base-he.yaml @@ -1,23 +1,3 @@ -steamPage: - shortText: shapez.io הוא משחק בנוגע לבניית מפעלים אוטמטים ליצירה של צורת מסובכות - יותר ויותר במפה אין סופית. - discordLinkShort: דיסקורד רשמי - intro: >- - אתה אוהב משחקי אוטומציה? אתה במקום הנכון! - - shapez.io הוא משחק שלווה שבו אתה בונה מפעל בשביל ליצור צורות גאומטריות אוטומטית. ככל שמתקדמים השלבים, הצורות נהיות יותר ויותר מסובכות, ואתה צריך להפתח על המפה האין סופית. - - ואם זה לא היה מספיק, אתה צריך ליצור יותר ויותר צורות בשביל לספק את הדרישה - הדבר היחיד שיכול לעזור זה להגדיל את המפעל! בזמן שבהתחלה אתה רק צריך לערוך צורות, בהמשך אתה צריך לצבוע אותם בעזרת צבעים שאתה מערבב. - - קניית המשחק בsteam תתן לך גישה למשחק המלא, אבל אתה יכול לשחק משחק דמו בhttps://shapez.io/ ולהחליט אחר כך. - what_others_say: What people say about shapez.io - nothernlion_comment: This game is great - I'm having a wonderful time playing, - and time has flown by. - notch_comment: Oh crap. I really should sleep, but I think I just figured out - how to make a computer in shapez.io - steam_review_comment: This game has stolen my life and I don't want it back. - Very chill factory game that won't let me stop making my lines more - efficient. global: loading: טוען error: שגיאה @@ -51,56 +31,19 @@ global: space: SPACE loggingIn: Logging in loadingResources: Downloading additional resources (<percentage> %) - discount: -<percentage>% - discountSummerSale: SPECIAL PROMOTION! Offer ends 7 July -demoBanners: - title: גרסאת דמו - intro: |- - Get the full game <strong>now</strong> to unlock:<ul> - <li>All 26 levels + infinite Freeplay</li> - <li>22 new buildings</li> - <li>Mod support</li> - <li>Achievements</li> - <li>Dark Mode</li> - <li>... and a lot more!</li> - </ul> - playtimeDisclaimer: The full version contains more than <strong>20 hours of content</strong>. - playerCount: <playerCount> players like you are currently playing shapez on Steam - untilEndOfDemo: Until end of demo - playtimeDisclaimerDownload: You can continue your savegame in the full version! - Click <strong>here</strong> to download your savegame. - titleV2: "Play the full version now for:" mainMenu: play: שחק continue: המשך newGame: משחק חדש - changelog: עדכונים - subreddit: רדיט importSavegame: יבוא - openSourceHint: המשחק הזה הוא עם קוד פתוח! - discordLink: שרת הדסקורד הרשמי helpTranslate: תעזור לתרגם! - madeBy: <author-link> :יוצר המשחק - browserWarning: מצטערים, אבל המשחק רק באיטיות על הדפדפן שלך! תשיג את הגרסא - להורדה או שתוריד גוגל לחוויה המלאה. savegameLevel: שלב <x> savegameLevelUnknown: שלב לא ידוע savegameUnnamed: ללא שם - puzzleMode: Puzzle Mode - back: Back - puzzleDlcText: Do you enjoy compacting and optimizing factories? Get the Puzzle - DLC now on Steam for even more fun! - puzzleDlcWishlist: Wishlist now! - puzzleDlcViewNow: View Dlc mods: - title: Active Mods warningPuzzleDLC: Playing the Puzzle DLC is not possible with mods. Please disable all mods to play the DLC. - playingFullVersion: You are now playing the full version! - logout: Logout noActiveSavegames: No active savegames found - Click play to start a new game! - playFullVersionV2: Bough shapez on Steam? Play the full version in your Browser! - playFullVersionStandalone: You can now also play the full version in your Browser! dialogs: buttons: ok: אישור @@ -109,7 +52,6 @@ dialogs: later: מאוחר יותר restart: פתיחה מחדש reset: אפס - getStandalone: השג את הגרסא להורדה deleteGame: אני יודע מה אני עושה viewUpdate: צפה בעדכון showUpgrades: הצג שדרוגים @@ -145,14 +87,6 @@ dialogs: keybindingsResetOk: title: אפס את המקשים desc: זה יחזיר את כל המקשים להגדרה המקורית! - featureRestriction: - title: גראת דמו - desc: ניסית להשתמש בפיצ'ר (<feature>) שהוא לא זמין בדמו. תשיג את הגרסה להורדה - בשביל החוויה המלאה! - oneSavegameLimit: - title: הגבלת שמירה - desc: אתה יכול לשמור רק שמירה אחת בכל זמן נתון בגרסת הדמו. אתה יכול למחוק את - האחד הנוכחי או להשיג את הגרסה להורדה! updateSummary: title: עדכון חדש! desc: "כאן כל השינויים מאז ששיחקת לאחרונה:" @@ -193,9 +127,6 @@ dialogs: descItems: "תבחר מההבאים:" descShortKey: ... או שתשתמש ב <strong>קוד קצר</strong> של צורה (שאתה יכול לייצר <link>כאן</link>) - markerDemoLimit: - desc: אתה יכול ליצור רק שני סימונים בגרסאת הדמו. תשיג את הגרסה להורדה בשביל כמול - לא מוגבלת של סימונים! exportScreenshotWarning: title: יצוא צילום מסך desc: אתה ביקשת לייצא את המפעל שלך כצילום מסך. בבקשה תהיה מודע שזה יכול להיות די @@ -282,28 +213,9 @@ dialogs: newMods: Newly installed Mods resourceLoadFailed: title: Resources failed to load - demoLinkText: shapez demo on Steam - descWeb: "One ore more resources could not be loaded. Make sure you have a - stable internet connection and try again. If this still doesn't - work, make sure to also disable any browser extensions (including - adblockers).<br><br> As an alternative, you can also play the - <demoOnSteamLinkText>. <br><br> Error Message:" descSteamDemo: "One ore more resources could not be loaded. Try restarting the game - If that does not help, try reinstalling and verifying the game files via Steam. <br><br> Error Message:" - steamSsoError: - title: Full Version Logout - desc: You have been logged out from the Full Browser Version since either your - network connection is unstable or you are playing on another - device.<br><br> Please make sure you don't have shapez open in any - other browser tab or another computer with the same Steam - account.<br><br> You can login again in the main menu. - steamSsoNoOwnership: - title: Full Edition not owned - desc: In order to play the Full Edition in your Browser, you need to own both - the base game and the Puzzle DLC on your Steam account.<br><br> - Please make sure you own both, signed in with the correct Steam - account and then try again. ingame: keybindingsOverlay: moveMap: הזזה @@ -436,40 +348,6 @@ ingame: one_miner: חוצב 1 n_miners: <amount> חוצבים limited_items: מוגבל ל<max_throughput> - watermark: - title: גרסת דמו - desc: לחץ פה בשביל לראות את היתרונות של הגרסה להורדה! - get_on_steam: Steamהשג ב - standaloneAdvantages: - no_thanks: "!לא, תודה" - points: - levels: - title: 12 שלבים חדשים - desc: '!סה"כ 26 שלבים' - buildings: - title: 22 מבנים חדשים - desc: "!אפשרות להפוך את המפעל לאוטומטי לגמרי" - achievements: - title: הישגים - desc: "!השג את כולם" - markers: - title: סימונים ∞ - desc: "!אף פעם לא תלך לאיבוד במפעל שלך" - wires: - title: כבלים - desc: "!מימד חדש לגמרי" - darkmode: - title: תצוגה כהה - desc: "!תפסיק להכאיב לעיניים שלך" - support: - title: תמוך בי - desc: "!אני יצרתי רת המשחק הזה בזמני החופשי" - mods: - title: Modding support! - desc: Over 80 mods available! - titleV2: "Get the full version now on Steam to unlock:" - titleExpiredV2: Demo completed! - titleEnjoyingDemo: Enjoy the demo? puzzleEditorSettings: zoneTitle: Zone zoneWidth: Width @@ -573,7 +451,7 @@ buildings: name: חותך (מרובע) description: חותך את הצורות לארבעה חלקים. <strong>אם אתה משתמש רק בחלק אחד, תוודא שאתה הורס את השאר החלקים או שיווצר סתימה!</strong> - rotater: + rotator: default: name: מסובב description: מסובב חלקים עם כיוון השעון ב90 מעלות. @@ -691,7 +569,7 @@ buildings: default: name: חותך וירטואלי description: חותך באופן וירטואלי את הצורה לשני חצאים. - rotater: + rotator: name: מסובב וירטואלי description: מסובב באופן וירטואלי את הצורה עם כיוון השעון. unstacker: @@ -726,7 +604,7 @@ storyRewards: <strong>לא משנה איזה כיוון הוא</strong>!<br><br>וודא שאתה נפתר מהשאריות, אחרת <strong>הוא יסתם ויתקע</strong> - בשביל המטרה הזאת אני הבאתי לך את ה<strong>פח</strong>, שהורס כל מה שאתה מכניס אליו! - reward_rotater: + reward_rotator: title: סיבוב desc: קיבלת גישה ל<strong>מסובב</strong> ! הוא מסובב צורות עם כיוון השעון ב90 מעלות. @@ -754,7 +632,7 @@ storyRewards: title: מנהרה desc: קיבלת גישה ל<strong>מנהרה</strong> - אתה יכול עכשיו להעביר חפצים מתחת למסועים ומבנים עם זה! - reward_rotater_ccw: + reward_rotator_ccw: title: סיבוב נגד כיוון השעון desc: קיבלת גישה לצורה נוספת של <strong>מסובב</strong> - זה מאפשר לך לסובב צורות נגד כיוון השעון! בשביל לבנות את זה, תבחר מסובב, ו<strong>תלחץ 'T' @@ -801,7 +679,7 @@ storyRewards: (החזק CTRL, וגרור עם העכבר), ואז לחץ 'C' בשביל להעתיק את זה.<br><br>הדבקה זה <strong>לא חינם</strong>, אתה צריך ליצר <strong>צורת תבנית</strong> בשביל לקנות את זה! (אלו שבדיוק יצרת). - reward_rotater_180: + reward_rotator_180: title: מסובב (180°) desc: קיבלת גישה <strong>מסובב</strong> 180 מעלות! - זה מאפשר לך לסובב צורה ב 180 מעולות (הפתעה! :D) @@ -1056,7 +934,7 @@ keybindings: underground_belt: מנהרה miner: חוצב cutter: חותך - rotater: מסובב + rotator: מסובב stacker: מחבר mixer: מערבב painter: צובע @@ -1117,12 +995,6 @@ about: changelog: title: עדכונים demo: - features: - restoringGames: שחזור משחקים שמורים - importingGames: יבוא משחקים שמורים - oneGameLimit: מוגבל לשמירה אחת - customizeKeybindings: מקשים בהתאמה אישית - exportingBase: יצוא כל המפעל כתמונה settingNotAvailable: .הגדרה לא זמינה בדמו tips: - "!ההאב יקבל כל צורה, לא רק את הצורה הנדרשת עכשיו" @@ -1275,16 +1147,7 @@ mods: version: Version modWebsite: Website openFolder: Open Mods Folder - folderOnlyStandalone: Opening the mod folder is only possible when running the standalone. browseMods: Browse Mods modsInfo: To install and manage mods, copy them to the mods folder within the game directory. You can also use the 'Open Mods Folder' button on the top right. - noModSupport: You need the standalone version on Steam to install mods. - togglingComingSoon: - title: Coming Soon - description: Enabling or disabling mods is currently only possible by copying - the mod file from or to the mods/ folder. However, being able to - toggle them here is planned for a future update! - browserNoSupport: Due to browser restrictions it is currently only possible to - install mods in the Steam version - Sorry! diff --git a/translations/base-hr.yaml b/translations/base-hr.yaml index 28e83978..8123e6a0 100644 --- a/translations/base-hr.yaml +++ b/translations/base-hr.yaml @@ -1,26 +1,3 @@ -steamPage: - shortText: shapez.io je igra o izradi tvornica za automatizaciju stvaranja i - spajanja sve složenijih oblika unutar beskonačno velike mape. - discordLinkShort: Official Discord - intro: >- - Shapez.io is a relaxed game in which you have to build factories for the - automated production of geometric shapes. - - As the level increases, the shapes become more and more complex, and you have to spread out on the infinite map. - - And as if that wasn't enough, you also have to produce exponentially more to satisfy the demands - the only thing that helps is scaling! - - While you only process shapes at the beginning, you have to color them later - for this you have to extract and mix colors! - - Buying the game on Steam gives you access to the full version, but you can also play a demo on shapez.io first and decide later! - what_others_say: What people say about shapez.io - nothernlion_comment: This game is great - I'm having a wonderful time playing, - and time has flown by. - notch_comment: Oh crap. I really should sleep, but I think I just figured out - how to make a computer in shapez.io - steam_review_comment: This game has stolen my life and I don't want it back. - Very chill factory game that won't let me stop making my lines more - efficient. global: loading: Učitavanje error: Greška @@ -54,56 +31,19 @@ global: space: SPACE loggingIn: Logging in loadingResources: Downloading additional resources (<percentage> %) - discount: -<percentage>% - discountSummerSale: SPECIAL PROMOTION! Offer ends 7 July -demoBanners: - title: Demo Verzija - intro: |- - Get the full game <strong>now</strong> to unlock:<ul> - <li>All 26 levels + infinite Freeplay</li> - <li>22 new buildings</li> - <li>Mod support</li> - <li>Achievements</li> - <li>Dark Mode</li> - <li>... and a lot more!</li> - </ul> - playtimeDisclaimer: The full version contains more than <strong>20 hours of content</strong>. - playerCount: <playerCount> players like you are currently playing shapez on Steam - untilEndOfDemo: Until end of demo - playtimeDisclaimerDownload: You can continue your savegame in the full version! - Click <strong>here</strong> to download your savegame. - titleV2: "Play the full version now for:" mainMenu: play: Igraj continue: Nastavi newGame: Nova Igra - changelog: Promjene - subreddit: Reddit importSavegame: Uvezi - openSourceHint: Ovo je igra otvorenog koda! - discordLink: Službeni Discord Server helpTranslate: Pomogni s prevođenjem! - madeBy: Izradio <author-link> - browserWarning: Sorry, but the game is known to run slow on your browser! Get - the standalone version or download chrome for the full experience. savegameLevel: Nivo <x> savegameLevelUnknown: Nepoznati Nivo savegameUnnamed: Unnamed - puzzleMode: Puzzle Mode - back: Back - puzzleDlcText: Do you enjoy compacting and optimizing factories? Get the Puzzle - DLC now on Steam for even more fun! - puzzleDlcWishlist: Wishlist now! - puzzleDlcViewNow: View Dlc mods: - title: Active Mods warningPuzzleDLC: Playing the Puzzle DLC is not possible with mods. Please disable all mods to play the DLC. - playingFullVersion: You are now playing the full version! - logout: Logout noActiveSavegames: No active savegames found - Click play to start a new game! - playFullVersionV2: Bough shapez on Steam? Play the full version in your Browser! - playFullVersionStandalone: You can now also play the full version in your Browser! dialogs: buttons: ok: OK @@ -112,7 +52,6 @@ dialogs: later: Kasnije restart: Ponovno pokreni reset: Resetiraj - getStandalone: Nabavi samostalnu igru deleteGame: Znam što radim viewUpdate: Pogledaj ažuriranje showUpgrades: Pokaži Nadogradnje @@ -151,14 +90,6 @@ dialogs: keybindingsResetOk: title: Tipke resetirane desc: Tipke su resetirane na svoje zadane vrijednosti! - featureRestriction: - title: Demo Verzija - desc: Pokušao si pristupiti značajki (<feature>) koja nije dostupna u demu. Za - puno iskustvo, nabavi samostalnu igru! - oneSavegameLimit: - title: Ograničen broj spremljenih igara - desc: Možeš imati samo jednu spremljenu igru u demo verziji. Ukloni postojeću - ili nabavi samostalnu igru. updateSummary: title: Novi update! desc: "Evo sve promjene od zadnjeg igranja:" @@ -191,9 +122,6 @@ dialogs: desc: Give it a meaningful name, you can also include a <strong>short key</strong> of a shape (Which you can generate <link>here</link>) titleEdit: Edit Marker - markerDemoLimit: - desc: U demo verziji se mogu stvoriti samo dva putokaza istovremeno. Nabavi - samostalnu igru za beskonačno mnogo putokaza! exportScreenshotWarning: title: Izvezi sliku zaslona desc: Zatražen je izvoz cijele baze u obliku slike zaslone. Ovo može biti jako @@ -290,28 +218,9 @@ dialogs: newMods: Newly installed Mods resourceLoadFailed: title: Resources failed to load - demoLinkText: shapez demo on Steam - descWeb: "One ore more resources could not be loaded. Make sure you have a - stable internet connection and try again. If this still doesn't - work, make sure to also disable any browser extensions (including - adblockers).<br><br> As an alternative, you can also play the - <demoOnSteamLinkText>. <br><br> Error Message:" descSteamDemo: "One ore more resources could not be loaded. Try restarting the game - If that does not help, try reinstalling and verifying the game files via Steam. <br><br> Error Message:" - steamSsoError: - title: Full Version Logout - desc: You have been logged out from the Full Browser Version since either your - network connection is unstable or you are playing on another - device.<br><br> Please make sure you don't have shapez open in any - other browser tab or another computer with the same Steam - account.<br><br> You can login again in the main menu. - steamSsoNoOwnership: - title: Full Edition not owned - desc: In order to play the Full Edition in your Browser, you need to own both - the base game and the Puzzle DLC on your Steam account.<br><br> - Please make sure you own both, signed in with the correct Steam - account and then try again. ingame: keybindingsOverlay: moveMap: Kretanje @@ -450,40 +359,6 @@ ingame: one_miner: 1 Miner n_miners: <amount> Miners limited_items: Limited to <max_throughput> - watermark: - title: Demo version - desc: Click here to see the Steam version advantages! - get_on_steam: Get on steam - standaloneAdvantages: - no_thanks: No, thanks! - points: - levels: - title: 12 New Levels - desc: For a total of 26 levels! - buildings: - title: 22 New Buildings - desc: Fully automate your factory! - markers: - title: ∞ Markers - desc: Never get lost in your factory! - wires: - title: Wires - desc: An entirely new dimension! - darkmode: - title: Dark Mode - desc: Stop hurting your eyes! - support: - title: Support me - desc: I develop it in my spare time! - achievements: - title: Achievements - desc: Hunt them all! - mods: - title: Modding support! - desc: Over 80 mods available! - titleV2: "Get the full version now on Steam to unlock:" - titleExpiredV2: Demo completed! - titleEnjoyingDemo: Enjoy the demo? puzzleEditorSettings: zoneTitle: Zone zoneWidth: Width @@ -580,7 +455,7 @@ buildings: name: Rezač (Četverostruki) description: Reže oblike u četiri dijela. <strong>Ako se koristi samo jedan dio, ostali se moraju uništiti da bi se spriječio zastoj!</strong> - rotater: + rotator: default: name: Obrtač (↻) description: Okreće oblike za 90 stupnjeva u smjeru kazaljke na satu. @@ -710,8 +585,8 @@ buildings: default: name: Virtual Cutter description: Virtually cuts the shape into two halves. - rotater: - name: Virtual Rotater + rotator: + name: Virtual Rotator description: Virtually rotates the shape, both clockwise and counter-clockwise. unstacker: name: Virtual Unstacker @@ -750,7 +625,7 @@ storyRewards: otherwise <strong>it will clog and stall</strong> - For this purpose I have given you the <strong>trash</strong>, which destroys everything you put into it! - reward_rotater: + reward_rotator: title: Obrtanje desc: <strong>Obrtač</strong> je otključan! Ovaj stroj okreće oblike za 90 stupnjeva u smjeru kazaljke na satu. @@ -778,7 +653,7 @@ storyRewards: title: Tunel desc: <strong>Tunel</strong> je otključan - Omogućava slanje stvari ispod traka i ostalih građevina! - reward_rotater_ccw: + reward_rotator_ccw: title: Rotacija u smjeru suprotnom od kazaljke na satu desc: Varijanta <strong>obrtača</strong> je otključana - Omogućuje okretanje u smjeru suprotnom od kazaljke! Odaberi obrtač i <strong>pritisni 'T' @@ -846,9 +721,9 @@ storyRewards: desc: You have now unlocked the <strong>belt reader</strong>! It allows you to measure the throughput of a belt.<br><br>And wait until you unlock wires - then it gets really useful! - reward_rotater_180: - title: Rotater (180 degrees) - desc: You just unlocked the 180 degrees <strong>rotater</strong>! - It allows + reward_rotator_180: + title: Rotator (180 degrees) + desc: You just unlocked the 180 degrees <strong>rotator</strong>! - It allows you to rotate a shape by 180 degrees (Surprise! :D) reward_display: title: Display @@ -873,7 +748,7 @@ storyRewards: title: Virtual Processing desc: I just gave a whole bunch of new buildings which allow you to <strong>simulate the processing of shapes</strong>!<br><br> You can - now simulate a cutter, rotater, stacker and more on the wires layer! + now simulate a cutter, rotator, stacker and more on the wires layer! With this you now have three options to continue the game:<br><br> - Build an <strong>automated machine</strong> to create any possible shape requested by the HUB (I recommend to try it!).<br><br> - Build @@ -1102,7 +977,7 @@ keybindings: underground_belt: Tunel miner: Rudar cutter: Rezač - rotater: Obrtač (↻) + rotator: Obrtač (↻) stacker: Slagač mixer: Miješalica boja painter: Bojač @@ -1166,12 +1041,6 @@ about: changelog: title: Ispravci demo: - features: - restoringGames: Obnavljanje spremljenih igara - importingGames: Uvoz spremeljenih igara - oneGameLimit: Ograničeno na jednu spremljenu igru - customizeKeybindings: Prilagodba Tipki - exportingBase: Izvoz cijele Baze u obliku Slike settingNotAvailable: Nije dostupno u demo verziji. tips: - The hub accepts input of any kind, not just the current shape! @@ -1325,16 +1194,7 @@ mods: version: Version modWebsite: Website openFolder: Open Mods Folder - folderOnlyStandalone: Opening the mod folder is only possible when running the standalone. browseMods: Browse Mods modsInfo: To install and manage mods, copy them to the mods folder within the game directory. You can also use the 'Open Mods Folder' button on the top right. - noModSupport: You need the standalone version on Steam to install mods. - togglingComingSoon: - title: Coming Soon - description: Enabling or disabling mods is currently only possible by copying - the mod file from or to the mods/ folder. However, being able to - toggle them here is planned for a future update! - browserNoSupport: Due to browser restrictions it is currently only possible to - install mods in the Steam version - Sorry! diff --git a/translations/base-hu.yaml b/translations/base-hu.yaml index 1e9afe17..0a3ba828 100644 --- a/translations/base-hu.yaml +++ b/translations/base-hu.yaml @@ -1,24 +1,3 @@ -steamPage: - shortText: A shapez-ben gyárak építésével kell automatizálni az egyre - összetettebb alakzatok gyártását és kombinálását egy végtelen méretű - térképen. - discordLinkShort: Hivatalos Discord - intro: >- - Szereted az automatizálós játékokat? Akkor jó helyen vagy! - - A Shapez egy nyugtató játék, amelyben gyárakat kell építened, hogy geometrikus alakzatokat gyárts és automatizálj. Ahogy szintet lépsz, az alakzatok egyre összetettebbek lesznek, és tovább kell terjeszkedned a végtelen pályán. - - És ha ez nem lenne elég, exponenciálisan többet kell termelned az igények kielégítése érdekében - az egyetlen dolog, ami segít, az a termelés mennyisége! Az alakzatokat a játék elején csak feldolgoznod kell, később azonban színezned is kell őket - ehhez bányászni és keverni kell a színeket! - - A játék Steamen történő megvásárlása hozzáférést biztosít a teljes verzióhoz, de kipróbálhatod a játékot a shapez oldalon, és később dönthetsz! - what_others_say: Mit mondanak mások a shapez-ról - nothernlion_comment: Ez a játék nagyszerű - Csodás élmény vele játszani, az idő - meg csak repül. - notch_comment: Basszus... aludnom kéne, de épp most jöttem rá, hogyan tudok - számítógépet építeni a shapez-ban! - steam_review_comment: Ez a játék ellopta az életemet, de nem kérem vissza! - Nagyon nyugis gyárépítős játék, amiben nem győzöm a futószalagjaimat - optimalizálni. global: loading: Betöltés error: Hiba @@ -52,57 +31,19 @@ global: space: SZÓKÖZ loggingIn: Bejelentkezés loadingResources: Downloading additional resources (<percentage> %) - discount: -<percentage>% - discountSummerSale: SPECIAL PROMOTION! Offer ends 7 July -demoBanners: - title: Demó verzió - intro: |- - Szerezd meg a teljes játékot <strong>most</strong> a feloldáshoz:<ul> - <li>Mind a 26 szint + végtelen szabadjáték</li> - <li>22 új épület</li> - <li>Mod támogatás</li> - <li>Eredmények</li> - <li>Sötét üzemmód</li> - <li>... és még sok más!</li> - </ul> - playtimeDisclaimer: A teljes verzió több mint <strong>20 órányi tartalmat</strong> tartalmaz. - playerCount: <playerCount> hozzád hasonló játékosok jelenleg shapez-t játszanak - a Steamen - untilEndOfDemo: A demó végéig - playtimeDisclaimerDownload: Folytathatja a mentést a teljes verzióban! - Kattintson <strong>ide</strong> a mentett játék letöltéséhez. - titleV2: "Játssz a teljes verzióval most:" mainMenu: play: Játék continue: Folytatás newGame: Új Játék - changelog: Változások - subreddit: Reddit importSavegame: Importálás - openSourceHint: Nyílt forráskód - discordLink: Hivatalos Discord Szerver helpTranslate: Segíts fordítani! - madeBy: Készítette <author-link> - browserWarning: A játék ezen a böngészőn problémásan futhat. Vásárold meg az - Önálló Verziót, vagy töltsd le a Chrome-ot a teljes játékélményért. savegameLevel: <x>. szint savegameLevelUnknown: Ismeretlen szint savegameUnnamed: Névtelen - puzzleMode: Fejtörő Mód - back: Vissza - puzzleDlcText: Szereted optimalizálni a gyáraid méretét és hatákonyságát? - Szerezd meg a Puzzle DLC-t a Steamen most! - puzzleDlcWishlist: Kívánságlistára vele! - puzzleDlcViewNow: View Dlc mods: - title: Active Mods warningPuzzleDLC: Playing the Puzzle DLC is not possible with mods. Please disable all mods to play the DLC. - playingFullVersion: You are now playing the full version! - logout: Logout noActiveSavegames: No active savegames found - Click play to start a new game! - playFullVersionV2: Bough shapez on Steam? Play the full version in your Browser! - playFullVersionStandalone: You can now also play the full version in your Browser! dialogs: buttons: ok: OK @@ -111,7 +52,6 @@ dialogs: later: Később restart: Újrakezdés reset: Visszaállítás - getStandalone: Teljes Verzió deleteGame: Játék Törlése viewUpdate: Frissítés Megtekintése showUpgrades: Fejlesztések @@ -150,14 +90,6 @@ dialogs: keybindingsResetOk: title: Gyorsbillentyűk visszaállítva desc: A gyorsbillentyűk az eredeti értékekre visszaállítva! - featureRestriction: - title: Demó Verzió - desc: Egy olyan funkciót próbáltál elérni (<feature>), amely nem elérhető a - Demóban. Vásárold meg an Önálló verziót a teljes játékélményért! - oneSavegameLimit: - title: Egy Játékmentés - desc: A Demó verzióban egyszerre csak egy játékmentésed lehet. Töröld a meglévő - mentésedet, vagy vásárold meg az Önálló verziót! updateSummary: title: Új frissítés! desc: "Íme a változások a legutóbbi játékod óta:" @@ -201,9 +133,6 @@ dialogs: descItems: "Válassz egy előre definiált elemet:" descShortKey: ... vagy írd be egy alakzat <strong>gyorskódját</strong>, amit <link>itt</link> generálhatsz le) - markerDemoLimit: - desc: A Demó verzióban csak két Jelölőd lehet. Vásárold meg az Önálló verziót, - hogy feloldd ezt a korlátozást! exportScreenshotWarning: title: Képernyőkép exportálása desc: A teljes bázisod képének lementését választottad. Vedd figyelembe, hogy ez @@ -293,28 +222,9 @@ dialogs: newMods: Newly installed Mods resourceLoadFailed: title: Resources failed to load - demoLinkText: shapez demo on Steam - descWeb: "One ore more resources could not be loaded. Make sure you have a - stable internet connection and try again. If this still doesn't - work, make sure to also disable any browser extensions (including - adblockers).<br><br> As an alternative, you can also play the - <demoOnSteamLinkText>. <br><br> Error Message:" descSteamDemo: "One ore more resources could not be loaded. Try restarting the game - If that does not help, try reinstalling and verifying the game files via Steam. <br><br> Error Message:" - steamSsoError: - title: Full Version Logout - desc: You have been logged out from the Full Browser Version since either your - network connection is unstable or you are playing on another - device.<br><br> Please make sure you don't have shapez open in any - other browser tab or another computer with the same Steam - account.<br><br> You can login again in the main menu. - steamSsoNoOwnership: - title: Full Edition not owned - desc: In order to play the Full Edition in your Browser, you need to own both - the base game and the Puzzle DLC on your Steam account.<br><br> - Please make sure you own both, signed in with the correct Steam - account and then try again. ingame: keybindingsOverlay: moveMap: Mozgatás @@ -455,40 +365,6 @@ ingame: one_miner: 1 Bánya n_miners: <amount> Bánya limited_items: Maximum <max_throughput> - watermark: - title: Demó verzió - desc: Kattints ide, hogy megnézd az Önálló Verzió előnyeit! - get_on_steam: Megvásárlás Steamen - standaloneAdvantages: - no_thanks: Nem, köszönöm! - points: - levels: - title: 12 Új Szint - desc: Összesen 26 - buildings: - title: 22 Új Épület - desc: Egy teljesen automatikus gyárhoz! - markers: - title: ∞ Jelölő - desc: Hogy mindig tudd, mi hol van! - wires: - title: Vezetékek - desc: Egy teljesen új dimenzióhoz! - darkmode: - title: Sötét Mód - desc: Ha már fáj a szemed! - support: - title: Támogass - desc: A játékot továbbfejlesztem szabadidőmben - achievements: - title: Steam Achievementek - desc: Szerezd meg mindet! - mods: - title: Modding support! - desc: Over 80 mods available! - titleV2: "Get the full version now on Steam to unlock:" - titleExpiredV2: Demo completed! - titleEnjoyingDemo: Enjoy the demo? puzzleEditorSettings: zoneTitle: Zóna zoneWidth: Szélesség @@ -599,7 +475,7 @@ buildings: description: Négyfelé vágja az alakzatokat. <strong>Ha csak az egyik részét akarod használni, ne felejtsd el a többit a Kukába küldeni, különben eldugul!</strong> - rotater: + rotator: default: name: Forgató description: Elforgatja az alakzatot 90 fokkal, az óramutató irányában. @@ -720,7 +596,7 @@ buildings: default: name: Virtuális Vágó description: Virtuálisan félbevág egy alakzatot. - rotater: + rotator: name: Virtuális Forgató description: Virtuálisan elforgat egy alakzatot, az óramutató járásával megegyező és ellenkező irányba is. @@ -762,7 +638,7 @@ storyRewards: irányától</strong>!<br><br>Szabadulj meg a feleslegtől, ellenkező esetben <strong>eldugulhat a rendszer</strong> - Ezért kaptál egy <strong>Kukát</strong>, amely elpusztít mindent, amit belevezetsz! - reward_rotater: + reward_rotator: title: Forgatás desc: Feloldottad a <strong>Forgatót</strong>, amely az óramutató járásával 90 fokban forgatja el az alakzatokat. @@ -786,7 +662,7 @@ storyRewards: title: Alagút desc: Feloldottad az <strong>Alagutat</strong> - Átvihetsz egy futószalagot másik futószalag, vagy akár épület alatt! - reward_rotater_ccw: + reward_rotator_ccw: title: Ellentétes Forgatás desc: Feloldottad a <strong>Forgató</strong> ellentétes irányú variánsát, amellyel az óramutató járásával ellentétes irányban forgathatsz @@ -854,7 +730,7 @@ storyRewards: desc: Feloldottad a <strong>Futószalag-mérőt</strong>! Segítségével megmérheted a Futószalag átmenő teljesítményét.<br><br>Várj csak, amíg feloldod a Vezetékeket - utána lesz csak igazán hasznos! - reward_rotater_180: + reward_rotator_180: title: Forgató (180°) desc: Feloldottad a <strong>180 fokos Forgatót</strong>, amellyel megfordíthatsz egy alakzatot 180 fokban (Meglepő, mi? :D) @@ -1132,7 +1008,7 @@ keybindings: underground_belt: Alagút miner: Bánya cutter: Vágó - rotater: Forgató + rotator: Forgató stacker: Egyesítő mixer: Színkeverő painter: Festő @@ -1193,12 +1069,6 @@ about: changelog: title: Változások demo: - features: - restoringGames: Mentések visszaállítása - importingGames: Mentések importálása - oneGameLimit: Egyetlen játékmentés elérhető - customizeKeybindings: Gyorsbillentyűk beállítása - exportingBase: Képernyőkép készítése a teljes bázisról settingNotAvailable: Nem elérhető a Demó Verzióban. tips: - A Központ mindenféle bemenetet elfogad, nemcsak az éppen aktuálisan kívánt @@ -1361,16 +1231,7 @@ mods: version: Version modWebsite: Website openFolder: Open Mods Folder - folderOnlyStandalone: Opening the mod folder is only possible when running the standalone. browseMods: Browse Mods modsInfo: To install and manage mods, copy them to the mods folder within the game directory. You can also use the 'Open Mods Folder' button on the top right. - noModSupport: You need the standalone version on Steam to install mods. - togglingComingSoon: - title: Coming Soon - description: Enabling or disabling mods is currently only possible by copying - the mod file from or to the mods/ folder. However, being able to - toggle them here is planned for a future update! - browserNoSupport: Due to browser restrictions it is currently only possible to - install mods in the Steam version - Sorry! diff --git a/translations/base-ind.yaml b/translations/base-ind.yaml index 7aa95ae4..9a1afa1a 100644 --- a/translations/base-ind.yaml +++ b/translations/base-ind.yaml @@ -1,24 +1,3 @@ -steamPage: - shortText: shapez.io adalah game tentang membangun pabrik untuk mengotomatiskan - pembuatan dan pemrosesan bentuk-bentuk yang semakin lama semakin - kompleks di dalam peta yang meluas tanpa batas. - discordLinkShort: Server Discord Resmi - intro: >- - Kamu suka game otomasi? Maka kamu berada di tempat yang tepat! - - shapez.io adalah game santai dimana kamu harus membuat pabrik untuk mengotomatiskan produksi bentuk-bentuk geometris. Semakin meningkatnya level, bentuk-bentuknya menjadi lebih kompleks, dan kamu perlu meluaskan pabrikmu semakin jauh lagi. - - Dan jita itu tidak cukup, kamu juga perlu memproduksi bentuk secara eksponensial untuk memenuhkan kebutuhan - hal yang membantu hanyalah memperbesar pabrik! Walaupun kamu hanya perlu memproses bentuk di awal, nantinya kamu harus memberinya warna - dengan mengekstrak dan mencampur warna! - - Membeli game ini di Steam memberikan kamu akses ke versi lengkap, namun kamu juga dapat mencoba demo dan memutuskan nanti! - what_others_say: Apa yang orang lain katakan tentang shapez.io - nothernlion_comment: Game ini bagus - Saya sangat menikmati waktu saya ketika - memainkan game ini, dan waktu telah berlalu. - notch_comment: Oh sial. Saya benar-benar harus tidur, namun sepertinya saya baru - menemukan bagaimana cara membuat komputer di shapez.io - steam_review_comment: Game ini telah mencuri hidup saya dan saya tidak - menginginkannya kembali. Game pembuatan pabrik yang sangat santai yang - tidak akan membiarkan saya berhenti membuat pabrik saya lebih efisien. global: loading: Memuat error: Terjadi kesalahan @@ -52,56 +31,19 @@ global: space: SPACE loggingIn: Logging in loadingResources: Downloading additional resources (<percentage> %) - discount: -<percentage>% - discountSummerSale: SPECIAL PROMOTION! Offer ends 7 July -demoBanners: - title: Versi Demo - intro: |- - Get the full game <strong>now</strong> to unlock:<ul> - <li>All 26 levels + infinite Freeplay</li> - <li>22 new buildings</li> - <li>Mod support</li> - <li>Achievements</li> - <li>Dark Mode</li> - <li>... and a lot more!</li> - </ul> - playtimeDisclaimer: The full version contains more than <strong>20 hours of content</strong>. - playerCount: <playerCount> players like you are currently playing shapez on Steam - untilEndOfDemo: Until end of demo - playtimeDisclaimerDownload: You can continue your savegame in the full version! - Click <strong>here</strong> to download your savegame. - titleV2: "Play the full version now for:" mainMenu: play: Mulai Permainan continue: Lanjutkan newGame: Permainan Baru - changelog: Catatan Perubahan - subreddit: Reddit importSavegame: Impor Data Simpanan - openSourceHint: Game ini open source! - discordLink: Server Discord Resmi helpTranslate: Bantu Terjemahkan! - madeBy: Dibuat oleh <author-link> - browserWarning: Maaf, tetapi permainan ini biasanya lambat pada browser kamu! - Dapatkan versi lengkap atau unduh Chrome untuk pengalaman sepenuhnya. savegameLevel: Level <x> savegameLevelUnknown: Level tidak diketahui savegameUnnamed: Tidak Dinamai - puzzleMode: Puzzle Mode - back: Back - puzzleDlcText: Do you enjoy compacting and optimizing factories? Get the Puzzle - DLC now on Steam for even more fun! - puzzleDlcWishlist: Wishlist now! - puzzleDlcViewNow: View Dlc mods: - title: Active Mods warningPuzzleDLC: Playing the Puzzle DLC is not possible with mods. Please disable all mods to play the DLC. - playingFullVersion: You are now playing the full version! - logout: Logout noActiveSavegames: No active savegames found - Click play to start a new game! - playFullVersionV2: Bough shapez on Steam? Play the full version in your Browser! - playFullVersionStandalone: You can now also play the full version in your Browser! dialogs: buttons: ok: OK @@ -110,7 +52,6 @@ dialogs: later: Nanti restart: Mulai Ulang reset: Setel Ulang - getStandalone: Dapatkan Versi Lengkap deleteGame: Aku tahu apa yang aku lakukan viewUpdate: Tampilkan Update showUpgrades: Tunjukkan Tingkatan @@ -148,15 +89,6 @@ dialogs: keybindingsResetOk: title: Setel Ulang desc: Tombol-tombol pintas sudah disetel ulang ke pengaturan awalnya! - featureRestriction: - title: Versi Demo - desc: Kamu mencoba untuk mengakses fitur (<feature>) yang tidak tersedia pada - versi demo. Pertimbangkan untuk mendapatkan versi lengkap untuk - pengalaman sepenuhnya! - oneSavegameLimit: - title: Penyimpanan Permainan Terbatas - desc: Kamu hanya dapat memiliki satu data simpanan dalam versi demo. Harap hapus - yang telah ada atau dapatkan versi lengkap! updateSummary: title: Update Baru! desc: "Berikut perubahan-perubahan yang telah dibuat sejak kamu main terakhir @@ -197,9 +129,6 @@ dialogs: desc: Berikan nama yang berguna, kamu juga bisa memasukkan <strong>short key </strong> dari sebuah bentuk (Yang bisa kamu buat sendiri <link>disini</link>) - markerDemoLimit: - desc: Kamu hanya dapat membuat dua penanda pada versi demo. Dapatkan versi - lengkap untuk penanda-penanda tak terbatas! exportScreenshotWarning: title: Ekspor Screenshot desc: Kamu meminta untuk mengekspor pabrikmu sebagai screenshot. Harap ketahui @@ -294,28 +223,9 @@ dialogs: newMods: Newly installed Mods resourceLoadFailed: title: Resources failed to load - demoLinkText: shapez demo on Steam - descWeb: "One ore more resources could not be loaded. Make sure you have a - stable internet connection and try again. If this still doesn't - work, make sure to also disable any browser extensions (including - adblockers).<br><br> As an alternative, you can also play the - <demoOnSteamLinkText>. <br><br> Error Message:" descSteamDemo: "One ore more resources could not be loaded. Try restarting the game - If that does not help, try reinstalling and verifying the game files via Steam. <br><br> Error Message:" - steamSsoError: - title: Full Version Logout - desc: You have been logged out from the Full Browser Version since either your - network connection is unstable or you are playing on another - device.<br><br> Please make sure you don't have shapez open in any - other browser tab or another computer with the same Steam - account.<br><br> You can login again in the main menu. - steamSsoNoOwnership: - title: Full Edition not owned - desc: In order to play the Full Edition in your Browser, you need to own both - the base game and the Puzzle DLC on your Steam account.<br><br> - Please make sure you own both, signed in with the correct Steam - account and then try again. ingame: keybindingsOverlay: moveMap: Geser @@ -459,40 +369,6 @@ ingame: one_miner: 1 Ekstraktor n_miners: <amount> Ekstraktor limited_items: Terbatas hingga <max_throughput> - watermark: - title: Versi Demo - desc: Klik disini untuk melihat keunggulan pada versi Steam! - get_on_steam: Dapatkan di Steam - standaloneAdvantages: - no_thanks: Tidak, terima kasih! - points: - levels: - title: 12 Level Baru - desc: Dengan total 26 level! - buildings: - title: 22 Bangunan Baru - desc: Untuk membuat pabrik yang otomatis sepenuhnya! - markers: - title: ∞ Penanda - desc: Kamu tidak akan tersesat di pabrikmu sendiri! - wires: - title: Kabel - desc: Sebuah dimensi yang benar-benar berbeda! - darkmode: - title: Mode Gelap - desc: Berhenti merusak matamu! - support: - title: Dukung saya - desc: Saya membuat game ini di waktu luang! - achievements: - title: Achievements - desc: Hunt them all! - mods: - title: Modding support! - desc: Over 80 mods available! - titleV2: "Get the full version now on Steam to unlock:" - titleExpiredV2: Demo completed! - titleEnjoyingDemo: Enjoy the demo? puzzleEditorSettings: zoneTitle: Zone zoneWidth: Width @@ -597,7 +473,7 @@ buildings: description: Memotong bentuk-bentuk menjadi empat bagian. <strong> Apabila kamu hanya menggunakan satu bagian, pastikan kamu lenyapkan bagian-bagian lain atau mesin akan tersumbat dan macet!</strong> - rotater: + rotator: default: name: Pemutar description: Memutar bentuk searah jarum jam sebesar 90 derajat. @@ -734,7 +610,7 @@ buildings: default: name: Pemotong Virtual description: Memotong bentuk menjadi dua bagian secara virtual. - rotater: + rotator: name: Pemutar Virtual description: Memutar bentuk searah jarum jam secara virtual. unstacker: @@ -774,7 +650,7 @@ storyRewards: tidak <strong>ini dapat tersumbat dan macet</strong> - Oleh karena itu kamu diberikan <strong>tong sampah</strong>, yang menghapus semua yang kamu masukkan! - reward_rotater: + reward_rotator: title: Memutar desc: <strong>Pemutar</strong> telah dibuka! Ia memutar bentuk-bentuk searah jarum jam sebesar 90 derajat. @@ -806,7 +682,7 @@ storyRewards: desc: <strong>Terowongan</strong> telah dibuka – Sekarang kamu dapat memindahkan bentuk-bentuk melalui terowongan di bawah sabuk-sabuk konveyor dan bangungan-bangunan dengannya! - reward_rotater_ccw: + reward_rotator_ccw: title: Memutar Berlawanan Arah Jarum Jam desc: Kamu telah membuka varian dari <strong>Pemutar</strong> - Bangunan ini memungkinkan kamu untuk memutar bentuk-bentuk berlawanan arah jarum @@ -888,7 +764,7 @@ storyRewards: memungkinkan kamu untuk mengukur penghasilan dalam sebuah sabuk konveyor.<br><br> Dan tunggu sampai kamu membuka kabel - maka bangunan ini akan sangat berguna! - reward_rotater_180: + reward_rotator_180: title: Pemutar (180 derajat) desc: Kamu telah membuka <strong>pemutar</strong> 180 derajat! - Bangunan ini memungkinkan kamu untuk memutar bentuk dalam 180 derajat (Kejutan! @@ -1162,7 +1038,7 @@ keybindings: underground_belt: Terowongan miner: Ekstraktor cutter: Pemotong - rotater: Pemutar + rotator: Pemutar stacker: Penumpuk mixer: Pencampur Warna painter: Pengecat @@ -1225,12 +1101,6 @@ about: changelog: title: Catatan Perubahan demo: - features: - restoringGames: Mengembalikan simpanan permainan - importingGames: Memasukkan simpanan permainan - oneGameLimit: Terbatas pada satu simpanan permainan - customizeKeybindings: Sesuaikan tombol-tombol pintas - exportingBase: Mengekspor keseluruhan pangkalan pusat sebagai gambar settingNotAvailable: Tidak tersedia dalam versi demo. tips: - Bangunan pusat akan menerima segala input, tidak hanya bentuk sekarang! @@ -1406,16 +1276,7 @@ mods: version: Version modWebsite: Website openFolder: Open Mods Folder - folderOnlyStandalone: Opening the mod folder is only possible when running the standalone. browseMods: Browse Mods modsInfo: To install and manage mods, copy them to the mods folder within the game directory. You can also use the 'Open Mods Folder' button on the top right. - noModSupport: You need the standalone version on Steam to install mods. - togglingComingSoon: - title: Coming Soon - description: Enabling or disabling mods is currently only possible by copying - the mod file from or to the mods/ folder. However, being able to - toggle them here is planned for a future update! - browserNoSupport: Due to browser restrictions it is currently only possible to - install mods in the Steam version - Sorry! diff --git a/translations/base-it.yaml b/translations/base-it.yaml index 5935d5f1..a9f3209d 100644 --- a/translations/base-it.yaml +++ b/translations/base-it.yaml @@ -1,27 +1,3 @@ -steamPage: - shortText: In shapez.io potrai costruire delle fabbriche per automatizzare la - creazione e la combinazione di forme sempre più complesse, in una mappa - infinita. - discordLinkShort: Discord ufficiale - intro: >- - Shapez.io è un gioco tranquillo nel quale dovrai costruire delle - fabbriche per la produzione automatizzata di forme geometriche. - - All'aumentare del livello, le forme diventeranno sempre più complesse e dovrai espanderti sempre di più nella mappa infinita. - - E come se non bastasse, dovrai produrre esponenzialmente di più per soddisfare le richieste, l'unica possibilità è andare su grande scala! - - All'inizio lavorerai solo con le forme, ma in seguito dovrai colorarle; a questo scopo dovrai estrarre e mescolare i colori! - - Comprare il gioco su Steam ti garantirà l'accesso alla versone completa, ma puoi anche giocare una demo su shapez.io e decidere in seguito! - what_others_say: "Hanno detto di shapez.io:" - nothernlion_comment: This game is great - I'm having a wonderful time playing, - and time has flown by. - notch_comment: Oh crap. I really should sleep, but I think I just figured out - how to make a computer in shapez.io - steam_review_comment: This game has stolen my life and I don't want it back. - Very chill factory game that won't let me stop making my lines more - efficient. global: loading: Caricamento error: Errore @@ -55,58 +31,20 @@ global: space: SPAZIO loggingIn: Logging in loadingResources: Downloading additional resources (<percentage> %) - discount: -<percentage>% - discountSummerSale: SPECIAL PROMOTION! Offer ends 7 July -demoBanners: - title: Versione Demo - intro: |- - Acquista il gioco completo <strong>ora</strong> per sbloccarlo:<ul> - <li>Tutti i 26 livelli + gioco libero infinito</li> - <li>22 nuovi edifici</li> - <li>Supporto mod</li> - <li>Risultati</li> - <li>Modalità scura</li> - <li>... e molto altro ancora!</li> - </ul> - playtimeDisclaimer: La versione completa contiene oltre <strong>20 ore di contenuti</strong>. - playerCount: <playerCount> giocatori come te stanno attualmente giocando a - shapez su Steam - untilEndOfDemo: Fino alla fine della demo - playtimeDisclaimerDownload: Puoi continuare il tuo salvataggio nella versione - completa! Fai clic <strong>qui</strong> per scaricare il tuo - salvataggio. - titleV2: "Play the full version now for:" mainMenu: play: Gioca - changelog: Registro modifiche importSavegame: Importa - openSourceHint: Questo gioco è open source! - discordLink: Server ufficiale Discord helpTranslate: Aiutaci a tradurre! - browserWarning: Ci spiace, ma il gioco è molto lento su questo browser! Ottieni - la versione completa oppure scarica Chrome per l'intera esperienza. savegameLevel: Livello <x> savegameLevelUnknown: Livello sconosciuto continue: Continua newGame: Nuova partita - madeBy: Creato da <author-link> - subreddit: Reddit savegameUnnamed: Senza nome - puzzleMode: Modalità puzzle - back: Back - puzzleDlcText: Ti piace miniaturizzare e ottimizzare le tue fabbriche? Ottini il - Puzzle DLC ora su steam per un divertimento ancora maggiore! - puzzleDlcWishlist: Aggiungi alla lista dei desideri ora! - puzzleDlcViewNow: View Dlc mods: - title: Active Mods - warningPuzzleDLC: Playing the Puzzle DLC is not possible with mods. Please - disable all mods to play the DLC. - playingFullVersion: You are now playing the full version! - logout: Logout - noActiveSavegames: No active savegames found - Click play to start a new game! - playFullVersionV2: Bough shapez on Steam? Play the full version in your Browser! - playFullVersionStandalone: You can now also play the full version in your Browser! + warningPuzzleDLC: Non è possibile giocare al Puzzle DLC con le mods. Per favore + disabilita tutte le mod per poter giocare al DLC. + noActiveSavegames: Nessun salvataggio trovato - Clicca su gioca per iniziare una + nuova partita! dialogs: buttons: ok: OK @@ -115,7 +53,6 @@ dialogs: later: Più tardi restart: Riavvia reset: Reset - getStandalone: Ottieni la versione completa deleteGame: So cosa sto facendo viewUpdate: Mostra aggiornamento showUpgrades: Mostra miglioramenti @@ -152,17 +89,8 @@ dialogs: desc: Così riporterai tutti comandi al loro stato predefinto. Per favore conferma. keybindingsResetOk: - title: Successo nel reset dei comandi + title: Comandi resettati con successo desc: I comandi predefiniti sono stati ripristinati! - featureRestriction: - title: Versione Demo - desc: Hai provato ad accedere ad una caratteristica (<feature>) che non è - disponibile nella Demo. Considera di prendere la versione completa - per l'intera esperienza! - oneSavegameLimit: - title: Salvataggi limitati - desc: Puoi avere solo un salvataggio nella versione Demo. Perfavore rimuovi il - salvataggio già esistente o prendi la versione completa! updateSummary: title: Nuovo aggiornamento! desc: "Qui puoi trovare i cambiamenti dall'ultima volta che hai giocato:" @@ -185,7 +113,7 @@ dialogs: costruzione delle fabbriche. Qui ce ne sono un paio, ma dovresti <strong>controllare i comandi</strong>!<br><br> <code class='keybinding'>CTRL</code> + Drag: Seleziona l'area da copiare / - cancella.<br> <code class='keybinding'>SHIFT</code>: Tieni premuto + cancellare.<br> <code class='keybinding'>SHIFT</code>: Tieni premuto per costruire copie dell'edificio.<br> <code class='keybinding'>ALT</code>: Inverti l'orientamento dei nastri trasportatori.<br>" @@ -195,9 +123,6 @@ dialogs: <strong>codice</strong> di una forma (Che puoi generare <link>qui</link>) titleEdit: Modifica etichetta - markerDemoLimit: - desc: Puoi creare solo due etichette personalizzate nella Demo. Ottieni la - versione completa per avere etichette personalizzate illimitati! massCutConfirm: title: Conferma taglio desc: Stai tagliando molte strutture (<count> per essere precisi)! Sei sicuro di @@ -295,46 +220,28 @@ dialogs: desc: Sei sicuro di voler cancellare '<title>'? Questa azione non può essere annullata! modsDifference: - title: Mod Warning - desc: The currently installed mods differ from the mods the savegame was created - with. This might cause the savegame to break or not load at all. Are - you sure you want to continue? - missingMods: Missing Mods - newMods: Newly installed Mods + title: Attenzione + desc: Le mod attualmente installate differiscono rispetto alle mod con cui è + stato creato il salvataggio. Questo potrebbe causare la corruzione o + il mancato caricamento dei dati di salvataggio. Sei sicuro di voler + continuare? + missingMods: Mods mancanti + newMods: Mods installate di recente resourceLoadFailed: - title: Resources failed to load - demoLinkText: shapez demo on Steam - descWeb: "One ore more resources could not be loaded. Make sure you have a - stable internet connection and try again. If this still doesn't - work, make sure to also disable any browser extensions (including - adblockers).<br><br> As an alternative, you can also play the - <demoOnSteamLinkText>. <br><br> Error Message:" - descSteamDemo: "One ore more resources could not be loaded. Try restarting the - game - If that does not help, try reinstalling and verifying the - game files via Steam. <br><br> Error Message:" - steamSsoError: - title: Full Version Logout - desc: You have been logged out from the Full Browser Version since either your - network connection is unstable or you are playing on another - device.<br><br> Please make sure you don't have shapez open in any - other browser tab or another computer with the same Steam - account.<br><br> You can login again in the main menu. - steamSsoNoOwnership: - title: Full Edition not owned - desc: In order to play the Full Edition in your Browser, you need to own both - the base game and the Puzzle DLC on your Steam account.<br><br> - Please make sure you own both, signed in with the correct Steam - account and then try again. + title: Impossibile caricare le risorse + descSteamDemo: "Impossibile caricare una o più risorse. Prova a riavviare il + gioco - Se questo non aiuta, prova a reinstallare e verificare i + file di gioco da Steam. <br><br> Messaggio di errore:" ingame: keybindingsOverlay: moveMap: Sposta selectBuildings: Seleziona area stopPlacement: Concludi posizionamento rotateBuilding: Ruota edificio - placeMultiple: Posiziona multiplo + placeMultiple: Posizionamento multiplo reverseOrientation: Inverti orientamento disableAutoOrientation: Disabilita orientamento automatico - toggleHud: Mostra/Nascondi HUD + toggleHud: Mostra/Nascondi interfaccia placeBuilding: Posiziona costruzione createMarker: Crea etichetta delete: Cancella @@ -346,7 +253,7 @@ ingame: clearSelection: Annulla selezione pipette: Contagocce switchLayers: Cambia livello - clearBelts: Clear belts + clearBelts: Sgombra nastri buildingPlacement: cycleBuildingVariants: Premi <key> per cambiare variante. hotkeyLabel: "Hotkey: <key>" @@ -403,7 +310,7 @@ ingame: waypoints: waypoints: Punti di interesse hub: HUB - description: Clic sinistro su un etichetta per raggiungerla, clic destro per + description: Click sinistro su un etichetta per raggiungerla, click destro per cancellarla. <br><br>Premi <keybinding> per creare un etichetta dalla visuale corrente, oppure <strong>click destro</strong> per creare un etichetta nella posizione selezionata. @@ -419,7 +326,7 @@ ingame: 1_3_expand: "Questo <strong>NON</strong> è un idle game! Costruisci più estrattori e nastri per raggiungere l'obiettivo più velocemente.<br><br>Suggerimento: Tieni premuto - <strong>MAIUSC</strong> per piazzare estrattori multipli, e usa + <strong>MAIUSC</strong> per piazzare estrattori i, e usa <strong>R</strong> per ruotarli." 2_1_place_cutter: "Ora posiziona un <strong>Taglierino</strong> per tagliare i cerchi in due metà!<br><br> PS: Il taglierino taglia sempre @@ -447,7 +354,7 @@ ingame: 21_4_press_button: "Premi l'interruttore per fargli <strong>emettere un segnale di verità </strong> e fargli quindi attivare il vernciatore.<br><br> PS: Non c'è bisogno che tu connetta tutti - gli imput! Prova a collegarne solo due." + gli input! Prova a collegarne solo due." 1_2_hold_and_drag: Mantenere e trascinare colors: red: Rosso @@ -455,7 +362,7 @@ ingame: blue: Blu yellow: Giallo purple: Magenta - cyan: Azzurro + cyan: Ciano white: Bianco uncolored: Senza colore black: Nero @@ -467,40 +374,6 @@ ingame: one_miner: 1 trivella n_miners: <amount> trivelle limited_items: Limitato a <max_throughput> - watermark: - title: Versione demo - desc: Clicca qui per vedere i vantaggi della versione Completa! - get_on_steam: Ottieni su Steam - standaloneAdvantages: - no_thanks: No, grazie! - points: - levels: - title: 12 nuovi livelli! - desc: Per un totale di 26 livelli! - buildings: - title: 22 nuovi edifici - desc: Automatizza completamente la tua fabbrica! - markers: - title: ∞ etichette - desc: Non perderti nella tua fabbrica! - wires: - title: Cavi - desc: Un'intera nuova dimensione! - darkmode: - title: Modalità scura - desc: Smettila di maltrattare i tuoi occhi! - support: - title: Sostienimi - desc: Lo sviluppo nel tempo libero! - achievements: - title: Achievement - desc: Collezionali tutti! - mods: - title: Modding support! - desc: Over 80 mods available! - titleV2: "Get the full version now on Steam to unlock:" - titleExpiredV2: Demo completed! - titleEnjoyingDemo: Enjoy the demo? puzzleEditorSettings: zoneTitle: Zona zoneWidth: Larghezza @@ -535,8 +408,7 @@ ingame: title: Puzzle completato! titleLike: "Clicca il cuore se ti è piaciuto:" titleRating: Quanto è stato difficile il puzzle? - titleRatingDesc: La tua valutazione mi aiuterà a darti raccomandazioni migliori - in futuro + titleRatingDesc: La tua valutazione mi aiuterà a darti suggerimenti migliori in futuro continueBtn: Continua a giocare menuBtn: Menù nextPuzzle: Puzzle successivo @@ -548,7 +420,7 @@ ingame: completionRate: Tasso di completamento shopUpgrades: belt: - name: Nastri, distribuzione e tunnel + name: Nastri, distributori e tunnel description: Velocità x<currentMult> → x<newMult> miner: name: Estrazione @@ -591,7 +463,7 @@ buildings: name: Taglierino (4x) description: Taglia le forme in quattro parti. <strong>Se usi meno di quattro parti, distruggi le altre o la macchina si fermerà!</strong> - rotater: + rotator: default: name: Ruotatore description: Ruota le forme di 90 gradi in senso orario. @@ -740,7 +612,7 @@ buildings: default: name: Taglierino virtuale description: Taglia virtualmente la forma in due metà. - rotater: + rotator: name: Ruotatore virtuale description: Ruota virtualmente la forma, sia in senso orario sia antiorario. unstacker: @@ -781,7 +653,7 @@ storyRewards: altrimenti <strong>si intaserà e andrà in stallo </strong> - Per questo ti ho dato il <strong>cestino</strong>, che distrugge tutto quello che riceve! - reward_rotater: + reward_rotator: title: Rotazione desc: Il <strong>ruotatore</strong> è stato sbloccato! Ruota le forme di 90 gradi in senso orario. @@ -798,14 +670,14 @@ storyRewards: puoi combinare due colori mediante la <strong>sintesi additiva</strong>! reward_splitter: - title: Separatore/Agrregatore + title: Separatore/Aggregatore desc: Il <strong>separatore</strong> è stato sbloccato! è una variante del - <strong>bilanciatore</strong> - Accetta un imput e lo divide in due! + <strong>bilanciatore</strong> - Accetta un input e lo divide in due! reward_tunnel: title: Tunnel desc: Il <strong>tunnel</strong> è stato sbloccato! In questo modo puoi trasportare oggetti al di sotto di nastri ed edifici! - reward_rotater_ccw: + reward_rotator_ccw: title: Rotazione antioraria desc: Hai sbloccato una variante del <strong>ruotatore</strong>! Consente di ruotare in senso antiorario! Per costruirla, seleziona il ruotatore @@ -885,7 +757,7 @@ storyRewards: desc: Hai sbloccato il <strong>lettore di nastri</strong>! Consente di misurare la portata di un nastro.<br><br>E aspetta di sbloccare i cavi, allora sì che sarà molto utile! - reward_rotater_180: + reward_rotator_180: title: Ruotatore (180 gradi) desc: Hai appena sbloccato il <strong>ruotatore</strong> a 180 gradi! Consente di ruotare una forma di 180 gradi (Sorpresa! :D) @@ -995,11 +867,14 @@ settings: dark: Scuro light: Chiaro refreshRate: - title: Obiettivo della simulazione - description: Se hai un monitor a 144Hz, cambia la frequenza di aggiornamento, in - modo tale che il gioco possa correttamente simulare alla - frequenza di aggiornamento più alta. Questo potrebbe abbasare i - frame al secondo se il tuo computer è troppo lento. + title: Tick Rate + description: Questo determina quanti tick di gioco avvengono al secondo. In + generale ad un maggior tick rate corrisponde una maggiore + precisione ma performance peggiore. Se hai un monitor con una + frequenza superiore a 60Hz, aumenta questo valore, in modo tale + che il gioco possa correttamente simulare alla frequenza di + aggiornamento più alta. Questo potrebbe abbassare i frame al + secondo se il tuo computer è troppo lento. alwaysMultiplace: title: Posizionamento multiplo description: Se abilitato, tutti gli edifici rimarranno selezionati dopo il @@ -1118,7 +993,7 @@ settings: premere 'ALT'. rangeSliderPercentage: <amount> % tickrateHz: <amount> Hz - newBadge: New! + newBadge: Novità! keybindings: title: Comandi hint: "Suggerimento: Usa spesso CTRL, MAIUSC e ALT! Abilitano opzioni di @@ -1132,7 +1007,7 @@ keybindings: massSelect: Selezione di massa buildings: Scorciatoie edifici placementModifiers: Modificatori piazzamento - mods: Provided by Mods + mods: Fornito dalle mod mappings: confirm: Conferma back: Indietro @@ -1146,13 +1021,13 @@ keybindings: createMarker: Crea Etichetta menuOpenShop: Miglioramenti menuOpenStats: Statistiche - toggleHud: Mostra/Nascondi HUD + toggleHud: Mostra/Nascondi interfaccia toggleFPSInfo: Mostra/Nascondi FPS e info debug belt: Nastro Trasportatore underground_belt: Tunnel miner: Estrattore cutter: Taglierino - rotater: Ruotatore + rotator: Ruotatore stacker: Impilatrice mixer: Miscelatore di vernice painter: Verniciatore @@ -1219,12 +1094,6 @@ about: changelog: title: Registro modifiche demo: - features: - restoringGames: Recupero salvataggi - importingGames: Importazione salvataggi - oneGameLimit: Limite di un salvataggio - customizeKeybindings: Scorciatoie personalizzabili - exportingBase: Esportazione dell'intera base come immagine settingNotAvailable: Non disponibile nella demo. tips: - L'hub centrale accetta qualsiasi forma, non solo l'obiettivo corrente! @@ -1235,7 +1104,7 @@ tips: - Tenere premuto <b>CTRL</b> consente di trascinare i nastri senza auto-orientamento. - I rapporti rimangono uguali, fintanto che tutti i miglioramenti sono allo - stesso livello, + stesso livello. - L'esecuzione seriale è più efficiente di quella parallela. - Più avanti nel gioco sbloccherai altre varianti degli edifici! - Puoi usare <b>T</b> per cambiare variante. @@ -1331,7 +1200,7 @@ puzzleMenu: noProducers: Per favore posiziona un Produttore costante! noGoalAcceptors: Per favore posiziona un accettore di obiettivi! goalAcceptorNoItem: Uno o più degli accettori di obiettivi non hanno un oggetto - assegnato. Consgnagli una forma per impostare l'obiettivo. + assegnato. Consegna una forma per impostare l'obiettivo. goalAcceptorRateNotMet: Uno o più degli accettori di obiettivi non ricevono abbastanza oggetti. Assicurati che gli indicatori siano verdi per tutti gli accettori. @@ -1389,20 +1258,11 @@ backendErrors: no-permission: Non hai i permessi per eseguire questa azione. mods: title: Mods - author: Author - version: Version - modWebsite: Website - openFolder: Open Mods Folder - folderOnlyStandalone: Opening the mod folder is only possible when running the standalone. - browseMods: Browse Mods - modsInfo: To install and manage mods, copy them to the mods folder within the - game directory. You can also use the 'Open Mods Folder' button on the - top right. - noModSupport: You need the standalone version on Steam to install mods. - togglingComingSoon: - title: Coming Soon - description: Enabling or disabling mods is currently only possible by copying - the mod file from or to the mods/ folder. However, being able to - toggle them here is planned for a future update! - browserNoSupport: Due to browser restrictions it is currently only possible to - install mods in the Steam version - Sorry! + author: Autore + version: Versione + modWebsite: Sito + openFolder: Apri cartella delle Mod + browseMods: Sfoglia Mods + modsInfo: Per installare e gestire le mod, copiale nella cartella 'mods' + all'interno della directory del gioco. Puoi usare anche il tasto 'Apri + cartella delle Mod' in alto a destra. diff --git a/translations/base-ja.yaml b/translations/base-ja.yaml index 65a2c5a9..c9b4961c 100644 --- a/translations/base-ja.yaml +++ b/translations/base-ja.yaml @@ -1,19 +1,3 @@ -steamPage: - shortText: shapez.ioは無限のマップ内で様々な"形"を資源とし、段々と複雑になっていく形の作成や合成の自動化を目指して工場を構築するゲームです。 - discordLinkShort: 公式Discord - intro: >- - 工場の自動化ゲームはお好きですか? それならここに来て正解です! - - shapez.ioは、様々な幾何学形状の自動生産工場を建設する、リラックスして遊べるゲームです。レベルが上がるにつれ生産すべき形はどんどん複雑になり、無限に広がるマップに工場を拡張していくことになります。 - - しかし、それだけでは不十分です。指数関数的に増大していく形状の要求量に対応する必要があり――「スケーリング」が、唯一の対抗策と成り得ます。また、最初は形状を加工するだけですが、後々着色も必要になってきます。色を抽出して混ぜ合わせましょう! - - Steamでゲームを購入するとフルバージョンで遊べますが、まずshapez.ioでデモをプレイし、その後で決めることもできます! - what_others_say: shapez.ioプレイヤーの反応: - nothernlion_comment: これはすごいゲーム。プレイ体験が最高な上に時間の溶け方が尋常でない。 - notch_comment: いややばいって。マジで寝なきゃなんだけど、今ならshapez.ioで計算機組めそうな気がする。 - steam_review_comment: このゲームは私の生活を奪い去っていったが、返して欲しいとは思わない。 - いくらでも気の向くままに生産ラインを効率化させてくれる、そんなとても穏やかな工場ゲームだ。 global: loading: ロード中 error: エラー @@ -47,55 +31,19 @@ global: space: SPACE loggingIn: Logging in loadingResources: 追加リソースのダウンロード(<percentage>%) - discount: -<percentage>% - discountSummerSale: SPECIAL PROMOTION! Offer ends 7 July -demoBanners: - title: デモ版 - intro: |- - ゲーム本編を<strong>今すぐ</strong>ゲットしてアンロックしよう。<ul> - <li>全26レベル+無限のフリープレイ</li> - <li>22の新棟</li> - <li>Mod対応</li> - <li>実績</li> - <li>ダークモード</li> - <li>...などなど、盛りだくさんです</li> - </ul> - playtimeDisclaimer: フルバージョンには、<strong>20時間以上のコンテンツ</strong>が含まれています。 - playerCount: <playerCount> あなたと同じようなプレイヤーがSteamでshapezをプレイしています。 - untilEndOfDemo: デモ終了まで - playtimeDisclaimerDownload: フルバージョンでセーブゲームを続けることができます! - セーブゲームをダウンロードするには、<strong>こちら</strong>をクリックしてください。 - titleV2: 今すぐフルバージョンをプレイしてください。 mainMenu: play: プレイ continue: 続きから newGame: 新規ゲーム - changelog: 更新履歴 - subreddit: Reddit importSavegame: インポート - openSourceHint: このゲームはオープンソースです - discordLink: 公式Discordサーバー helpTranslate: 翻訳にご協力ください! - madeBy: <author-link>によって作られました - browserWarning: このゲームはお使いのブラウザでは速度が落ちることがあります。スタンドアローン版を入手するか、Chromeでプレイすることで解決できます。 savegameLevel: レベル <x> savegameLevelUnknown: 不明なレベル savegameUnnamed: 無名のデータ - puzzleMode: Puzzle Mode - back: Back - puzzleDlcText: Do you enjoy compacting and optimizing factories? Get the Puzzle - DLC now on Steam for even more fun! - puzzleDlcWishlist: Wishlist now! - puzzleDlcViewNow: View Dlc mods: - title: Active Mods warningPuzzleDLC: Playing the Puzzle DLC is not possible with mods. Please disable all mods to play the DLC. - playingFullVersion: You are now playing the full version! - logout: Logout noActiveSavegames: No active savegames found - Click play to start a new game! - playFullVersionV2: Bough shapez on Steam? Play the full version in your Browser! - playFullVersionStandalone: You can now also play the full version in your Browser! dialogs: buttons: ok: OK @@ -104,7 +52,6 @@ dialogs: later: 後で restart: 再起動 reset: リセット - getStandalone: スタンドアローン版を入手 deleteGame: 何が起きるか理解しています viewUpdate: アップデートを見る showUpgrades: アップグレード表示 @@ -140,12 +87,6 @@ dialogs: keybindingsResetOk: title: キー設定のリセット desc: キー設定を初期値に設定しました! - featureRestriction: - title: デモ版 - desc: アクセスした要素 (<feature>) はデモ版では利用できません。スタンドアローン版の入手をご検討ください! - oneSavegameLimit: - title: セーブデータ制限 - desc: デモ版ではひとつのセーブデータのみ保持できます。既存のデータを削除するか、スタンドアローン版の入手をご検討ください! updateSummary: title: 新アップデート! desc: "前回からの変更点:" @@ -179,8 +120,6 @@ dialogs: title: 信号を設定 descItems: "プリセットを選択:" descShortKey: もしくは形を表す<strong>短いキー</strong>を入力してください。 (<link>ここ</link>から生成できます) - markerDemoLimit: - desc: デモ版ではマーカー設置は2つまでに制限されています。スタンドアローン版は無制限です! exportScreenshotWarning: title: スクリーンショット出力 desc: スクリーンショット出力を実行します。この処理は工場の全体像があまりに大きいと、 ゲームが遅くなったりクラッシュしてしまう可能性があります! @@ -266,28 +205,9 @@ dialogs: newMods: Newly installed Mods resourceLoadFailed: title: Resources failed to load - demoLinkText: shapez demo on Steam - descWeb: "One ore more resources could not be loaded. Make sure you have a - stable internet connection and try again. If this still doesn't - work, make sure to also disable any browser extensions (including - adblockers).<br><br> As an alternative, you can also play the - <demoOnSteamLinkText>. <br><br> Error Message:" descSteamDemo: "One ore more resources could not be loaded. Try restarting the game - If that does not help, try reinstalling and verifying the game files via Steam. <br><br> Error Message:" - steamSsoError: - title: Full Version Logout - desc: You have been logged out from the Full Browser Version since either your - network connection is unstable or you are playing on another - device.<br><br> Please make sure you don't have shapez open in any - other browser tab or another computer with the same Steam - account.<br><br> You can login again in the main menu. - steamSsoNoOwnership: - title: Full Edition not owned - desc: In order to play the Full Edition in your Browser, you need to own both - the base game and the Puzzle DLC on your Steam account.<br><br> - Please make sure you own both, signed in with the correct Steam - account and then try again. ingame: keybindingsOverlay: moveMap: マップ移動 @@ -416,40 +336,6 @@ ingame: one_miner: 1個の抽出機 n_miners: <amount>個の抽出機 limited_items: <max_throughput>に制限 - watermark: - title: デモバージョン - desc: Steamバージョンの特典を確認するには、ここをクリックしてください! - get_on_steam: steamで購入 - standaloneAdvantages: - no_thanks: いいえ、結構です - points: - levels: - title: 新しい12個のレベル - desc: 全部で26個のレベルになります! - buildings: - title: 新しい18個の設置物 - desc: あなたの工場を完全自動化しましょう! - markers: - title: 無限個のマップマーカー - desc: これでもうあなたの工場を見失いません! - wires: - title: ワイヤ - desc: 新次元の体験を得られます! - darkmode: - title: ダークモード - desc: 目に優しい! - support: - title: 製作者をサポート - desc: 余暇に制作しています! - achievements: - title: アチーブメント - desc: 取り尽くせ! - mods: - title: Modding support! - desc: Over 80 mods available! - titleV2: "Get the full version now on Steam to unlock:" - titleExpiredV2: Demo completed! - titleEnjoyingDemo: Enjoy the demo? puzzleEditorSettings: zoneTitle: Zone zoneWidth: Width @@ -551,7 +437,7 @@ buildings: quad: name: 切断機 (四分割) description: 形を四分割します。<strong>ひとつの出力しか使わない場合、他の出力を破棄しないと詰まって停止してしまうことに注意してください!</strong> - rotater: + rotator: default: name: 回転機 description: 形を時計回りに90度回転します。 @@ -655,7 +541,7 @@ buildings: default: name: 仮想切断機 description: 形状の信号を2つに切断できます。 - rotater: + rotator: name: 仮想回転機 description: 形状の信号を時計回りに回転させます。 unstacker: @@ -689,7 +575,7 @@ storyRewards: desc: <strong>切断機</strong>が利用可能になりました。これは入力された形を、<strong>向きを考慮せず上下の直線で</strong>半分に切断します! <br><br>利用しない側の出力に注意しましょう、破棄しなければ<strong>詰まって停止してしまいます。</strong> - このために<strong>ゴミ箱</strong>も用意しました。入力アイテムをすべて破棄できます! - reward_rotater: + reward_rotator: title: 回転 desc: <strong>回転機</strong>が利用可能になりました! 形を時計回り方向に90度回転させます。 reward_painter: @@ -709,7 +595,7 @@ storyRewards: reward_tunnel: title: トンネル desc: <strong>トンネル</strong>が利用可能になりました。他のベルトや建造物の地下を通してベルトが配置可能です! - reward_rotater_ccw: + reward_rotator_ccw: title: 反時計回りの回転 desc: <strong>回転機</strong>のバリエーションが利用可能になりました。反時計回りの回転ができるようになります! 回転機を選択し、<strong>'T'キーを押すことで方向の切り替えができます。</strong> reward_miner_chainable: @@ -747,7 +633,7 @@ storyRewards: title: ブループリント desc: 工場の建造物の<strong>コピー&ペースト</strong>が利用可能になりました! 範囲選択(CTRLキーを押したままマウスドラッグ)した状態で、'C'キーを押すことでコピーができます。<br><br>ただしペーストは<strong>タダではありません。</strong><strong>ブループリントの形</strong>を生産する必要があります!(たった今納品した形です) - reward_rotater_180: + reward_rotator_180: title: 回転(180°) desc: <strong>回転機</strong>のバリエーションが利用可能になりました! 180°の回転ができるようになります!(サプライズ! :D) reward_wires_painter_and_levers: @@ -983,7 +869,7 @@ keybindings: underground_belt: トンネル miner: 抽出機 cutter: 切断機 - rotater: 回転機 + rotator: 回転機 stacker: 積層機 mixer: 混色機 painter: 着色機 @@ -1044,12 +930,6 @@ about: changelog: title: 更新履歴 demo: - features: - restoringGames: セーブデータのリストア - importingGames: セーブデータのインポート - oneGameLimit: セーブデータの1個制限 - customizeKeybindings: キー設定のカスタマイズ - exportingBase: 工場の全体像の画像出力 settingNotAvailable: デモ版では利用できません。 tips: - ハブは現在指定されている形状だけではなく、あらゆる種類の入力を受け付けることができます。 @@ -1199,16 +1079,7 @@ mods: version: Version modWebsite: Website openFolder: Open Mods Folder - folderOnlyStandalone: Opening the mod folder is only possible when running the standalone. browseMods: Browse Mods modsInfo: To install and manage mods, copy them to the mods folder within the game directory. You can also use the 'Open Mods Folder' button on the top right. - noModSupport: You need the standalone version on Steam to install mods. - togglingComingSoon: - title: Coming Soon - description: Enabling or disabling mods is currently only possible by copying - the mod file from or to the mods/ folder. However, being able to - toggle them here is planned for a future update! - browserNoSupport: Due to browser restrictions it is currently only possible to - install mods in the Steam version - Sorry! diff --git a/translations/base-kor.yaml b/translations/base-kor.yaml index e6daa9e0..d6b28f68 100644 --- a/translations/base-kor.yaml +++ b/translations/base-kor.yaml @@ -1,21 +1,3 @@ -steamPage: - shortText: shapez.io는 무한한 공간에서 점점 더 복잡한 도형의 생산과 조합을 자동화하는 공장을 짓는 게임입니다. - discordLinkShort: 공식 Discord - intro: >- - Shapez.io는 다양한 기하학적 도형을 만드는 공장을 건설하는 편안한 게임입니다. 레벨이 올라갈수록 더욱 복잡한 도형을 - 만들어야 하고 그만큼 무한히 커지는 지도 안에 당신의 공장을 세워야 합니다. - - 심지어 그것만으로는 충분하지 않을 겁니다. 수요는 기하급수적으로 늘어나게 될 것이고, 더욱 복잡한 도형을 더욱 많이 생산하여야 하므로, 유일하게 도움이 되는 것은 끊임없이 확장을 하는 것입니다! 처음에는 단순한 도형만을 만들지만, 나중에는 색소를 추출하고 혼합하여 도형에 색칠을 해야 합니다! - - Steam에서 게임을 구매하여 정식 버전의 콘텐츠를 사용하실 수 있지만, 먼저 Shapez.io의 체험판 버전을 플레이해보시고 구매를 고려하셔도 됩니다! - what_others_say: shapez.io에 대한 의견들 - nothernlion_comment: This game is great - I'm having a wonderful time playing, - and time has flown by. - notch_comment: Oh crap. I really should sleep, but I think I just figured out - how to make a computer in shapez.io - steam_review_comment: This game has stolen my life and I don't want it back. - Very chill factory game that won't let me stop making my lines more - efficient. global: loading: 불러오는 중 error: 오류 @@ -49,54 +31,18 @@ global: space: SPACE loggingIn: 로그인 중 loadingResources: Downloading additional resources (<percentage> %) - discount: -<percentage>% - discountSummerSale: SPECIAL PROMOTION! Offer ends 7 July -demoBanners: - title: 체험판 버전 - intro: |- - <strong>지금</strong> 정식 게임을 다운로드하여 다음을 잠금 해제하세요.<ul> - <li>모든 26개 레벨 + 무한 무료 플레이</li> - <li>22개의 새로운 건물</li> - <li>모드 지원</li> - <li>업적</li> - <li>다크 모드</li> - <li>... 그리고 더 많이!</li> - </ul> - playtimeDisclaimer: 정식 버전에는 <strong>20시간 이상의 콘텐츠</strong>가 포함되어 있습니다. - playerCount: <playerCount>명의 플레이어가 현재 Steam에서 shapez를 플레이하고 있습니다. - untilEndOfDemo: 데모가 끝날 때까지 - playtimeDisclaimerDownload: 정식 버전에서 저장 게임을 계속할 수 있습니다! <strong>여기</strong>를 클릭하여 저장 게임을 다운로드하세요. - titleV2: "지금 정식 버전을 플레이하세요:" mainMenu: play: 시작 - changelog: 버전 기록 importSavegame: 가져오기 - openSourceHint: 이 게임은 오픈 소스입니다! - discordLink: 공식 디스코드 서버 helpTranslate: 번역을 도와주세요! - browserWarning: - 이 게임은 현재 브라우저에서 느리게 작동하는 것으로 알려져 있습니다! 더 좋은 성능을 위해 정식 버전을 구매하거나 - Google Chrome 브라우저를 다운로드하세요. savegameLevel: 레벨 <x> savegameLevelUnknown: 미확인 레벨 continue: 계속하기 newGame: 새로하기 - madeBy: 제작 <author-link> - subreddit: Reddit savegameUnnamed: 이름 없음 - puzzleMode: 퍼즐 모드 - back: Back - puzzleDlcText: 공장의 크기를 줄이고 최적화하는 데 관심이 많으신가요? 지금 퍼즐 DLC를 구입하세요! - puzzleDlcWishlist: 지금 찜 목록에 추가하세요! - puzzleDlcViewNow: DLC 보러 가기 mods: - title: Active Mods warningPuzzleDLC: 퍼즐 DLC는 모드와 호환되지 않습니다. DLC 플레이 전 모든 모드를 비활성화 해주세요. - playingFullVersion: You are now playing the full version! - logout: Logout noActiveSavegames: No active savegames found - Click play to start a new game! - playFullVersionV2: Bough shapez on Steam? Play the full version in your Browser! - playFullVersionStandalone: You can now also play the full version in your Browser! dialogs: buttons: ok: 확인 @@ -105,7 +51,6 @@ dialogs: later: 나중에 restart: 다시 시작 reset: 리셋 - getStandalone: 유료 버전 구매하기 deleteGame: 확실합니다 viewUpdate: 업데이트 보기 showUpgrades: 업그레이드 보기 @@ -141,12 +86,6 @@ dialogs: keybindingsResetOk: title: 조작법 초기화 완료 desc: 모든 조작법이 기본값으로 초기화 되었습니다! - featureRestriction: - title: 체험판 버전 - desc: 체험판 버전에는 없는 콘텐츠 (<feature>)로 시도했습니다. 정식 버전을 구입해서 모든 콘텐츠를 사용해보세요! - oneSavegameLimit: - title: 세이브 파일 개수 제한 - desc: 체험판 버전에서는 세이브 파일을 한 번에 한 개만 사용할 수 있습니다. 이미 있는 세이브 파일을 지우거나 정식 버전을 구입 해주새요. updateSummary: title: 새로운 업데이트! desc: 지난번 플레이 이후 변경 사항은 다음과 같습니다. @@ -177,8 +116,6 @@ dialogs: titleEdit: 마커 변경 desc: 의미있는 이름을 정해주거나 <strong>단축키</strong>를 통해 도형을 직접 삽입할 수도 있습니다. (<link>여기</link>에서 만드실 수 있습니다). - markerDemoLimit: - desc: 체험판 버전에서는 마커를 2개 까지만 배치할 수 있습니다. 정식 버전을 구입하면 마커를 무제한으로 배치할 수 있습니다! exportScreenshotWarning: title: 스크린샷 내보내기 desc: @@ -268,28 +205,9 @@ dialogs: newMods: Newly installed Mods resourceLoadFailed: title: Resources failed to load - demoLinkText: shapez demo on Steam - descWeb: "One ore more resources could not be loaded. Make sure you have a - stable internet connection and try again. If this still doesn't - work, make sure to also disable any browser extensions (including - adblockers).<br><br> As an alternative, you can also play the - <demoOnSteamLinkText>. <br><br> Error Message:" descSteamDemo: "One ore more resources could not be loaded. Try restarting the game - If that does not help, try reinstalling and verifying the game files via Steam. <br><br> Error Message:" - steamSsoError: - title: Full Version Logout - desc: You have been logged out from the Full Browser Version since either your - network connection is unstable or you are playing on another - device.<br><br> Please make sure you don't have shapez open in any - other browser tab or another computer with the same Steam - account.<br><br> You can login again in the main menu. - steamSsoNoOwnership: - title: Full Edition not owned - desc: In order to play the Full Edition in your Browser, you need to own both - the base game and the Puzzle DLC on your Steam account.<br><br> - Please make sure you own both, signed in with the correct Steam - account and then try again. ingame: keybindingsOverlay: moveMap: 이동 @@ -425,40 +343,6 @@ ingame: one_miner: 추출기 1 개 n_miners: 추출기 <amount>개 limited_items: <max_throughput>까지가 한계임 - watermark: - title: 체험판 버전 - desc: 정식 버전의 장점을 보려면 여기를 클릭하세요! - get_on_steam: Steam으로 가기 - standaloneAdvantages: - no_thanks: 괜찮아요! - points: - levels: - title: 새로운 12 레벨 - desc: 최대 26 레벨로 확장됩니다! - buildings: - title: 새로운 18개의 건축물 - desc: 완벽한 자동화된 공장을 위한 건물들입니다! - markers: - title: 무한한 마커 배치 - desc: 넓은 공장에서 길을 잃지 마세요! - wires: - title: 전선 - desc: 완전히 새로운 차원의 도입! - darkmode: - title: 다크 모드 - desc: 당신의 눈을 피곤하게 만들지 마세요! - support: - title: 저를 지원해주세요 - desc: 저는 여가 시간에 게임을 개발합니다! - achievements: - title: 도전 과제 - desc: 모두 잡아 보세요! - mods: - title: Modding support! - desc: Over 80 mods available! - titleV2: "Get the full version now on Steam to unlock:" - titleExpiredV2: Demo completed! - titleEnjoyingDemo: Enjoy the demo? puzzleEditorSettings: zoneTitle: 구역 zoneWidth: 너비 @@ -542,7 +426,7 @@ buildings: description: 도형을 즉시 네 개로 자릅니다. <strong>한쪽만 사용할 경우라면 다른 부분을 파괴하지 않을 경우 절단기가 막혀 멈추게 됩니다!</strong> - rotater: + rotator: default: name: 회전기 description: 도형을 시계 방향으로 90도 회전시킵니다. @@ -668,7 +552,7 @@ buildings: default: name: 가상 절단기 description: 가상으로 도형을 잘라 반으로 나눕니다. - rotater: + rotator: name: 가상 회전기 description: 가상으로 도형을 시계 방향이나 반시계 방향으로 회전합니다. unstacker: @@ -704,7 +588,7 @@ storyRewards: <strong>반으로 나눕니다</strong>!<br><br> 쓰지 않는 도형은 쓰레기로 처리하세요, 그렇지 않으면 <strong>작동을 멈출 것입니다</strong>! 이러한 목적을 위해 <strong>휴지통</strong>도 함께 지급되었습니다. 휴지통에 들어간 것은 모두 파괴됩니다! - reward_rotater: + reward_rotator: title: 회전기 desc: <strong>회전기</strong>가 잠금 해제되었습니다! 회전기는 들어오는 도형을 시계 방향으로 90도 회전시켜줍니다. reward_painter: @@ -731,7 +615,7 @@ storyRewards: reward_tunnel: title: 터널 desc: <strong>터널</strong>이 잠금 해제되었습니다! 이제 벨트와 건물 아래로 공간을 만들어내 옮길 수 있습니다! - reward_rotater_ccw: + reward_rotator_ccw: title: 반시계 방향 회전기 desc: <strong>반시계 방향 회전기</strong>가 잠금 해제되었습니다! 반시계 방향 회전기는 회전기의 다른 형태로, 이름처럼 @@ -798,7 +682,7 @@ storyRewards: desc: <strong>벨트 판독기</strong>가 잠금 해제되었습니다! 이제 벨트의 처리량을 확인할 수 있습니다.<br><br>그리고, 전선이 잠금 해제될 때 까지 기다리신다면 정말 유용하게 사용할 수 있을 겁니다! - reward_rotater_180: + reward_rotator_180: title: 220도 회전기 desc: <strong>180도 회전기</strong>가 잠금 해제되었습니다! 이제 도형을 바로 180도로 회전시킬 수 있습니다. (짜잔! @@ -1029,7 +913,7 @@ keybindings: underground_belt: 터널 miner: 추출기 cutter: 절단기 - rotater: 회전기 + rotator: 회전기 stacker: 결합기 mixer: 혼합기 painter: 색칠기 @@ -1095,12 +979,6 @@ about: changelog: title: 업데이트 목록 demo: - features: - restoringGames: 게임 세이브 파일 복원 하기 - importingGames: 게임 세이브 파일 가져오기 - oneGameLimit: 게임 세이브 파일 최대 1개 - customizeKeybindings: 조작법 설정하기 - exportingBase: 공장 전체를 이미지로 내보내기 settingNotAvailable: 체험판 버전에서 사용 불가 tips: - 허브는 단지 필요로 하는 도형은 물론, 모든 형태의 도형 입력을 허용합니다! @@ -1243,15 +1121,5 @@ mods: version: 버전 modWebsite: 웹사이트 openFolder: 모드폴더 열기 - folderOnlyStandalone: 모드폴더를 여는것은 정식버전에서만 가능합니다. browseMods: 모드 찾기 modsInfo: 모드를 설치하기위해 모드폴더에 모드 파일을 넣으세요. 오른쪽 위에있는 '모드폴더 열기' 버튼으로 모드폴더를 열수 있습니다. - noModSupport: 모드를 사용하기 위해 정식버전이 필요합니다. - togglingComingSoon: - title: 곧 업데이트 됩니다! - description: - 지금은 mods/ 폴더에 모드 파일을 넣고 빼는 방식으로만 모드를 활성화/비활성화할 수 있습니다. 모드를 이곳에서 켜고 - 끌수 있는 기능은 추후 업데이트될 예정입니다. 모드폴더에 넣고/빼는것으로 할수있습니다. 모드를 토글하는 기능은 곧 업데이트 - 됩니다! - browserNoSupport: Due to browser restrictions it is currently only possible to - install mods in the Steam version - Sorry! diff --git a/translations/base-lt.yaml b/translations/base-lt.yaml index 0b557fde..22fe4658 100644 --- a/translations/base-lt.yaml +++ b/translations/base-lt.yaml @@ -1,26 +1,3 @@ -steamPage: - shortText: shapez.io yra gamyklų kūrimo žaidimas, kurio tikslas automatizuoti - vis sudėtingesnių formų gaminimą begaliniame žemėlapyje. - discordLinkShort: Nuoroda į oficialų Discord - intro: >- - Shapez.io tai raminantis žaidimas, kuriame jums reikia statyti gamyklas - geometrinių formų kūrimo automatizavimui. - - Sunkėjant lygiams, geometrinės formos tampa sudėtingesnės, todėl jums reikės išsiplėsti begaliniame žemėlapyje. - - Ir jei to negana, jūs taip pat turite pagaminti eksponentiškai daugiau, tam, kad patenkintumėte poreikius, todėl vienintelis dalykas, kuris padeda yra padidinti gamybą! - - Pradžioje jums reikės paruošti tik formas, tačiau vėliau jas taip pat reikės ir nudažyti, todėl pasiruoškite išgauti ir maišyti spalvas! - - Perkant žaidimą Steam platformoje gausite prieigą prie pilnos žaidimo versijos, bet pirma galite išbandyti demo versiją shapez.io svetainėje ir nuspręsti vėliau! - what_others_say: Ką kiti sako apie shapez.io - nothernlion_comment: Šis žaidimas yra puikus - puikiai praleidžiu laiką ir jis - labai greitai pabėga. - notch_comment: Po velnių. Tikrai turėčiau miegoti, bet manau, kad ką tik - supratau kaip susikurti kompiuterį šiame žaidime. - steam_review_comment: Šis žaidimas pavogė mano gyvenimą ir aš nenoriu jo - susigrąžinti. Labai šaunus gamyklinis žaidimas, kuris neleis man nustoti - tobulinti savo produkcijos linijas. global: loading: Kraunasi error: Klaida @@ -54,57 +31,19 @@ global: space: SPACE loggingIn: Prisijungiama loadingResources: Downloading additional resources (<percentage> %) - discount: -<percentage>% - discountSummerSale: SPECIAL PROMOTION! Offer ends 7 July -demoBanners: - title: Demo Versija - intro: |- - Įsigykite pilną žaidimo versiją <strong>dabar</strong> atrakinti:<ul> - <li>Visus 26 lygius + neribotą Freeplay rėžimą </li> - <li>22 nauji pastatai</li> - <li>Modifikacijų palaikymas</li> - <li>Pasiekimai</li> - <li>Dark Mode</li> - <li>... ir daug daugiau!</li> - </ul> - playtimeDisclaimer: Pilnoje žaidimo versijoje yra daugiau nei <strong>20 turinio - valandos</strong>. - playerCount: <playerCount> players like you are currently playing shapez on Steam - untilEndOfDemo: Until end of demo - playtimeDisclaimerDownload: Galite tęsti savo žaidimą pilnoje versijoje! - Spustelėkite <strong>čia</strong>, kad atsisiųstumėte išsaugotą žaidimą. - titleV2: "Žaisk pilną versiją dabar:" mainMenu: play: Pradėti - changelog: Pakeitimai importSavegame: Įkelti - openSourceHint: Šis žaidimas yra atvirojo kodo! - discordLink: Nuoroda į oficialų Discord serverį helpTranslate: Prisidėkite prie žaidimo vertimo! - browserWarning: Sorry, but the game is known to run slow on your browser! Get - the standalone version or download chrome for the full experience. savegameLevel: Level <x> savegameLevelUnknown: Unknown Level continue: Continue newGame: New Game - madeBy: Made by <author-link> - subreddit: Reddit savegameUnnamed: Unnamed - puzzleMode: Puzzle Mode - back: Back - puzzleDlcText: Do you enjoy compacting and optimizing factories? Get the Puzzle - DLC now on Steam for even more fun! - puzzleDlcWishlist: Wishlist now! - puzzleDlcViewNow: View Dlc mods: - title: Active Mods warningPuzzleDLC: Playing the Puzzle DLC is not possible with mods. Please disable all mods to play the DLC. - playingFullVersion: You are now playing the full version! - logout: Logout noActiveSavegames: No active savegames found - Click play to start a new game! - playFullVersionV2: Bough shapez on Steam? Play the full version in your Browser! - playFullVersionStandalone: You can now also play the full version in your Browser! dialogs: buttons: ok: OK @@ -113,7 +52,6 @@ dialogs: later: Later restart: Restart reset: Reset - getStandalone: Get Standalone deleteGame: I know what I do viewUpdate: View Update showUpgrades: Show Upgrades @@ -150,14 +88,6 @@ dialogs: keybindingsResetOk: title: Keybindings reset desc: The keybindings have been reset to their respective defaults! - featureRestriction: - title: Demo Version - desc: You tried to access a feature (<feature>) which is not available in the - demo. Consider to get the standalone for the full experience! - oneSavegameLimit: - title: Limited savegames - desc: You can only have one savegame at a time in the demo version. Please - remove the existing one or get the standalone! updateSummary: title: New update! desc: "Here are the changes since you last played:" @@ -187,9 +117,6 @@ dialogs: desc: Give it a meaningful name, you can also include a <strong>short key</strong> of a shape (Which you can generate <link>here</link>) titleEdit: Edit Marker - markerDemoLimit: - desc: You can only create two custom markers in the demo. Get the standalone for - unlimited markers! massCutConfirm: title: Confirm cut desc: You are cutting a lot of buildings (<count> to be exact)! Are you sure you @@ -290,28 +217,9 @@ dialogs: newMods: Newly installed Mods resourceLoadFailed: title: Resources failed to load - demoLinkText: shapez demo on Steam - descWeb: "One ore more resources could not be loaded. Make sure you have a - stable internet connection and try again. If this still doesn't - work, make sure to also disable any browser extensions (including - adblockers).<br><br> As an alternative, you can also play the - <demoOnSteamLinkText>. <br><br> Error Message:" descSteamDemo: "One ore more resources could not be loaded. Try restarting the game - If that does not help, try reinstalling and verifying the game files via Steam. <br><br> Error Message:" - steamSsoError: - title: Full Version Logout - desc: You have been logged out from the Full Browser Version since either your - network connection is unstable or you are playing on another - device.<br><br> Please make sure you don't have shapez open in any - other browser tab or another computer with the same Steam - account.<br><br> You can login again in the main menu. - steamSsoNoOwnership: - title: Full Edition not owned - desc: In order to play the Full Edition in your Browser, you need to own both - the base game and the Puzzle DLC on your Steam account.<br><br> - Please make sure you own both, signed in with the correct Steam - account and then try again. ingame: keybindingsOverlay: moveMap: Move @@ -450,40 +358,6 @@ ingame: one_miner: 1 Miner n_miners: <amount> Miners limited_items: Limited to <max_throughput> - watermark: - title: Demo version - desc: Click here to see the Steam version advantages! - get_on_steam: Get on steam - standaloneAdvantages: - no_thanks: No, thanks! - points: - levels: - title: 12 New Levels - desc: For a total of 26 levels! - buildings: - title: 22 New Buildings - desc: Fully automate your factory! - markers: - title: ∞ Markers - desc: Never get lost in your factory! - wires: - title: Wires - desc: An entirely new dimension! - darkmode: - title: Dark Mode - desc: Stop hurting your eyes! - support: - title: Support me - desc: I develop it in my spare time! - achievements: - title: Achievements - desc: Hunt them all! - mods: - title: Modding support! - desc: Over 80 mods available! - titleV2: "Get the full version now on Steam to unlock:" - titleExpiredV2: Demo completed! - titleEnjoyingDemo: Enjoy the demo? puzzleEditorSettings: zoneTitle: Zone zoneWidth: Width @@ -572,7 +446,7 @@ buildings: name: Cutter (Quad) description: Cuts shapes into four parts. <strong>If you use only one part, be sure to destroy the other parts or it will stall!</strong> - rotater: + rotator: default: name: Rotate description: Rotates shapes clockwise by 90 degrees. @@ -712,8 +586,8 @@ buildings: default: name: Virtual Cutter description: Virtually cuts the shape into two halves. - rotater: - name: Virtual Rotater + rotator: + name: Virtual Rotator description: Virtually rotates the shape, both clockwise and counter-clockwise. unstacker: name: Virtual Unstacker @@ -752,9 +626,9 @@ storyRewards: otherwise <strong>it will clog and stall</strong> - For this purpose I have given you the <strong>trash</strong>, which destroys everything you put into it! - reward_rotater: + reward_rotator: title: Rotating - desc: The <strong>rotater</strong> has been unlocked! It rotates shapes + desc: The <strong>rotator</strong> has been unlocked! It rotates shapes clockwise by 90 degrees. reward_painter: title: Painting @@ -781,10 +655,10 @@ storyRewards: title: Tunnel desc: The <strong>tunnel</strong> has been unlocked - You can now tunnel items through belts and buildings with it! - reward_rotater_ccw: + reward_rotator_ccw: title: CCW Rotating - desc: You have unlocked a variant of the <strong>rotater</strong> - It allows to - rotate counter clockwise! To build it, select the rotater and + desc: You have unlocked a variant of the <strong>rotator</strong> - It allows to + rotate counter clockwise! To build it, select the rotator and <strong>press 'T' to cycle its variants</strong>! reward_miner_chainable: title: Chaining Extractor @@ -850,9 +724,9 @@ storyRewards: desc: You have now unlocked the <strong>belt reader</strong>! It allows you to measure the throughput of a belt.<br><br>And wait until you unlock wires - then it gets really useful! - reward_rotater_180: - title: Rotater (180 degrees) - desc: You just unlocked the 180 degrees <strong>rotater</strong>! - It allows + reward_rotator_180: + title: Rotator (180 degrees) + desc: You just unlocked the 180 degrees <strong>rotator</strong>! - It allows you to rotate a shape by 180 degrees (Surprise! :D) reward_display: title: Display @@ -877,7 +751,7 @@ storyRewards: title: Virtual Processing desc: I just gave a whole bunch of new buildings which allow you to <strong>simulate the processing of shapes</strong>!<br><br> You can - now simulate a cutter, rotater, stacker and more on the wires layer! + now simulate a cutter, rotator, stacker and more on the wires layer! With this you now have three options to continue the game:<br><br> - Build an <strong>automated machine</strong> to create any possible shape requested by the HUB (I recommend to try it!).<br><br> - Build @@ -1105,7 +979,7 @@ keybindings: underground_belt: Tunnel miner: Extractor cutter: Cutter - rotater: Rotate + rotator: Rotate stacker: Stacker mixer: Color Mixer painter: Painter @@ -1172,12 +1046,6 @@ about: changelog: title: Changelog demo: - features: - restoringGames: Restoring savegames - importingGames: Importing savegames - oneGameLimit: Limited to one savegame - customizeKeybindings: Customizing Keybindings - exportingBase: Exporting whole Base as Image settingNotAvailable: Not available in the demo. tips: - The hub accepts input of any kind, not just the current shape! @@ -1331,16 +1199,7 @@ mods: version: Version modWebsite: Website openFolder: Open Mods Folder - folderOnlyStandalone: Opening the mod folder is only possible when running the standalone. browseMods: Browse Mods modsInfo: To install and manage mods, copy them to the mods folder within the game directory. You can also use the 'Open Mods Folder' button on the top right. - noModSupport: You need the standalone version on Steam to install mods. - togglingComingSoon: - title: Coming Soon - description: Enabling or disabling mods is currently only possible by copying - the mod file from or to the mods/ folder. However, being able to - toggle them here is planned for a future update! - browserNoSupport: Due to browser restrictions it is currently only possible to - install mods in the Steam version - Sorry! diff --git a/translations/base-nl.yaml b/translations/base-nl.yaml index 9ce9173f..60e7be0e 100644 --- a/translations/base-nl.yaml +++ b/translations/base-nl.yaml @@ -1,27 +1,3 @@ -steamPage: - shortText: shapez.io is een spel dat draait om het bouwen van fabrieken voor het - produceren en automatiseren van steeds complexere vormen in een oneindig - grote wereld. - discordLinkShort: Officiële Discord server - intro: >- - Shapez.io is een spel waarin je fabrieken bouwt voor de automatische - productie van geometrische vormen. - - Naarmate het spel vordert, worden de vormen complexer, en zul je moeten uitbreiden in de oneindige wereld. - - En als dat nog niet genoeg is produceer je ook nog steeds meer om aan de vraag te kunnen voldoen. Het enige dat helpt is uitbreiden! - - Ondanks het feit dat je in het begin alleen vormen maakt, komt er een punt waarop je ze gaat kleuren. Deze kleuren kun je vinden en mengen! - - Door het spel op Steam te kopen kun je de volledige versie spelen. Je kunt echter ook een demo versie spelen op shapez.io en later beslissen om over te schakelen zonder voortgang te verliezen. - what_others_say: Wat anderen vinden van shapez.io - nothernlion_comment: This game is great - I'm having a wonderful time playing, - and time has flown by. - notch_comment: Oh crap. I really should sleep, but I think I just figured out - how to make a computer in shapez.io - steam_review_comment: This game has stolen my life and I don't want it back. - Very chill factory game that won't let me stop making my lines more - efficient. global: loading: Laden error: Fout @@ -55,57 +31,19 @@ global: space: SPATIE loggingIn: Inloggen loadingResources: Downloading additional resources (<percentage> %) - discount: -<percentage>% - discountSummerSale: SPECIAL PROMOTION! Offer ends 7 July -demoBanners: - title: Demoversie - intro: |- - Haal het volledige spel <strong>nu</strong> om het vrij te spelen:<ul> - <li>Alle 26 levels + oneindige Freeplay</li> - <li>22 nieuwe gebouwen</li> - <li>Mod ondersteuning</li> - <li>Prestaties</li> - <li>Donkere modus</li> - <li>... en nog veel meer!</li> - </ul> - playtimeDisclaimer: De volledige versie bevat meer dan <strong>20 uur aan inhoud</strong>. - playerCount: <playerCount> spelers zoals jij spelen op dit moment shapez op Steam - untilEndOfDemo: Tot het einde van de demo - playtimeDisclaimerDownload: Je kunt doorgaan met je savegame in de volledige - versie! Klik <strong>hier</strong> om je savegame te downloaden. - titleV2: "Speel de volledige versie nu voor:" mainMenu: play: Spelen - changelog: Wijzigingenlijst importSavegame: Importeren - openSourceHint: Dit spel is open source! - discordLink: Officiële Discord-server (Engelstalig) helpTranslate: Help ons met vertalen! - browserWarning: Sorry, maar dit spel draait langzaam in je huidige browser! Koop - de standalone versie of download Google Chrome voor de volledige - ervaring. savegameLevel: Level <x> savegameLevelUnknown: Level onbekend continue: Ga verder newGame: Nieuw Spel - madeBy: Gemaakt door <author-link> - subreddit: Reddit savegameUnnamed: Naamloos - puzzleMode: Puzzel Modus - back: Terug - puzzleDlcText: Houd je van het comprimeren en optimaliseren van fabrieken? - Verkrijg de puzzel DLC nu op Steam voor nog meer plezier! - puzzleDlcWishlist: Voeg nu toe aan je verlanglijst! - puzzleDlcViewNow: Bekijk DLC mods: - title: Actieve Mods warningPuzzleDLC: Het spelen van de Puzzle DLC is niet mogelijk met mods. Schakel alsjeblieft alle mods uit om de DLC te spelen. - playingFullVersion: You are now playing the full version! - logout: Logout noActiveSavegames: No active savegames found - Click play to start a new game! - playFullVersionV2: Bough shapez on Steam? Play the full version in your Browser! - playFullVersionStandalone: You can now also play the full version in your Browser! dialogs: buttons: ok: OK @@ -114,7 +52,6 @@ dialogs: later: Later restart: Herstart reset: Reset - getStandalone: Koop de Standalone deleteGame: Ik weet wat ik doe viewUpdate: Zie Update showUpgrades: Zie Upgrades @@ -153,15 +90,6 @@ dialogs: keybindingsResetOk: title: Sneltoetsen gereset desc: De sneltoetsen zijn gereset naar hun originele instellingen! - featureRestriction: - title: Demoversie - desc: Je probeerde een functie te gebruiken (<feature>) die niet beschikbaar is - in de demo. Overweeg om de standalone te kopen voor de volledige - ervaring! - oneSavegameLimit: - title: Gelimiteerd aantal savegames - desc: Je kunt maar één savegame tegelijk hebben in de demoversie. Verwijder de - bestaande savegame of koop de standalone! updateSummary: title: Nieuwe update! desc: "Dit zijn de veranderingen sinds je voor het laatst gespeeld hebt:" @@ -193,9 +121,6 @@ dialogs: desc: Geef het een nuttige naam, Je kan ook een <strong>sleutel</strong> van een vorm gebruiken (die je <link>hier</link> kunt genereren). titleEdit: Bewerk markering - markerDemoLimit: - desc: Je kunt maar twee markeringen plaatsen in de demo. Koop de standalone voor - een ongelimiteerde hoeveelheid markeringen! massCutConfirm: title: Bevestig knippen desc: Je bent veel vormen aan het knippen (<count> om precies te zijn)! Weet je @@ -300,28 +225,9 @@ dialogs: newMods: Nieuw geïnstalleerde Mods resourceLoadFailed: title: Resources failed to load - demoLinkText: shapez demo on Steam - descWeb: "One ore more resources could not be loaded. Make sure you have a - stable internet connection and try again. If this still doesn't - work, make sure to also disable any browser extensions (including - adblockers).<br><br> As an alternative, you can also play the - <demoOnSteamLinkText>. <br><br> Error Message:" descSteamDemo: "One ore more resources could not be loaded. Try restarting the game - If that does not help, try reinstalling and verifying the game files via Steam. <br><br> Error Message:" - steamSsoError: - title: Full Version Logout - desc: You have been logged out from the Full Browser Version since either your - network connection is unstable or you are playing on another - device.<br><br> Please make sure you don't have shapez open in any - other browser tab or another computer with the same Steam - account.<br><br> You can login again in the main menu. - steamSsoNoOwnership: - title: Full Edition not owned - desc: In order to play the Full Edition in your Browser, you need to own both - the base game and the Puzzle DLC on your Steam account.<br><br> - Please make sure you own both, signed in with the correct Steam - account and then try again. ingame: keybindingsOverlay: moveMap: Beweeg rond de wereld @@ -463,40 +369,6 @@ ingame: one_miner: 1 Ontginner n_miners: <amount> Ontginners limited_items: "Gelimiteerd tot: <max_throughput>" - watermark: - title: Demo versie - desc: Klik hier om het spel op Steam te kopen! - get_on_steam: Krijg het op Steam - standaloneAdvantages: - no_thanks: Nee, dankjewel! - points: - levels: - title: 12 Nieuwe Levels - desc: Voor een totaal van 26 levels! - buildings: - title: 22 Nieuwe Gebouwen - desc: Automatiseer je fabrieken nog beter en maak ze nog sneller! - markers: - title: ∞ Markeringen - desc: Verdwaal nooit meer in je fabriek! - wires: - title: Kabels - desc: Een volledig nieuwe laag! - darkmode: - title: Dark Mode - desc: Minder vervelend voor je ogen! - support: - title: Help mij - desc: Ik maak dit spel in mijn vrije tijd! - achievements: - title: Achievements - desc: Krijg ze allemaal! - mods: - title: Modding support! - desc: Over 80 mods available! - titleV2: "Get the full version now on Steam to unlock:" - titleExpiredV2: Demo completed! - titleEnjoyingDemo: Enjoy the demo? puzzleEditorSettings: zoneTitle: Gebied zoneWidth: Breedte @@ -586,7 +458,7 @@ buildings: description: Knipt vormen in vier delen. <strong>Als je maar één deel gebruikt, zorg dat je de andere delen vernietigt, anders loopt de machine vast!</strong> - rotater: + rotator: default: name: Roteerder description: Draait vormen 90 graden met de klok mee. @@ -727,7 +599,7 @@ buildings: default: name: Virtuele Knipper description: Knipt de vorm virtueel in twee helften. - rotater: + rotator: name: Virtuele Draaier description: Draait de vorm virtueel met de klok mee en tegen de klok in. unstacker: @@ -767,7 +639,7 @@ storyRewards: <strong>zal het vastlopen</strong> - Daarom heb ik je de <strong>vuilnisbak</strong> gegeven, die alles vernietigt wat je erin laat stromen! - reward_rotater: + reward_rotator: title: Roteren desc: De <strong>roteerder</strong> is ontgrendeld - Het draait vormen 90 graden met de klok mee. @@ -797,7 +669,7 @@ storyRewards: title: Tunnel desc: De <strong>tunnel</strong> is ontgrendeld - Je kunt nu voorwerpen onder gebouwen en lopende banden door laten lopen. - reward_rotater_ccw: + reward_rotator_ccw: title: Roteren (andersom) desc: Je hebt een variant van de <strong>roteerder</strong> ontgrendeld - Deze roteert voorwerpen tegen de klok in! Om hem te plaatsen selecteer je @@ -872,7 +744,7 @@ storyRewards: desc: Je hebt de <strong>lopende band sensor</strong> vrijgespeeld! Dit gebouw geeft de doorvoer op een lopende band weer.<br><br>Wacht maar tot je kabels vrijspeeld, dan wordt het pas echt interessant! - reward_rotater_180: + reward_rotator_180: title: Draaier (180 graden) desc: Je hebt de <strong>180 graden draaier</strong> vrijgespeeld! - Hiermee kun je een item op de lopende band 180 graden draaien! @@ -1134,7 +1006,7 @@ keybindings: underground_belt: Tunnel miner: Ontginner cutter: Knipper - rotater: Roteerder + rotator: Roteerder stacker: Stapelaar mixer: Kleurenmenger painter: Verver @@ -1201,12 +1073,6 @@ about: changelog: title: Changelog demo: - features: - restoringGames: Savegames terughalen - importingGames: Savegames importeren - oneGameLimit: Gelimiteerd tot één savegame - customizeKeybindings: Aangepaste sneltoetsen - exportingBase: Exporteer volledige basis als afbeelding settingNotAvailable: Niet beschikbaar in de demo. tips: - De HUB accepteert elke vorm van invoer, niet alleen de huidige vorm! @@ -1374,18 +1240,7 @@ mods: version: Versie modWebsite: Website openFolder: Open Mods Map - folderOnlyStandalone: Het openen van de mod map is alleen mogelijk bij het - gebruiken van de zelfstandige versie. browseMods: Door mods bladeren modsInfo: Om mods te installeren en te beheren, kopieert u ze naar de map mods in de spel map. U kunt ook de knop 'Open Mods Map' gebruiken rechtsboven. - noModSupport: Je hebt de zelfstandige versie op Steam nodig om mods te installeren. - togglingComingSoon: - title: Binnenkort Beschikbaar - description: Mods in- of uitschakelen is momenteel alleen mogelijk door het - mod-bestand van of naar de mods map te kopiëren. Echter, in staat - zijn om ze hier in- of uitteschakelen is gepland voor een - toekomstige update! - browserNoSupport: Due to browser restrictions it is currently only possible to - install mods in the Steam version - Sorry! diff --git a/translations/base-no.yaml b/translations/base-no.yaml index 8576ccf0..9104ec5b 100644 --- a/translations/base-no.yaml +++ b/translations/base-no.yaml @@ -1,27 +1,3 @@ -steamPage: - shortText: shapez.io er et spill som handler om å bygge fabrikker for å - automatisere produksjon og kombinasjon av former med økende kompleksitet - på et uendelig ekspanderende brett. - discordLinkShort: Offissiell Discord - intro: >- - Shapez.io er et avslappende spill der du skal bygge fabrikker - for automatisert produksjon av geometriske former. - - Etterhvert som du når høyere nivåer blir formene mer og mer komplekse, og du må spre deg ut over det uendelige kartet. - - Og som om det ikke var nok må du også produsere eksponensielt for å tilfredsstille etterspørselen - det eneste som hjelper er skalering! - - Mens du kun produserer former i starten må du fargelegge de senere - for å gjøre dette må du hente ut og blande farger! - - Ved å kjøpe spillet på Steam får du tilgang til fullversjonen, men du kan også spille en demo på shapez.io først og bestemme deg senere! - what_others_say: Hva sier andre om shapez.io - nothernlion_comment: Dette er et fantastisk spill - Jeg storkoser meg når jeg - spiller det, og tiden bare flyr avgårde. - notch_comment: Oops. Jeg burde egentlig sove, men jeg tror jeg nettop fant ut - hvordan man lager en PC i shapez.io - steam_review_comment: Dette spillet har sjålet livet mitt, og jeg vil ikke ha - det tilbake. Veldig avslappende fabrikkspill som ikke stopper meg fra å - lage båndene mere effektive. global: loading: Laster error: Feil @@ -55,57 +31,19 @@ global: space: MELLOMROM loggingIn: Logger inn loadingResources: Laster ned tilleggsressurser (<percentage> %) - discount: -<percentage>% - discountSummerSale: SPECIAL PROMOTION! Offer ends 7 July -demoBanners: - title: Demo Versjon - intro: |- - Få hele spillet <strong>nå</strong> for å låse opp:<ul> - <li>Alle 26 nivåer + uendelig Freeplay</li> - <li>22 nye bygg</li> - <li>Mod støtte</li> - <li>Prestasjoner</li> - <li>Mørk modus</li> - <li>... og mye mer!</li> - </ul> - playtimeDisclaimer: Fullversjonen inneholder mer enn <strong>20 timer med innhold</strong>. - playerCount: <playerCount> spillere som deg spiller for øyeblikket shapez på Steam - untilEndOfDemo: Helt til slutten av demoen - playtimeDisclaimerDownload: Du kan fortsette lagringsspillet i fullversjonen! - Klikk <strong>her</strong> for å laste ned savegame. - titleV2: "Spill fullversjonen nå for:" mainMenu: play: Spill - changelog: Endringshistorikk importSavegame: Importer - openSourceHint: Dette spillet er åpen kildekode! - discordLink: Offisiel Discord-Server helpTranslate: Hjelp oversetting! - browserWarning: Beklager, men spillet er kjent for å kjøre sakte i din - nettleser! Skaff deg den frittstående versjonen, eller last ned Chrome - for den fulle opplevelsen. savegameLevel: Nivå <x> savegameLevelUnknown: Ukjent Nivå continue: Fortsett newGame: Nytt Spill - madeBy: Laget av <author-link> - subreddit: Reddit savegameUnnamed: Ingen Navn - puzzleMode: Puzzle Modus - back: Tilbake - puzzleDlcText: Liker du å bygge kompate og optimaliserte fabrikker? Skaff deg - Puzzle tilleggspakken nå på Steam for mere moro! - puzzleDlcWishlist: Legg i ønskeliste nå! - puzzleDlcViewNow: View Dlc mods: - title: Active Mods warningPuzzleDLC: Playing the Puzzle DLC is not possible with mods. Please disable all mods to play the DLC. - playingFullVersion: You are now playing the full version! - logout: Logout noActiveSavegames: No active savegames found - Click play to start a new game! - playFullVersionV2: Bough shapez on Steam? Play the full version in your Browser! - playFullVersionStandalone: You can now also play the full version in your Browser! dialogs: buttons: ok: OK @@ -114,7 +52,6 @@ dialogs: later: Senere restart: Omstart reset: Nullstill - getStandalone: Få frittstående deleteGame: Jeg vet hva jeg gjør viewUpdate: Vis Oppdatering showUpgrades: Vis Oppgraderinger @@ -151,15 +88,6 @@ dialogs: keybindingsResetOk: title: Hurtigtaster tilbakestillt desc: Hurtigtastene har blitt tilbakestillt til standard! - featureRestriction: - title: Demoversjon - desc: Du prøvde å benytte deg av en funksjon (<feature>) som ikke er - tilgjengelig i demoen. Vurder å skaffe den frittstående versjonen - for den fulle opplevelsen! - oneSavegameLimit: - title: Begrenset antall Lagringsfiler - desc: Du kan du ha en lagringsfil om gangen i demoversjonen. Vennligst slett den - eksisterende, eller skaff den frittstående versjonen! updateSummary: title: Ny oppdatering!! desc: "Her er endringene siden sist du spilte:" @@ -193,9 +121,6 @@ dialogs: desc: Gi den et meningsfult navn, du kan også inkludere <strong>korte koder</strong> av en form (Som du kan generere <link>her</link>) titleEdit: Rediger markør - markerDemoLimit: - desc: Du kan kun ha to markører i demoverjsonen. Skaff deg den frittstående - versjonen for ubegrensede markører! exportScreenshotWarning: title: Eksporter skjermbilde desc: Du forespurte å eksportere bilde av basen din som et skjermbilde. Vær obs @@ -295,28 +220,9 @@ dialogs: newMods: Newly installed Mods resourceLoadFailed: title: Resources failed to load - demoLinkText: shapez demo on Steam - descWeb: "One ore more resources could not be loaded. Make sure you have a - stable internet connection and try again. If this still doesn't - work, make sure to also disable any browser extensions (including - adblockers).<br><br> As an alternative, you can also play the - <demoOnSteamLinkText>. <br><br> Error Message:" descSteamDemo: "One ore more resources could not be loaded. Try restarting the game - If that does not help, try reinstalling and verifying the game files via Steam. <br><br> Error Message:" - steamSsoError: - title: Full Version Logout - desc: You have been logged out from the Full Browser Version since either your - network connection is unstable or you are playing on another - device.<br><br> Please make sure you don't have shapez open in any - other browser tab or another computer with the same Steam - account.<br><br> You can login again in the main menu. - steamSsoNoOwnership: - title: Full Edition not owned - desc: In order to play the Full Edition in your Browser, you need to own both - the base game and the Puzzle DLC on your Steam account.<br><br> - Please make sure you own both, signed in with the correct Steam - account and then try again. ingame: keybindingsOverlay: moveMap: Beveg @@ -455,40 +361,6 @@ ingame: one_miner: 1 Utgraver n_miners: <amount> Utgravere limited_items: Begrenset til <max_throughput> - watermark: - title: Demo versjon - desc: Trykk her for å se fordelene med Steam verjsonen! - get_on_steam: Få på Steam - standaloneAdvantages: - no_thanks: Nei takk! - points: - levels: - title: 12 Nye Nivåer - desc: Tilsvarer totalt 26 nivåer! - buildings: - title: 22 Nye Bygninger - desc: Fullautomatiser din fabrikk! - markers: - title: ∞ Markører - desc: Aldri gå deg vill i din fabrikk! - wires: - title: Kabler - desc: En helt ny dimensjon! - darkmode: - title: Mørkmodus - desc: Slutt å skade øynene dine! - support: - title: Støtt meg - desc: Jeg utvikler dette på min fritid! - achievements: - title: Prestasjoner - desc: Skaff dem alle! - mods: - title: Modding support! - desc: Over 80 mods available! - titleV2: "Get the full version now on Steam to unlock:" - titleExpiredV2: Demo completed! - titleEnjoyingDemo: Enjoy the demo? puzzleEditorSettings: zoneTitle: Sone zoneWidth: Bredde @@ -582,7 +454,7 @@ buildings: description: Kutter objekter til 4 biter. <strong>Hvis du bare skal bruke den ene biten, sørg for å ødelegge den andre biten ellers vil det stoppe opp!</strong> - rotater: + rotator: default: name: Roter description: Roter former med klokken, 90 grader. @@ -723,7 +595,7 @@ buildings: default: name: Virituell Kutter description: Kutt former virituelt i to deler. - rotater: + rotator: name: Virituell Roterer description: Virituelt roterer formen, både med klokken og mot klokken. unstacker: @@ -763,7 +635,7 @@ storyRewards: <strong>vil det samle seg og tette</strong> - For dette formålet så har jeg gitt deg en <strong>søppelkasse</strong>, som ødelegger alt du kaster i den! - reward_rotater: + reward_rotator: title: Rotering desc: <strong>Rotereren</strong> har blitt tilgjengelig! Den roterer objekter med klokken 90 grader. @@ -792,7 +664,7 @@ storyRewards: title: Tunnel desc: <strong>Tunnelen</strong> har blitt tilgjengelig - Du kan nå transportere objekter under samlebånd og bygninger med den! - reward_rotater_ccw: + reward_rotator_ccw: title: Rotering mot klokken desc: Du har åpnte en variant av <strong>rotereren</strong> - Den tillater rotasjoner mot klokken! For å bygge den, velg rotereren og @@ -864,7 +736,7 @@ storyRewards: desc: Du har låst opp <strong>belte leseren</strong>! Den lar deg måle trafikken på et belte.<br><br>Og vent til du låser opp kabler - da blir den veldig nyttig! - reward_rotater_180: + reward_rotator_180: title: Roterer (180 grader) desc: Du åpnet opp 180 graders <strong>rotereren</strong>! - Den lar deg rotere en form 180 grader (Overraskelse! :D) @@ -1125,7 +997,7 @@ keybindings: underground_belt: Tunnel miner: Utdrager cutter: Kutter - rotater: Roter + rotator: Roter stacker: Stabler mixer: Fargemikser painter: Maler @@ -1190,12 +1062,6 @@ about: changelog: title: Endringshistorikk demo: - features: - restoringGames: Gjenopprette lagringsfiler - importingGames: Importer lagringsfiler - oneGameLimit: Begrenset til en lagringsfil - customizeKeybindings: Forandre Hurtigtaster - exportingBase: Eksporter hele basen som bilde settingNotAvailable: Ikke tilgjengelig i demoversjonen. tips: - Hovedbygningen godtar alle mulige former, ikke kun nåværende form! @@ -1354,16 +1220,7 @@ mods: version: Version modWebsite: Website openFolder: Open Mods Folder - folderOnlyStandalone: Opening the mod folder is only possible when running the standalone. browseMods: Browse Mods modsInfo: To install and manage mods, copy them to the mods folder within the game directory. You can also use the 'Open Mods Folder' button on the top right. - noModSupport: You need the standalone version on Steam to install mods. - togglingComingSoon: - title: Coming Soon - description: Enabling or disabling mods is currently only possible by copying - the mod file from or to the mods/ folder. However, being able to - toggle them here is planned for a future update! - browserNoSupport: Due to browser restrictions it is currently only possible to - install mods in the Steam version - Sorry! diff --git a/translations/base-pl.yaml b/translations/base-pl.yaml index 5ca595d7..f7c46565 100644 --- a/translations/base-pl.yaml +++ b/translations/base-pl.yaml @@ -1,27 +1,3 @@ -steamPage: - shortText: shapez.io to gra polegająca na budowaniu fabryki automatyzującej - tworzenie i łączenie ze sobą coraz bardziej skomplikowanych kształtów na - mapie, która nie ma końca. - discordLinkShort: Oficjalny serwer Discord - intro: >- - Shapez.io jest spokojną grą, której celem jest budowanie automatycznych - fabryk produkujących różne kształty geometryczne. - - W miarę zwiększania się poziomów, kształty będą stawać się coraz bardziej skomplikowane, a Twoja fabryka będzie musiała się rozprzestrzenić na mapie o nieskończonej wielkości. - - A jeżeli to było mało, będziesz również musiał produkować coraz więcej kształtów, by zaspokoić wymagania - jedynym rozwiązaniem jest skalowanie fabryki! - - Początkowo przekształcanie kształtów będzie proste, ale później będziesz również musiał je malować - wymaga to wydobywania i łączenia barwników! - - Kupienie gry w serwisie Steam przyznaje Ci dostęp do pełnej wersji, ale możesz również skorzystać z wersji demonstracyjnej na stronie shapez.io i rozważyć zakup później! - what_others_say: Co inni ludzie mówią o shapez.io - nothernlion_comment: Ta gra jest świetna - świetnie się bawię grając, a czas - minął bardzo szybko. - notch_comment: O kurcze. Naprawdę powinienem spać, ale chyba właśnie się - wymyśliłem jak stworzyć komputer w shapez.io - steam_review_comment: Ta gra skradła mi życie i nie chce go z powrotem! Bardzo - luźna gra o fabrykach która nie pozwoli mi robić moich lini bardziej - wydajnych. global: loading: Ładowanie error: Wystąpił błąd @@ -55,56 +31,19 @@ global: space: SPACJA loggingIn: Logowanie loadingResources: Pobieranie dodatkowych zasobów (<percentage> %) - discount: -<percentage>% - discountSummerSale: SPECIAL PROMOTION! Offer ends 7 July -demoBanners: - title: Wersja demonstracyjna - intro: |- - Pobierz pełną wersję gry <strong>teraz</strong>, aby ją odblokować:<ul> - <li>Wszystkie 26 poziomów + nieskończona darmowa rozgrywka</li> - <li>22 nowe budynki</li> - <li>Obsługa modów</li> - <li>Osiągnięcia</li> - <li>Tryb ciemny</li> - <li>... i wiele innych!</li> - </ul> - playtimeDisclaimer: Pełna wersja zawiera ponad <strong>20 godziny treści</strong>. - playerCount: <playerCount> gracze tacy jak Ty grają obecnie w shapez na Steamie - untilEndOfDemo: Do końca demonstracji - playtimeDisclaimerDownload: Możesz kontynuować zapis stanu gry w pełnej wersji! - Kliknij <strong>tutaj</strong>, aby pobrać zapisaną grę. - titleV2: "Zagraj teraz w pełną wersję gry:" mainMenu: play: Rozpocznij continue: Kontynuuj newGame: Nowa gra - changelog: Dziennik Zmian importSavegame: Importuj - openSourceHint: Gra z otwartym kodem źródłowym - discordLink: Oficjalny serwer Discord helpTranslate: Pomóż w tłumaczeniu - browserWarning: Przepraszamy, lecz ta gra może działać wolno w Twojej - przeglądarce! Kup pełną wersję gry lub pobierz przeglądarkę chrome. savegameLevel: Poziom <x> savegameLevelUnknown: Nieznany poziom - madeBy: Gra wykonana przez <author-link> - subreddit: Reddit savegameUnnamed: Zapis bez nazwy - puzzleMode: Tryb Układanek - back: Wróć - puzzleDlcText: Czy lubisz kompaktować i optymalizować fabryki? Zainstaluj DLC - układanek na Steamie, aby mieć jeszcze więcej zabawy! - puzzleDlcWishlist: Ustaw jako wyczekiwane! - puzzleDlcViewNow: Zobacz Dlc mods: - title: Aktywne Mody warningPuzzleDLC: Granie w DLC Układanek nie jest możliwe z modami. Proszę wyłącz wszystkie mody, aby otworzyć DLC. - playingFullVersion: You are now playing the full version! - logout: Logout noActiveSavegames: No active savegames found - Click play to start a new game! - playFullVersionV2: Bough shapez on Steam? Play the full version in your Browser! - playFullVersionStandalone: You can now also play the full version in your Browser! dialogs: buttons: ok: Ok @@ -113,7 +52,6 @@ dialogs: later: Później restart: Restart reset: Reset - getStandalone: Kup grę deleteGame: Wiem, co robię viewUpdate: Zobacz aktualizację showUpgrades: Pokaż ulepszenia @@ -152,14 +90,6 @@ dialogs: keybindingsResetOk: title: Reset Klawiszologii desc: Klawiszologia została przywrócona do ustawień domyślnych! - featureRestriction: - title: Wersja Demonstracyjna - desc: Próbujesz skorzystać z "<feature>", który nie jest dostępny w wersji demo. - Rozważ zakup pełnej wersji gry dla pełni doświadczeń! - oneSavegameLimit: - title: Limit Zapisów Gry - desc: W wersji demo możesz posiadać wyłącznie jeden zapis gry. Proszę usuń - obecny lub zakup pełną wersję gry! updateSummary: title: Nowa aktualizacja! desc: "Lista zmian od Twojej ostatniej rozgrywki:" @@ -191,9 +121,6 @@ dialogs: desc: Nadaj mu nazwę. Możesz w niej zawrzeć <strong>kod</strong> kształtu (Który możesz wygenerować <link>tutaj</link>) titleEdit: Edytuj Znacznik - markerDemoLimit: - desc: Możesz stworzyć tylko dwa własne znaczniki w wersji demo. Zakup pełną - wersję gry dla nielimitowanych znaczników! massCutConfirm: title: Potwierdź wycinanie desc: Wycinasz sporą ilość maszyn (<count> w roli ścisłości)! Czy na pewno @@ -297,28 +224,9 @@ dialogs: newMods: Ostatnio zainstalowane Mody resourceLoadFailed: title: Resources failed to load - demoLinkText: shapez demo on Steam - descWeb: "One ore more resources could not be loaded. Make sure you have a - stable internet connection and try again. If this still doesn't - work, make sure to also disable any browser extensions (including - adblockers).<br><br> As an alternative, you can also play the - <demoOnSteamLinkText>. <br><br> Error Message:" descSteamDemo: "One ore more resources could not be loaded. Try restarting the game - If that does not help, try reinstalling and verifying the game files via Steam. <br><br> Error Message:" - steamSsoError: - title: Full Version Logout - desc: You have been logged out from the Full Browser Version since either your - network connection is unstable or you are playing on another - device.<br><br> Please make sure you don't have shapez open in any - other browser tab or another computer with the same Steam - account.<br><br> You can login again in the main menu. - steamSsoNoOwnership: - title: Full Edition not owned - desc: In order to play the Full Edition in your Browser, you need to own both - the base game and the Puzzle DLC on your Steam account.<br><br> - Please make sure you own both, signed in with the correct Steam - account and then try again. ingame: keybindingsOverlay: moveMap: Ruch @@ -458,40 +366,6 @@ ingame: one_miner: 1 ekstraktor n_miners: <amount> ekstraktorów limited_items: Ograniczone do <max_throughput> - watermark: - title: Wersja demo - desc: Kliknij tutaj, aby zobaczyć co potrafi wersja Steam! - get_on_steam: Kup na Steam - standaloneAdvantages: - no_thanks: Nie, dziękuję! - points: - levels: - title: 12 Nowych Poziomów - desc: Aby otrzymać 26 poziomów! - buildings: - title: 22 Nowych Budynków - desc: W pełni zautomatyzuj produkcję! - markers: - title: ∞ Znaczników - desc: Nigdy nie zgub się w swojej fabryce! - wires: - title: Przewody - desc: Całkowicie nowy wymiar! - darkmode: - title: Tryb Ciemny - desc: Przestań psuć swój wzrok! - support: - title: Wspomóż mnie - desc: Tworzę tą grę w swoim wolnym czasie! - achievements: - title: Osiągnięcia - desc: Zbierz je wszystkie! - mods: - title: Modding support! - desc: Over 80 mods available! - titleV2: "Get the full version now on Steam to unlock:" - titleExpiredV2: Demo completed! - titleEnjoyingDemo: Enjoy the demo? puzzleEditorSettings: zoneTitle: Obszar zoneWidth: Wysokość @@ -584,7 +458,7 @@ buildings: description: Tnie kształty na cztery ćwiartki. <strong>Jeśli nie korzystasz z wszystkich ćwiartek, upewnij się, że niszczysz pozostałe, by nie zatkać budynku!</strong> - rotater: + rotator: default: name: Obracacz description: Obraca kształt zgodnie z ruchem wskazówek zegara o 90 stopni. @@ -732,7 +606,7 @@ buildings: default: name: Wirtualny Przecinak description: Wirtualnie przecina kształt na 2 połówki - rotater: + rotator: name: Wirtualny Obracacz description: Wirtualnie obraca kształt, potrafi to robić w oba kierunki. unstacker: @@ -772,7 +646,7 @@ storyRewards: przeciwnym przypadku <strong>maszyna zapcha się i przestanie działać!</strong> Do tego celu dałem ci <strong>śmietnik</strong>, który usuwa wszystko, co do niego włożysz! - reward_rotater: + reward_rotator: title: Obracanie desc: "Odblokowano nową maszynę: <strong>Obracacz</strong>! Obraca wejście o 90 stopni zgodnie ze wskazówkami zegara." @@ -802,7 +676,7 @@ storyRewards: title: Tunel desc: <strong>Tunel</strong> został odblokowany - Możesz teraz prowadzić podziemne taśmociągi! - reward_rotater_ccw: + reward_rotator_ccw: title: Obracanie odwrotne desc: Odblokowano nowy wariant <strong>Obracacza</strong> - Pozwala odwracać przeciwnie do wskazówek zegara! Aby zbudować, zaznacz Obracacz i @@ -876,7 +750,7 @@ storyRewards: mierzenie przepustowości taśmociągu.<br><br>Czekaj tylko, aż odblokujesz przewody logiczne - dopiero wtedy staje się bardzo użyteczny! - reward_rotater_180: + reward_rotator_180: title: Obracacz (180°) desc: Właśnie odblokowałeś kolejny wariant <strong>obrazacza</strong>! - Pozwala ci na obrócenie kształtu o 180 stopni! @@ -1133,7 +1007,7 @@ keybindings: underground_belt: Tunel miner: Ekstraktor cutter: Przecinak - rotater: Obracacz + rotator: Obracacz stacker: Sklejacz mixer: Mieszadło Kolorów painter: Malarz @@ -1200,12 +1074,6 @@ about: changelog: title: Dziennik zmian demo: - features: - restoringGames: Przywracanie zapisów gry - importingGames: Importowanie zapisów gry - oneGameLimit: Limit jednego zapisu gry - customizeKeybindings: Personalizowanie Klawiszologii - exportingBase: Eksportowanie całej fabryki jako zrzut ekranu settingNotAvailable: Niedostępne w wersji demo. tips: - Budynek główny akceptuje wejście każdego rodzaju - nie tylko aktualny @@ -1372,17 +1240,7 @@ mods: version: Wersja modWebsite: Strona openFolder: Otwórz folder modów - folderOnlyStandalone: Otwarcie folderu modów jest możliwe tylko podczas pracy w - wersji samodzielnej. browseMods: Szukaj Modów modsInfo: Aby zainstalować i zarządzać modami, skopiuj je do folderu mods w katalogu gry. Możesz także użyć przycisku „Otwórz folder modów” w prawym górnym rogu. - noModSupport: Aby zainstalować mody, potrzebujesz osobną wersję na Steam. - togglingComingSoon: - title: Już wkrótce - description: Włączanie lub wyłączanie modów jest obecnie możliwe tylko przez - kopiowanie pliku modów do folderu "mods/". Jednak w przyszłej - aktualizacji będziesz/asz mógł zmieniać ich status tutaj! - browserNoSupport: Due to browser restrictions it is currently only possible to - install mods in the Steam version - Sorry! diff --git a/translations/base-pt-BR.yaml b/translations/base-pt-BR.yaml index 0d79878b..db9a71fc 100644 --- a/translations/base-pt-BR.yaml +++ b/translations/base-pt-BR.yaml @@ -1,26 +1,3 @@ -steamPage: - shortText: Shapez.io é um jogo sobre construir fábricas, automatizando a criação - e combinação de formas cada vez mais complexas num mapa infinito. - discordLinkShort: Discord Oficial - intro: >- - Shapez.io é um jogo relaxante no qual você deve construir fábricas para - produzir formas geométricas automaticamente. - - Conforme os níveis aumentam, as formas se tornam mais complexas, e você terá que explorar o mapa infinito. - - Como se já não bastasse, sua produção deve crescer exponencialmente para satisfazer a demanda - a única solução é expandir! - - Enquanto no começo você apenas processa as formas, mais a frente você deve pintá-las - para isso você deve extrair e misturar cores! - - Comprar o jogo na Steam te garante acesso à versão completa, mas você pode jogar a versão demo em shapez.io primeiro e decidir depois! - what_others_say: O que as pessoas dizem sobre o shapez.io - nothernlion_comment: Este jogo é ótimo - estou me divertindo muito jogando e o - tempo passou voando. - notch_comment: Droga. Eu realmente deveria dormir, mas eu acho que acabei de - descobrir como fazer um computador no shapez.io - steam_review_comment: Este jogo roubou minha vida e eu não a quero de volta. - Jogo de fábrica muito descontraído que não me deixa parar de fazer - linhas mais eficientes. global: loading: Carregando error: Erro @@ -54,57 +31,19 @@ global: space: Espaço loggingIn: Entrando loadingResources: Downloading additional resources (<percentage> %) - discount: -<percentage>% - discountSummerSale: SPECIAL PROMOTION! Offer ends 7 July -demoBanners: - title: Versão Demo - intro: |- - Obtenha o jogo completo <forte>suave</forte> para desbloquear:<ul> - <li>Todos os 26 níveis + infinite Freeplay</li> - <li>22 novas construções</li> - <li>Suporte Mod</li> - <li>Realizações</li> - <li>Dark Mod</li> - <li>...e muito mais!</li> - </ul> - playtimeDisclaimer: The full version contains more than <strong>20 hours of content</strong>. - playerCount: <playerCount> Jogadores como você está jogando shapez on Steam - agora mesmo. - untilEndOfDemo: Até o final da demo - playtimeDisclaimerDownload: Você pode continuar seu jogo salvo na versão - completa! Clique <strong>aqui</strong> para baixar seu jogo salvo. - titleV2: "Reproduzir a versão completa agora para:" mainMenu: play: Jogar continue: Continuar newGame: Novo jogo - changelog: Changelog - subreddit: Reddit importSavegame: Importar save - openSourceHint: Esse jogo tem código aberto! - discordLink: Discord oficial helpTranslate: Ajude a traduzir! - madeBy: Feito por <author-link> - browserWarning: O jogo pode ficar lento em seu navegador! Compre a versão - completa ou baixe o Chrome para obter uma experiência completa. savegameLevel: Nível <x> savegameLevelUnknown: Nível desconhecido savegameUnnamed: Sem nome - puzzleMode: Modo Puzzle - back: Voltar - puzzleDlcText: Você gosta de compactar e otimizar fábricas? Adquira a Puzzle DLC - já disponível na Steam para se divertir ainda mais! - puzzleDlcWishlist: Adicione já a sua lista de desejos! - puzzleDlcViewNow: View Dlc mods: - title: Active Mods warningPuzzleDLC: Playing the Puzzle DLC is not possible with mods. Please disable all mods to play the DLC. - playingFullVersion: You are now playing the full version! - logout: Logout noActiveSavegames: No active savegames found - Click play to start a new game! - playFullVersionV2: Bough shapez on Steam? Play the full version in your Browser! - playFullVersionStandalone: You can now also play the full version in your Browser! dialogs: buttons: ok: OK @@ -113,7 +52,6 @@ dialogs: later: Voltar restart: Reiniciar reset: Resetar - getStandalone: Obter versão completa deleteGame: Sei o que estou fazendo viewUpdate: Atualizações showUpgrades: Melhorias @@ -149,14 +87,6 @@ dialogs: keybindingsResetOk: title: Controles resetados desc: Os controles foram resetados para as definições padrão. - featureRestriction: - title: Versão Demo - desc: Você tentou acessar um recurso (<feature>) que não está disponível na - demo. Considere obter a versão completa para a proceder! - oneSavegameLimit: - title: Limite de jogos salvos - desc: Você pode ter apenas um jogo salvo por vez na versão demo. Remova o - existente ou obtenha a versão completa! updateSummary: title: Nova Atualização! desc: "Aqui estão as alterações desde a última vez que você jogou:" @@ -195,9 +125,6 @@ dialogs: titleEdit: Editar Marcador desc: Dê um nome significativo, você também pode incluir um <strong>código </strong> de uma forma (Você pode gerá-lo <link>aqui</link>) - markerDemoLimit: - desc: Você só pode criar dois marcadores na versão demo. Adquira a versão - completa para marcadores ilimitados! exportScreenshotWarning: title: Exportar captura de tela desc: Você está prestes a exportar uma captura de tela da sua base. Note que @@ -293,28 +220,9 @@ dialogs: newMods: Newly installed Mods resourceLoadFailed: title: Resources failed to load - demoLinkText: shapez demo on Steam - descWeb: "One ore more resources could not be loaded. Make sure you have a - stable internet connection and try again. If this still doesn't - work, make sure to also disable any browser extensions (including - adblockers).<br><br> As an alternative, you can also play the - <demoOnSteamLinkText>. <br><br> Error Message:" descSteamDemo: "One ore more resources could not be loaded. Try restarting the game - If that does not help, try reinstalling and verifying the game files via Steam. <br><br> Error Message:" - steamSsoError: - title: Full Version Logout - desc: You have been logged out from the Full Browser Version since either your - network connection is unstable or you are playing on another - device.<br><br> Please make sure you don't have shapez open in any - other browser tab or another computer with the same Steam - account.<br><br> You can login again in the main menu. - steamSsoNoOwnership: - title: Full Edition not owned - desc: In order to play the Full Edition in your Browser, you need to own both - the base game and the Puzzle DLC on your Steam account.<br><br> - Please make sure you own both, signed in with the correct Steam - account and then try again. ingame: keybindingsOverlay: moveMap: Mover @@ -457,40 +365,6 @@ ingame: one_miner: 1 Extrator n_miners: <amount> Extratores limited_items: Limite de <max_throughput> - watermark: - title: Versão Demo - desc: Clique aqui para ver as vantagens da versão completa! - get_on_steam: Adquira na Steam - standaloneAdvantages: - no_thanks: Não, obrigado! - points: - levels: - title: 12 Novos Níveis - desc: Para um total de 26 novos níveis! - buildings: - title: 22 Novas Construções - desc: Automatize sua fábrica inteira! - markers: - title: Marcadores ∞ - desc: Nunca se perca na sua fábrica! - wires: - title: Fiação - desc: Uma dimensão completamente nova! - darkmode: - title: Modo Escuro - desc: Não machuque mais seus olhos! - support: - title: Me ajuda - desc: Eu desenvolvo o jogo no meu tempo livre! - achievements: - title: Conquistas - desc: Consiga todas elas! - mods: - title: Modding support! - desc: Over 80 mods available! - titleV2: "Get the full version now on Steam to unlock:" - titleExpiredV2: Demo completed! - titleEnjoyingDemo: Enjoy the demo? puzzleEditorSettings: zoneTitle: Zona zoneWidth: Largura @@ -593,7 +467,7 @@ buildings: description: Corta as formas em quatro partes. <strong>Se você usar apenas uma parte, não se esqueça de destruir as outras, ou ela irá parar a produção!</strong> - rotater: + rotator: default: name: Rotacionador description: Gira as formas no sentido horário em 90 graus. @@ -729,7 +603,7 @@ buildings: default: name: Cortador Virtual description: Corta virtualmente as formas em duas metades. - rotater: + rotator: name: Rotacionador Virtual description: Rotaciona virtualmente a forma, tanto no sentido horário quanto no anti-horário. @@ -771,7 +645,7 @@ storyRewards: orientação</strong>!<br><br>Lembre-se de se livrar do lixo, caso contrário, <strong>a máquina irá entupir</strong> - Por isso eu te dei o <strong>lixo</strong>, que destrói tudo que você coloca nele! - reward_rotater: + reward_rotator: title: Rotação desc: O <strong>rotacionador</strong> foi desbloqueado! Gira as formas no sentido horário em 90 graus. @@ -800,7 +674,7 @@ storyRewards: title: Túnel desc: O <strong>túnel</strong> foi desbloqueado - Agora você pode transportar itens abaixo do solo! - reward_rotater_ccw: + reward_rotator_ccw: title: Rotação anti-horária desc: Você desbloqueou uma variante do <strong>rotacionador</strong> - permite girar no sentido anti-horário! Para construí-lo, selecione o @@ -875,7 +749,7 @@ storyRewards: desc: Você desbloqueou o <strong>leitor de esteira</strong>! Ele permite que você meça a passagem de itens em uma esteira.<br><br>Espere até você desbloquear os fios - ele se torna muito útil! - reward_rotater_180: + reward_rotator_180: title: Rotacionador (180°) desc: Você acabou de desbloquear o <strong>rotacionador</strong> de 180 graus! - Ele permite que você rotacione uma forma em 180 graus (Surpresa! :D) @@ -1102,8 +976,7 @@ settings: zoom). shapeTooltipAlwaysOn: title: Dica de ferramente de forma - Mostrar sempre - description: >- - Deve sempre mostrar a dica de ferramenta de forma ao pairar sobre + description: Deve sempre mostrar a dica de ferramenta de forma ao pairar sobre construções em vez de ter que segurar 'ALT'. rangeSliderPercentage: <amount> % tickrateHz: <amount> Hz @@ -1145,7 +1018,7 @@ keybindings: underground_belt: Túnel miner: Extrator cutter: Cortador - rotater: Rotacionador + rotator: Rotacionador stacker: Empilhador mixer: Misturador de Cores painter: Pintor @@ -1208,12 +1081,6 @@ about: changelog: title: Alterações demo: - features: - restoringGames: Restaurar jogos salvos - importingGames: Carregar jogos salvos - oneGameLimit: Limitado a um jogo salvo - customizeKeybindings: Modificar Teclas - exportingBase: Exportar Base inteira como Imagem settingNotAvailable: Não disponível na versão demo. tips: - O HUB aceita qualquer tipo de forma, não apenas a pedida! @@ -1377,16 +1244,7 @@ mods: version: Version modWebsite: Website openFolder: Open Mods Folder - folderOnlyStandalone: Opening the mod folder is only possible when running the standalone. browseMods: Browse Mods modsInfo: To install and manage mods, copy them to the mods folder within the game directory. You can also use the 'Open Mods Folder' button on the top right. - noModSupport: You need the standalone version on Steam to install mods. - togglingComingSoon: - title: Coming Soon - description: Enabling or disabling mods is currently only possible by copying - the mod file from or to the mods/ folder. However, being able to - toggle them here is planned for a future update! - browserNoSupport: Due to browser restrictions it is currently only possible to - install mods in the Steam version - Sorry! diff --git a/translations/base-pt-PT.yaml b/translations/base-pt-PT.yaml index 061fcc38..df3c00c8 100644 --- a/translations/base-pt-PT.yaml +++ b/translations/base-pt-PT.yaml @@ -1,27 +1,3 @@ -steamPage: - shortText: shapez.io é um jogo cujo objetivo é construir fábricas para - automatizar a criação e fusão de formas geométricas cada vez mais - complexas num mapa infinito. - discordLinkShort: Discord Oficial - intro: >- - Shapez.io é um jogo relaxante onde tens de construir fábricas para - automatizar a produção de formas geométricas. - - Com o aumento do nível, as formas começam a ser cada vez mais e mais complexas, e tu terás de te expandir num mapa infinito. - - E como se isso não fosse suficiente, tu também terás de produzir de forma exponencial para satisfazeres as tuas necessidades - a única coisa que ajuda é aumentar! - - Embora no inicio apenas tenhas de processar formas, mais tarde, vais ter de as colorir - para isto terás de extrair e misturar cores! - - Comprar o jogo na Steam dar-te-á acesso à versão completa, mas também podes jogar a versão demo em shapez.io primeiro e decidir mais tarde! - what_others_say: O que dizem sobre o shapez.io - nothernlion_comment: Este é um jogo fantástico - Estou a ter um bom bocado - enquanto o jogo, e o tempo parece que voa. - notch_comment: Ora bolas. Eu devia ir dormir, mas acho que acabei de descobrir - como criar um computador no shapez.io - steam_review_comment: Este jogo roubou a minha vida e não a quero de volta. Jogo - de fábrica relaxante que não me deixa parar de fazer as minhas linhas - cada vez mais eficientes. global: loading: A Carregar error: Erro @@ -55,57 +31,19 @@ global: space: SPACE loggingIn: A conectar loadingResources: A descarregar recursos adicionais (<percentage> %) - discount: -<percentage>% - discountSummerSale: PROMOÇÃO ESPECIAL! A oferta acaba dia 7 de julho -demoBanners: - title: Versão Demo - intro: |- - Obtém o jogo completo <strong>agora</strong> para desbloqueares:<ul> - <li>Todos os 26 níveis + Freeplay infinito</li> - <li>22 novos edifícios</li> - <li>Suporte a modos</li> - <li>Conquistas</li> - <li>Modo escuro</li> - <li>... e muito mais!</li> - </ul> - playtimeDisclaimer: A versão completa contém mais de <strong>20 horas de conteúdo</strong>. - playerCount: <playerCount> jogadores como tu estão a jogar shapez na Steam - untilEndOfDemo: Até o final da demo - playtimeDisclaimerDownload: Podes continuar o teu savegame na versão completa! - Cliqua <strong>aqui</strong> para baixares o teu savegame. - titleV2: "Reproduzir a versão completa agora para:" mainMenu: play: Jogar - changelog: Changelog importSavegame: Importar - openSourceHint: Este jogo é código aberto! - discordLink: Discord oficial helpTranslate: Ajuda a traduzir! - browserWarning: Desculpa, mas este jogo parece correr mais lentamente no teu - navegador! Compra o jogo completo ou descarrega o chrome para melhorares a - tua experiência. savegameLevel: Nível <x> savegameLevelUnknown: Nível desconhecido continue: Continuar newGame: Novo Jogo - madeBy: Criado por <author-link> - subreddit: Reddit savegameUnnamed: Sem Nome - puzzleMode: Modo Puzzle - back: Voltar - puzzleDlcText: Gostas de compactar e otimizar fábricas? Adquire agora o DLC - Puzzle na Steam para ainda mais diversão! - puzzleDlcWishlist: Lista de desejos agora! - puzzleDlcViewNow: Ver DLC mods: - title: Modos Ativados warningPuzzleDLC: Jogar o DLC Modo Puzzle não é possível com modos. Por favor desativa todos os modos antes de jogares o DLC - playingFullVersion: Estás agora a jogar a versão completa! - logout: Desconectar - noActiveSavegames: Não foram encontrados savegames - Clica para começar um jogo novo! - playFullVersionV2: Compraste o Shapez na Steam? Joga a versão completa no teu Navegador! - playFullVersionStandalone: Também podes jogar a versão completa no teu Navegador! + noActiveSavegames: Não foram encontrados jogo salvos - Clica para começar um jogo novo! dialogs: buttons: ok: OK @@ -113,10 +51,9 @@ dialogs: cancel: Cancelar later: Mais tarde restart: Recomeçar - reset: Resetar - getStandalone: Compra o jogo completo + reset: Repor deleteGame: Eu sei o que faço - viewUpdate: Ver Update + viewUpdate: Ver atualização showUpgrades: Mostrar Upgrades showKeybindings: Mostrar Atalhos retry: Tentar novamente @@ -124,20 +61,20 @@ dialogs: playOffline: Jogar Offline importSavegameError: title: Erro de importação - text: "Erro ao importar o teu savegame:" + text: "Erro ao importar o teu jogo salvo:" importSavegameSuccess: - title: Savegame importado - text: O teu savegame foi importado com sucesso. + title: Jogo salvo importado + text: O teu jogo guardado foi importado com sucesso. gameLoadFailure: - title: O jogo está em baixo - text: "Erro ao carregar o teu savegame:" + title: O jogo está indisponível + text: "Erro ao carregar o teu jogo salvo:" confirmSavegameDelete: title: Confirmar eliminação text: Tens a certeza que queres apagar o seguinte jogo?<br><br> '<savegameName>' no nível <savegameLevel><br><br> Isto não pode ser desfeito! savegameDeletionError: title: Erro de eliminação - text: "Erro ao eliminar o teu savegame:" + text: "Erro ao eliminar o teu jogo salvo:" restartRequired: title: Necessário reiniciar text: Precisas de reiniciar o jogo para aplicar as mudanças. @@ -146,21 +83,12 @@ dialogs: desc: Pressiona a tecla ou botão do rato que pretendes definir, ou Escape para cancelar. resetKeybindingsConfirmation: - title: Resetar Atalhos - desc: Isto irá resetar todos os Atalhos para os seus valores pré-definidos. + title: Repor Atalhos + desc: Isto irá repor todos os Atalhos para os seus valores pré-definidos. Confirma por favor. keybindingsResetOk: - title: Atalhos resetados - desc: Os Atalhos foram resetados para os respetivos valores pré-definidos! - featureRestriction: - title: Versão Demo - desc: Tentaste aceder a uma funcionalidade (<feature>) que não está disponivel - no Demo. Considera adquirir o jogo completo para a melhor - experiência do jogo! - oneSavegameLimit: - title: Savegames limitados - desc: Apenas podes ter um savegame de cada vez na versão Demo. Por favor remove - o savegame existente ou adquire a versão completa! + title: Atalhos repostos + desc: Os Atalhos foram repostos para os respetivos valores pré-definidos! updateSummary: title: Nova atualização! desc: "Aqui estão as mudanças desde a última vez que jogaste:" @@ -193,9 +121,6 @@ dialogs: <strong>pequeno código</strong> de uma forma. (Pode ser gerado <link>aqui</link>) titleEdit: Editar Marco - markerDemoLimit: - desc: Apenas podes criar dois marcos na versão Demo. Adquire o jogo completo - para colocar marcos infinitos! massCutConfirm: title: Confirmar corte desc: Estás a cortar muitas construções (<count> para ser exato)! Tens a @@ -215,8 +140,8 @@ dialogs: descShortKey: ... ou insere o <strong>pequeno código</strong> de uma forma (Pode ser gerado <link>aqui</link>) renameSavegame: - title: Renomear Savegame - desc: Podes renomear o teu savegame aqui. + title: Renomear jogo salvo + desc: Podes renomear o teu jogo salvo aqui. tutorialVideoAvailable: title: Tutorial Disponível desc: Existe um vídeo de tutorial disponível para este nível! Gostarias de o @@ -250,8 +175,8 @@ dialogs: em modo offline. Por favor assegura-te de que tens uma boa conexão de internet. puzzleDownloadError: - title: Falha no Download - desc: "Falha ao fazer o download do puzzle:" + title: Falha na descarga + desc: "Falha ao fazer a descarga do puzzle:" puzzleSubmitError: title: Erro ao submeter desc: "Falha ao submeter o teu puzzle:" @@ -302,28 +227,9 @@ dialogs: newMods: Modos instalados recentemente resourceLoadFailed: title: Falha ao carregar recursos - demoLinkText: Versão Shapez demo na Steam - descWeb: "Um ou mais recursos não carrega. Verifica se tens uma conexão estável - à internet e tenta de novo. Se mesmo assim não funcionar, desativa - extensões do browser (incluindo adblockers). <br><br> Como - alternativa, também podes jogar a <demoOnSteamLinkText>. <br><br> - Mensagem de Erro:" descSteamDemo: "Um ou mais recursos não carrega. Tenta reiniciar o jogo - Se isto não ajudar, tenta reinstalar e verifica os ficheiros do jogo na Steam. <br><br> Mensagem de Erro:" - steamSsoError: - title: Desconectar da Versão Completa - desc: Tu desconectaste da Versão Completa no Browser devido a uma conexão - instável ou devido a estares a jogar noutro dispositivo. <br><br> - Por favor garante que não tens o shapez aberto noutra tab do teu - Browser ou noutro computador com a mesma conta Steam.<br><br> Podes - fazer login novamente no menu principal. - steamSsoNoOwnership: - title: Vesão completa não obtida - desc: Para jogares a versão completa no teu Browser, precisas de teres tanto o - jogo base como o DLC Puzzle na tua conta Steam.<br><br> Por favor - garante que tens ambos, conecta-te com a conta Steam correta e tenta - novamente. ingame: keybindingsOverlay: moveMap: Mover @@ -363,12 +269,12 @@ ingame: unlockText: <reward> desbloqueado! buttonNextLevel: Próximo nível notifications: - newUpgrade: Está disponível um novo upgrade! + newUpgrade: Está disponível uma nova melhoria! gameSaved: O teu jogo foi guardado. freeplayLevelComplete: Nível <level> completo! shop: - title: Upgrades - buttonUnlock: Upgrade + title: Melhorias + buttonUnlock: Melhoria tier: Nível <x> maximumLevel: NÍVEL MÁXIMO (Velocidade x<currentMult>) statistics: @@ -465,40 +371,6 @@ ingame: one_miner: 1 Extrator n_miners: <amount> Extratores limited_items: Limite de <max_throughput> - watermark: - title: Versão Demo - desc: Clica aqui para veres as vantagens da versão na Steam! - get_on_steam: Compra na Steam - standaloneAdvantages: - no_thanks: Não, obrigado! - points: - levels: - title: 12 Novos Níveis - desc: Para um total de 26 Níveis! - buildings: - title: 22 Novas contruções - desc: Para uma fábrica totalmente automatizada! - markers: - title: Marcos ∞ - desc: Nunca te percas na tua Fábrica! - wires: - title: Fios - desc: Uma dimensão totalmente nova!! - darkmode: - title: Modo Escuro - desc: Não magoes os teus olhos! - support: - title: Ajuda-me - desc: Eu desenvolvo este jogo no meu tempo livre! - achievements: - title: Conquistas - desc: Tenta obtê-las todas! - mods: - title: Suporte para Modos! - desc: Mais de 80 modos disponíveis! - titleV2: "Adquire agora a Versão completa na Steam para desbloquares:" - titleExpiredV2: Versão Demo completa! - titleEnjoyingDemo: Estás a gostar da versão demo? puzzleEditorSettings: zoneTitle: Zona zoneWidth: Largura @@ -583,11 +455,11 @@ buildings: apenas usares uma parte, destrói a outra para não encravar a produção!</strong> quad: - name: Cortador (Quád) + name: Cortador (Quad) description: Corta as formas geométricas em quatro partes. <strong>Se apenas usares uma parte, destrói as outras partes para não encravar a produção!</strong> - rotater: + rotator: default: name: Rodar description: Roda as formas 90º no sentido dos ponteiros do relógio. @@ -738,7 +610,7 @@ buildings: default: name: Cortador Virtual description: Virtualmente, corta as formas em duas metades. - rotater: + rotator: name: Rodador Virtual description: Virtualmente, roda a forma tanto no sentido horário quanto no anti-horário. @@ -781,7 +653,7 @@ storyRewards: desperdício, caso contrário <strong>irá encravar e parar</strong> - Para este propósito eu dei-te um <strong>lixo</strong>, que destrói tudo o que lá colocares! - reward_rotater: + reward_rotator: title: Rotação desc: O <strong>Rodador</strong> foi desbloqueado! Ele roda as formas geométricas 90º no sentido dos ponteiros do relógio. @@ -811,7 +683,7 @@ storyRewards: title: Túnel desc: O <strong>Túnel</strong> foi desbloqueado - Com ele podes passar itens através de tapetes e construções! - reward_rotater_ccw: + reward_rotator_ccw: title: Rotação CCW desc: Desbloqueaste uma variante do <strong>Rodador</strong> - Permite rodar no sentido contrário ao dos ponteiros do relógio! Para construí-lo, @@ -886,7 +758,7 @@ storyRewards: desc: Desbloqueaste o <strong>leitor de tapete</strong>! Permite-te medires a passagem média de itens no tapete.<br><br>E espera por desbloqueares os fios - aí é que vão ser bastante úteis! - reward_rotater_180: + reward_rotator_180: title: Rodar (180º) desc: Desbloqueaste o <strong>rodador</strong> de 180 graus! - Permite-te rodares formas 180 graus (Surpresa! :D) @@ -1148,7 +1020,7 @@ keybindings: underground_belt: Túnel miner: Extrator cutter: Cortador - rotater: Rodar + rotator: Rodar stacker: Empilhador mixer: Misturador de cor painter: Pintor @@ -1215,12 +1087,6 @@ about: changelog: title: Changelog demo: - features: - restoringGames: Restauro de savegames - importingGames: Importação de savegames - oneGameLimit: Limitado a um savegame - customizeKeybindings: Costumizar Atalhos - exportingBase: Exportar base como uma imagem settingNotAvailable: Não disponível no Demo. tips: - O edifício central aceita qualquer entrada, não apenas a da forma atual! @@ -1283,7 +1149,7 @@ tips: - Tenta também Factorio! É o meu jogo favorito. - O cortador quádruplo corta no sentido dos ponteiros do relógio começando no canto superior direito! - - Podes fazer download dos teus savegames no menu principal! + - Podes descarregar os teus jogos guardados no menu principal! - Este jogo tem muitos atalhos de teclado úteis! Não te esqueças de verificar a página de configurações. - Este jogo tem muitas definições, não te esqueças de as verificar! @@ -1357,10 +1223,10 @@ puzzleMenu: backendErrors: ratelimit: Estás a realizar as tuas ações demasiado rápido. Aguarda um pouco. invalid-api-key: Falha ao comunicar com o backend, por favor tenta - atualizar/resetar o Jogo (Chave Api inválida). - unauthorized: Falha ao comunicar com o backend, por favor tenta atualizar/resetar + atualizar/repor o Jogo (Chave Api inválida). + unauthorized: Falha ao comunicar com o backend, por favor tenta atualizar/repor o Jogo (Não autorizado). - bad-token: Falha ao comunicar com o backend, por favor tenta atualizar/resetar o + bad-token: Falha ao comunicar com o backend, por favor tenta atualizar/repor o Jogo (Mau Token). bad-id: Identificador de Puzzle inválido. not-found: O Puzzle pedido não foi encontrado. @@ -1386,18 +1252,7 @@ mods: version: Versão modWebsite: Website openFolder: Abrir a pasta dos Modos - folderOnlyStandalone: Abrir a pasta dos Modos apenas é possível quando estás a - jogar a versão completa. browseMods: Pesquisar Modos modsInfo: Para instalar e gerir os modos, copia-os para a pasta dos modos dentro da pasta do jogo. Tu também podes usar o botão 'Abrir a pasta dos Modos' no canto superior direito. - noModSupport: Precisas da versão completa na Steam para instalar modos. - togglingComingSoon: - title: Brevemente - description: Ativar ou desativar modos atualmente apenas é possível copiando o - ficheiro do modo a partir de ou para a pasta dos modos. No entanto, - ser possível ativar ou desativar os modos está planeado para uma - atualização futura! - browserNoSupport: Devido a restrições no browser atualmente apenas é possível - instalar modos na versão Steam - Desculpa! diff --git a/translations/base-ro.yaml b/translations/base-ro.yaml index c471c54c..c91c001b 100644 --- a/translations/base-ro.yaml +++ b/translations/base-ro.yaml @@ -1,27 +1,3 @@ -steamPage: - shortText: shapez.io este un joc despre construirea fabricilor pentru a - automatiza crearea și combinarea a din ce in ce mai complexe forme - într-o hartă infinită. - discordLinkShort: Official Discord - intro: >- - Shapez.io un joc relaxant in care trebuie sa construiești fabrici pentru - a automatiza producția de figuri geometrice - - Pe măsură ce nivelul crește figurile devin din ce în ce mai complexe și vei fi nevoit să te extinzi pe harta infinită. - - Daca asta nu era suficient, vei fi nevoit sa produci exponențial mai multe figuri pentru a satisface cererea - singurul lucru care te poate ajuta este extinderea ! - - Dacă la început procesezi doar forme, vei fi nevoit sa le colorezi mai târziu - pentru asta vei avea nevoie să extragi și să amesteci culori ! - - Cumpărarea jocului pe Steam îți va asigura acesul la versiunea finală, dar te poți juca o versiune demonstrativă pe shapez.io mai întâi și să te decizi mai târziu ! - what_others_say: Ce spun alții despre shapez.io - nothernlion_comment: This game is great - I'm having a wonderful time playing, - and time has flown by. - notch_comment: Oh crap. I really should sleep, but I think I just figured out - how to make a computer in shapez.io - steam_review_comment: This game has stolen my life and I don't want it back. - Very chill factory game that won't let me stop making my lines more - efficient. global: loading: Se Încarcă error: Eroare @@ -55,58 +31,19 @@ global: space: SPACE loggingIn: Logare loadingResources: Downloading additional resources (<percentage> %) - discount: -<percentage>% - discountSummerSale: SPECIAL PROMOTION! Offer ends 7 July -demoBanners: - title: Versiunea Demo - intro: |- - Obțineți jocul complet <strong>acum</strong> pentru a-l debloca:<ul> - <li>Toate cele 26 de nivele + Freeplay infinit</li> - <li>22 de clădiri noi</li> - <li>Suport pentru moduri</li> - <li>Realizări</li> - <li>Modul întunecat</li> - <li>...și multe altele!</li> - </ul> - playtimeDisclaimer: Versiunea completă conține mai mult de <strong>20 de ore de - conținut</strong>. - playerCount: <playerCount> jucători ca tine se joacă în prezent shapez pe Steam - untilEndOfDemo: Până la sfârșitul demonstrației - playtimeDisclaimerDownload: Puteți continua salvarea în versiunea completă! - Faceți clic <strong>aici</strong> pentru a descărca jocul salvat. - titleV2: "Joacă acum versiunea completă pentru:" mainMenu: play: Joacă - changelog: Jurnalul schimbărilor importSavegame: Importă - openSourceHint: Acest joc este open source! - discordLink: Serverul oficial de Discord helpTranslate: Ajută să traducem! - browserWarning: Scuze, dar jocul este cunoscut să ruleze încet pe browser-ul - tău! Instalează versiunea standalone sau descarcă chrome pentru - experiența completă. savegameLevel: Nivelul <x> savegameLevelUnknown: Nivel necunoscut continue: Continuă newGame: Joc nou - madeBy: Făcut de <author-link> - subreddit: Reddit savegameUnnamed: Fară nume - puzzleMode: Puzzle Mode - back: Back - puzzleDlcText: Îți place să compresezi și să optimizezi fabricile ? Ia acum - Puzzle DLC pe Steam pentru mai multă distracție ! - puzzleDlcWishlist: Adaugă în Wishlist acum! - puzzleDlcViewNow: Vezi DlC mods: - title: Active Mods warningPuzzleDLC: Playing the Puzzle DLC is not possible with mods. Please disable all mods to play the DLC. - playingFullVersion: You are now playing the full version! - logout: Logout noActiveSavegames: No active savegames found - Click play to start a new game! - playFullVersionV2: Bough shapez on Steam? Play the full version in your Browser! - playFullVersionStandalone: You can now also play the full version in your Browser! dialogs: buttons: ok: OK @@ -115,7 +52,6 @@ dialogs: later: Mai târziu restart: Restartează reset: Resetează - getStandalone: Instalează Standalone-ul deleteGame: Știu ce fac viewUpdate: Vezi Update-ul showUpgrades: Vezi Upgrade-urile @@ -153,15 +89,6 @@ dialogs: keybindingsResetOk: title: Tastele configurate se resetează desc: Tastele configurate au fost resetate la valorile lor obișnuite! - featureRestriction: - title: Versiune Demo - desc: Ai încercat să accesezi o funcție (<feature>) care nu este disponibilă în - demo. Consideră să instalezi standalone-ul pentru experiența - completă! - oneSavegameLimit: - title: Salvări limitate - desc: Poți avea doar o salvare în același timp în versiunea demo. Te rugăm - șterge-o pe cea existentă sau instalează standalone-ul! updateSummary: title: Update nou! desc: "Aici sunt schimbările de când ai jucat ultima oară:" @@ -194,9 +121,6 @@ dialogs: desc: Dă-i un nume sugestiv, poți include și o <strong> scurtă cheie </strong> a unei figuri (Pe care o poți genera <link>aici</link>) titleEdit: Editează Marker - markerDemoLimit: - desc: Poți crea decât două waypoint-uri personalizate în demo. Ia standalone-ul - pentru Waypoint-uri nelimitate! massCutConfirm: title: Confirmă decuparea desc: Tu decupezi mai multe construcții (<count> pentru a fi precis)! Sunteți @@ -300,28 +224,9 @@ dialogs: newMods: Newly installed Mods resourceLoadFailed: title: Resources failed to load - demoLinkText: shapez demo on Steam - descWeb: "One ore more resources could not be loaded. Make sure you have a - stable internet connection and try again. If this still doesn't - work, make sure to also disable any browser extensions (including - adblockers).<br><br> As an alternative, you can also play the - <demoOnSteamLinkText>. <br><br> Error Message:" descSteamDemo: "One ore more resources could not be loaded. Try restarting the game - If that does not help, try reinstalling and verifying the game files via Steam. <br><br> Error Message:" - steamSsoError: - title: Full Version Logout - desc: You have been logged out from the Full Browser Version since either your - network connection is unstable or you are playing on another - device.<br><br> Please make sure you don't have shapez open in any - other browser tab or another computer with the same Steam - account.<br><br> You can login again in the main menu. - steamSsoNoOwnership: - title: Full Edition not owned - desc: In order to play the Full Edition in your Browser, you need to own both - the base game and the Puzzle DLC on your Steam account.<br><br> - Please make sure you own both, signed in with the correct Steam - account and then try again. ingame: keybindingsOverlay: moveMap: Mișcă-te @@ -463,40 +368,6 @@ ingame: one_miner: 1 Miner n_miners: <amount> Miners limited_items: Limitat la <max_throughput> - watermark: - title: Versiune Demo - desc: Apasă aici pentru a vedea avantajele versiunii Steam! - get_on_steam: Ia pe Steam - standaloneAdvantages: - no_thanks: Nu, mersi! - points: - levels: - title: 12 Nivele Noi - desc: Pentru un total de 26 de nivele! - buildings: - title: 22 construcții noi - desc: Automatizează-ti complet fabrica! - markers: - title: Marcatoare infinite - desc: Nu te rătăci niciodată în fabrică! - wires: - title: Cabluri - desc: O dimeniune complet nouă! - darkmode: - title: Mod întunecat - desc: Nu mai lăsa ochii să te doară! - support: - title: Ajută-mă - desc: Dezvolt jocuri în timpul meu liber! - achievements: - title: Realizări - desc: Vânează-le pe toate! - mods: - title: Modding support! - desc: Over 80 mods available! - titleV2: "Get the full version now on Steam to unlock:" - titleExpiredV2: Demo completed! - titleEnjoyingDemo: Enjoy the demo? puzzleEditorSettings: zoneTitle: Zonă zoneWidth: Lățime @@ -584,7 +455,7 @@ buildings: description: Taie formele în patru părți. <strong>Dacă folosești doar o parte, ține minte să o distrugi pe cealaltă sau producția se va opri!</strong> - rotater: + rotator: default: name: Rotitor description: Rotește formele în sensul acelor de ceasornic la 90 de grade. @@ -737,7 +608,7 @@ buildings: default: name: Tăietor virtual description: Taie virtual forma în două - rotater: + rotator: name: Rotitor virtual description: Rotește virtual forma în ambele direcții. unstacker: @@ -777,7 +648,7 @@ storyRewards: altfel <strong>o să se înfunde și o să se oprească</strong> - Pentru asta ți-am dat <strong>cosul de gunoi</strong> care distruge tot ce pui în el! - reward_rotater: + reward_rotator: title: Rotitul desc: <strong>Rotitorul</strong> a fost deblocat! El rotește formele la 90 de grade. @@ -805,7 +676,7 @@ storyRewards: title: Tunel desc: <strong>Tunelul</strong> a fost deblocat - Acum poți deplasa obiecte pe sub benzi și construcții cu el! - reward_rotater_ccw: + reward_rotator_ccw: title: Rotitul CCW desc: Ai deblocat o variantă a <strong>rotitorului</strong> - El permite rotația în sensul invers al acelor de ceasornic! Pentru a îl construi, @@ -879,7 +750,7 @@ storyRewards: desc: Ai deblocat <strong>cititirul de bandă</strong>! Iți permite să măsori debitul unei benzi.<br><br>Și așteaptă pană deblochezi cablurile - atungi devine foarte util! - reward_rotater_180: + reward_rotator_180: title: Rotator (180 degrees) desc: Tocmai ai deblocat un <strong>rotator</strong> de 180 de grade! - Îți permite să rotești o formă cu 180 de grade (Surpriză! :D) @@ -1140,7 +1011,7 @@ keybindings: underground_belt: Tunel miner: Extractor cutter: Tăietor - rotater: Rotitor + rotator: Rotitor stacker: Mașină de presat mixer: Mixer de culori painter: Mașină de pictat @@ -1207,12 +1078,6 @@ about: changelog: title: Lista de schimbări demo: - features: - restoringGames: Restabilirea savegame-urilor - importingGames: Importarea savegame-urilor - oneGameLimit: Limitat la un savegame - customizeKeybindings: Taste customizabile - exportingBase: Exportul întregii baze ca imagine settingNotAvailable: Nu este valabil în demo. tips: - HUB-ul acceptă orice fel de input, nu doar forma cerută curent! @@ -1372,16 +1237,7 @@ mods: version: Version modWebsite: Website openFolder: Open Mods Folder - folderOnlyStandalone: Opening the mod folder is only possible when running the standalone. browseMods: Browse Mods modsInfo: To install and manage mods, copy them to the mods folder within the game directory. You can also use the 'Open Mods Folder' button on the top right. - noModSupport: You need the standalone version on Steam to install mods. - togglingComingSoon: - title: Coming Soon - description: Enabling or disabling mods is currently only possible by copying - the mod file from or to the mods/ folder. However, being able to - toggle them here is planned for a future update! - browserNoSupport: Due to browser restrictions it is currently only possible to - install mods in the Steam version - Sorry! diff --git a/translations/base-ru.yaml b/translations/base-ru.yaml index 994aecc4..fe3564ea 100644 --- a/translations/base-ru.yaml +++ b/translations/base-ru.yaml @@ -1,24 +1,3 @@ -steamPage: - shortText: shapez — это игра о строительстве фабрик для автоматизации создания и - объединения все более сложных фигур на бесконечной карте. - discordLinkShort: Официальный Discord сервер - intro: >- - Любите игры про автоматизацию? Тогда вы по адресу! - - shapez это спокойная игра, в которой вам предстоит строить фабрики по автоматизированному производству геометрических фигур. По мере прохождения, фигуры становятся все сложнее, так что Вам придется расширять фабрику за счет бесконечной карты. - - Если этого мало, то Вам так же придется экспоненциально увеличивать производство, чтобы удовлетворить потребности Вашей фабрики. Ключ к успеху - масштабирование! - - Вначале игры Вам понадобится производить только фигуры, однако позже фигуры надо будет окрашивать. Для этого добывайте и смешивайте краски! - - Приобретение игры в Steam предоставляет доступ к полной версии игры, но вы можете опробовать демоверсию игры на shapez.io и принять решение позже! - what_others_say: Что говорят люди о shapez - nothernlion_comment: Отличная игра, в которой теряешь ход времени. - notch_comment: Ох! Мне определенно нужно поспать, но я узнал как собрать - компьютер в shapez! - steam_review_comment: Игра похитила меня из реальной жизни, но я не хочу - обратно! Спокойная игра про строительство фабрик. Постоянно думаю о том - как сделать производство более эффективным. global: loading: Загрузка error: Ошибка @@ -52,59 +31,19 @@ global: space: ПРОБЕЛ loggingIn: Вход loadingResources: Скачивание дополнительных ресурсов (<percentage> %) - discount: -<percentage>% - discountSummerSale: SPECIAL PROMOTION! Offer ends 7 July -demoBanners: - title: Демоверсия - intro: >- - Приобретите полную версию игры <strong>сейчас</strong> чтобы - получить:<ul> - <li>Все 26 уровней + бесконечную свободную игру</li> - <li>22 новых строения</li> - <li>Поддержку модификаций</li> - <li>Достижения</li> - <li>Темный Режим</li> - <li>... и многое другое!</li> - </ul> - playtimeDisclaimer: Полная версия содержит более <strong>20 часов контента</strong>. - playerCount: <playerCount> игроков, как Вы, сейчас играют в shapez в Steam - untilEndOfDemo: До окончания демоверсии - playtimeDisclaimerDownload: Вы можете продолжить сохранение в полной версии! - Нажмите <strong>здесь</strong>, чтобы скачать файл сохранения. - titleV2: "Играйте в полную версию прямо сейчас:" mainMenu: play: Играть continue: Продолжить newGame: Новая игра - changelog: Список изменений - subreddit: Reddit importSavegame: Импорт - openSourceHint: Это игра с открытым исходным кодом! - discordLink: Официальный Дискорд сервер helpTranslate: Помоги с переводом! - madeBy: Создал <author-link> - browserWarning: Извините, но игра работает медленно в Вашем браузере! - Приобретите полную версию или загрузите Google Chrome, чтобы ознакомится - с игрой в полной мере. savegameLevel: Уровень <x> savegameLevelUnknown: Неизвестный уровень savegameUnnamed: Без названия - puzzleMode: Головоломка - back: Назад - puzzleDlcText: Нравится оптимизировать фабрики и делать их меньше? Купите Puzzle - DLC в Steam сейчас и получите еще больше удовольствия! - puzzleDlcWishlist: Добавь в список желаемого! - puzzleDlcViewNow: Посмотреть mods: - title: Активные моды warningPuzzleDLC: Puzzle DLC несовместима с модами. Пожалуйста, отключите все моды, чтобы играть в DLC. - playingFullVersion: Вы играете в полную версию игры! - logout: Выход noActiveSavegames: Сохранения не найдены - нажмите "Играть", чтобы начать новую игру! - playFullVersionV2: Приобрели shapez в Steam? Вы можете играть в полную версию - прямо в Вашем браузере! - playFullVersionStandalone: You can now also play the full version in your Browser! dialogs: buttons: ok: OK @@ -113,7 +52,6 @@ dialogs: later: Позже restart: Перезапустить reset: Сбросить - getStandalone: Приобрести полную версию deleteGame: Я знаю, что делаю viewUpdate: Посмотреть обновление showUpgrades: Показать улучшения @@ -151,15 +89,6 @@ dialogs: keybindingsResetOk: title: Сброс управления desc: Настройки управления сброшены до соответствующих значений по умолчанию! - featureRestriction: - title: Демоверсия - desc: Вы попытались получить доступ к функции (<feature>), которая недоступна в - демоверсии. Вы можете приобрести полную версию, чтобы получить - доступ ко всем функциям! - oneSavegameLimit: - title: Лимит сохранений - desc: Вы можете иметь только одно сохранение игры в демоверсии. Пожалуйста, - удалите существующее или приобретите полную версию! updateSummary: title: Новое обновление! desc: "Вот что мы обновили с тех пор, когда вы в последний раз играли:" @@ -204,9 +133,6 @@ dialogs: descItems: "Выберите объект:" descShortKey: ... или введите <strong>короткий ключ</strong> фигуры (который можно сгенерировать <link>здесь</link>) - markerDemoLimit: - desc: Вы можете создать только 2 своих маркера в демоверсии. Приобретите полную - версию, чтобы снять это ограничение. exportScreenshotWarning: title: Экспорт скриншота desc: Вы собираетесь экспортировать вашу базу в виде скриншота. Обратите @@ -296,29 +222,10 @@ dialogs: newMods: Недавно установленные моды resourceLoadFailed: title: Ошибка загрузки ресурсов - demoLinkText: Демоверсия shapez в Steam - descWeb: "Один или более ресурсов не могут быть загружены. Убедитесь в наличии - стабильного интернет соединения и повторите попытку. Если ошибка - повторяется - убедитесь, что сторонние браузерные расширения - отключены (включая блокировщики рекламы).<br><br> В качестве - альтернативы, Вы также можете играть в <demoOnSteamLinkText>. - <br><br> Ошибка:" descSteamDemo: "Один или более ресурсов не могут быть загружены. Попробуйте перезапустить игру - если это не помогает, попробуйте переустановить игру и проверить целостность локальных файлов в Steam. <br><br> Ошибка:" - steamSsoError: - title: Выход из полной версии - desc: Вы вышли из полной браузерной версии игры, так как либо у Вас нестабильное - подключение к интернету или вы уже играете на другом устройстве. - <br><br> Пожалуйста, убедитесь, что shapez не открыт в другой - вкладке браузера или на другом компьютере под той же учётной записью - Steam <br><br> Вы можете снова войти в главном меню. - steamSsoNoOwnership: - title: Вы не владеете полной версией игры - desc: Чтобы играть в полную браузерную версиию, вам необходимо приобрести игру и - Puzzle DLC в Steam.<br><br> Пожалуйста убедитесь, что вы владеете - ими, вошли в правильный Steam аккаунт и повторите попытку. ingame: keybindingsOverlay: moveMap: Передвижение @@ -459,40 +366,6 @@ ingame: one_miner: 1 экстрактор n_miners: <amount> экстрактора(-ов) limited_items: Ограничено до <max_throughput> - watermark: - title: Демоверсия - desc: Нажмите сюда, чтобы узнать о преимуществах Steam версии! - get_on_steam: Приобрести в Steam - standaloneAdvantages: - no_thanks: Нет, спасибо! - points: - levels: - title: 12 новых уровней - desc: Всего 26 уровней! - buildings: - title: 22 новых построек - desc: Полностью автоматизируйте свою фабрику! - achievements: - title: Достижения - desc: Получите их все! - markers: - title: ∞ маркеров - desc: Никогда не теряйтесь в своей фабрике! - wires: - title: Провода - desc: Полноценное дополнительное измерение! - darkmode: - title: Темная тема - desc: Дайте глазам отдохнуть! - support: - title: Поддержите меня - desc: Я занимаюсь разработкой в свободное время! - mods: - title: Поддержка модов - desc: Доступно более 80 модов! - titleV2: "Приобретите полную версию в Steam, чтобы разблокировать:" - titleExpiredV2: Вы прошли демоверсию! - titleEnjoyingDemo: Понравилась демоверсия? puzzleEditorSettings: zoneTitle: Область zoneWidth: Ширина @@ -603,7 +476,7 @@ buildings: description: Разрезает фигуры на четыре части. <strong>Если вы собираетесь использовать не все части - уничтожьте оставшиеся, иначе производство остановится!</strong> - rotater: + rotator: default: name: Вращатель description: Поворачивает фигуры по часовой стрелке на 90 градусов. @@ -727,7 +600,7 @@ buildings: default: name: Виртуальный резак description: Виртуально разрезает фигуру пополам. - rotater: + rotator: name: Виртуальный вращатель description: Виртуально вращает фигуру как по часовой стрелке, так и против часовой стрелки. @@ -768,7 +641,7 @@ storyRewards: остановится</strong> - для этого для Вас также доступна <strong>мусорка</strong>, которая уничтожает все, что в неё попадает! - reward_rotater: + reward_rotator: title: Вращение desc: Разблокирован <strong>вращатель</strong>! Он поворачивает фигуры по часовой стрелке на 90 градусов. @@ -797,7 +670,7 @@ storyRewards: title: Туннель desc: Разблокирован <strong>туннель</strong>! Теперь вы можете транспортировать предметы под другими конвейерами и зданиями! - reward_rotater_ccw: + reward_rotator_ccw: title: Обратный вращатель desc: Разблокирован вариант <strong>вращателя</strong>, вращающий фигуры против часовой стрелки! Чтобы построить его, выберите вращатель и @@ -849,7 +722,7 @@ storyRewards: нажмите 'C', чтобы скопировать её.<br><br> Вставка <strong>не бесплатна</strong>, для этого необходимо произвести <strong>фигуры для чертежей</strong> (которые вы только что доставили)! - reward_rotater_180: + reward_rotator_180: title: Вращатель (180°) desc: Разблокирован <strong>вращатель</strong> на 180 градусов! - он позволяет вращать фигуры на 180 градусов (если Вы не догадались :D) @@ -1135,7 +1008,7 @@ keybindings: underground_belt: Туннель miner: Экстрактор cutter: Резак - rotater: Вращатель + rotator: Вращатель stacker: Объединитель mixer: Смешиватель painter: Покрасчик @@ -1197,12 +1070,6 @@ about: changelog: title: Список изменений demo: - features: - restoringGames: Восстановить сохранения игр - importingGames: Импортировать сохранения игр - oneGameLimit: Ограниченность одним сохранением игры - customizeKeybindings: Пользовательская настройка управления - exportingBase: Экспорт всей базы в виде изображения settingNotAvailable: Недоступно в демоверсии. tips: - ХАБ принимает любые ресурсы, не только текущую фигуру! @@ -1367,16 +1234,7 @@ mods: version: Версия modWebsite: Веб-сайт openFolder: Открыть папку с модами - folderOnlyStandalone: Открытие папки модов возможно только в десктопной версии. browseMods: Просмотреть моды modsInfo: Чтобы установить и управлять модами, скопируйте их в папку модов в директории игры. Вы также можете воспользоваться кнопкой "Открыть папку модов" в справа вверху. - noModSupport: Для установки модов вам нужна десктопная версия в Steam. - togglingComingSoon: - title: Совсем скоро - description: Включение или отключение модов в настоящее время возможно только - путем копирования файла мода из/в папку mods/. Однако, возможность - управлять ими здесь планируется в будущем обновлении! - browserNoSupport: В связи с ограничениями браузера устанавливать моды сейчас - возможно только в Steam версии, простите! diff --git a/translations/base-sl.yaml b/translations/base-sl.yaml index f65c35c6..6615bcbe 100644 --- a/translations/base-sl.yaml +++ b/translations/base-sl.yaml @@ -1,27 +1,3 @@ -steamPage: - shortText: shapez.io je igra grajenja tovarne katere cilj je avtomatiziranje - kreiranja in procesiranja vse bolj zapletenih oblik na neskončni - ravnini. - discordLinkShort: Official Discord - intro: >- - Shapez.io is a relaxed game in which you have to build factories for the - automated production of geometric shapes. - - As the level increases, the shapes become more and more complex, and you have to spread out on the infinite map. - - And as if that wasn't enough, you also have to produce exponentially more to satisfy the demands - the only thing that helps is scaling! - - While you only process shapes at the beginning, you have to color them later - for this you have to extract and mix colors! - - Buying the game on Steam gives you access to the full version, but you can also play a demo on shapez.io first and decide later! - what_others_say: What people say about shapez.io - nothernlion_comment: This game is great - I'm having a wonderful time playing, - and time has flown by. - notch_comment: Oh crap. I really should sleep, but I think I just figured out - how to make a computer in shapez.io - steam_review_comment: This game has stolen my life and I don't want it back. - Very chill factory game that won't let me stop making my lines more - efficient. global: loading: Loading error: Error @@ -55,56 +31,19 @@ global: space: SPACE loggingIn: Logging in loadingResources: Downloading additional resources (<percentage> %) - discount: -<percentage>% - discountSummerSale: SPECIAL PROMOTION! Offer ends 7 July -demoBanners: - title: Demo Version - intro: |- - Get the full game <strong>now</strong> to unlock:<ul> - <li>All 26 levels + infinite Freeplay</li> - <li>22 new buildings</li> - <li>Mod support</li> - <li>Achievements</li> - <li>Dark Mode</li> - <li>... and a lot more!</li> - </ul> - playtimeDisclaimer: The full version contains more than <strong>20 hours of content</strong>. - playerCount: <playerCount> players like you are currently playing shapez on Steam - untilEndOfDemo: Until end of demo - playtimeDisclaimerDownload: You can continue your savegame in the full version! - Click <strong>here</strong> to download your savegame. - titleV2: "Play the full version now for:" mainMenu: play: Play continue: Continue newGame: New Game - changelog: Changelog - subreddit: Reddit importSavegame: Import - openSourceHint: This game is open source! - discordLink: Official Discord Server helpTranslate: Help translate! - madeBy: Made by <author-link> - browserWarning: Sorry, but the game is known to run slow on your browser! Get - the standalone version or download chrome for the full experience. savegameLevel: Level <x> savegameLevelUnknown: Unknown Level savegameUnnamed: Unnamed - puzzleMode: Puzzle Mode - back: Back - puzzleDlcText: Do you enjoy compacting and optimizing factories? Get the Puzzle - DLC now on Steam for even more fun! - puzzleDlcWishlist: Wishlist now! - puzzleDlcViewNow: View Dlc mods: - title: Active Mods warningPuzzleDLC: Playing the Puzzle DLC is not possible with mods. Please disable all mods to play the DLC. - playingFullVersion: You are now playing the full version! - logout: Logout noActiveSavegames: No active savegames found - Click play to start a new game! - playFullVersionV2: Bough shapez on Steam? Play the full version in your Browser! - playFullVersionStandalone: You can now also play the full version in your Browser! dialogs: buttons: ok: OK @@ -113,7 +52,6 @@ dialogs: later: Later restart: Restart reset: Reset - getStandalone: Get Standalone deleteGame: I know what I am doing viewUpdate: View Update showUpgrades: Show Upgrades @@ -150,15 +88,6 @@ dialogs: keybindingsResetOk: title: Keybindings reset desc: The keybindings have been reset to their respective defaults! - featureRestriction: - title: Demo Version - desc: You tried to access a feature (<feature>) which is not available in the - demo. Consider getting the standalone version for the full - experience! - oneSavegameLimit: - title: Limited savegames - desc: You can only have one savegame at a time in the demo version. Please - remove the existing one or get the standalone version! updateSummary: title: New update! desc: "Here are the changes since you last played:" @@ -195,9 +124,6 @@ dialogs: desc: Give it a meaningful name, you can also include a <strong>short key</strong> of a shape (Which you can generate <link>here</link>) titleEdit: Edit Marker - markerDemoLimit: - desc: You can only create two custom markers in the demo. Get the standalone for - unlimited markers! exportScreenshotWarning: title: Export screenshot desc: You requested to export your base as a screenshot. Please note that this @@ -291,28 +217,9 @@ dialogs: newMods: Newly installed Mods resourceLoadFailed: title: Resources failed to load - demoLinkText: shapez demo on Steam - descWeb: "One ore more resources could not be loaded. Make sure you have a - stable internet connection and try again. If this still doesn't - work, make sure to also disable any browser extensions (including - adblockers).<br><br> As an alternative, you can also play the - <demoOnSteamLinkText>. <br><br> Error Message:" descSteamDemo: "One ore more resources could not be loaded. Try restarting the game - If that does not help, try reinstalling and verifying the game files via Steam. <br><br> Error Message:" - steamSsoError: - title: Full Version Logout - desc: You have been logged out from the Full Browser Version since either your - network connection is unstable or you are playing on another - device.<br><br> Please make sure you don't have shapez open in any - other browser tab or another computer with the same Steam - account.<br><br> You can login again in the main menu. - steamSsoNoOwnership: - title: Full Edition not owned - desc: In order to play the Full Edition in your Browser, you need to own both - the base game and the Puzzle DLC on your Steam account.<br><br> - Please make sure you own both, signed in with the correct Steam - account and then try again. ingame: keybindingsOverlay: moveMap: Move @@ -451,40 +358,6 @@ ingame: one_miner: 1 Miner n_miners: <amount> Miners limited_items: Limited to <max_throughput> - watermark: - title: Demo version - desc: Click here to see the Steam version advantages! - get_on_steam: Get on steam - standaloneAdvantages: - no_thanks: No, thanks! - points: - levels: - title: 12 New Levels - desc: For a total of 26 levels! - buildings: - title: 22 New Buildings - desc: Fully automate your factory! - markers: - title: ∞ Markers - desc: Never get lost in your factory! - wires: - title: Wires - desc: An entirely new dimension! - darkmode: - title: Dark Mode - desc: Stop hurting your eyes! - support: - title: Support me - desc: I develop it in my spare time! - achievements: - title: Achievements - desc: Hunt them all! - mods: - title: Modding support! - desc: Over 80 mods available! - titleV2: "Get the full version now on Steam to unlock:" - titleExpiredV2: Demo completed! - titleEnjoyingDemo: Enjoy the demo? puzzleEditorSettings: zoneTitle: Zone zoneWidth: Width @@ -581,7 +454,7 @@ buildings: name: Cutter (Quad) description: Cuts shapes into four parts. <strong>If you use only one part, be sure to destroy the other parts or it will stall!</strong> - rotater: + rotator: default: name: Rotate description: Rotates shapes clockwise by 90 degrees. @@ -713,8 +586,8 @@ buildings: default: name: Virtual Cutter description: Virtually cuts the shape into two halves. - rotater: - name: Virtual Rotater + rotator: + name: Virtual Rotator description: Virtually rotates the shape, both clockwise and counter-clockwise. unstacker: name: Virtual Unstacker @@ -753,9 +626,9 @@ storyRewards: otherwise <strong>it will clog and stall</strong> - For this purpose I have given you the <strong>trash</strong>, which destroys everything you put into it! - reward_rotater: + reward_rotator: title: Rotating - desc: The <strong>rotater</strong> has been unlocked! It rotates shapes + desc: The <strong>rotator</strong> has been unlocked! It rotates shapes clockwise by 90 degrees. reward_painter: title: Painting @@ -782,11 +655,11 @@ storyRewards: title: Tunnel desc: The <strong>tunnel</strong> has been unlocked - You can now tunnel items through belts and buildings with it! - reward_rotater_ccw: + reward_rotator_ccw: title: CCW Rotating - desc: You have unlocked a variant of the <strong>rotater</strong> - It allows + desc: You have unlocked a variant of the <strong>rotator</strong> - It allows you to rotate shapes counter-clockwise! To build it, select the - rotater and <strong>press 'T' to cycle through its + rotator and <strong>press 'T' to cycle through its variants</strong>! reward_miner_chainable: title: Chaining Extractor @@ -852,9 +725,9 @@ storyRewards: desc: You have now unlocked the <strong>belt reader</strong>! It allows you to measure the throughput of a belt.<br><br>And wait until you unlock wires - then it gets really useful! - reward_rotater_180: - title: Rotater (180 degrees) - desc: You just unlocked the 180 degrees <strong>rotater</strong>! - It allows + reward_rotator_180: + title: Rotator (180 degrees) + desc: You just unlocked the 180 degrees <strong>rotator</strong>! - It allows you to rotate a shape by 180 degrees (Surprise! :D) reward_display: title: Display @@ -879,7 +752,7 @@ storyRewards: title: Virtual Processing desc: I just gave a whole bunch of new buildings which allow you to <strong>simulate the processing of shapes</strong>!<br><br> You can - now simulate a cutter, rotater, stacker and more on the wires layer! + now simulate a cutter, rotator, stacker and more on the wires layer! With this you now have three options to continue the game:<br><br> - Build an <strong>automated machine</strong> to create any possible shape requested by the HUB (I recommend to try it!).<br><br> - Build @@ -1111,7 +984,7 @@ keybindings: underground_belt: Tunnel miner: Extractor cutter: Cutter - rotater: Rotate + rotator: Rotate stacker: Stacker mixer: Color Mixer painter: Painter @@ -1174,12 +1047,6 @@ about: changelog: title: Changelog demo: - features: - restoringGames: Restoring savegames - importingGames: Importing savegames - oneGameLimit: Limited to one savegame - customizeKeybindings: Customizing Keybindings - exportingBase: Exporting whole Base as Image settingNotAvailable: Not available in the demo. tips: - The hub accepts input of any kind, not just the current shape! @@ -1333,16 +1200,7 @@ mods: version: Version modWebsite: Website openFolder: Open Mods Folder - folderOnlyStandalone: Opening the mod folder is only possible when running the standalone. browseMods: Browse Mods modsInfo: To install and manage mods, copy them to the mods folder within the game directory. You can also use the 'Open Mods Folder' button on the top right. - noModSupport: You need the standalone version on Steam to install mods. - togglingComingSoon: - title: Coming Soon - description: Enabling or disabling mods is currently only possible by copying - the mod file from or to the mods/ folder. However, being able to - toggle them here is planned for a future update! - browserNoSupport: Due to browser restrictions it is currently only possible to - install mods in the Steam version - Sorry! diff --git a/translations/base-sr.yaml b/translations/base-sr.yaml index 935f35c1..71181f5d 100644 --- a/translations/base-sr.yaml +++ b/translations/base-sr.yaml @@ -1,25 +1,3 @@ -steamPage: - shortText: shapez.io je igra o pravljenju fabrika za automatizaciju stvaranja i - spajanja sve složenijih oblika na beskonačno velikoj mapi. - discordLinkShort: Oficijalni Diskord - intro: >- - Shapez.io je opuštena igra u kojoj gradite fabrike za automatizaciju - proizvodnje geometrijskih oblika. - - Kako se nivo povećava, oblici postaju sve složeniji, tako da morate da proširite fabriku na beskonačno velikoj mapi. - - I ako sve to nije dovoljno, moraćete da proizvodite sve više i više oblika kako bi zadovoljili zahteve proizvodnje - jedina stvar koja pomaže je skaliranje! - - Na početku je dovoljno samo da prerađujete oblike, ali kasnije morate da ih farbate - za ovaj posao morate da vadite i mešate boje! - - Kupovinom ove igre na Steam-u dobijate pristup punoj verziji igre, ali prvo možete da odigrate probnu verziju na shapez.io pa onda da se odlučite kasnije! - what_others_say: Šta pričaju ljudi o shapez.io - nothernlion_comment: Igra je sjajna - vreme je proletelo, divno sam se proveo igrajući. - notch_comment: Dođavola. Stvarno bi trebao da spavam, ali mislim da sam upravo - shvatio kako da napravim računar u shapez.io - steam_review_comment: Ova igra mi je ukrala život i više ga ne želim nazad. - Veoma opuštena igra o građenju fabrika, koja me stalno tera da - prerađujem fabrike kako bi bile efikasnije. global: loading: Učitavanje error: Greška @@ -53,57 +31,19 @@ global: space: SPACE loggingIn: Prijavljivanje loadingResources: Downloading additional resources (<percentage> %) - discount: -<percentage>% - discountSummerSale: SPECIAL PROMOTION! Offer ends 7 July -demoBanners: - title: Probna Verzija - intro: |- - Get the full game <strong>now</strong> to unlock:<ul> - <li>All 26 levels + infinite Freeplay</li> - <li>22 new buildings</li> - <li>Mod support</li> - <li>Achievements</li> - <li>Dark Mode</li> - <li>... and a lot more!</li> - </ul> - playtimeDisclaimer: The full version contains more than <strong>20 hours of content</strong>. - playerCount: <playerCount> players like you are currently playing shapez on Steam - untilEndOfDemo: Until end of demo - playtimeDisclaimerDownload: You can continue your savegame in the full version! - Click <strong>here</strong> to download your savegame. - titleV2: "Play the full version now for:" mainMenu: play: Igraj continue: Nastavi newGame: Nova Igra - changelog: Promene - subreddit: Reddit importSavegame: Uvezi - openSourceHint: Ova igra je otvorenog koda! - discordLink: Oficijalni Diskord server helpTranslate: Pomozite sa prevođenjem! - madeBy: Napravio <author-link> - browserWarning: Izvinjavamo se, pošto je poznato da se ova igra koči u - pretraživaču! Za puno iskustvo nabavite samostalnu verziju ili koristite - chrome. savegameLevel: Nivo <x> savegameLevelUnknown: Nepoznat Nivo savegameUnnamed: Neimenovano - puzzleMode: Slagalice - back: Nazad - puzzleDlcText: Uživate u optimizovanju i minimizovanju fabrika? Nabavite - ekspanziju sa Slagalicama na Steam-u, za još više zabave! - puzzleDlcWishlist: Dodaj na listu želja! - puzzleDlcViewNow: Pogledaj ekspanziju mods: - title: Active Mods warningPuzzleDLC: Playing the Puzzle DLC is not possible with mods. Please disable all mods to play the DLC. - playingFullVersion: You are now playing the full version! - logout: Logout noActiveSavegames: No active savegames found - Click play to start a new game! - playFullVersionV2: Bough shapez on Steam? Play the full version in your Browser! - playFullVersionStandalone: You can now also play the full version in your Browser! dialogs: buttons: ok: OK @@ -112,7 +52,6 @@ dialogs: later: Kasnije restart: Ponovo pokreni reset: Resetuj - getStandalone: Nabavite samostalnu igru deleteGame: Znam šta radim viewUpdate: Pogledajte ažuriranje showUpgrades: Prikaži Nadogradnje @@ -151,14 +90,6 @@ dialogs: keybindingsResetOk: title: Podešavanja tastera su resetovana desc: Podešavanja tastera su resetovana na njihove početne vrednosti! - featureRestriction: - title: Probna Verzija - desc: Pokušali ste da pristupite funkciji (<feature>) koja nije dostupna u demo - verziji. Za puno iskustvo, nabavite samostalnu igru! - oneSavegameLimit: - title: Ograničen broj sačuvanih igara - desc: Možete imati samo jednu sačuvanu igru u probnoj verziji. Izbrišite - postojeću ili nabavite samostalnu igru! updateSummary: title: Novo ažuriranje! desc: "Ovo su promene od zadnjeg igranja:" @@ -194,9 +125,6 @@ dialogs: titleEdit: Uredi Putokaz desc: Dajte mu neko smisleno ime, možete koristiti i <strong>kratak kod</strong> oblika (Koji možete da generišete <link>ovde</link>) - markerDemoLimit: - desc: U probnoj verziji možete imati samo dva putokaza istovremeno. Nabavite - samostalnu igru za beskonačno mnogo putokaza! exportScreenshotWarning: title: Izvezi sliku ekrana desc: Hoćete da izvezete sliku cele fabrike kao snimak ekrana. Ovaj proces može @@ -291,28 +219,9 @@ dialogs: newMods: Newly installed Mods resourceLoadFailed: title: Resources failed to load - demoLinkText: shapez demo on Steam - descWeb: "One ore more resources could not be loaded. Make sure you have a - stable internet connection and try again. If this still doesn't - work, make sure to also disable any browser extensions (including - adblockers).<br><br> As an alternative, you can also play the - <demoOnSteamLinkText>. <br><br> Error Message:" descSteamDemo: "One ore more resources could not be loaded. Try restarting the game - If that does not help, try reinstalling and verifying the game files via Steam. <br><br> Error Message:" - steamSsoError: - title: Full Version Logout - desc: You have been logged out from the Full Browser Version since either your - network connection is unstable or you are playing on another - device.<br><br> Please make sure you don't have shapez open in any - other browser tab or another computer with the same Steam - account.<br><br> You can login again in the main menu. - steamSsoNoOwnership: - title: Full Edition not owned - desc: In order to play the Full Edition in your Browser, you need to own both - the base game and the Puzzle DLC on your Steam account.<br><br> - Please make sure you own both, signed in with the correct Steam - account and then try again. ingame: keybindingsOverlay: moveMap: Kretanje @@ -452,40 +361,6 @@ ingame: one_miner: 1 Rudar n_miners: <amount> Rudara limited_items: Ograničeno na <max_throughput> - watermark: - title: Probna Verzija - desc: Kliknite ovde da pogledate prednosti Steam verzije! - get_on_steam: Nabavite na Steam-u - standaloneAdvantages: - no_thanks: Neka, hvala! - points: - levels: - title: 12 Novih Nivoa - desc: Ukupno 26 nivoa! - buildings: - title: 22 Novih Građevina - desc: U potpunosti automatizujte fabriku! - markers: - title: ∞ Putokaza - desc: Nikada se nećete izgubiti u fabrici! - wires: - title: Žice - desc: Potpuno nova dimenzija! - darkmode: - title: Tamna Tema - desc: Recite stop bolu u očima! - support: - title: Podržite me - desc: Razvijam igru u slobodno vreme! - achievements: - title: Dostignuća - desc: Osvojite ih sve! - mods: - title: Modding support! - desc: Over 80 mods available! - titleV2: "Get the full version now on Steam to unlock:" - titleExpiredV2: Demo completed! - titleEnjoyingDemo: Enjoy the demo? puzzleEditorSettings: zoneTitle: Zone zoneWidth: Širina @@ -584,7 +459,7 @@ buildings: name: Rezač (četvorostruki) description: Reže oblike na četiri dela. <strong>Ako se koristi samo jedan deo, ostali se moraju uništiti kako bi sprečili zastoj!</strong> - rotater: + rotator: default: name: Obrtač (↻) description: Okreće oblike za 90 stepeni u smeru kazaljke na satu. @@ -714,7 +589,7 @@ buildings: default: name: Virtuelni Rezač description: Virtuelno reže oblik na dve polovine. - rotater: + rotator: name: Virtuelni Obrtač description: Virtuelno okreće oblike, i u smeru kazaljke na satu i suprotno od smera kazaljke na satu. @@ -754,7 +629,7 @@ storyRewards: suprotnom će se<strong>začepiti i stati sa radom</strong> - Baš zbog toga imate <strong>Smeće</strong>, koje uništava sve što uđe u njega! - reward_rotater: + reward_rotator: title: Obrtanje desc: <strong>Obrtač</strong> je otključan! On okreće oblike za 90 stepeni u smeru kazaljke na satu. @@ -782,7 +657,7 @@ storyRewards: title: Tunel desc: <strong>Tunel</strong> je otključan - Omogućava prenos stvari ispod traka i ostalih građevina! - reward_rotater_ccw: + reward_rotator_ccw: title: Rotacija u smeru suprotnom od kazaljke na satu desc: Varijacija <strong>obrtača</strong> je otključana - Omogućuje okretanje u smeru suprotnom od kazaljke na satu! Odaberi obrtač i @@ -853,7 +728,7 @@ storyRewards: desc: <strong>Čitač Trake</strong> je otključan! On meri propusnost pokretne trake.<br><br>Biće vam od velike pomoći, samo se stripite dok ne otključate žice! - reward_rotater_180: + reward_rotator_180: title: Obrtač (180 stepeni) desc: Otključali ste <strong>obrtač</strong> od 180 stepeni! - On može da okreće oblike za 180 stepeni (Iznenađenje! :D) @@ -1116,7 +991,7 @@ keybindings: underground_belt: Tunel miner: Rudar cutter: Rezač - rotater: Obrtač (↻) + rotator: Obrtač (↻) stacker: Slagač mixer: Mešalica boja painter: Farbač @@ -1179,12 +1054,6 @@ about: changelog: title: Promene demo: - features: - restoringGames: Obnavljanje sačuvanih igara - importingGames: Uvoz sačuvanih igara - oneGameLimit: Ograničenje od jedne sačuvane igre - customizeKeybindings: Prilagođena Podešavanja Tastera - exportingBase: Izvoz slike cele fabrike kao snimak ekrana settingNotAvailable: Nije dostupno u demo verziji. tips: - Središte prihvata oblike bilo koje vrste, ne samo trenutni oblik! @@ -1346,16 +1215,7 @@ mods: version: Version modWebsite: Website openFolder: Open Mods Folder - folderOnlyStandalone: Opening the mod folder is only possible when running the standalone. browseMods: Browse Mods modsInfo: To install and manage mods, copy them to the mods folder within the game directory. You can also use the 'Open Mods Folder' button on the top right. - noModSupport: You need the standalone version on Steam to install mods. - togglingComingSoon: - title: Coming Soon - description: Enabling or disabling mods is currently only possible by copying - the mod file from or to the mods/ folder. However, being able to - toggle them here is planned for a future update! - browserNoSupport: Due to browser restrictions it is currently only possible to - install mods in the Steam version - Sorry! diff --git a/translations/base-sv.yaml b/translations/base-sv.yaml index 4ec90926..94855b03 100644 --- a/translations/base-sv.yaml +++ b/translations/base-sv.yaml @@ -1,26 +1,3 @@ -steamPage: - shortText: shapez.io är ett spel som går ut på att automatisera skapandet av - former med ökande komplexitet inom den oändligt stora världen. - discordLinkShort: Officiell Discord - intro: >- - Shapez.io är att avslappnat spel där man ska bygga fabriker för att - automatisera produktionen av geometriska former. - - Formerna blir svårare och mer komplexa vartefter nivån ökar och du blir tvungen att sprida ut dig över den oändliga kartan. - - Och som om det inte vore nog så som behöver du samtidigt producera exponentiellt mer för att mätta efterfrågan - den enda lösningar är att skala upp produktionen! - - I början skapar man bara former, men senare behöver du även färga dem - för att uppnå detta behöver du samla och blanda färger! - - På Steam kan du köpa den fulla versionen av spelet, men du kan även spela demot på shapez.io först och bestämma dig senare! - what_others_say: Vad folk säger om shapez.io - nothernlion_comment: This game is great - I'm having a wonderful time playing, - and time has flown by. - notch_comment: Oh crap. I really should sleep, but I think I just figured out - how to make a computer in shapez.io - steam_review_comment: This game has stolen my life and I don't want it back. - Very chill factory game that won't let me stop making my lines more - efficient. global: loading: Laddar error: Fel @@ -54,58 +31,19 @@ global: space: MELLANSLAG loggingIn: Loggar in loadingResources: Downloading additional resources (<percentage> %) - discount: -<percentage>% - discountSummerSale: SPECIAL PROMOTION! Offer ends 7 July -demoBanners: - title: Demo-version - intro: |- - Skaffa hela spelet <strong>nu</strong> för att låsa upp:<ul> - <li>Alla 26 nivåer + oändligt Freeplay</li> - <li>22 nya byggnader</li> - <li>Mod-stöd</li> - <li>Resultat</li> - <li>Mörkt läge</li> - <li>... och mycket mer!</li> - </ul> - playtimeDisclaimer: Den fullständiga versionen innehåller mer än <strong>20 - timmars innehåll</strong>. - playerCount: <playerCount> spelare som du spelar just nu shapez på Steam - untilEndOfDemo: Till slutet av demonstrationen - playtimeDisclaimerDownload: Du kan fortsätta ditt savegame i den fullständiga - versionen! Klicka <strong>här</strong> för att ladda ner ditt savegame. - titleV2: "Spela den fullständiga versionen nu för:" mainMenu: play: Spela - changelog: Ändringslogg importSavegame: Importera - openSourceHint: Detta spelet har öppen källkod! - discordLink: Officiell Discord Server helpTranslate: Hjälp till att översätta! - browserWarning: Förlåt, men det är känt att spelet spelar långsamt i din - webbläsare! Skaffa den fristående versionen eller ladda ned Chrome för - en bättre upplevelse. savegameLevel: Nivå <x> savegameLevelUnknown: Okänd Nivå continue: Fortsätt newGame: Nytt spel - madeBy: Skapad av <author-link> - subreddit: Reddit savegameUnnamed: Namnlöst - puzzleMode: Pusselläge - back: Tillbaka - puzzleDlcText: Tycker du om att komprimera och optimera fabriker? Skaffa - pussel-DLCn nu på Steam för ännu mer kul! - puzzleDlcWishlist: Önskelista nu! - puzzleDlcViewNow: Se DLC mods: - title: Active Mods warningPuzzleDLC: Playing the Puzzle DLC is not possible with mods. Please disable all mods to play the DLC. - playingFullVersion: You are now playing the full version! - logout: Logout noActiveSavegames: No active savegames found - Click play to start a new game! - playFullVersionV2: Bough shapez on Steam? Play the full version in your Browser! - playFullVersionStandalone: You can now also play the full version in your Browser! dialogs: buttons: ok: OK @@ -114,7 +52,6 @@ dialogs: later: Senare restart: Starta Om reset: Återställ - getStandalone: Skaffa Fristående deleteGame: Jag vet vad jag gör viewUpdate: Se Uppdatering showUpgrades: Visa Uppgraderingar @@ -152,15 +89,6 @@ dialogs: keybindingsResetOk: title: Tangentbindningarna återställts desc: Alla tangentbindningar har återställts till sina standardvärden! - featureRestriction: - title: Demoversion - desc: Du försökte komma åt en funktion (<feature>) som inte är tillgänglig i - demoversionen. Överväg att skaffa den fristående versionen för den - fulla upplevelsen! - oneSavegameLimit: - title: Begränsad mängd sparfiler - desc: Du kan bara ha en sparfil åt gången i demoversionen. Var snäll och ta bort - den befintliga eller skaffa den fristående versionen! updateSummary: title: Ny uppdatering! desc: "Här är ändringarna sen du senast spelade:" @@ -191,9 +119,6 @@ dialogs: <strong>kortkommando</strong> av en form (som du kan skapa <link>här</link>) titleEdit: Ändra Markör - markerDemoLimit: - desc: Du kan endast skapa två anpassade markörer i demoversionen. Skaffa den - fristående versionen för obegränsade markörer! massCutConfirm: title: Bekräfta Klippning desc: Du klipper många byggnader (<count> för att vara exakt)! Är du säker på @@ -296,28 +221,9 @@ dialogs: newMods: Newly installed Mods resourceLoadFailed: title: Resources failed to load - demoLinkText: shapez demo on Steam - descWeb: "One ore more resources could not be loaded. Make sure you have a - stable internet connection and try again. If this still doesn't - work, make sure to also disable any browser extensions (including - adblockers).<br><br> As an alternative, you can also play the - <demoOnSteamLinkText>. <br><br> Error Message:" descSteamDemo: "One ore more resources could not be loaded. Try restarting the game - If that does not help, try reinstalling and verifying the game files via Steam. <br><br> Error Message:" - steamSsoError: - title: Full Version Logout - desc: You have been logged out from the Full Browser Version since either your - network connection is unstable or you are playing on another - device.<br><br> Please make sure you don't have shapez open in any - other browser tab or another computer with the same Steam - account.<br><br> You can login again in the main menu. - steamSsoNoOwnership: - title: Full Edition not owned - desc: In order to play the Full Edition in your Browser, you need to own both - the base game and the Puzzle DLC on your Steam account.<br><br> - Please make sure you own both, signed in with the correct Steam - account and then try again. ingame: keybindingsOverlay: moveMap: Flytta @@ -456,40 +362,6 @@ ingame: one_miner: 1 Miner n_miners: <amount> Extraktor limited_items: Begränsad till <max_throughput> - watermark: - title: Demoversion - desc: Klicka här för att se fördelarna med Steam-versionen! - get_on_steam: Skaffa på Steam - standaloneAdvantages: - no_thanks: Nej tack! - points: - levels: - title: 12 nya nivåer! - desc: Totalt 26 nivåer! - buildings: - title: 22 nya byggnader! - desc: Helautomatisera din fabrik! - markers: - title: ∞ med markeringar! - desc: Tappa aldrig bort dig i din fabrik längre! - wires: - title: Kablar - desc: En helt ny dimension! - darkmode: - title: Mörkt läge - desc: Sluta skada dina ögon! - support: - title: Stöd mig - desc: Jag utvecklar det på min fritid! - achievements: - title: Achievements - desc: Hunt them all! - mods: - title: Modding support! - desc: Over 80 mods available! - titleV2: "Get the full version now on Steam to unlock:" - titleExpiredV2: Demo completed! - titleEnjoyingDemo: Enjoy the demo? puzzleEditorSettings: zoneTitle: Zone zoneWidth: Width @@ -575,7 +447,7 @@ buildings: description: Klipper former i fyra delar. <strong>Om du endast använder en del, se till att förstöra de andra, annars kommer de blockera maskinen!</strong> - rotater: + rotator: default: name: Roterare description: Roterar former 90 grader. @@ -718,7 +590,7 @@ buildings: default: name: Virtuell skärare description: Skär virtuellt formen till två halvor. - rotater: + rotator: name: Virtuell roterare description: Roterar virtuellt formen, både medurs och moturs. unstacker: @@ -757,7 +629,7 @@ storyRewards: otherwise <strong>it will clog and stall</strong> - For this purpose I have given you the <strong>trash</strong>, which destroys everything you put into it! - reward_rotater: + reward_rotator: title: Rotation desc: <strong>Roteraren</strong> har blivit upplåst! Den roterar former 90 grader medsols. @@ -786,7 +658,7 @@ storyRewards: title: Tunnel desc: <strong>Tunneln</strong> blivit upplåst- Du kan nu transportera saker under rullband och byggnader med den! - reward_rotater_ccw: + reward_rotator_ccw: title: Motsols rotation desc: Du har låst upp en variant av <strong>roteraren</strong> - Den låter dig rotera saker motsols! För att bygga den, välj roteraren och @@ -860,7 +732,7 @@ storyRewards: desc: You have now unlocked the <strong>belt reader</strong>! It allows you to measure the throughput of a belt.<br><br>And wait until you unlock wires - then it gets really useful! - reward_rotater_180: + reward_rotator_180: title: Roterare (180 grader) desc: Du låste precis upp <strong>roteraren</strong>! - Den låter dig rotera former med 180 grader (Vilken överraskning! :D) @@ -887,7 +759,7 @@ storyRewards: title: Virtual Processing desc: I just gave a whole bunch of new buildings which allow you to <strong>simulate the processing of shapes</strong>!<br><br> You can - now simulate a cutter, rotater, stacker and more on the wires layer! + now simulate a cutter, rotator, stacker and more on the wires layer! With this you now have three options to continue the game:<br><br> - Build an <strong>automated machine</strong> to create any possible shape requested by the HUB (I recommend to try it!).<br><br> - Build @@ -1116,7 +988,7 @@ keybindings: underground_belt: Tunnel miner: Extraktor cutter: Klippare - rotater: Roterare + rotator: Roterare stacker: Staplare mixer: Färgblandare painter: Färgläggare @@ -1183,12 +1055,6 @@ about: changelog: title: Changelog demo: - features: - restoringGames: Återställer sparfiler - importingGames: Importerar sparfiler - oneGameLimit: Limiterad till endast en sparfil - customizeKeybindings: Finjustera snabbtangenter - exportingBase: Exportera hela fabriken som en bild settingNotAvailable: Inte tillgänglig i demoversionen. tips: - Hubben accepterar alla sorters former, inte bara den nuvarande formen! @@ -1342,16 +1208,7 @@ mods: version: Version modWebsite: Website openFolder: Open Mods Folder - folderOnlyStandalone: Opening the mod folder is only possible when running the standalone. browseMods: Browse Mods modsInfo: To install and manage mods, copy them to the mods folder within the game directory. You can also use the 'Open Mods Folder' button on the top right. - noModSupport: You need the standalone version on Steam to install mods. - togglingComingSoon: - title: Coming Soon - description: Enabling or disabling mods is currently only possible by copying - the mod file from or to the mods/ folder. However, being able to - toggle them here is planned for a future update! - browserNoSupport: Due to browser restrictions it is currently only possible to - install mods in the Steam version - Sorry! diff --git a/translations/base-tr.yaml b/translations/base-tr.yaml index 1d2b0c21..60b1eb8c 100644 --- a/translations/base-tr.yaml +++ b/translations/base-tr.yaml @@ -1,25 +1,3 @@ -steamPage: - shortText: shapez.io giderek karmaşıklaşan şekillerin sonsuz bir harita üzerinde - üretimi ve birleştirilmesi hakında bir oyundur. - discordLinkShort: Official Discord - intro: >- - Shapez.io geometrik şekillerin otomatik üretimi için fabrika - kurabildiğiniz sakinleştirici bir oyundur. - - Seviye arttıkça şekiller daha karmaşık hale gelecek ve sonsuz haritada genişlemen gerekecek! - - Ve bu yeterli değilmiş gibi, şekiller için talebi karşılamak için daha fazla üretim yapmalısın - buna tek yardımcı olacak şey ise genişleme! - - En başta sadece şekilleri işlerken, sonradan onları boyaman gerekecek - bunun için boyaları çıkarmalı ve karıştırmalısın! - - Oyunu Steam'de satın almak tam sürüme erişimi sağlayacak, ama herzaman shapez.io deneme sürümünü oynayıp sonradan karar verebilirsin! - what_others_say: İnsanlar shapez.io hakkında ne düşünüyor? - nothernlion_comment: Bu oyun süper - Oynarken harika vakit geçiriyorum ve zaman akıp gidiyor. - notch_comment: Hay aksi. Gerçekten uyumalıyım, ama şimdi shapez.io'da nasıl bir - bilgisayar yapabileceğimi çözdüm. - steam_review_comment: Bu oyun hayatımı çaldı ve geri istemiyorum. Üretim - hatlarımı daha verimli yapmamı engelleyemeyecek kadar güzel bir fabrika - oyunu. global: loading: Yükleniyor error: Hata @@ -53,57 +31,19 @@ global: space: SPACE loggingIn: Giriş yapılıyor loadingResources: Ek kaynaklar indiriliyor... (<percentage> %) - discount: -<percentage>% - discountSummerSale: SPECIAL PROMOTION! Offer ends 7 July -demoBanners: - title: Deneme Sürümü - intro: |- - Kilidi açmak için tam oyunu <strong>şimdi</strong> edinin:<ul> - <li>26 seviyenin tamamı + sonsuz Serbest Oynanış</li> - <li>22 yeni bina</li> - <li>Mod desteği</li> - <li>Başarılar</li> - <li>Karanlık Mod</li> - <li>... ve çok daha fazlası!</li> - </ul> - playtimeDisclaimer: Tam sürüm <strong>20 saatten fazla içerik</strong> içerir. - playerCount: <playerCount> sizin gibi oyuncular şu anda Steam'de shapez oynuyor - untilEndOfDemo: Demo sonuna kadar - playtimeDisclaimerDownload: Kayıtlı oyununuza tam sürümde devam edebilirsiniz! - Kayıtlı oyununuzu indirmek için <strong>burayı</strong> tıklayın. - titleV2: "Tam sürümü şimdi oynayın:" mainMenu: play: Oyna - changelog: Değişiklik Günlüğü importSavegame: Kayıt Yükle - openSourceHint: Bu oyun açık kaynak kodlu! - discordLink: Resmi Discord Sunucusu helpTranslate: Çeviriye yardım et! - browserWarning: Üzgünüz, bu oyunun tarayıcınızda yavaş çalıştığı biliniyor! Tam - sürümü satın alın veya iyi performans için Chrome tarayıcısını kullanın. savegameLevel: Seviye <x> savegameLevelUnknown: Bilinmeyen seviye continue: Devam et newGame: YENİ OYUN - madeBy: <author-link> tarafından yapıldı - subreddit: Reddit savegameUnnamed: İsimsiz - puzzleMode: Yapboz Modu - back: Geri - puzzleDlcText: Fabrikaları küçültmeyi ve verimli hale getirmekten keyif mi - alıyorsun? Şimdi Yapboz Paketini (DLC) Steam'de alarak keyfine keyif - katabilirsin! - puzzleDlcWishlist: İstek listene ekle! - puzzleDlcViewNow: Paketi (DLC) görüntüle mods: - title: Aktif Modlar warningPuzzleDLC: Modlarla Yapboz DLC'sini oynamak mümkün değil. Lütfen Yapboz DLC'sini oynayabilmek için bütün modları devre dışı bırakınız. - playingFullVersion: You are now playing the full version! - logout: Logout - noActiveSavegames: No active savegames found - Click play to start a new game! - playFullVersionV2: Bough shapez on Steam? Play the full version in your Browser! - playFullVersionStandalone: You can now also play the full version in your Browser! + noActiveSavegames: Aktif kayıtlı oyun bulunamadı - Yeni bir oyun başlatmak için tıkla! dialogs: buttons: ok: OK @@ -112,7 +52,6 @@ dialogs: later: Sonra restart: Yeniden başla reset: Sıfırla - getStandalone: Tam sürüme eriş deleteGame: Ne yaptığımi biliyorum viewUpdate: Güncellemeleri Görüntüle showUpgrades: Geliştirmeleri Göster @@ -150,14 +89,6 @@ dialogs: keybindingsResetOk: title: Tuş Atamaları sıfırlandı desc: Tuş atamaları varsayılan duruma sıfırlandı! - featureRestriction: - title: Deneme Sürümü - desc: Demoda olmayan bir özelliğe (<feature>) erişmeye çalıştınız. Tam deneyim - için tam versiyonu satın alın. - oneSavegameLimit: - title: Sınırlı Oyun Kaydı - desc: Deneme sürümünde sadece tek bir oyun kaydınız olabilir. Lütfen mevcut - kaydı silin veya tam sürümü satın alın! updateSummary: title: Yeni güncelleme! desc: "Son oynadığınızdan bu yana gelen değişikler:" @@ -187,9 +118,6 @@ dialogs: desc: Anlamlı bir isim ver. Ayrıca <strong>Şekil kodu da</strong> koyabilirsiniz (<link>Buradan</link> kod yapabilirsiniz ) titleEdit: Konum İşaretini Düzenle - markerDemoLimit: - desc: Deneme sürümünde sadece iki adet yer imi oluşturabilirsiniz. Sınırsız yer - imi için tam sürümü alın! massCutConfirm: title: Kesmeyi onayla desc: Çok fazla yapı kesiyorsunuz (tam olarak <count> adet)! Bunu yapmak @@ -238,7 +166,7 @@ dialogs: title: Kötü Yapboz desc: "Yapboz yüklenirken hata oluştu:" offlineMode: - title: Çevrİmdışı Modu + title: Çevrimdışı Modu desc: Sunuculara ulaşamadık, bu yüzden oyun çevrimdışı modda çalışmak zorunda. Lütfen aktif bir internet bağlantısı olduğundan emin olunuz. puzzleDownloadError: @@ -293,29 +221,10 @@ dialogs: missingMods: Eksik Modlar newMods: Yeni yüklenen Modlar resourceLoadFailed: - title: Resources failed to load - demoLinkText: shapez demo on Steam - descWeb: "One ore more resources could not be loaded. Make sure you have a - stable internet connection and try again. If this still doesn't - work, make sure to also disable any browser extensions (including - adblockers).<br><br> As an alternative, you can also play the - <demoOnSteamLinkText>. <br><br> Error Message:" - descSteamDemo: "One ore more resources could not be loaded. Try restarting the - game - If that does not help, try reinstalling and verifying the - game files via Steam. <br><br> Error Message:" - steamSsoError: - title: Full Version Logout - desc: You have been logged out from the Full Browser Version since either your - network connection is unstable or you are playing on another - device.<br><br> Please make sure you don't have shapez open in any - other browser tab or another computer with the same Steam - account.<br><br> You can login again in the main menu. - steamSsoNoOwnership: - title: Full Edition not owned - desc: In order to play the Full Edition in your Browser, you need to own both - the base game and the Puzzle DLC on your Steam account.<br><br> - Please make sure you own both, signed in with the correct Steam - account and then try again. + title: Kaynaklar yüklenemedi + descSteamDemo: "Bir veya birden fazla kaynak yüklenemedi. Oyunu tekrar + başlatmayı deneyin - Eğer bu yardımcı olmazsa yeniden yüklemeyi ve + oyun dosyalarını Steam üzerinden doğrulayın. <br><br> Hata Mesajı:" ingame: keybindingsOverlay: moveMap: Hareket Et @@ -456,40 +365,6 @@ ingame: one_miner: 1 Üretici n_miners: <amount> Üretici limited_items: Sınır <max_throughput> - watermark: - title: Deneme sürümü - desc: Steam sürümü avantajlarını görmek için buraya tıklayın! - get_on_steam: Steam'de al - standaloneAdvantages: - no_thanks: Hayır, teşekkürler! - points: - levels: - title: 12 Yeni Seviye - desc: Toplamda 26 seviye! - buildings: - title: 22 Yeni Yapı - desc: Fabrikanı tamamen otomatikleştir! - markers: - title: ∞ Yer imleri - desc: Fabrikanda asla kaybolma! - wires: - title: Kablolar - desc: Tamamen yeni bir boyut! - darkmode: - title: Gece Modu - desc: Gözlerin artık yorulmasın! - support: - title: Beni destekleyin - desc: Boş zamanımda bu oyunu geliştiriyorum! - achievements: - title: Başarımlar - desc: Bütün başarımları açmaya çalış! - mods: - title: Modding support! - desc: Over 80 mods available! - titleV2: "Get the full version now on Steam to unlock:" - titleExpiredV2: Demo completed! - titleEnjoyingDemo: Enjoy the demo? puzzleEditorSettings: zoneTitle: Alan zoneWidth: Genişlik @@ -498,8 +373,8 @@ ingame: clearItems: Eşyaları temizle share: Paylaş report: Şikayet et - clearBuildings: Clear Buildings - resetPuzzle: Reset Puzzle + clearBuildings: Yapıları temizle + resetPuzzle: Yapbozu sıfırla puzzleEditorControls: title: Yapboz Oluşturucu instructions: @@ -584,7 +459,7 @@ buildings: description: Şekilleri dört parçaya böler. <strong>Eğer sadece bir çıktıyı kullanıyorsanız diğer çıkan parçaları yok etmeyi unutmayın, yoksa kesim durur!</strong> - rotater: + rotator: default: name: Döndürücü description: Şekilleri saat yönünde 90 derece döndürür. @@ -723,7 +598,7 @@ buildings: default: name: Sanal Kesici description: Sanal olarak şekli ikiye böler. - rotater: + rotator: name: Sanal Döndürücü description: Sanal olarak şekli saat yönünde veya saatin tersi yönünde döndürür. unstacker: @@ -761,7 +636,7 @@ storyRewards: kullanmayı veya çöpe atmayı unutma yoksa <strong>makine tıkanır</strong>! - Bu nedenle sana gönderdiğin bütün her şeyi yok eden <strong>çöpü</strong> de verdim! - reward_rotater: + reward_rotator: title: Döndürme desc: <strong>Döndürücü</strong> açıldı! Döndürücü şekilleri saat yönünde 90 derece döndürür. @@ -789,7 +664,7 @@ storyRewards: title: Tünel desc: <strong>Tünel</strong> açıldı - Artık eşyaları taşıma bantları ve yapılar altından geçirebilirsiniz! - reward_rotater_ccw: + reward_rotator_ccw: title: Saat yönünün tersinde Döndürme desc: <strong>Döndürücünün</strong> farklı bir türünü açtın - Şekiller artık saat yönünün tersinde döndürülebilir! İnşa etmek için döndürücüyü @@ -866,7 +741,7 @@ storyRewards: desc: <strong>Bant okuyucu</strong> açıldı! Bu yapı taşıma bandındaki akış hızını ölçmeyi sağlar.<br><br>Kabloları açana kadar bekle - o zaman çok kullanışlı olacak. - reward_rotater_180: + reward_rotator_180: title: Döndürücü (180 derece) desc: 180 derece <strong>döndürücüyü</strong> açtınız! - Şekilleri 180 derece döndürür (Süpriz! :D) @@ -953,7 +828,7 @@ settings: language: title: Dil description: Dili değiştirir. Tüm çeviriler kullanıcı katkılarıyla - oluşturulmuştur ve tam olmayabilir! + oluşturulmuştur ve eksiksiz olmayabilir! fullscreen: title: Tam Ekran description: En iyi oyun tecrübesi için oyunun tam ekranda oynanması tavsiye @@ -1098,10 +973,10 @@ keybindings: general: Genel ingame: Oyun navigation: Hareket - placement: Yerleştİrme - massSelect: Çoklu Seçİm + placement: Yerleştirme + massSelect: Çoklu Seçim buildings: Yapı Kısayolları - placementModifiers: Yerleştİrme Özellİklerİ + placementModifiers: Yerleştirme Özellikleri mods: Modlar tarafından sağlandı mappings: confirm: Kabul @@ -1122,7 +997,7 @@ keybindings: underground_belt: Tünel miner: Üretici cutter: Kesici - rotater: Döndürücü + rotator: Döndürücü stacker: Kaynaştırıcı mixer: Renk Karıştırıcısı painter: Boyayıcı @@ -1189,12 +1064,6 @@ about: changelog: title: Değişiklik Günlüğü demo: - features: - restoringGames: Oyun kayıtlarını yükleme - importingGames: Oyun kayıtlarını indirme - oneGameLimit: Bir oyun kaydıyla sınırlı - customizeKeybindings: Tuş Atamalarını Değiştirme - exportingBase: Bütün merkezi resim olarak dışa aktarma settingNotAvailable: Demo sürümünde mevcut değil tips: - Merkez, sadece mevcut şekli değil, her türlü girişi kabul eder! @@ -1282,17 +1151,17 @@ puzzleMenu: noPuzzles: Bu kısımda yapboz yok. categories: levels: Seviyeler - new: Yenİ - top-rated: En İyİ Değerlendirilen + new: Yeni + top-rated: En İyi Değerlendirilen mine: Yapbozlarım easy: Kolay hard: Zor completed: Tamamlanan medium: Orta - official: Resmİ + official: Resmi trending: Bugün öne çıkan trending-weekly: Haftalık öne çıkan - categories: Kategorİler + categories: Kategoriler difficulties: Zorluğa göre account: Yapbozlarım search: Ara @@ -1362,16 +1231,7 @@ mods: version: Sürüm modWebsite: İnternet sitesi openFolder: Mod Klasörünü Aç - folderOnlyStandalone: Mod klasörünü açmak sadece tam sürümü çalıştırıyorken mümkün. browseMods: Modlara Gözat modsInfo: Modları yüklemek ve yönetmek için, bunları oyun dizini içerisindeki Modlar klasörüne kopyalayın. Ayrıca sağ üstteki 'Modlar Klasörünü Aç' düğmesini de kullanabilirsiniz. - noModSupport: Mod yükleyebilmek için tam sürümü çalıştırmalısınız. - togglingComingSoon: - title: Yakında Gelecek - description: Modları etkinleştirmek veya devre dışı bırakmak şu anda yalnızca - dosyaları Mod klasörüne kopyalayarak mümkündür. Ancak modları burada - değiştirmek gelecekteki bir güncelleme için planlanmıştır! - browserNoSupport: Due to browser restrictions it is currently only possible to - install mods in the Steam version - Sorry! diff --git a/translations/base-uk.yaml b/translations/base-uk.yaml index 259426ea..4fef9d33 100644 --- a/translations/base-uk.yaml +++ b/translations/base-uk.yaml @@ -1,26 +1,3 @@ -steamPage: - shortText: shapez.io — це гра про будування фабрик для автоматизації створення - та обробки все більш складних форм на нескінченно розширюваній карті. - discordLinkShort: Official Discord - intro: >- - Shapez.io - розслабляюча гра, в якій вам потрібно будувати фабрики для - автоматизованого виробництва геометричних фігур. - - З кожним рівнем зростає складність фігур, і вам доведеться розширювати виробництво на нескінченній карті. - - Якщо цього не достатньо, вам також необхідно виробляти експоненціально більше, аби задовільнити потреби що ростуть - може допомогти лише масштабування! - - Спочатку ви лише обробляєте фігури, пізніше їх потрібно буде ще й фарбувати - для цього необхідно буде видобувати та змішувати кольори! - - Купуючи гру в Steam ви отримаєте доступ до повної версії гри, але ви також можете спробувати демо-версію гри на shapez.io та вирішити пізніше! - what_others_say: Відгуки людей про shapez.io - nothernlion_comment: Ця гра неймовірна - я чудово проводжу у ній час, не - помічаючи як швидко він плине. - notch_comment: От дідько. Я вже повинен спати, але я щойно розібрався як зробити - комп'ютер у shapez.io - steam_review_comment: Ця гра вкрала моє життя, і я не хочу його повертати. Дуже - крута гра, і вона не зупинить мене постійно підвищувати ефективність - ліній. global: loading: Завантаження error: Помилка @@ -54,57 +31,19 @@ global: space: SPACE loggingIn: Вхід у систему loadingResources: Downloading additional resources (<percentage> %) - discount: -<percentage>% - discountSummerSale: SPECIAL PROMOTION! Offer ends 7 July -demoBanners: - title: Демо-версія - intro: |- - Отримайте повну гру <strong>зараз</strong>, щоб розблокувати:<ul> - <li>Усі 26 рівнів + нескінченна безкоштовна гра</li> - <li>22 нових будівель</li> - <li>Підтримка модифікацій</li> - <li>Досягнення</li> - <li>Темний режим</li> - <li>... та багато іншого!</li> - </ul> - playtimeDisclaimer: Повна версія містить понад <strong>20 години вмісту</strong>. - playerCount: <playerCount> гравців як ви зараз грають у shapez на Steam - untilEndOfDemo: До кінця демонстрації - playtimeDisclaimerDownload: Ви можете продовжити свою збережену гру в повній - версії! Натисніть <strong>тут</strong>, щоб завантажити збережену гру. - titleV2: "Грайте у повну версію зараз для:" mainMenu: play: Грати continue: Продовжити newGame: Нова гра - changelog: Список змін - subreddit: Reddit importSavegame: Імпортувати - openSourceHint: Ця гра з відкритим вихідним кодом! - discordLink: Офіційний Discord сервер helpTranslate: Допоможіть з перекладом! - madeBy: Зробив <author-link> - browserWarning: Вибачте, але гра, як відомо, працює повільно у вашому браузері! - Завантажте повну версію чи Google Chrome, щоб отримати більше - задоволення від гри. savegameLevel: Рівень <x> savegameLevelUnknown: Невідомий рівень savegameUnnamed: Без назви - puzzleMode: Режим Головоломки - back: Назад - puzzleDlcText: Вам подобається ущільнювати та оптимізовувати фабрики? Тоді - отримайте доповнення Puzzle DLC у Steam для ще більших веселощів! - puzzleDlcWishlist: Додай у список бажаного! - puzzleDlcViewNow: Переглянути DLC mods: - title: Активні моди warningPuzzleDLC: Гра з модами у Puzzle DLC не є можливою. Будь-ласка відключіть усі моди щоб грати у цей режим. - playingFullVersion: You are now playing the full version! - logout: Logout noActiveSavegames: No active savegames found - Click play to start a new game! - playFullVersionV2: Bough shapez on Steam? Play the full version in your Browser! - playFullVersionStandalone: You can now also play the full version in your Browser! dialogs: buttons: ok: Гаразд @@ -113,7 +52,6 @@ dialogs: later: Пізніше restart: Перезавантажити reset: Скинути - getStandalone: Купити гру deleteGame: Я знаю, що роблю viewUpdate: Переглянути оновлення showUpgrades: Показати поліпшення @@ -151,15 +89,6 @@ dialogs: keybindingsResetOk: title: Гарячі клавіші скинуто desc: Гарячі клавіші скинуто до їх початкових значень! - featureRestriction: - title: Демо-версія - desc: Ви спробували отримати доступ до функції (<feature>), яка недоступна в - демонстраційній версії. Подумайте про отримання повної версії, щоб - насолодитися грою сповна! - oneSavegameLimit: - title: Обмежені збереження - desc: Ви можете мати лише одне збереження одночасно в демо-версії. Будь ласка, - видаліть існуюче збереження чи купіть повну версію гри! updateSummary: title: Нове оновлення! desc: "Ось зміни з вашої останньої гри:" @@ -202,9 +131,6 @@ dialogs: descItems: "Виберіть попередньо визначений елемент:" descShortKey: ... або введіть <strong>код</strong> фігури (Який ви можете створити <link>тут</link>) - markerDemoLimit: - desc: Ви можете створити тільки 2 позначки в демо-версії. Отримайте повну версію - гри для створення необмеженної кількості позначок. exportScreenshotWarning: title: Експортувати знімок desc: Ви запросили експорт бази в зображення. Зверніть увагу, що для великої @@ -296,28 +222,9 @@ dialogs: newMods: Нещодавно встановлені моди resourceLoadFailed: title: Resources failed to load - demoLinkText: shapez demo on Steam - descWeb: "One ore more resources could not be loaded. Make sure you have a - stable internet connection and try again. If this still doesn't - work, make sure to also disable any browser extensions (including - adblockers).<br><br> As an alternative, you can also play the - <demoOnSteamLinkText>. <br><br> Error Message:" descSteamDemo: "One ore more resources could not be loaded. Try restarting the game - If that does not help, try reinstalling and verifying the game files via Steam. <br><br> Error Message:" - steamSsoError: - title: Full Version Logout - desc: You have been logged out from the Full Browser Version since either your - network connection is unstable or you are playing on another - device.<br><br> Please make sure you don't have shapez open in any - other browser tab or another computer with the same Steam - account.<br><br> You can login again in the main menu. - steamSsoNoOwnership: - title: Full Edition not owned - desc: In order to play the Full Edition in your Browser, you need to own both - the base game and the Puzzle DLC on your Steam account.<br><br> - Please make sure you own both, signed in with the correct Steam - account and then try again. ingame: keybindingsOverlay: moveMap: Рухатися @@ -460,40 +367,6 @@ ingame: one_miner: 1 Екстрактор n_miners: <amount> Екстракторів limited_items: Обмежено до <max_throughput> - watermark: - title: Демо-версія - desc: Натисніть сюди аби переглянути переваги Steam версії! - get_on_steam: Отримати в Steam - standaloneAdvantages: - no_thanks: Ні, дякую! - points: - levels: - title: 12 нових Рівнів - desc: В цілому 26 рівнів! - buildings: - title: 22 нових Будівель - desc: Для повної автоматизації вашої фабрики! - markers: - title: ∞ Позначок - desc: Забудьте про блукання по фабриці! - wires: - title: Дроти - desc: Цілий новий вимір! - darkmode: - title: Нічний режим - desc: Дайте очам відпочити! - support: - title: Підтримайте мене - desc: Я розробляю гру в свій вільний час! - achievements: - title: Досягнення - desc: Отримайте їх всі! - mods: - title: Modding support! - desc: Over 80 mods available! - titleV2: "Get the full version now on Steam to unlock:" - titleExpiredV2: Demo completed! - titleEnjoyingDemo: Enjoy the demo? puzzleEditorSettings: zoneTitle: Зона zoneWidth: Ширина @@ -604,7 +477,7 @@ buildings: description: Розрізає фігури на 4 частини. <strong>Якщо ви використовуєте не всі частини, не забудьте знищити решту, інакше вони застрягнуть в механізмі!</strong> - rotater: + rotator: default: name: Обертач description: Обертає фігури за годинниковою стрілкою на 90 градусів. @@ -731,7 +604,7 @@ buildings: default: name: Віртуальний Різчик description: Віртуально розрізає фігуру на дві половинки. - rotater: + rotator: name: Віртуальний Обертач description: Віртуально обертає фігуру по часовій стрілці, та проти. unstacker: @@ -770,7 +643,7 @@ storyRewards: Обов'язково позбудьтесь відходів, інакше <strong>вони застрягнуть</strong> - Для цих цілей я дав вам <strong>Смітник</strong>, який знищує все що ви туда направите! - reward_rotater: + reward_rotator: title: Обертання desc: <strong>Обертач</strong> розблоковано! Він повертає фігури за годинниковою стрілкою на 90 градусів. @@ -802,7 +675,7 @@ storyRewards: title: Тунель desc: <strong>Тунель</strong> розблоковано. Ви можете створювати тунелі для преметів через стрічки і будівлі. - reward_rotater_ccw: + reward_rotator_ccw: title: Обертання проти годинникової стрілки desc: Ви розблокували новий варіант <strong>обертача</strong>. Він дозволяє обертати проти годинникової стрілки! Щоб побудувати його виберіть @@ -857,7 +730,7 @@ storyRewards: <strong>річ не безкоштовна</strong>, спочатку вам потрібно створити <strong>фігури креслень</strong>, щоб собі це дозволити! (ті, що ви щойно доставили). - reward_rotater_180: + reward_rotator_180: title: Обертач (180 градусів) desc: Ви щойно відкрили <strong>Обертач</strong> на 180! - Він повертає фігури за годинниковою стрілкою на 180 градусів. (Сюрприз! :D) @@ -1141,7 +1014,7 @@ keybindings: underground_belt: Тунель miner: Екстрактор cutter: Різчик - rotater: Обертач + rotator: Обертач stacker: Укладальник mixer: Змішувач кольорів painter: Фарбувач @@ -1203,12 +1076,6 @@ about: changelog: title: Журнал змін demo: - features: - restoringGames: Відновлення збережень - importingGames: Імпортування збережень - oneGameLimit: Обмежено одним збереженням - customizeKeybindings: Налаштування гарячих клавіш - exportingBase: Експортування цілої бази у вигляді зображення settingNotAvailable: Недоступно в демоверсії. tips: - Центр приймає фігури різного виду, не лише ті що наразі вимагаються! @@ -1377,16 +1244,7 @@ mods: version: Версія modWebsite: Веб-сайт openFolder: Відкрити папку модів - folderOnlyStandalone: Відкрити папку з модами можна лише у десктопній версії. browseMods: Переглянути моди modsInfo: Щоб установити моди та керувати ними, скопіюйте їх у папку mods у папці гри. Ви також можете використати кнопку 'Відкрити папку модів' зверху справа. - noModSupport: Для встановлення модів потрібна десктопна версія Steam. - togglingComingSoon: - title: Незабаром - description: Увімкнення або вимкнення модів наразі можливе лише шляхом - копіювання файлу моду з папки mods/ або до неї. Однак можливість їх - вимкнення та увімкнення тут запланована на майбутні оновлення! - browserNoSupport: Due to browser restrictions it is currently only possible to - install mods in the Steam version - Sorry! diff --git a/translations/base-zh-CN-ISBN.yaml b/translations/base-zh-CN-ISBN.yaml index c2c94617..47c0231a 100644 --- a/translations/base-zh-CN-ISBN.yaml +++ b/translations/base-zh-CN-ISBN.yaml @@ -1,23 +1,3 @@ -steamPage: - shortText: “唯一能限制您的,只有您的想象力!” 《异形工厂》(Shapez.io) - 是一款在无限拓展的地图上,通过建造各类工厂设施,来自动化生产与组合出愈加复杂图形的游戏。 - discordLinkShort: 官方 Discord 服务器 - intro: |- - “奇形怪状,放飞想象!” - “自动生产,尽情创造!” - 《异形工厂》(Shapez.io)是一款能让您尽情发挥创造力,充分享受思维乐趣的IO游戏。 - 游戏很轻松,只需建造工厂,布好设施,无需操作即能自动创造出各种各样的几何图形。 - 挑战很烧脑,随着等级提升,需要创造的图形将会越来越复杂,同时您还需要在无限扩展的地图中持续扩建优化您的工厂。 - 以为这就是全部了吗? 不!图形的生产需求将会指数性增长,持续的扩大规模和熵增带来的无序,将会是令人头痛的问题! - 这还不是全部! 一开始我们创造了图形,然后我们需要学会提取和混合来让它们五颜六色。 - 然后,还有吗? 当然,唯有思维,方能无限。 - - 欢迎免费体验试玩版:“让您的想象力插上翅膀!” - 和最聪明的玩家一起挑战,请访问 Steam 游戏商城购买《异形工厂》(Shapez.io)的完整版, - what_others_say: 来看看玩家们对《异形工厂》(Shapez.io)的评价 - nothernlion_comment: 非常棒的有游戏,我的游戏过程充满乐趣,不觉时间飞逝。 - notch_comment: 哦,天哪!我真得该去睡了!但我想我刚刚搞定如何在游戏里面制造一台电脑出来。 - steam_review_comment: 这是一个不知不觉偷走你时间,但你并不会想要追回的游戏。非常烧脑的挑战,让我这样的完美主义者停不下来,总是希望可以再高效一些。 global: loading: 加载中 error: 错误 @@ -51,46 +31,19 @@ global: space: 空格键 loggingIn: 登录 loadingResources: Downloading additional resources (<percentage> %) - discount: -<percentage>% - discountSummerSale: SPECIAL PROMOTION! Offer ends 7 July -demoBanners: - title: 试玩版 - intro: 购买完整版以解锁所有游戏内容! - playtimeDisclaimer: The full version contains more than <strong>20 hours of content</strong>. - playerCount: <playerCount> players like you are currently playing shapez on Steam - untilEndOfDemo: Until end of demo - playtimeDisclaimerDownload: You can continue your savegame in the full version! - Click <strong>here</strong> to download your savegame. - titleV2: "Play the full version now for:" mainMenu: play: 开始游戏 - changelog: 更新日志 importSavegame: 读取存档 - openSourceHint: 本游戏已开源! - discordLink: 官方Discord服务器 helpTranslate: 帮助我们翻译! - browserWarning: 很抱歉, 本游戏在当前浏览器上可能运行缓慢! 使用 谷歌浏览器 或者购买完整版以得到更好的体验。 savegameLevel: 第<x>关 savegameLevelUnknown: 未知关卡 continue: 继续游戏 newGame: 新游戏 - madeBy: 作者:<author-link> - subreddit: Reddit savegameUnnamed: 存档未命名 - puzzleMode: 谜题模式 - back: 返回 - puzzleDlcText: 新增谜题模式将带给您更多的游戏乐趣! - puzzleDlcWishlist: 添加心愿单! - puzzleDlcViewNow: View Dlc mods: - title: Active Mods warningPuzzleDLC: Playing the Puzzle DLC is not possible with mods. Please disable all mods to play the DLC. - playingFullVersion: You are now playing the full version! - logout: Logout noActiveSavegames: No active savegames found - Click play to start a new game! - playFullVersionV2: Bough shapez on Steam? Play the full version in your Browser! - playFullVersionStandalone: You can now also play the full version in your Browser! dialogs: buttons: ok: 确认 @@ -99,7 +52,6 @@ dialogs: later: 以后 restart: 重新开始 reset: 重置 - getStandalone: 获取完整版 deleteGame: 我没疯!我知道我在做什么! viewUpdate: 查看更新 showUpgrades: 显示设施升级 @@ -134,12 +86,6 @@ dialogs: keybindingsResetOk: title: 重置按键设定 desc: 成功重置所有按键设定! - featureRestriction: - title: 试玩版 - desc: 您尝试使用了<feature>一项功能。该功能在试玩版中不可用。请考虑购买完整版以获得更好的体验。 - oneSavegameLimit: - title: 存档数量限制 - desc: 试玩版中只能保存一份存档。请删除旧存档或者购买完整版! updateSummary: title: 新内容更新啦! desc: "以下为游戏最新更新内容:" @@ -165,8 +111,6 @@ dialogs: 填写一个有意义的名称, 还可以同时包含一个形状的 <strong>短代码</strong> (您可以 <link>点击这里</link> 生成短代码) titleEdit: 编辑地图标记 - markerDemoLimit: - desc: 在试玩版中您只能创建两个地图标记。请获取完整版以创建更多标记。 massCutConfirm: title: 确认剪切 desc: 您将要剪切很多设施,准确来说有<count>种! 您确定要这么做吗? @@ -253,28 +197,9 @@ dialogs: newMods: Newly installed Mods resourceLoadFailed: title: Resources failed to load - demoLinkText: shapez demo on Steam - descWeb: "One ore more resources could not be loaded. Make sure you have a - stable internet connection and try again. If this still doesn't - work, make sure to also disable any browser extensions (including - adblockers).<br><br> As an alternative, you can also play the - <demoOnSteamLinkText>. <br><br> Error Message:" descSteamDemo: "One ore more resources could not be loaded. Try restarting the game - If that does not help, try reinstalling and verifying the game files via Steam. <br><br> Error Message:" - steamSsoError: - title: Full Version Logout - desc: You have been logged out from the Full Browser Version since either your - network connection is unstable or you are playing on another - device.<br><br> Please make sure you don't have shapez open in any - other browser tab or another computer with the same Steam - account.<br><br> You can login again in the main menu. - steamSsoNoOwnership: - title: Full Edition not owned - desc: In order to play the Full Edition in your Browser, you need to own both - the base game and the Puzzle DLC on your Steam account.<br><br> - Please make sure you own both, signed in with the correct Steam - account and then try again. ingame: keybindingsOverlay: moveMap: 移动地图 @@ -397,40 +322,6 @@ ingame: one_miner: 1 个开采器 n_miners: <amount> 个开采器 limited_items: 限制在 <max_throughput> - watermark: - title: 试玩版 - desc: 点击这里了解完整版内容 - get_on_steam: 在Steam商城购买 - standaloneAdvantages: - no_thanks: 不需要,谢谢 - points: - levels: - title: 12 个全新关卡! - desc: 总共 26 个不同关卡! - buildings: - title: 22 个全新设施! - desc: 呈现完全体的全自动工厂! - markers: - title: 无限数量地图标记 - desc: 地图再大,不会迷路! - wires: - title: 电线更新包 - desc: 发挥创造力的全新维度! - darkmode: - title: 暗色模式 - desc: 优雅且护眼的配色! - support: - title: 支持作者 - desc: 我使用闲暇时间开发游戏! - achievements: - title: 成就 - desc: 挑战全成就解锁! - mods: - title: Modding support! - desc: Over 80 mods available! - titleV2: "Get the full version now on Steam to unlock:" - titleExpiredV2: Demo completed! - titleEnjoyingDemo: Enjoy the demo? puzzleEditorSettings: zoneTitle: 区域 zoneWidth: 宽度 @@ -503,7 +394,7 @@ buildings: quad: name: 切割机(四向) description: 将输入的图形切成四块。<strong>如果您只需要其中一块图形,使用<strong>垃圾桶</strong>清除其他图形,否则切割机会停止工作!</strong> - rotater: + rotator: default: name: 旋转机 description: 将<strong>图形</strong>顺时针旋转90度。 @@ -629,7 +520,7 @@ buildings: default: name: 虚拟切割机 description: 模拟将<strong>图形</strong>切割成两半。 - rotater: + rotator: name: 模拟旋转机 description: 模拟顺时针旋转<strong>图形</strong>。 unstacker: @@ -663,7 +554,7 @@ storyRewards: desc: 恭喜!您解锁了<strong>切割机</strong>,不管如何放置,它只会从上到下切开<strong>图形</strong>! <br>注意一定要处理掉切割后废弃的<strong>图形</strong>,不然它会<strong>阻塞</strong>传送带, <br>使用<strong>垃圾桶</strong>,它会清除所有放进去的图形! - reward_rotater: + reward_rotator: title: 旋转 desc: 恭喜!您解锁了<strong>旋转机</strong>。它会顺时针将输入的<strong>图形旋转90度</strong>。 reward_painter: @@ -684,7 +575,7 @@ storyRewards: reward_tunnel: title: 隧道 desc: 恭喜!您解锁了<strong>隧道</strong>。它可放置在<strong>传送带</strong>或<strong>设施</strong>下方以运送物品。 - reward_rotater_ccw: + reward_rotator_ccw: title: 逆时针旋转 desc: 恭喜!您解锁了<strong>旋转机</strong>的<strong>逆时针</strong>变体。它可以逆时针旋转<strong>图形</strong>。 @@ -735,7 +626,7 @@ storyRewards: title: 传送带读取器 desc: 恭喜!您解锁了<strong>传送带读取器</strong>!它能够测量传送带上的生产率。 <br><br>等您解锁了<strong>电线层</strong>后,它将会极其有用! - reward_rotater_180: + reward_rotator_180: title: 旋转机(180度) desc: 恭喜!您解锁了<strong>旋转器(180度)</strong>!它能帮您把一个图形旋转180度(惊喜! :D) reward_display: @@ -941,7 +832,7 @@ keybindings: underground_belt: 隧道 miner: 开采器 cutter: 切割机 - rotater: 旋转机 + rotator: 旋转机 stacker: 堆叠机 mixer: 混色器 painter: 上色器 @@ -1007,12 +898,6 @@ about: changelog: title: 版本日志 demo: - features: - restoringGames: 恢复存档 - importingGames: 导入存档 - oneGameLimit: 最多一个存档 - customizeKeybindings: 按键设定 - exportingBase: 导出工厂截图 settingNotAvailable: 在试玩版中不可用。 tips: - 基地接受所有创造后输入的图形!并不限于现有的图形! @@ -1153,16 +1038,7 @@ mods: version: Version modWebsite: Website openFolder: Open Mods Folder - folderOnlyStandalone: Opening the mod folder is only possible when running the standalone. browseMods: Browse Mods modsInfo: To install and manage mods, copy them to the mods folder within the game directory. You can also use the 'Open Mods Folder' button on the top right. - noModSupport: You need the standalone version on Steam to install mods. - togglingComingSoon: - title: Coming Soon - description: Enabling or disabling mods is currently only possible by copying - the mod file from or to the mods/ folder. However, being able to - toggle them here is planned for a future update! - browserNoSupport: Due to browser restrictions it is currently only possible to - install mods in the Steam version - Sorry! diff --git a/translations/base-zh-CN.yaml b/translations/base-zh-CN.yaml index 9e82890b..cb580f2c 100644 --- a/translations/base-zh-CN.yaml +++ b/translations/base-zh-CN.yaml @@ -1,23 +1,3 @@ -steamPage: - shortText: “唯一能限制您的,只有您的想象力!”《异形工厂》(shapez) - 是一款在无限拓展的地图上,通过建造各类工厂设施,来自动化生产与组合出愈加复杂图形的游戏。 - discordLinkShort: 官方 Discord 服务器 - intro: |- - “奇形怪状,放飞想象!” - “自动生产,尽情创造!” - 《异形工厂》(shapez)是一款能让您尽情发挥创造力,充分享受思维乐趣的 IO 游戏。 - 游戏很轻松,只需建造工厂,布好设施,无需操作即能自动创造出各种各样的几何图形。 - 挑战很烧脑,随着等级提升,需要创造的图形将会越来越复杂,同时您还需要在无限扩展的地图中持续扩建优化您的工厂。 - 以为这就是全部了吗?不!图形的生产需求将会指数性增长,持续的扩大规模和熵增带来的无序,将会是令人头痛的问题! - 这还不是全部!一开始我们创造了图形,然后我们需要学会提取和混合来让它们五颜六色。 - 然后,还有吗?当然,唯有思维,方能无限。 - - 欢迎免费体验试玩版:“让您的想象力插上翅膀!” - 和最聪明的玩家一起挑战,请访问 Steam 游戏商城购买《异形工厂》(shapez)的完整版, - what_others_say: 来看看玩家们对《异形工厂》(shapez)的评价 - nothernlion_comment: 非常棒的游戏,我的游戏过程充满乐趣,不觉时间飞逝。 - notch_comment: 哦,天哪!我真得该去睡了!但我想我刚刚搞定如何在游戏里面制造一台电脑出来。 - steam_review_comment: 这是一个不知不觉偷走你时间,但你并不会想要追回的游戏。非常烧脑的挑战,让我这样的完美主义者停不下来,总是希望可以再高效一些。 global: loading: 加载中 error: 错误 @@ -51,54 +31,18 @@ global: space: 空格键 loggingIn: 登录 loadingResources: 下载其他资源 (<percentage> %) - discount: -<percentage>% - discountSummerSale: SPECIAL PROMOTION! Offer ends 7 July -demoBanners: - title: 试玩版 - intro: |- - <strong>现在</strong>购买完整版以解锁<ul> - <li>所有 26 个关卡 + 无限可能的自由模式</li> - <li>22 个全新设施</li> - <li>游戏模组(Mods)支持</li> - <li>成就</li> - <li>暗色模式</li> - <li>……以及更多!</li> - </ul> - playtimeDisclaimer: 完整版包括多达 <strong>20 小时的新内容</strong> - playerCount: <playerCount> 个像你一样的玩家目前正在 Steam 上玩 shapez - untilEndOfDemo: 直到演示结束 - playtimeDisclaimerDownload: 您可以在完整版中继续您的存档游戏! 点击<strong>此处</strong>下载您的存档游戏。 - titleV2: 现在就玩完整版的。 mainMenu: play: 开始游戏 - changelog: 更新日志 importSavegame: 读取存档 - openSourceHint: 本游戏已开源! - discordLink: 官方 Discord 服务器 helpTranslate: 帮助我们翻译! - browserWarning: 很抱歉,本游戏在当前浏览器上可能运行缓慢!使用 Chrome 或者购买完整版以得到更好的体验。 savegameLevel: 第 <x> 关 savegameLevelUnknown: 未知关卡 continue: 继续游戏 newGame: 新游戏 - madeBy: 作者:<author-link> - subreddit: Reddit savegameUnnamed: 存档未命名 - puzzleMode: 谜题模式 - back: 返回 - puzzleDlcText: - 持续优化,追求极致效率。在限定空间内使用有限的设施来创造图形!《异形工厂》(Shapez)的首个 DLC - “谜题挑战者”将会给大家带来更烧脑、更自由的全新挑战! - puzzleDlcWishlist: 添加愿望单! - puzzleDlcViewNow: 查看 DLC mods: - title: 激活游戏模组(Mods) warningPuzzleDLC: 无法在任何游戏模组(Mods)下进行“谜题挑战者” DLC,请关闭所有游戏模组(Mods)。 - playingFullVersion: 您正在玩完整版游戏! - logout: 登出 noActiveSavegames: No active savegames found - Click play to start a new game! - playFullVersionV2: Bough shapez on Steam? Play the full version in your Browser! - playFullVersionStandalone: You can now also play the full version in your Browser! dialogs: buttons: ok: 确认 @@ -107,7 +51,6 @@ dialogs: later: 以后 restart: 重新开始 reset: 重置 - getStandalone: 获取完整版 deleteGame: 我没疯!我知道我在做什么! viewUpdate: 查看更新 showUpgrades: 显示设施升级 @@ -142,12 +85,6 @@ dialogs: keybindingsResetOk: title: 重置按键设定 desc: 成功重置所有按键设定! - featureRestriction: - title: 试玩版 - desc: 您尝试使用了 “<feature>” 一项功能。该功能在试玩版中不可用。请考虑购买完整版以获得更好的体验。 - oneSavegameLimit: - title: 存档数量限制 - desc: 试玩版中只能保存一份存档。请删除旧存档或者购买完整版! updateSummary: title: 新内容更新啦! desc: 以下为游戏最新更新内容: @@ -171,8 +108,6 @@ dialogs: title: 创建地图标记 desc: 填写一个有意义的名称,还可以同时包含一个形状的<strong>短代码</strong>(您可以 <link>点击这里</link> 生成短代码) titleEdit: 编辑地图标记 - markerDemoLimit: - desc: 在试玩版中您只能创建两个地图标记。请获取完整版以创建更多标记。 massCutConfirm: title: 确认剪切 desc: 您将要剪切很多设施,准确来说有 <count> 种!您确定要这么做吗? @@ -258,21 +193,9 @@ dialogs: newMods: 新安装的游戏模组(Mods) resourceLoadFailed: title: 资源载入失败 - demoLinkText: Steam上的《异形工厂》(Shapez)Demo - descWeb: “一个或多个资源载入失败,请确认您的网络连接正常并再次尝试。如果仍然载入失败, - 请确认您已经关闭了所有浏览器插件(比如"广告屏蔽")。<br><br>或者,你也可以试玩 - <demoOnSteamLinkText>。<br><br> 错误提示:” descSteamDemo: “一个或多个资源载入失败,请重新启动游戏。 如果仍然存在问题,请重新安装游戏或者通过Steam验证游戏安装文件。 <br><br> 错误提示:” - steamSsoError: - title: 从完整版登出 - desc: - 由于您的网络连接不稳定,或者您已在另一台设备上开始游戏。您已经从浏览器登出了完整版游戏。 <br><br> - 请确认您没有在其他浏览器或电脑用同一Steam账号登录《异形工厂》(Shapez)。<br><br> 您可以从主界面再次登录。 - steamSsoNoOwnership: - title: 尚未获得完整版 - desc: 在浏览器内玩完整版游戏,您需要通过Steam购买游戏本体和DLC<br><br> 请确认您已经购买,请使用同一Steam账号登录并再次尝试。 ingame: keybindingsOverlay: moveMap: 移动地图 @@ -397,40 +320,6 @@ ingame: one_miner: 1 个开采器 n_miners: <amount> 个开采器 limited_items: 限制在 <max_throughput> - watermark: - title: 试玩版 - desc: 点击这里了解完整版内容 - get_on_steam: 在 Steam 商城购买 - standaloneAdvantages: - no_thanks: 不需要,谢谢 - points: - levels: - title: 19 个全新关卡! - desc: 总共 26 个不同关卡! - buildings: - title: 22 个全新设施! - desc: 呈现完全体的全自动工厂! - mods: - title: 支持游戏模组(Mods)! - desc: 超过 80 个游戏模组可供下载! - markers: - title: 无限数量地图标记 - desc: 地图再大,不会迷路! - wires: - title: 电线更新包 - desc: 发挥创造力的全新维度! - darkmode: - title: 暗色模式 - desc: 优雅且护眼的配色! - support: - title: 支持作者 - desc: 我使用闲暇时间开发游戏! - achievements: - title: 成就 - desc: 挑战全成就解锁! - titleV2: “现在就购买完整版游戏以解锁” - titleExpiredV2: “恭喜,Demo通关!” - titleEnjoyingDemo: 游戏愉快! puzzleEditorSettings: zoneTitle: 区域 zoneWidth: 宽度 @@ -505,7 +394,7 @@ buildings: quad: name: 切割机(四向) description: 将输入的图形切成四块。<strong>如果您只需要其中一块图形,使用<strong>垃圾桶</strong>清除其他图形,否则切割机会停止工作!</strong> - rotater: + rotator: default: name: 旋转机 description: 将<strong>图形</strong>顺时针旋转 90 度。 @@ -631,7 +520,7 @@ buildings: default: name: 模拟切割机 description: 模拟将<strong>图形</strong>切割成两半。 - rotater: + rotator: name: 模拟旋转机 description: 模拟顺时针旋转<strong>图形</strong>。 unstacker: @@ -665,7 +554,7 @@ storyRewards: desc: 恭喜!您解锁了<strong>切割机</strong>,不管如何放置,它只会从上到下切开<strong>图形</strong>! <br>注意一定要用处理掉切割后废弃的<strong>图形</strong>,不然它会<strong>阻塞</strong>传送带, <br>使用<strong>垃圾桶</strong>,它会清除所有放进去的图形! - reward_rotater: + reward_rotator: title: 旋转 desc: 恭喜!您解锁了<strong>旋转机</strong>。它会顺时针将输入的<strong>图形旋转90度</strong>。 reward_painter: @@ -686,7 +575,7 @@ storyRewards: reward_tunnel: title: 隧道 desc: 恭喜!您解锁了<strong>隧道</strong>。它可放置在<strong>传送带</strong>或<strong>设施</strong>下方以运送物品。 - reward_rotater_ccw: + reward_rotator_ccw: title: 逆时针旋转 desc: 恭喜!您解锁了<strong>旋转机</strong>的<strong>逆时针</strong>变体。它可以逆时针旋转<strong>图形</strong>。 @@ -737,7 +626,7 @@ storyRewards: title: 传送带读取器 desc: 恭喜!您解锁了<strong>传送带读取器</strong>!它能够测量传送带上的生产率。 <br><br>等您解锁了<strong>电线层</strong>后,它将会极其有用! - reward_rotater_180: + reward_rotator_180: title: 旋转机(180度) desc: 恭喜!您解锁了<strong>旋转器(180度)</strong>!它能帮您把一个图形旋转 180 度(惊不惊喜!:D) reward_display: @@ -944,7 +833,7 @@ keybindings: underground_belt: 隧道 miner: 开采器 cutter: 切割机 - rotater: 旋转机 + rotator: 旋转机 stacker: 堆叠机 mixer: 混色器 painter: 上色器 @@ -1010,12 +899,6 @@ about: changelog: title: 更新日志 demo: - features: - restoringGames: 恢复存档 - importingGames: 导入存档 - oneGameLimit: 最多一个存档 - customizeKeybindings: 按键设定 - exportingBase: 导出工厂截图 settingNotAvailable: 在试玩版中不可用。 tips: - 基地接受所有创造后输入的图形!并不限于现有的图形! @@ -1155,14 +1038,6 @@ mods: author: 作者 version: 版本 openFolder: 打开游戏模组(Mods)文件夹 - folderOnlyStandalone: 只有完整版才可以打开游戏模组(Mods)文件夹。 browseMods: 浏览游戏模组(Mods) modsInfo: 要安装和管理游戏模组(Mods),请将它们复制到游戏模组文件夹(使用右上角的“打开游戏模组(Mods)文件夹”按钮)。记住在管理完游戏模组(Mods)后,请务必重启游戏,否则游戏模组(Mods)是不会出现的。 - noModSupport: 您需要在 Steam 平台获得完整版才可以安装游戏模组(Mods)。 - togglingComingSoon: - title: 即将开放 - description: - 当前只能通过将游戏模组(Mods)文件复制到 mods 文件夹或从 mods 文件夹移除来启用或禁用游戏模组(Mods)。 - 但是,可以切换游戏模组(Mods)已经计划在之后的更新中实现! modWebsite: 模组网站 - browserNoSupport: 由于浏览器功能限制,目前游戏模组(Mods)只能在Steam版本中安装进行。敬请理解! diff --git a/translations/base-zh-TW.yaml b/translations/base-zh-TW.yaml index d47fdb79..7cd67bf1 100644 --- a/translations/base-zh-TW.yaml +++ b/translations/base-zh-TW.yaml @@ -1,21 +1,3 @@ -steamPage: - shortText: shapez.io 是一款在一個無邊際的地圖上建造工廠、自動化生產與組合愈加複雜圖形的遊戲。 - discordLinkShort: 官方 Discord 伺服器 - intro: |- - 你喜歡自動化生產的遊戲類型嗎?那你來對地方了! - Shapez.io 是一款建造工廠、自動化生產與組合圖形的休閒遊戲。 - 每當玩家不斷晉級,形狀會越來越複雜,你的工廠將在一個無邊際的地圖上不斷擴展。 - 除此之外,你也必須不斷累加你的生產量來達到升級的需求,達成目標的方法無他,只有不斷地擴張! - 遊戲初期只需要組合特定圖形,接著玩家會被要求幫圖形上色,有時甚至需要先混色才能達到目標。 - 玩家可以在 Steam 購買本游戲的單機版,如果還在猶豫,也可以到 shapez.io 先免費試玩再決定! - what_others_say: What people say about shapez.io - nothernlion_comment: This game is great - I'm having a wonderful time playing, - and time has flown by. - notch_comment: Oh crap. I really should sleep, but I think I just figured out - how to make a computer in shapez.io - steam_review_comment: This game has stolen my life and I don't want it back. - Very chill factory game that won't let me stop making my lines more - efficient. global: loading: 載入中 error: 錯誤 @@ -49,54 +31,19 @@ global: space: 空白鍵 loggingIn: Logging in loadingResources: 下載其他資源 (<percentage> %) - discount: -<percentage>% - discountSummerSale: SPECIAL PROMOTION! Offer ends 7 July -demoBanners: - title: 試玩版 - intro: |- - <strong>立即</strong>獲取完整遊戲以解鎖:<ul> - <li>所有 26 個關卡 + 無限自由遊戲</li> - <li>22座新建築</li> - <li>模組支持</li> - <li>成就</li> - <li>黑暗模式</li> - <li>……還有更多!</li> - </ul> - playtimeDisclaimer: 完整版包含超過 <strong>20 小時的內容</strong>。 - playerCount: <playerCount> 個像你一樣的玩家目前正在 Steam 上玩 shapez - untilEndOfDemo: 直到演示結束 - playtimeDisclaimerDownload: 您可以在完整版中繼續您的存檔遊戲! 點擊<strong>此處</strong>下載您的存檔遊戲。 - titleV2: 立即播放完整版: mainMenu: play: 開始遊戲 - changelog: 更新日誌 importSavegame: 匯入存檔 - openSourceHint: 本遊戲已開源! - discordLink: 官方 Discord 伺服器 helpTranslate: 協助我們翻譯! - browserWarning: 很抱歉,本遊戲在目前的瀏覽器上執行效率可能會有所緩慢!使用 Chrome 或者取得單機版以得到更好的體驗。 savegameLevel: <x>級 savegameLevelUnknown: 未知關卡 continue: 繼續 newGame: 新遊戲 - madeBy: 作者:<author-link> - subreddit: Reddit savegameUnnamed: 未命名 - puzzleMode: Puzzle Mode - back: Back - puzzleDlcText: Do you enjoy compacting and optimizing factories? Get the Puzzle - DLC now on Steam for even more fun! - puzzleDlcWishlist: Wishlist now! - puzzleDlcViewNow: View Dlc mods: - title: Active Mods warningPuzzleDLC: Playing the Puzzle DLC is not possible with mods. Please disable all mods to play the DLC. - playingFullVersion: You are now playing the full version! - logout: Logout noActiveSavegames: No active savegames found - Click play to start a new game! - playFullVersionV2: Bough shapez on Steam? Play the full version in your Browser! - playFullVersionStandalone: You can now also play the full version in your Browser! dialogs: buttons: ok: 確認 @@ -105,7 +52,6 @@ dialogs: later: 之後 restart: 重啟 reset: 重置 - getStandalone: 取得單機版 deleteGame: 我知道我在做什麼 viewUpdate: 查看更新 showUpgrades: 顯示建築升級 @@ -140,12 +86,6 @@ dialogs: keybindingsResetOk: title: 重置了所有按鍵 desc: 成功重置了所有按鍵! - featureRestriction: - title: 試玩版 - desc: 你嘗試使用了 <feature> 功能。該功能在試玩版中不可用。請考慮購買單機版以獲得更好的體驗。 - oneSavegameLimit: - title: 存檔數量限制 - desc: 試玩版中只能保存一份存檔。請刪除舊存檔或者取得單機版! updateSummary: title: 更新了! desc: 以下為自上次遊戲以來更新的內容: @@ -171,8 +111,6 @@ dialogs: 給地圖標記取一個名字。你可以在名字中加入一個<strong>簡短代碼</strong>以加入圖形。(你可以在<link>這裡</link> 建立簡短代碼。) titleEdit: 修改標記 - markerDemoLimit: - desc: 在試玩版中你只能建立兩個地圖標記。請取得單機版以建立更多標記。 massCutConfirm: title: 確認剪下 desc: 你將要剪下很多建築,準確來說有<count>幢!你確定要這麼做嗎? @@ -268,28 +206,9 @@ dialogs: newMods: Newly installed Mods resourceLoadFailed: title: Resources failed to load - demoLinkText: shapez demo on Steam - descWeb: "One ore more resources could not be loaded. Make sure you have a - stable internet connection and try again. If this still doesn't - work, make sure to also disable any browser extensions (including - adblockers).<br><br> As an alternative, you can also play the - <demoOnSteamLinkText>. <br><br> Error Message:" descSteamDemo: "One ore more resources could not be loaded. Try restarting the game - If that does not help, try reinstalling and verifying the game files via Steam. <br><br> Error Message:" - steamSsoError: - title: Full Version Logout - desc: You have been logged out from the Full Browser Version since either your - network connection is unstable or you are playing on another - device.<br><br> Please make sure you don't have shapez open in any - other browser tab or another computer with the same Steam - account.<br><br> You can login again in the main menu. - steamSsoNoOwnership: - title: Full Edition not owned - desc: In order to play the Full Edition in your Browser, you need to own both - the base game and the Puzzle DLC on your Steam account.<br><br> - Please make sure you own both, signed in with the correct Steam - account and then try again. ingame: keybindingsOverlay: moveMap: 移動 @@ -416,40 +335,6 @@ ingame: one_miner: 1 個開採機 n_miners: <amount> 個開採機 limited_items: 上限 <max_throughput> - watermark: - title: 試玩版 - desc: 按這裡看 Steam 版的優勢! - get_on_steam: 在 Steam 上取得 - standaloneAdvantages: - no_thanks: 不用了,謝謝! - points: - levels: - title: 12 個新關卡 - desc: 總共26關! - buildings: - title: 22 個新建築 - desc: 邁向完全自動化! - markers: - title: ∞ 個地圖標記 - desc: 再也不會迷失在一望無際的地圖裡! - wires: - title: 電路 - desc: 全新次元! - darkmode: - title: 深色模式 - desc: 要保養眼球~ - support: - title: 支持我 - desc: 遊戲使我利用閒暇時間做的! - achievements: - title: Achievements - desc: Hunt them all! - mods: - title: Modding support! - desc: Over 80 mods available! - titleV2: "Get the full version now on Steam to unlock:" - titleExpiredV2: Demo completed! - titleEnjoyingDemo: Enjoy the demo? puzzleEditorSettings: zoneTitle: Zone zoneWidth: Width @@ -530,7 +415,7 @@ buildings: quad: name: 四分切割機 description: 將輸入的圖形切成四塊。 <strong>如果你只需要其中一塊,記得把其他的銷毀掉,否則切割機會停止運作! </strong> - rotater: + rotator: default: name: 旋轉機 description: 將圖形順時針旋轉90度。 @@ -660,7 +545,7 @@ buildings: default: name: 虛擬切割機 description: 虛擬地將圖形訊號從上到下切開。 - rotater: + rotator: name: 虛擬旋轉機 description: 虛擬地將圖形訊號順時針旋轉。 unstacker: @@ -695,7 +580,7 @@ storyRewards: <strong>切割機</strong>已解鎖!不論切割機的方向,它都會把圖形<strong>垂直地</strong>切成兩半。<br><br> 記得把不需要的部分處理掉,否則切割機會<strong>因為堵塞而停止運作</strong>。 為此我給你準備了<strong>垃圾桶</strong>,它會把所有放進去的物品銷毀掉。 - reward_rotater: + reward_rotator: title: 順時針旋轉 desc: <strong>順時針旋轉機</strong>已解鎖! 它會順時針旋轉輸入的圖形90度。 reward_painter: @@ -717,7 +602,7 @@ storyRewards: reward_tunnel: title: 隧道 desc: <strong>隧道</strong>已解鎖! 你現在可以在其他輸送帶或建築底下運送物品了! - reward_rotater_ccw: + reward_rotator_ccw: title: 逆時針旋轉 desc: <strong>逆時針旋轉機</strong>已解鎖! 它會逆時針旋轉輸入的圖形90度。 逆時針旋轉機是順時針旋轉機的變體。選擇「順時針旋轉機」並<strong>按「T」來切換變體</strong>就能使用。 @@ -769,7 +654,7 @@ storyRewards: reward_belt_reader: title: 讀取輸送帶 desc: <strong>輸送帶讀取機</strong>已解鎖! 它會讀取輸送帶的流量。<br><br> 當你解鎖電路層時,它會變得超有用! - reward_rotater_180: + reward_rotator_180: title: 220度旋轉 desc: <strong>180度旋轉機</strong>已解鎖! - 它可以180度旋轉物件(驚喜!:D) reward_display: @@ -983,7 +868,7 @@ keybindings: underground_belt: 隧道 miner: 開採機 cutter: 切割機 - rotater: 虛擬旋轉機 + rotator: 虛擬旋轉機 stacker: 虛擬堆疊機 mixer: 混色機 painter: 虛擬上色機 @@ -1049,12 +934,6 @@ about: changelog: title: 版本日誌 demo: - features: - restoringGames: 恢復存檔 - importingGames: 匯入存檔 - oneGameLimit: 最多一個存檔 - customizeKeybindings: 按鍵設定 - exportingBase: 匯出工廠截圖 settingNotAvailable: 在試玩版中不可用。 tips: - 基地接受任何輸入,不只是當下要求的圖形! @@ -1204,16 +1083,7 @@ mods: version: Version modWebsite: Website openFolder: Open Mods Folder - folderOnlyStandalone: Opening the mod folder is only possible when running the standalone. browseMods: Browse Mods modsInfo: To install and manage mods, copy them to the mods folder within the game directory. You can also use the 'Open Mods Folder' button on the top right. - noModSupport: You need the standalone version on Steam to install mods. - togglingComingSoon: - title: Coming Soon - description: Enabling or disabling mods is currently only possible by copying - the mod file from or to the mods/ folder. However, being able to - toggle them here is planned for a future update! - browserNoSupport: Due to browser restrictions it is currently only possible to - install mods in the Steam version - Sorry! diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..b0862a22 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": ["@tsconfig/node-lts/tsconfig"], + "include": ["./*", "./gulp/**/*"], + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "module": "nodenext", + "moduleResolution": "nodenext", + "noEmit": true, + "resolveJsonModule": true, + + // remove when comfortable + "strict": false + } +} diff --git a/version b/version deleted file mode 100644 index 03082db7..00000000 --- a/version +++ /dev/null @@ -1 +0,0 @@ -1.5.6 \ No newline at end of file diff --git a/yarn.lock b/yarn.lock deleted file mode 100644 index 1efc7228..00000000 --- a/yarn.lock +++ /dev/null @@ -1,8902 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz" - integrity sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g== - dependencies: - "@babel/highlight" "^7.8.3" - -"@babel/compat-data@^7.8.6", "@babel/compat-data@^7.9.0": - version "7.9.0" - resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.9.0.tgz" - integrity sha512-zeFQrr+284Ekvd9e7KAX954LkapWiOmQtsfHirhxqfdlX6MEC32iRE+pqUGlYIBchdevaCwvzxWGSy/YBNI85g== - dependencies: - browserslist "^4.9.1" - invariant "^2.2.4" - semver "^5.5.0" - -"@babel/core@^7.5.4": - version "7.9.0" - resolved "https://registry.npmjs.org/@babel/core/-/core-7.9.0.tgz" - integrity sha512-kWc7L0fw1xwvI0zi8OKVBuxRVefwGOrKSQMvrQ3dW+bIIavBY3/NpXmpjMy7bQnLgwgzWQZ8TlM57YHpHNHz4w== - dependencies: - "@babel/code-frame" "^7.8.3" - "@babel/generator" "^7.9.0" - "@babel/helper-module-transforms" "^7.9.0" - "@babel/helpers" "^7.9.0" - "@babel/parser" "^7.9.0" - "@babel/template" "^7.8.6" - "@babel/traverse" "^7.9.0" - "@babel/types" "^7.9.0" - convert-source-map "^1.7.0" - debug "^4.1.0" - gensync "^1.0.0-beta.1" - json5 "^2.1.2" - lodash "^4.17.13" - resolve "^1.3.2" - semver "^5.4.1" - source-map "^0.5.0" - -"@babel/generator@^7.9.0", "@babel/generator@^7.9.5": - version "7.9.5" - resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.9.5.tgz" - integrity sha512-GbNIxVB3ZJe3tLeDm1HSn2AhuD/mVcyLDpgtLXa5tplmWrJdF/elxB56XNqCuD6szyNkDi6wuoKXln3QeBmCHQ== - dependencies: - "@babel/types" "^7.9.5" - jsesc "^2.5.1" - lodash "^4.17.13" - source-map "^0.5.0" - -"@babel/helper-annotate-as-pure@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.8.3.tgz" - integrity sha512-6o+mJrZBxOoEX77Ezv9zwW7WV8DdluouRKNY/IR5u/YTMuKHgugHOzYWlYvYLpLA9nPsQCAAASpCIbjI9Mv+Uw== - dependencies: - "@babel/types" "^7.8.3" - -"@babel/helper-builder-binary-assignment-operator-visitor@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.8.3.tgz" - integrity sha512-5eFOm2SyFPK4Rh3XMMRDjN7lBH0orh3ss0g3rTYZnBQ+r6YPj7lgDyCvPphynHvUrobJmeMignBr6Acw9mAPlw== - dependencies: - "@babel/helper-explode-assignable-expression" "^7.8.3" - "@babel/types" "^7.8.3" - -"@babel/helper-compilation-targets@^7.8.7": - version "7.8.7" - resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.8.7.tgz" - integrity sha512-4mWm8DCK2LugIS+p1yArqvG1Pf162upsIsjE7cNBjez+NjliQpVhj20obE520nao0o14DaTnFJv+Fw5a0JpoUw== - dependencies: - "@babel/compat-data" "^7.8.6" - browserslist "^4.9.1" - invariant "^2.2.4" - levenary "^1.1.1" - semver "^5.5.0" - -"@babel/helper-create-regexp-features-plugin@^7.8.3", "@babel/helper-create-regexp-features-plugin@^7.8.8": - version "7.8.8" - resolved "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.8.8.tgz" - integrity sha512-LYVPdwkrQEiX9+1R29Ld/wTrmQu1SSKYnuOk3g0CkcZMA1p0gsNxJFj/3gBdaJ7Cg0Fnek5z0DsMULePP7Lrqg== - dependencies: - "@babel/helper-annotate-as-pure" "^7.8.3" - "@babel/helper-regex" "^7.8.3" - regexpu-core "^4.7.0" - -"@babel/helper-define-map@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.8.3.tgz" - integrity sha512-PoeBYtxoZGtct3md6xZOCWPcKuMuk3IHhgxsRRNtnNShebf4C8YonTSblsK4tvDbm+eJAw2HAPOfCr+Q/YRG/g== - dependencies: - "@babel/helper-function-name" "^7.8.3" - "@babel/types" "^7.8.3" - lodash "^4.17.13" - -"@babel/helper-explode-assignable-expression@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.8.3.tgz" - integrity sha512-N+8eW86/Kj147bO9G2uclsg5pwfs/fqqY5rwgIL7eTBklgXjcOJ3btzS5iM6AitJcftnY7pm2lGsrJVYLGjzIw== - dependencies: - "@babel/traverse" "^7.8.3" - "@babel/types" "^7.8.3" - -"@babel/helper-function-name@^7.8.3", "@babel/helper-function-name@^7.9.5": - version "7.9.5" - resolved "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.9.5.tgz" - integrity sha512-JVcQZeXM59Cd1qanDUxv9fgJpt3NeKUaqBqUEvfmQ+BCOKq2xUgaWZW2hr0dkbyJgezYuplEoh5knmrnS68efw== - dependencies: - "@babel/helper-get-function-arity" "^7.8.3" - "@babel/template" "^7.8.3" - "@babel/types" "^7.9.5" - -"@babel/helper-get-function-arity@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz" - integrity sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA== - dependencies: - "@babel/types" "^7.8.3" - -"@babel/helper-hoist-variables@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.8.3.tgz" - integrity sha512-ky1JLOjcDUtSc+xkt0xhYff7Z6ILTAHKmZLHPxAhOP0Nd77O+3nCsd6uSVYur6nJnCI029CrNbYlc0LoPfAPQg== - dependencies: - "@babel/types" "^7.8.3" - -"@babel/helper-member-expression-to-functions@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz" - integrity sha512-fO4Egq88utkQFjbPrSHGmGLFqmrshs11d46WI+WZDESt7Wu7wN2G2Iu+NMMZJFDOVRHAMIkB5SNh30NtwCA7RA== - dependencies: - "@babel/types" "^7.8.3" - -"@babel/helper-module-imports@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz" - integrity sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg== - dependencies: - "@babel/types" "^7.8.3" - -"@babel/helper-module-transforms@^7.9.0": - version "7.9.0" - resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.9.0.tgz" - integrity sha512-0FvKyu0gpPfIQ8EkxlrAydOWROdHpBmiCiRwLkUiBGhCUPRRbVD2/tm3sFr/c/GWFrQ/ffutGUAnx7V0FzT2wA== - dependencies: - "@babel/helper-module-imports" "^7.8.3" - "@babel/helper-replace-supers" "^7.8.6" - "@babel/helper-simple-access" "^7.8.3" - "@babel/helper-split-export-declaration" "^7.8.3" - "@babel/template" "^7.8.6" - "@babel/types" "^7.9.0" - lodash "^4.17.13" - -"@babel/helper-optimise-call-expression@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz" - integrity sha512-Kag20n86cbO2AvHca6EJsvqAd82gc6VMGule4HwebwMlwkpXuVqrNRj6CkCV2sKxgi9MyAUnZVnZ6lJ1/vKhHQ== - dependencies: - "@babel/types" "^7.8.3" - -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz" - integrity sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ== - -"@babel/helper-regex@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.8.3.tgz" - integrity sha512-BWt0QtYv/cg/NecOAZMdcn/waj/5P26DR4mVLXfFtDokSR6fyuG0Pj+e2FqtSME+MqED1khnSMulkmGl8qWiUQ== - dependencies: - lodash "^4.17.13" - -"@babel/helper-remap-async-to-generator@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.8.3.tgz" - integrity sha512-kgwDmw4fCg7AVgS4DukQR/roGp+jP+XluJE5hsRZwxCYGg+Rv9wSGErDWhlI90FODdYfd4xG4AQRiMDjjN0GzA== - dependencies: - "@babel/helper-annotate-as-pure" "^7.8.3" - "@babel/helper-wrap-function" "^7.8.3" - "@babel/template" "^7.8.3" - "@babel/traverse" "^7.8.3" - "@babel/types" "^7.8.3" - -"@babel/helper-replace-supers@^7.8.3", "@babel/helper-replace-supers@^7.8.6": - version "7.8.6" - resolved "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.8.6.tgz" - integrity sha512-PeMArdA4Sv/Wf4zXwBKPqVj7n9UF/xg6slNRtZW84FM7JpE1CbG8B612FyM4cxrf4fMAMGO0kR7voy1ForHHFA== - dependencies: - "@babel/helper-member-expression-to-functions" "^7.8.3" - "@babel/helper-optimise-call-expression" "^7.8.3" - "@babel/traverse" "^7.8.6" - "@babel/types" "^7.8.6" - -"@babel/helper-simple-access@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.8.3.tgz" - integrity sha512-VNGUDjx5cCWg4vvCTR8qQ7YJYZ+HBjxOgXEl7ounz+4Sn7+LMD3CFrCTEU6/qXKbA2nKg21CwhhBzO0RpRbdCw== - dependencies: - "@babel/template" "^7.8.3" - "@babel/types" "^7.8.3" - -"@babel/helper-split-export-declaration@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz" - integrity sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA== - dependencies: - "@babel/types" "^7.8.3" - -"@babel/helper-validator-identifier@^7.9.0", "@babel/helper-validator-identifier@^7.9.5": - version "7.9.5" - resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz" - integrity sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g== - -"@babel/helper-wrap-function@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.8.3.tgz" - integrity sha512-LACJrbUET9cQDzb6kG7EeD7+7doC3JNvUgTEQOx2qaO1fKlzE/Bf05qs9w1oXQMmXlPO65lC3Tq9S6gZpTErEQ== - dependencies: - "@babel/helper-function-name" "^7.8.3" - "@babel/template" "^7.8.3" - "@babel/traverse" "^7.8.3" - "@babel/types" "^7.8.3" - -"@babel/helpers@^7.9.0": - version "7.9.2" - resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.9.2.tgz" - integrity sha512-JwLvzlXVPjO8eU9c/wF9/zOIN7X6h8DYf7mG4CiFRZRvZNKEF5dQ3H3V+ASkHoIB3mWhatgl5ONhyqHRI6MppA== - dependencies: - "@babel/template" "^7.8.3" - "@babel/traverse" "^7.9.0" - "@babel/types" "^7.9.0" - -"@babel/highlight@^7.8.3": - version "7.9.0" - resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz" - integrity sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ== - dependencies: - "@babel/helper-validator-identifier" "^7.9.0" - chalk "^2.0.0" - js-tokens "^4.0.0" - -"@babel/parser@^7.8.6", "@babel/parser@^7.9.0": - version "7.9.4" - resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.9.4.tgz" - integrity sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA== - -"@babel/plugin-proposal-async-generator-functions@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.8.3.tgz" - integrity sha512-NZ9zLv848JsV3hs8ryEh7Uaz/0KsmPLqv0+PdkDJL1cJy0K4kOCFa8zc1E3mp+RHPQcpdfb/6GovEsW4VDrOMw== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/helper-remap-async-to-generator" "^7.8.3" - "@babel/plugin-syntax-async-generators" "^7.8.0" - -"@babel/plugin-proposal-dynamic-import@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.8.3.tgz" - integrity sha512-NyaBbyLFXFLT9FP+zk0kYlUlA8XtCUbehs67F0nnEg7KICgMc2mNkIeu9TYhKzyXMkrapZFwAhXLdnt4IYHy1w== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/plugin-syntax-dynamic-import" "^7.8.0" - -"@babel/plugin-proposal-json-strings@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.8.3.tgz" - integrity sha512-KGhQNZ3TVCQG/MjRbAUwuH+14y9q0tpxs1nWWs3pbSleRdDro9SAMMDyye8HhY1gqZ7/NqIc8SKhya0wRDgP1Q== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/plugin-syntax-json-strings" "^7.8.0" - -"@babel/plugin-proposal-nullish-coalescing-operator@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.8.3.tgz" - integrity sha512-TS9MlfzXpXKt6YYomudb/KU7nQI6/xnapG6in1uZxoxDghuSMZsPb6D2fyUwNYSAp4l1iR7QtFOjkqcRYcUsfw== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" - -"@babel/plugin-proposal-numeric-separator@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.8.3.tgz" - integrity sha512-jWioO1s6R/R+wEHizfaScNsAx+xKgwTLNXSh7tTC4Usj3ItsPEhYkEpU4h+lpnBwq7NBVOJXfO6cRFYcX69JUQ== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/plugin-syntax-numeric-separator" "^7.8.3" - -"@babel/plugin-proposal-object-rest-spread@^7.9.5": - version "7.9.5" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.9.5.tgz" - integrity sha512-VP2oXvAf7KCYTthbUHwBlewbl1Iq059f6seJGsxMizaCdgHIeczOr7FBqELhSqfkIl04Fi8okzWzl63UKbQmmg== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/plugin-syntax-object-rest-spread" "^7.8.0" - "@babel/plugin-transform-parameters" "^7.9.5" - -"@babel/plugin-proposal-optional-catch-binding@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.8.3.tgz" - integrity sha512-0gkX7J7E+AtAw9fcwlVQj8peP61qhdg/89D5swOkjYbkboA2CVckn3kiyum1DE0wskGb7KJJxBdyEBApDLLVdw== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" - -"@babel/plugin-proposal-optional-chaining@^7.9.0": - version "7.9.0" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.9.0.tgz" - integrity sha512-NDn5tu3tcv4W30jNhmc2hyD5c56G6cXx4TesJubhxrJeCvuuMpttxr0OnNCqbZGhFjLrg+NIhxxC+BK5F6yS3w== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/plugin-syntax-optional-chaining" "^7.8.0" - -"@babel/plugin-proposal-unicode-property-regex@^7.4.4", "@babel/plugin-proposal-unicode-property-regex@^7.8.3": - version "7.8.8" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.8.8.tgz" - integrity sha512-EVhjVsMpbhLw9ZfHWSx2iy13Q8Z/eg8e8ccVWt23sWQK5l1UdkoLJPN5w69UA4uITGBnEZD2JOe4QOHycYKv8A== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.8.8" - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-syntax-async-generators@^7.8.0": - version "7.8.4" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz" - integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-dynamic-import@^7.8.0": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz" - integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-json-strings@^7.8.0": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz" - integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.0": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz" - integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-numeric-separator@^7.8.0", "@babel/plugin-syntax-numeric-separator@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.8.3.tgz" - integrity sha512-H7dCMAdN83PcCmqmkHB5dtp+Xa9a6LKSvA2hiFBC/5alSHxM5VgWZXFqDi0YFe8XNGT6iCa+z4V4zSt/PdZ7Dw== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-syntax-object-rest-spread@^7.8.0": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz" - integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-optional-catch-binding@^7.8.0": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz" - integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-optional-chaining@^7.8.0": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz" - integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-top-level-await@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.8.3.tgz" - integrity sha512-kwj1j9lL/6Wd0hROD3b/OZZ7MSrZLqqn9RAZ5+cYYsflQ9HZBIKCUkr3+uL1MEJ1NePiUbf98jjiMQSv0NMR9g== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-transform-arrow-functions@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.8.3.tgz" - integrity sha512-0MRF+KC8EqH4dbuITCWwPSzsyO3HIWWlm30v8BbbpOrS1B++isGxPnnuq/IZvOX5J2D/p7DQalQm+/2PnlKGxg== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-transform-async-to-generator@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.8.3.tgz" - integrity sha512-imt9tFLD9ogt56Dd5CI/6XgpukMwd/fLGSrix2httihVe7LOGVPhyhMh1BU5kDM7iHD08i8uUtmV2sWaBFlHVQ== - dependencies: - "@babel/helper-module-imports" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/helper-remap-async-to-generator" "^7.8.3" - -"@babel/plugin-transform-block-scoped-functions@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.8.3.tgz" - integrity sha512-vo4F2OewqjbB1+yaJ7k2EJFHlTP3jR634Z9Cj9itpqNjuLXvhlVxgnjsHsdRgASR8xYDrx6onw4vW5H6We0Jmg== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-transform-block-scoping@^7.4.4", "@babel/plugin-transform-block-scoping@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.8.3.tgz" - integrity sha512-pGnYfm7RNRgYRi7bids5bHluENHqJhrV4bCZRwc5GamaWIIs07N4rZECcmJL6ZClwjDz1GbdMZFtPs27hTB06w== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - lodash "^4.17.13" - -"@babel/plugin-transform-classes@^7.5.5", "@babel/plugin-transform-classes@^7.9.5": - version "7.9.5" - resolved "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.9.5.tgz" - integrity sha512-x2kZoIuLC//O5iA7PEvecB105o7TLzZo8ofBVhP79N+DO3jaX+KYfww9TQcfBEZD0nikNyYcGB1IKtRq36rdmg== - dependencies: - "@babel/helper-annotate-as-pure" "^7.8.3" - "@babel/helper-define-map" "^7.8.3" - "@babel/helper-function-name" "^7.9.5" - "@babel/helper-optimise-call-expression" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/helper-replace-supers" "^7.8.6" - "@babel/helper-split-export-declaration" "^7.8.3" - globals "^11.1.0" - -"@babel/plugin-transform-computed-properties@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.8.3.tgz" - integrity sha512-O5hiIpSyOGdrQZRQ2ccwtTVkgUDBBiCuK//4RJ6UfePllUTCENOzKxfh6ulckXKc0DixTFLCfb2HVkNA7aDpzA== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-transform-destructuring@^7.9.5": - version "7.9.5" - resolved "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.9.5.tgz" - integrity sha512-j3OEsGel8nHL/iusv/mRd5fYZ3DrOxWC82x0ogmdN/vHfAP4MYw+AFKYanzWlktNwikKvlzUV//afBW5FTp17Q== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-transform-dotall-regex@^7.4.4", "@babel/plugin-transform-dotall-regex@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.8.3.tgz" - integrity sha512-kLs1j9Nn4MQoBYdRXH6AeaXMbEJFaFu/v1nQkvib6QzTj8MZI5OQzqmD83/2jEM1z0DLilra5aWO5YpyC0ALIw== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-transform-duplicate-keys@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.8.3.tgz" - integrity sha512-s8dHiBUbcbSgipS4SMFuWGqCvyge5V2ZeAWzR6INTVC3Ltjig/Vw1G2Gztv0vU/hRG9X8IvKvYdoksnUfgXOEQ== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-transform-exponentiation-operator@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.8.3.tgz" - integrity sha512-zwIpuIymb3ACcInbksHaNcR12S++0MDLKkiqXHl3AzpgdKlFNhog+z/K0+TGW+b0w5pgTq4H6IwV/WhxbGYSjQ== - dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-transform-for-of@^7.9.0": - version "7.9.0" - resolved "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.9.0.tgz" - integrity sha512-lTAnWOpMwOXpyDx06N+ywmF3jNbafZEqZ96CGYabxHrxNX8l5ny7dt4bK/rGwAh9utyP2b2Hv7PlZh1AAS54FQ== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-transform-function-name@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.8.3.tgz" - integrity sha512-rO/OnDS78Eifbjn5Py9v8y0aR+aSYhDhqAwVfsTl0ERuMZyr05L1aFSCJnbv2mmsLkit/4ReeQ9N2BgLnOcPCQ== - dependencies: - "@babel/helper-function-name" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-transform-literals@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.8.3.tgz" - integrity sha512-3Tqf8JJ/qB7TeldGl+TT55+uQei9JfYaregDcEAyBZ7akutriFrt6C/wLYIer6OYhleVQvH/ntEhjE/xMmy10A== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-transform-member-expression-literals@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.8.3.tgz" - integrity sha512-3Wk2EXhnw+rP+IDkK6BdtPKsUE5IeZ6QOGrPYvw52NwBStw9V1ZVzxgK6fSKSxqUvH9eQPR3tm3cOq79HlsKYA== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-transform-modules-amd@^7.9.0": - version "7.9.0" - resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.9.0.tgz" - integrity sha512-vZgDDF003B14O8zJy0XXLnPH4sg+9X5hFBBGN1V+B2rgrB+J2xIypSN6Rk9imB2hSTHQi5OHLrFWsZab1GMk+Q== - dependencies: - "@babel/helper-module-transforms" "^7.9.0" - "@babel/helper-plugin-utils" "^7.8.3" - babel-plugin-dynamic-import-node "^2.3.0" - -"@babel/plugin-transform-modules-commonjs@^7.9.0": - version "7.9.0" - resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.9.0.tgz" - integrity sha512-qzlCrLnKqio4SlgJ6FMMLBe4bySNis8DFn1VkGmOcxG9gqEyPIOzeQrA//u0HAKrWpJlpZbZMPB1n/OPa4+n8g== - dependencies: - "@babel/helper-module-transforms" "^7.9.0" - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/helper-simple-access" "^7.8.3" - babel-plugin-dynamic-import-node "^2.3.0" - -"@babel/plugin-transform-modules-systemjs@^7.9.0": - version "7.9.0" - resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.9.0.tgz" - integrity sha512-FsiAv/nao/ud2ZWy4wFacoLOm5uxl0ExSQ7ErvP7jpoihLR6Cq90ilOFyX9UXct3rbtKsAiZ9kFt5XGfPe/5SQ== - dependencies: - "@babel/helper-hoist-variables" "^7.8.3" - "@babel/helper-module-transforms" "^7.9.0" - "@babel/helper-plugin-utils" "^7.8.3" - babel-plugin-dynamic-import-node "^2.3.0" - -"@babel/plugin-transform-modules-umd@^7.9.0": - version "7.9.0" - resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.9.0.tgz" - integrity sha512-uTWkXkIVtg/JGRSIABdBoMsoIeoHQHPTL0Y2E7xf5Oj7sLqwVsNXOkNk0VJc7vF0IMBsPeikHxFjGe+qmwPtTQ== - dependencies: - "@babel/helper-module-transforms" "^7.9.0" - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-transform-named-capturing-groups-regex@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.8.3.tgz" - integrity sha512-f+tF/8UVPU86TrCb06JoPWIdDpTNSGGcAtaD9mLP0aYGA0OS0j7j7DHJR0GTFrUZPUU6loZhbsVZgTh0N+Qdnw== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.8.3" - -"@babel/plugin-transform-new-target@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.8.3.tgz" - integrity sha512-QuSGysibQpyxexRyui2vca+Cmbljo8bcRckgzYV4kRIsHpVeyeC3JDO63pY+xFZ6bWOBn7pfKZTqV4o/ix9sFw== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-transform-object-super@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.8.3.tgz" - integrity sha512-57FXk+gItG/GejofIyLIgBKTas4+pEU47IXKDBWFTxdPd7F80H8zybyAY7UoblVfBhBGs2EKM+bJUu2+iUYPDQ== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/helper-replace-supers" "^7.8.3" - -"@babel/plugin-transform-parameters@^7.9.5": - version "7.9.5" - resolved "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.9.5.tgz" - integrity sha512-0+1FhHnMfj6lIIhVvS4KGQJeuhe1GI//h5uptK4PvLt+BGBxsoUJbd3/IW002yk//6sZPlFgsG1hY6OHLcy6kA== - dependencies: - "@babel/helper-get-function-arity" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-transform-property-literals@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.8.3.tgz" - integrity sha512-uGiiXAZMqEoQhRWMK17VospMZh5sXWg+dlh2soffpkAl96KAm+WZuJfa6lcELotSRmooLqg0MWdH6UUq85nmmg== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-transform-regenerator@^7.8.7": - version "7.8.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.8.7.tgz" - integrity sha512-TIg+gAl4Z0a3WmD3mbYSk+J9ZUH6n/Yc57rtKRnlA/7rcCvpekHXe0CMZHP1gYp7/KLe9GHTuIba0vXmls6drA== - dependencies: - regenerator-transform "^0.14.2" - -"@babel/plugin-transform-reserved-words@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.8.3.tgz" - integrity sha512-mwMxcycN3omKFDjDQUl+8zyMsBfjRFr0Zn/64I41pmjv4NJuqcYlEtezwYtw9TFd9WR1vN5kiM+O0gMZzO6L0A== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-transform-shorthand-properties@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.8.3.tgz" - integrity sha512-I9DI6Odg0JJwxCHzbzW08ggMdCezoWcuQRz3ptdudgwaHxTjxw5HgdFJmZIkIMlRymL6YiZcped4TTCB0JcC8w== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-transform-spread@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.8.3.tgz" - integrity sha512-CkuTU9mbmAoFOI1tklFWYYbzX5qCIZVXPVy0jpXgGwkplCndQAa58s2jr66fTeQnA64bDox0HL4U56CFYoyC7g== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-transform-sticky-regex@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.8.3.tgz" - integrity sha512-9Spq0vGCD5Bb4Z/ZXXSK5wbbLFMG085qd2vhL1JYu1WcQ5bXqZBAYRzU1d+p79GcHs2szYv5pVQCX13QgldaWw== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/helper-regex" "^7.8.3" - -"@babel/plugin-transform-template-literals@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.8.3.tgz" - integrity sha512-820QBtykIQOLFT8NZOcTRJ1UNuztIELe4p9DCgvj4NK+PwluSJ49we7s9FB1HIGNIYT7wFUJ0ar2QpCDj0escQ== - dependencies: - "@babel/helper-annotate-as-pure" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-transform-typeof-symbol@^7.8.4": - version "7.8.4" - resolved "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.8.4.tgz" - integrity sha512-2QKyfjGdvuNfHsb7qnBBlKclbD4CfshH2KvDabiijLMGXPHJXGxtDzwIF7bQP+T0ysw8fYTtxPafgfs/c1Lrqg== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-transform-unicode-regex@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.8.3.tgz" - integrity sha512-+ufgJjYdmWfSQ+6NS9VGUR2ns8cjJjYbrbi11mZBTaWm+Fui/ncTLFF28Ei1okavY+xkojGr1eJxNsWYeA5aZw== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/preset-env@^7.5.4": - version "7.9.5" - resolved "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.9.5.tgz" - integrity sha512-eWGYeADTlPJH+wq1F0wNfPbVS1w1wtmMJiYk55Td5Yu28AsdR9AsC97sZ0Qq8fHqQuslVSIYSGJMcblr345GfQ== - dependencies: - "@babel/compat-data" "^7.9.0" - "@babel/helper-compilation-targets" "^7.8.7" - "@babel/helper-module-imports" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/plugin-proposal-async-generator-functions" "^7.8.3" - "@babel/plugin-proposal-dynamic-import" "^7.8.3" - "@babel/plugin-proposal-json-strings" "^7.8.3" - "@babel/plugin-proposal-nullish-coalescing-operator" "^7.8.3" - "@babel/plugin-proposal-numeric-separator" "^7.8.3" - "@babel/plugin-proposal-object-rest-spread" "^7.9.5" - "@babel/plugin-proposal-optional-catch-binding" "^7.8.3" - "@babel/plugin-proposal-optional-chaining" "^7.9.0" - "@babel/plugin-proposal-unicode-property-regex" "^7.8.3" - "@babel/plugin-syntax-async-generators" "^7.8.0" - "@babel/plugin-syntax-dynamic-import" "^7.8.0" - "@babel/plugin-syntax-json-strings" "^7.8.0" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" - "@babel/plugin-syntax-numeric-separator" "^7.8.0" - "@babel/plugin-syntax-object-rest-spread" "^7.8.0" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" - "@babel/plugin-syntax-optional-chaining" "^7.8.0" - "@babel/plugin-syntax-top-level-await" "^7.8.3" - "@babel/plugin-transform-arrow-functions" "^7.8.3" - "@babel/plugin-transform-async-to-generator" "^7.8.3" - "@babel/plugin-transform-block-scoped-functions" "^7.8.3" - "@babel/plugin-transform-block-scoping" "^7.8.3" - "@babel/plugin-transform-classes" "^7.9.5" - "@babel/plugin-transform-computed-properties" "^7.8.3" - "@babel/plugin-transform-destructuring" "^7.9.5" - "@babel/plugin-transform-dotall-regex" "^7.8.3" - "@babel/plugin-transform-duplicate-keys" "^7.8.3" - "@babel/plugin-transform-exponentiation-operator" "^7.8.3" - "@babel/plugin-transform-for-of" "^7.9.0" - "@babel/plugin-transform-function-name" "^7.8.3" - "@babel/plugin-transform-literals" "^7.8.3" - "@babel/plugin-transform-member-expression-literals" "^7.8.3" - "@babel/plugin-transform-modules-amd" "^7.9.0" - "@babel/plugin-transform-modules-commonjs" "^7.9.0" - "@babel/plugin-transform-modules-systemjs" "^7.9.0" - "@babel/plugin-transform-modules-umd" "^7.9.0" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.8.3" - "@babel/plugin-transform-new-target" "^7.8.3" - "@babel/plugin-transform-object-super" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.9.5" - "@babel/plugin-transform-property-literals" "^7.8.3" - "@babel/plugin-transform-regenerator" "^7.8.7" - "@babel/plugin-transform-reserved-words" "^7.8.3" - "@babel/plugin-transform-shorthand-properties" "^7.8.3" - "@babel/plugin-transform-spread" "^7.8.3" - "@babel/plugin-transform-sticky-regex" "^7.8.3" - "@babel/plugin-transform-template-literals" "^7.8.3" - "@babel/plugin-transform-typeof-symbol" "^7.8.4" - "@babel/plugin-transform-unicode-regex" "^7.8.3" - "@babel/preset-modules" "^0.1.3" - "@babel/types" "^7.9.5" - browserslist "^4.9.1" - core-js-compat "^3.6.2" - invariant "^2.2.2" - levenary "^1.1.1" - semver "^5.5.0" - -"@babel/preset-modules@^0.1.3": - version "0.1.3" - resolved "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.3.tgz" - integrity sha512-Ra3JXOHBq2xd56xSF7lMKXdjBn3T772Y1Wet3yWnkDly9zHvJki029tAFzvAAK5cf4YV3yoxuP61crYRol6SVg== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-proposal-unicode-property-regex" "^7.4.4" - "@babel/plugin-transform-dotall-regex" "^7.4.4" - "@babel/types" "^7.4.4" - esutils "^2.0.2" - -"@babel/runtime@^7.8.4": - version "7.9.2" - resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.2.tgz" - integrity sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q== - dependencies: - regenerator-runtime "^0.13.4" - -"@babel/template@^7.8.3", "@babel/template@^7.8.6": - version "7.8.6" - resolved "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz" - integrity sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg== - dependencies: - "@babel/code-frame" "^7.8.3" - "@babel/parser" "^7.8.6" - "@babel/types" "^7.8.6" - -"@babel/traverse@^7.8.3", "@babel/traverse@^7.8.6", "@babel/traverse@^7.9.0": - version "7.9.5" - resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.9.5.tgz" - integrity sha512-c4gH3jsvSuGUezlP6rzSJ6jf8fYjLj3hsMZRx/nX0h+fmHN0w+ekubRrHPqnMec0meycA2nwCsJ7dC8IPem2FQ== - dependencies: - "@babel/code-frame" "^7.8.3" - "@babel/generator" "^7.9.5" - "@babel/helper-function-name" "^7.9.5" - "@babel/helper-split-export-declaration" "^7.8.3" - "@babel/parser" "^7.9.0" - "@babel/types" "^7.9.5" - debug "^4.1.0" - globals "^11.1.0" - lodash "^4.17.13" - -"@babel/types@^7.4.4", "@babel/types@^7.8.3", "@babel/types@^7.8.6", "@babel/types@^7.9.0", "@babel/types@^7.9.5": - version "7.9.5" - resolved "https://registry.npmjs.org/@babel/types/-/types-7.9.5.tgz" - integrity sha512-XjnvNqenk818r5zMaba+sLQjnbda31UfUURv3ei0qPQw4u+j2jMyJ5b11y8ZHYTRSI3NnInQkkkRT4fLqqPdHg== - dependencies: - "@babel/helper-validator-identifier" "^7.9.5" - lodash "^4.17.13" - to-fast-properties "^2.0.0" - -"@csstools/convert-colors@^1.4.0": - version "1.4.0" - resolved "https://registry.npmjs.org/@csstools/convert-colors/-/convert-colors-1.4.0.tgz" - integrity sha512-5a6wqoJV/xEdbRNKVo6I4hO3VjyDq//8q2f9I6PBAvMesJHFauXDorcNCsr9RzvsZnaWi5NYCcfyqP1QeFHFbw== - -"@jimp/bmp@^0.6.8": - version "0.6.8" - resolved "https://registry.npmjs.org/@jimp/bmp/-/bmp-0.6.8.tgz" - integrity sha512-uxVgSkI62uAzk5ZazYHEHBehow590WAkLKmDXLzkr/XP/Hv2Fx1T4DKwJ/15IY5ktq5VAhAUWGXTyd8KWFsx7w== - dependencies: - "@jimp/utils" "^0.6.8" - bmp-js "^0.1.0" - core-js "^2.5.7" - -"@jimp/core@^0.6.8": - version "0.6.8" - resolved "https://registry.npmjs.org/@jimp/core/-/core-0.6.8.tgz" - integrity sha512-JOFqBBcSNiDiMZJFr6OJqC6viXj5NVBQISua0eacoYvo4YJtTajOIxC4MqWyUmGrDpRMZBR8QhSsIOwsFrdROA== - dependencies: - "@jimp/utils" "^0.6.8" - any-base "^1.1.0" - buffer "^5.2.0" - core-js "^2.5.7" - exif-parser "^0.1.12" - file-type "^9.0.0" - load-bmfont "^1.3.1" - mkdirp "0.5.1" - phin "^2.9.1" - pixelmatch "^4.0.2" - tinycolor2 "^1.4.1" - -"@jimp/custom@^0.6.8": - version "0.6.8" - resolved "https://registry.npmjs.org/@jimp/custom/-/custom-0.6.8.tgz" - integrity sha512-FrYlzZRVXP2vuVwd7Nc2dlK+iZk4g6IaT1Ib8Z6vU5Kkwlt83FJIPJ2UUFABf3bF5big0wkk8ZUihWxE4Nzdng== - dependencies: - "@jimp/core" "^0.6.8" - core-js "^2.5.7" - -"@jimp/gif@^0.6.8": - version "0.6.8" - resolved "https://registry.npmjs.org/@jimp/gif/-/gif-0.6.8.tgz" - integrity sha512-yyOlujjQcgz9zkjM5ihZDEppn9d1brJ7jQHP5rAKmqep0G7FU1D0AKcV+Ql18RhuI/CgWs10wAVcrQpmLnu4Yw== - dependencies: - "@jimp/utils" "^0.6.8" - core-js "^2.5.7" - omggif "^1.0.9" - -"@jimp/jpeg@^0.6.8": - version "0.6.8" - resolved "https://registry.npmjs.org/@jimp/jpeg/-/jpeg-0.6.8.tgz" - integrity sha512-rGtXbYpFXAn471qLpTGvhbBMNHJo5KiufN+vC5AWyufntmkt5f0Ox2Cx4ijuBMDtirZchxbMLtrfGjznS4L/ew== - dependencies: - "@jimp/utils" "^0.6.8" - core-js "^2.5.7" - jpeg-js "^0.3.4" - -"@jimp/plugin-blit@^0.6.8": - version "0.6.8" - resolved "https://registry.npmjs.org/@jimp/plugin-blit/-/plugin-blit-0.6.8.tgz" - integrity sha512-7Tl6YpKTSpvwQbnGNhsfX2zyl3jRVVopd276Y2hF2zpDz9Bycow7NdfNU/4Nx1jaf96X6uWOtSVINcQ7rGd47w== - dependencies: - "@jimp/utils" "^0.6.8" - core-js "^2.5.7" - -"@jimp/plugin-blur@^0.6.8": - version "0.6.8" - resolved "https://registry.npmjs.org/@jimp/plugin-blur/-/plugin-blur-0.6.8.tgz" - integrity sha512-NpZCMKxXHLDQsX9zPlWtpMA660DQStY6/z8ZetyxCDbqrLe9YCXpeR4MNhdJdABIiwTm1W5FyFF4kp81PHJx3Q== - dependencies: - "@jimp/utils" "^0.6.8" - core-js "^2.5.7" - -"@jimp/plugin-color@^0.6.8": - version "0.6.8" - resolved "https://registry.npmjs.org/@jimp/plugin-color/-/plugin-color-0.6.8.tgz" - integrity sha512-jjFyU0zNmGOH2rjzHuOMU4kaia0oo82s/7UYfn5h7OUkmUZTd6Do3ZSK1PiXA7KR+s4B76/Omm6Doh/0SGb7BQ== - dependencies: - "@jimp/utils" "^0.6.8" - core-js "^2.5.7" - tinycolor2 "^1.4.1" - -"@jimp/plugin-contain@^0.6.8": - version "0.6.8" - resolved "https://registry.npmjs.org/@jimp/plugin-contain/-/plugin-contain-0.6.8.tgz" - integrity sha512-p/P2wCXhAzbmEgXvGsvmxLmbz45feF6VpR4m9suPSOr8PC/i/XvTklTqYEUidYYAft4vHgsYJdS74HKSMnH8lw== - dependencies: - "@jimp/utils" "^0.6.8" - core-js "^2.5.7" - -"@jimp/plugin-cover@^0.6.8": - version "0.6.8" - resolved "https://registry.npmjs.org/@jimp/plugin-cover/-/plugin-cover-0.6.8.tgz" - integrity sha512-2PvWgk+PJfRsfWDI1G8Fpjrsu0ZlpNyZxO2+fqWlVo6y/y2gP4v08FqvbkcqSjNlOu2IDWIFXpgyU0sTINWZLg== - dependencies: - "@jimp/utils" "^0.6.8" - core-js "^2.5.7" - -"@jimp/plugin-crop@^0.6.8": - version "0.6.8" - resolved "https://registry.npmjs.org/@jimp/plugin-crop/-/plugin-crop-0.6.8.tgz" - integrity sha512-CbrcpWE2xxPK1n/JoTXzhRUhP4mO07mTWaSavenCg664oQl/9XCtL+A0FekuNHzIvn4myEqvkiTwN7FsbunS/Q== - dependencies: - "@jimp/utils" "^0.6.8" - core-js "^2.5.7" - -"@jimp/plugin-displace@^0.6.8": - version "0.6.8" - resolved "https://registry.npmjs.org/@jimp/plugin-displace/-/plugin-displace-0.6.8.tgz" - integrity sha512-RmV2bPxoPE6mrPxtYSPtHxm2cGwBQr5a2p+9gH6SPy+eUMrbGjbvjwKNfXWUYD0leML+Pt5XOmAS9pIROmuruQ== - dependencies: - "@jimp/utils" "^0.6.8" - core-js "^2.5.7" - -"@jimp/plugin-dither@^0.6.8": - version "0.6.8" - resolved "https://registry.npmjs.org/@jimp/plugin-dither/-/plugin-dither-0.6.8.tgz" - integrity sha512-x6V/qjxe+xypjpQm7GbiMNqci1EW5UizrcebOhHr8AHijOEqHd2hjXh5f6QIGfrkTFelc4/jzq1UyCsYntqz9Q== - dependencies: - "@jimp/utils" "^0.6.8" - core-js "^2.5.7" - -"@jimp/plugin-flip@^0.6.8": - version "0.6.8" - resolved "https://registry.npmjs.org/@jimp/plugin-flip/-/plugin-flip-0.6.8.tgz" - integrity sha512-4il6Da6G39s9MyWBEee4jztEOUGJ40E6OlPjkMrdpDNvge6hYEAB31BczTYBP/CEY74j4LDSoY5LbcU4kv06yA== - dependencies: - "@jimp/utils" "^0.6.8" - core-js "^2.5.7" - -"@jimp/plugin-gaussian@^0.6.8": - version "0.6.8" - resolved "https://registry.npmjs.org/@jimp/plugin-gaussian/-/plugin-gaussian-0.6.8.tgz" - integrity sha512-pVOblmjv7stZjsqloi4YzHVwAPXKGdNaHPhp4KP4vj41qtc6Hxd9z/+VWGYRTunMFac84gUToe0UKIXd6GhoKw== - dependencies: - "@jimp/utils" "^0.6.8" - core-js "^2.5.7" - -"@jimp/plugin-invert@^0.6.8": - version "0.6.8" - resolved "https://registry.npmjs.org/@jimp/plugin-invert/-/plugin-invert-0.6.8.tgz" - integrity sha512-11zuLiXDHr6tFv4U8aieXqNXQEKbDbSBG/h+X62gGTNFpyn8EVPpncHhOqrAFtZUaPibBqMFlNJ15SzwC7ExsQ== - dependencies: - "@jimp/utils" "^0.6.8" - core-js "^2.5.7" - -"@jimp/plugin-mask@^0.6.8": - version "0.6.8" - resolved "https://registry.npmjs.org/@jimp/plugin-mask/-/plugin-mask-0.6.8.tgz" - integrity sha512-hZJ0OiKGJyv7hDSATwJDkunB1Ie80xJnONMgpUuUseteK45YeYNBOiZVUe8vum8QI1UwavgBzcvQ9u4fcgXc9g== - dependencies: - "@jimp/utils" "^0.6.8" - core-js "^2.5.7" - -"@jimp/plugin-normalize@^0.6.8": - version "0.6.8" - resolved "https://registry.npmjs.org/@jimp/plugin-normalize/-/plugin-normalize-0.6.8.tgz" - integrity sha512-Q4oYhU+sSyTJI7pMZlg9/mYh68ujLfOxXzQGEXuw0sHGoGQs3B0Jw7jmzGa6pIS06Hup5hD2Zuh1ppvMdjJBfQ== - dependencies: - "@jimp/utils" "^0.6.8" - core-js "^2.5.7" - -"@jimp/plugin-print@^0.6.8": - version "0.6.8" - resolved "https://registry.npmjs.org/@jimp/plugin-print/-/plugin-print-0.6.8.tgz" - integrity sha512-2aokejGn4Drv1FesnZGqh5KEq0FQtR0drlmtyZrBH+r9cx7hh0Qgf4D1BOTDEgXkfSSngjGRjKKRW/fwOrVXYw== - dependencies: - "@jimp/utils" "^0.6.8" - core-js "^2.5.7" - load-bmfont "^1.4.0" - -"@jimp/plugin-resize@^0.6.8": - version "0.6.8" - resolved "https://registry.npmjs.org/@jimp/plugin-resize/-/plugin-resize-0.6.8.tgz" - integrity sha512-27nPh8L1YWsxtfmV/+Ub5dOTpXyC0HMF2cu52RQSCYxr+Lm1+23dJF70AF1poUbUe+FWXphwuUxQzjBJza9UoA== - dependencies: - "@jimp/utils" "^0.6.8" - core-js "^2.5.7" - -"@jimp/plugin-rotate@^0.6.8": - version "0.6.8" - resolved "https://registry.npmjs.org/@jimp/plugin-rotate/-/plugin-rotate-0.6.8.tgz" - integrity sha512-GbjETvL05BDoLdszNUV4Y0yLkHf177MnqGqilA113LIvx9aD0FtUopGXYfRGVvmtTOTouoaGJUc+K6qngvKxww== - dependencies: - "@jimp/utils" "^0.6.8" - core-js "^2.5.7" - -"@jimp/plugin-scale@^0.6.8": - version "0.6.8" - resolved "https://registry.npmjs.org/@jimp/plugin-scale/-/plugin-scale-0.6.8.tgz" - integrity sha512-GzIYWR/oCUK2jAwku23zt19V1ssaEU4pL0x2XsLNKuuJEU6DvEytJyTMXCE7OLG/MpDBQcQclJKHgiyQm5gIOQ== - dependencies: - "@jimp/utils" "^0.6.8" - core-js "^2.5.7" - -"@jimp/plugins@^0.6.8": - version "0.6.8" - resolved "https://registry.npmjs.org/@jimp/plugins/-/plugins-0.6.8.tgz" - integrity sha512-fMcTI72Vn/Lz6JftezTURmyP5ml/xGMe0Ljx2KRJ85IWyP33vDmGIUuutFiBEbh2+y7lRT+aTSmjs0QGa/xTmQ== - dependencies: - "@jimp/plugin-blit" "^0.6.8" - "@jimp/plugin-blur" "^0.6.8" - "@jimp/plugin-color" "^0.6.8" - "@jimp/plugin-contain" "^0.6.8" - "@jimp/plugin-cover" "^0.6.8" - "@jimp/plugin-crop" "^0.6.8" - "@jimp/plugin-displace" "^0.6.8" - "@jimp/plugin-dither" "^0.6.8" - "@jimp/plugin-flip" "^0.6.8" - "@jimp/plugin-gaussian" "^0.6.8" - "@jimp/plugin-invert" "^0.6.8" - "@jimp/plugin-mask" "^0.6.8" - "@jimp/plugin-normalize" "^0.6.8" - "@jimp/plugin-print" "^0.6.8" - "@jimp/plugin-resize" "^0.6.8" - "@jimp/plugin-rotate" "^0.6.8" - "@jimp/plugin-scale" "^0.6.8" - core-js "^2.5.7" - timm "^1.6.1" - -"@jimp/png@^0.6.8": - version "0.6.8" - resolved "https://registry.npmjs.org/@jimp/png/-/png-0.6.8.tgz" - integrity sha512-JHHg/BZ7KDtHQrcG+a7fztw45rdf7okL/YwkN4qU5FH7Fcrp41nX5QnRviDtD9hN+GaNC7kvjvcqRAxW25qjew== - dependencies: - "@jimp/utils" "^0.6.8" - core-js "^2.5.7" - pngjs "^3.3.3" - -"@jimp/tiff@^0.6.8": - version "0.6.8" - resolved "https://registry.npmjs.org/@jimp/tiff/-/tiff-0.6.8.tgz" - integrity sha512-iWHbxd+0IKWdJyJ0HhoJCGYmtjPBOusz1z1HT/DnpePs/Lo3TO4d9ALXqYfUkyG74ZK5jULZ69KLtwuhuJz1bg== - dependencies: - core-js "^2.5.7" - utif "^2.0.1" - -"@jimp/types@^0.6.8": - version "0.6.8" - resolved "https://registry.npmjs.org/@jimp/types/-/types-0.6.8.tgz" - integrity sha512-vCZ/Cp2osy69VP21XOBACfHI5HeR60Rfd4Jidj4W73UL+HrFWOtyQiJ7hlToyu1vI5mR/NsUQpzyQvz56ADm5A== - dependencies: - "@jimp/bmp" "^0.6.8" - "@jimp/gif" "^0.6.8" - "@jimp/jpeg" "^0.6.8" - "@jimp/png" "^0.6.8" - "@jimp/tiff" "^0.6.8" - core-js "^2.5.7" - timm "^1.6.1" - -"@jimp/utils@^0.6.8": - version "0.6.8" - resolved "https://registry.npmjs.org/@jimp/utils/-/utils-0.6.8.tgz" - integrity sha512-7RDfxQ2C/rarNG9iso5vmnKQbcvlQjBIlF/p7/uYj72WeZgVCB+5t1fFBKJSU4WhniHX4jUMijK+wYGE3Y3bGw== - dependencies: - core-js "^2.5.7" - -"@nastyox/rando.js@^2.0.5": - version "2.0.5" - resolved "https://registry.yarnpkg.com/@nastyox/rando.js/-/rando.js-2.0.5.tgz#ecce9adb3251512016ae4d3baed6832679651a8a" - integrity sha512-a7s3h4vnpCVEYT+W+mBc4ykmwtO29DnFQQR/X6YSbJ+YenAvpYBRCeUgEoPEWGlUS1Hbw1IWIsiIFVXw0g2QLw== - -"@octokit/auth-token@^2.4.0": - version "2.4.2" - resolved "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.4.2.tgz" - integrity sha512-jE/lE/IKIz2v1+/P0u4fJqv0kYwXOTujKemJMFr6FeopsxlIK3+wKDCJGnysg81XID5TgZQbIfuJ5J0lnTiuyQ== - dependencies: - "@octokit/types" "^5.0.0" - -"@octokit/core@^3.0.0": - version "3.1.2" - resolved "https://registry.npmjs.org/@octokit/core/-/core-3.1.2.tgz" - integrity sha512-AInOFULmwOa7+NFi9F8DlDkm5qtZVmDQayi7TUgChE3yeIGPq0Y+6cAEXPexQ3Ea+uZy66hKEazR7DJyU+4wfw== - dependencies: - "@octokit/auth-token" "^2.4.0" - "@octokit/graphql" "^4.3.1" - "@octokit/request" "^5.4.0" - "@octokit/types" "^5.0.0" - before-after-hook "^2.1.0" - universal-user-agent "^6.0.0" - -"@octokit/endpoint@^6.0.1": - version "6.0.6" - resolved "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.6.tgz" - integrity sha512-7Cc8olaCoL/mtquB7j/HTbPM+sY6Ebr4k2X2y4JoXpVKQ7r5xB4iGQE0IoO58wIPsUk4AzoT65AMEpymSbWTgQ== - dependencies: - "@octokit/types" "^5.0.0" - is-plain-object "^5.0.0" - universal-user-agent "^6.0.0" - -"@octokit/graphql@^4.3.1": - version "4.5.6" - resolved "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.5.6.tgz" - integrity sha512-Rry+unqKTa3svswT2ZAuqenpLrzJd+JTv89LTeVa5UM/5OX8o4KTkPL7/1ABq4f/ZkELb0XEK/2IEoYwykcLXg== - dependencies: - "@octokit/request" "^5.3.0" - "@octokit/types" "^5.0.0" - universal-user-agent "^6.0.0" - -"@octokit/plugin-paginate-rest@^2.2.0": - version "2.4.0" - resolved "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.4.0.tgz" - integrity sha512-YT6Klz3LLH6/nNgi0pheJnUmTFW4kVnxGft+v8Itc41IIcjl7y1C8TatmKQBbCSuTSNFXO5pCENnqg6sjwpJhg== - dependencies: - "@octokit/types" "^5.5.0" - -"@octokit/plugin-request-log@^1.0.0": - version "1.0.0" - resolved "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.0.tgz" - integrity sha512-ywoxP68aOT3zHCLgWZgwUJatiENeHE7xJzYjfz8WI0goynp96wETBF+d95b8g/uL4QmS6owPVlaxiz3wyMAzcw== - -"@octokit/plugin-rest-endpoint-methods@4.2.0": - version "4.2.0" - resolved "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-4.2.0.tgz" - integrity sha512-1/qn1q1C1hGz6W/iEDm9DoyNoG/xdFDt78E3eZ5hHeUfJTLJgyAMdj9chL/cNBHjcjd+FH5aO1x0VCqR2RE0mw== - dependencies: - "@octokit/types" "^5.5.0" - deprecation "^2.3.1" - -"@octokit/request-error@^2.0.0": - version "2.0.2" - resolved "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.0.2.tgz" - integrity sha512-2BrmnvVSV1MXQvEkrb9zwzP0wXFNbPJij922kYBTLIlIafukrGOb+ABBT2+c6wZiuyWDH1K1zmjGQ0toN/wMWw== - dependencies: - "@octokit/types" "^5.0.1" - deprecation "^2.0.0" - once "^1.4.0" - -"@octokit/request@^5.3.0", "@octokit/request@^5.4.0": - version "5.4.9" - resolved "https://registry.npmjs.org/@octokit/request/-/request-5.4.9.tgz" - integrity sha512-CzwVvRyimIM1h2n9pLVYfTDmX9m+KHSgCpqPsY8F1NdEK8IaWqXhSBXsdjOBFZSpEcxNEeg4p0UO9cQ8EnOCLA== - dependencies: - "@octokit/endpoint" "^6.0.1" - "@octokit/request-error" "^2.0.0" - "@octokit/types" "^5.0.0" - deprecation "^2.0.0" - is-plain-object "^5.0.0" - node-fetch "^2.6.1" - once "^1.4.0" - universal-user-agent "^6.0.0" - -"@octokit/rest@^18.0.6": - version "18.0.6" - resolved "https://registry.npmjs.org/@octokit/rest/-/rest-18.0.6.tgz" - integrity sha512-ES4lZBKPJMX/yUoQjAZiyFjei9pJ4lTTfb9k7OtYoUzKPDLl/M8jiHqt6qeSauyU4eZGLw0sgP1WiQl9FYeM5w== - dependencies: - "@octokit/core" "^3.0.0" - "@octokit/plugin-paginate-rest" "^2.2.0" - "@octokit/plugin-request-log" "^1.0.0" - "@octokit/plugin-rest-endpoint-methods" "4.2.0" - -"@octokit/types@^5.0.0", "@octokit/types@^5.0.1", "@octokit/types@^5.5.0": - version "5.5.0" - resolved "https://registry.npmjs.org/@octokit/types/-/types-5.5.0.tgz" - integrity sha512-UZ1pErDue6bZNjYOotCNveTXArOMZQFG6hKJfOnGnulVCMcVVi7YIIuuR4WfBhjo7zgpmzn/BkPDnUXtNx+PcQ== - dependencies: - "@types/node" ">= 8" - -"@sindresorhus/is@^0.7.0": - version "0.7.0" - resolved "https://registry.npmjs.org/@sindresorhus/is/-/is-0.7.0.tgz" - integrity sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow== - -"@types/color-name@^1.1.1": - version "1.1.1" - resolved "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz" - integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== - -"@types/cordova@^0.0.34": - version "0.0.34" - resolved "https://registry.npmjs.org/@types/cordova/-/cordova-0.0.34.tgz" - integrity sha1-6nrd907Ow9dimCegw54smt3HPQQ= - -"@types/eslint-visitor-keys@^1.0.0": - version "1.0.0" - resolved "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz" - integrity sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag== - -"@types/filesystem@^0.0.29": - version "0.0.29" - resolved "https://registry.npmjs.org/@types/filesystem/-/filesystem-0.0.29.tgz" - integrity sha512-85/1KfRedmfPGsbK8YzeaQUyV1FQAvMPMTuWFQ5EkLd2w7szhNO96bk3Rh/SKmOfd9co2rCLf0Voy4o7ECBOvw== - dependencies: - "@types/filewriter" "*" - -"@types/filewriter@*": - version "0.0.28" - resolved "https://registry.npmjs.org/@types/filewriter/-/filewriter-0.0.28.tgz" - integrity sha1-wFTor02d11205jq8dviFFocU1LM= - -"@types/json-schema@^7.0.3": - version "7.0.4" - resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.4.tgz" - integrity sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA== - -"@types/node@>= 8": - version "14.11.2" - resolved "https://registry.npmjs.org/@types/node/-/node-14.11.2.tgz" - integrity sha512-jiE3QIxJ8JLNcb1Ps6rDbysDhN4xa8DJJvuC9prr6w+1tIh+QAbYyNF3tyiZNLDBIuBCf4KEcV2UvQm/V60xfA== - -"@types/q@^1.5.1": - version "1.5.2" - resolved "https://registry.npmjs.org/@types/q/-/q-1.5.2.tgz" - integrity sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw== - -"@typescript-eslint/eslint-plugin@3.0.1": - version "3.0.1" - resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.0.1.tgz" - integrity sha512-RxGldRQD3hgOK2xtBfNfA5MMV3rn5gVChe+MIf14hKm51jO2urqF64xOyVrGtzThkrd4rS1Kihqx2nkSxkXHvA== - dependencies: - "@typescript-eslint/experimental-utils" "3.0.1" - functional-red-black-tree "^1.0.1" - regexpp "^3.0.0" - semver "^7.3.2" - tsutils "^3.17.1" - -"@typescript-eslint/experimental-utils@3.0.1": - version "3.0.1" - resolved "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-3.0.1.tgz" - integrity sha512-GdwOVz80MOWxbc/br1DC30eeqlxfpVzexHgHtf3L0hcbOu1xAs1wSCNcaBTLMOMZbh1gj/cKZt0eB207FxWfFA== - dependencies: - "@types/json-schema" "^7.0.3" - "@typescript-eslint/typescript-estree" "3.0.1" - eslint-scope "^5.0.0" - eslint-utils "^2.0.0" - -"@typescript-eslint/parser@3.0.1": - version "3.0.1" - resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-3.0.1.tgz" - integrity sha512-Pn2tDmOc4Ri93VQnT70W0pqQr6i/pEZqIPXfWXm4RuiIprL0t6SG13ViVXHgfScknL2Fm2G4IqXhUzxSRCWXCw== - dependencies: - "@types/eslint-visitor-keys" "^1.0.0" - "@typescript-eslint/experimental-utils" "3.0.1" - "@typescript-eslint/typescript-estree" "3.0.1" - eslint-visitor-keys "^1.1.0" - -"@typescript-eslint/typescript-estree@3.0.1": - version "3.0.1" - resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-3.0.1.tgz" - integrity sha512-FrbMdgVCeIGHKaP9OYTttFTlF8Ds7AkjMca2GzYCE7pVch10PAJc1mmAFt+DfQPgu/2TrLAprg2vI0PK/WTdcg== - dependencies: - debug "^4.1.1" - eslint-visitor-keys "^1.1.0" - glob "^7.1.6" - is-glob "^4.0.1" - lodash "^4.17.15" - semver "^7.3.2" - tsutils "^3.17.1" - -"@webassemblyjs/ast@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz" - integrity sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA== - dependencies: - "@webassemblyjs/helper-module-context" "1.9.0" - "@webassemblyjs/helper-wasm-bytecode" "1.9.0" - "@webassemblyjs/wast-parser" "1.9.0" - -"@webassemblyjs/floating-point-hex-parser@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz" - integrity sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA== - -"@webassemblyjs/helper-api-error@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz" - integrity sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw== - -"@webassemblyjs/helper-buffer@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz" - integrity sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA== - -"@webassemblyjs/helper-code-frame@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz" - integrity sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA== - dependencies: - "@webassemblyjs/wast-printer" "1.9.0" - -"@webassemblyjs/helper-fsm@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz" - integrity sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw== - -"@webassemblyjs/helper-module-context@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz" - integrity sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g== - dependencies: - "@webassemblyjs/ast" "1.9.0" - -"@webassemblyjs/helper-wasm-bytecode@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz" - integrity sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw== - -"@webassemblyjs/helper-wasm-section@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz" - integrity sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-buffer" "1.9.0" - "@webassemblyjs/helper-wasm-bytecode" "1.9.0" - "@webassemblyjs/wasm-gen" "1.9.0" - -"@webassemblyjs/ieee754@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz" - integrity sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg== - dependencies: - "@xtuc/ieee754" "^1.2.0" - -"@webassemblyjs/leb128@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.9.0.tgz" - integrity sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw== - dependencies: - "@xtuc/long" "4.2.2" - -"@webassemblyjs/utf8@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.9.0.tgz" - integrity sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w== - -"@webassemblyjs/wasm-edit@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz" - integrity sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-buffer" "1.9.0" - "@webassemblyjs/helper-wasm-bytecode" "1.9.0" - "@webassemblyjs/helper-wasm-section" "1.9.0" - "@webassemblyjs/wasm-gen" "1.9.0" - "@webassemblyjs/wasm-opt" "1.9.0" - "@webassemblyjs/wasm-parser" "1.9.0" - "@webassemblyjs/wast-printer" "1.9.0" - -"@webassemblyjs/wasm-gen@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz" - integrity sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-wasm-bytecode" "1.9.0" - "@webassemblyjs/ieee754" "1.9.0" - "@webassemblyjs/leb128" "1.9.0" - "@webassemblyjs/utf8" "1.9.0" - -"@webassemblyjs/wasm-opt@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz" - integrity sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-buffer" "1.9.0" - "@webassemblyjs/wasm-gen" "1.9.0" - "@webassemblyjs/wasm-parser" "1.9.0" - -"@webassemblyjs/wasm-parser@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz" - integrity sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-api-error" "1.9.0" - "@webassemblyjs/helper-wasm-bytecode" "1.9.0" - "@webassemblyjs/ieee754" "1.9.0" - "@webassemblyjs/leb128" "1.9.0" - "@webassemblyjs/utf8" "1.9.0" - -"@webassemblyjs/wast-parser@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz" - integrity sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/floating-point-hex-parser" "1.9.0" - "@webassemblyjs/helper-api-error" "1.9.0" - "@webassemblyjs/helper-code-frame" "1.9.0" - "@webassemblyjs/helper-fsm" "1.9.0" - "@xtuc/long" "4.2.2" - -"@webassemblyjs/wast-printer@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz" - integrity sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/wast-parser" "1.9.0" - "@xtuc/long" "4.2.2" - -"@xtuc/ieee754@^1.2.0": - version "1.2.0" - resolved "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz" - integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== - -"@xtuc/long@4.2.2": - version "4.2.2" - resolved "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz" - integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== - -accepts@~1.3.7: - version "1.3.7" - resolved "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz" - integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== - dependencies: - mime-types "~2.1.24" - negotiator "0.6.2" - -acorn-jsx@^5.2.0: - version "5.2.0" - resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.2.0.tgz" - integrity sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ== - -acorn-walk@^7.1.1: - version "7.1.1" - resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.1.1.tgz" - integrity sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ== - -acorn@^6.4.1: - version "6.4.1" - resolved "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz" - integrity sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA== - -acorn@^7.1.1: - version "7.1.1" - resolved "https://registry.npmjs.org/acorn/-/acorn-7.1.1.tgz" - integrity sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg== - -ajv-errors@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz" - integrity sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ== - -ajv-keywords@^3.1.0, ajv-keywords@^3.4.1: - version "3.5.1" - resolved "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.1.tgz" - integrity sha512-KWcq3xN8fDjSB+IMoh2VaXVhRI0BBGxoYp3rx7Pkb6z0cFjYR9Q9l4yZqqals0/zsioCmocC5H6UvsGD4MoIBA== - -ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.0: - version "6.12.0" - resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz" - integrity sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw== - 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" - -alphanum-sort@^1.0.0: - version "1.0.2" - resolved "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz" - integrity sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM= - -amdefine@>=0.0.4: - version "1.0.1" - resolved "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz" - integrity sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU= - -ansi-escapes@^4.2.1: - version "4.3.1" - resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz" - integrity sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA== - dependencies: - type-fest "^0.11.0" - -ansi-regex@^2.0.0: - version "2.1.1" - resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz" - integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= - -ansi-regex@^4.1.0: - version "4.1.0" - resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz" - integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== - -ansi-regex@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz" - integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== - -ansi-styles@^2.2.1: - version "2.2.1" - resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz" - integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= - -ansi-styles@^3.2.0, ansi-styles@^3.2.1: - 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== - dependencies: - color-convert "^1.9.0" - -ansi-styles@^4.0.0, ansi-styles@^4.1.0: - version "4.2.1" - resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz" - integrity sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA== - dependencies: - "@types/color-name" "^1.1.1" - color-convert "^2.0.1" - -any-base@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/any-base/-/any-base-1.1.0.tgz" - integrity sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg== - -anymatch@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" - integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== - dependencies: - micromatch "^3.1.4" - normalize-path "^2.1.1" - -anymatch@~3.1.1: - version "3.1.1" - resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz" - integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg== - dependencies: - normalize-path "^3.0.0" - picomatch "^2.0.4" - -aproba@^1.1.1: - version "1.2.0" - resolved "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz" - integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== - -arch@^2.1.0: - version "2.1.1" - resolved "https://registry.npmjs.org/arch/-/arch-2.1.1.tgz" - integrity sha512-BLM56aPo9vLLFVa8+/+pJLnrZ7QGGTVHWsCwieAWT9o9K8UeGaQbzZbGoabWLOo2ksBCztoXdqBZBplqLDDCSg== - -archive-type@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/archive-type/-/archive-type-4.0.0.tgz" - integrity sha1-+S5yIzBW38aWlHJ0nCZ72wRrHXA= - dependencies: - file-type "^4.2.0" - -argparse@^1.0.7: - version "1.0.10" - resolved "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz" - integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== - dependencies: - sprintf-js "~1.0.2" - -arr-diff@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz" - integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= - -arr-flatten@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz" - integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== - -arr-union@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz" - integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= - -array-find-index@^1.0.1: - version "1.0.2" - resolved "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz" - integrity sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E= - -array-flatten@1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz" - integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= - -array-unique@^0.3.2: - version "0.3.2" - resolved "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz" - integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= - -asn1.js@^4.0.0: - version "4.10.1" - resolved "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz" - integrity sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw== - dependencies: - bn.js "^4.0.0" - inherits "^2.0.1" - minimalistic-assert "^1.0.0" - -assert@^1.1.1: - version "1.5.0" - resolved "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz" - integrity sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA== - dependencies: - object-assign "^4.1.1" - util "0.10.3" - -assets@^3.0.0: - version "3.0.1" - resolved "https://registry.npmjs.org/assets/-/assets-3.0.1.tgz" - integrity sha512-fTyLNf/9V24y5zO83f4DAEuvaKj7MWBixbnqdZneAhsv1r21yQ/6ogZfvXHmphJAHsz4DhuOwHeJKVbGqqvk0Q== - dependencies: - async "^2.5.0" - bluebird "^3.4.6" - calipers "^2.0.0" - calipers-gif "^2.0.0" - calipers-jpeg "^2.0.0" - calipers-png "^2.0.0" - calipers-svg "^2.0.0" - calipers-webp "^2.0.0" - glob "^7.0.6" - lodash "^4.15.0" - mime "^2.4.0" - -assign-symbols@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz" - integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= - -ast-types@0.9.6: - version "0.9.6" - resolved "https://registry.npmjs.org/ast-types/-/ast-types-0.9.6.tgz" - integrity sha1-ECyenpAF0+fjgpvwxPok7oYu6bk= - -astral-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz" - integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== - -async-each@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" - integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== - -async-limiter@~1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz" - integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== - -async@^2.5.0: - version "2.6.3" - resolved "https://registry.npmjs.org/async/-/async-2.6.3.tgz" - integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg== - dependencies: - lodash "^4.17.14" - -async@~0.2.10: - version "0.2.10" - resolved "https://registry.npmjs.org/async/-/async-0.2.10.tgz" - integrity sha1-trvgsGdLnXGXCMo43owjfLUmw9E= - -atob@^2.1.2: - version "2.1.2" - resolved "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz" - integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== - -autoprefixer@^9.4.3, autoprefixer@^9.4.7, autoprefixer@^9.6.1: - version "9.7.6" - resolved "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.7.6.tgz" - integrity sha512-F7cYpbN7uVVhACZTeeIeealwdGM6wMtfWARVLTy5xmKtgVdBNJvbDRoCK3YO1orcs7gv/KwYlb3iXwu9Ug9BkQ== - dependencies: - browserslist "^4.11.1" - caniuse-lite "^1.0.30001039" - chalk "^2.4.2" - normalize-range "^0.1.2" - num2fraction "^1.2.2" - postcss "^7.0.27" - postcss-value-parser "^4.0.3" - -babel-code-frame@^6.26.0: - version "6.26.0" - resolved "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz" - integrity sha1-Y/1D99weO7fONZR9uP42mj9Yx0s= - dependencies: - chalk "^1.1.3" - esutils "^2.0.2" - js-tokens "^3.0.2" - -babel-core@^6.26.0, babel-core@^6.26.3: - version "6.26.3" - resolved "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz" - integrity sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA== - dependencies: - babel-code-frame "^6.26.0" - babel-generator "^6.26.0" - babel-helpers "^6.24.1" - babel-messages "^6.23.0" - babel-register "^6.26.0" - babel-runtime "^6.26.0" - babel-template "^6.26.0" - babel-traverse "^6.26.0" - babel-types "^6.26.0" - babylon "^6.18.0" - convert-source-map "^1.5.1" - debug "^2.6.9" - json5 "^0.5.1" - lodash "^4.17.4" - minimatch "^3.0.4" - path-is-absolute "^1.0.1" - private "^0.1.8" - slash "^1.0.0" - source-map "^0.5.7" - -babel-generator@^6.26.0: - version "6.26.1" - resolved "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz" - integrity sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA== - dependencies: - babel-messages "^6.23.0" - babel-runtime "^6.26.0" - babel-types "^6.26.0" - detect-indent "^4.0.0" - jsesc "^1.3.0" - lodash "^4.17.4" - source-map "^0.5.7" - trim-right "^1.0.1" - -babel-helpers@^6.24.1: - version "6.24.1" - resolved "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz" - integrity sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI= - dependencies: - babel-runtime "^6.22.0" - babel-template "^6.24.1" - -babel-loader@^8.0.4: - version "8.1.0" - resolved "https://registry.npmjs.org/babel-loader/-/babel-loader-8.1.0.tgz" - integrity sha512-7q7nC1tYOrqvUrN3LQK4GwSk/TQorZSOlO9C+RZDZpODgyN4ZlCqE5q9cDsyWOliN+aU9B4JX01xK9eJXowJLw== - dependencies: - find-cache-dir "^2.1.0" - loader-utils "^1.4.0" - mkdirp "^0.5.3" - pify "^4.0.1" - schema-utils "^2.6.5" - -babel-messages@^6.23.0: - version "6.23.0" - resolved "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz" - integrity sha1-8830cDhYA1sqKVHG7F7fbGLyYw4= - dependencies: - babel-runtime "^6.22.0" - -babel-plugin-closure-elimination@^1.3.0: - version "1.3.0" - resolved "https://registry.npmjs.org/babel-plugin-closure-elimination/-/babel-plugin-closure-elimination-1.3.0.tgz" - integrity sha512-ClKuSxKLLNhe69bvTMuONDI0dQDW49lXB2qtQyyKCzvwegRGel/q4/e+1EoDNDN97Hf1QkxGMbzpAGPmU4Tfjw== - -babel-plugin-console-source@^2.0.2: - version "2.0.4" - resolved "https://registry.npmjs.org/babel-plugin-console-source/-/babel-plugin-console-source-2.0.4.tgz" - integrity sha512-OGhrdhuMjiEW0Ma0P9e2B4dFddCpJ/xN/RRaM/4wwDLl+6ZKf+Xd77FtVjpNeDzNRNk8wjRdStA4hpZizXzl1g== - -babel-plugin-danger-remove-unused-import@^1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/babel-plugin-danger-remove-unused-import/-/babel-plugin-danger-remove-unused-import-1.1.2.tgz" - integrity sha512-3bNmVAaakP3b1aROj7O3bOWj2kBa85sZR5naZ3Rn8L9buiZaAyZLgjfrPDL3zhX4wySOA5jrTm/wSmJPsMm3cg== - -babel-plugin-dynamic-import-node@^2.3.0: - version "2.3.0" - resolved "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz" - integrity sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ== - dependencies: - object.assign "^4.1.0" - -babel-register@^6.26.0: - version "6.26.0" - resolved "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz" - integrity sha1-btAhFz4vy0htestFxgCahW9kcHE= - dependencies: - babel-core "^6.26.0" - babel-runtime "^6.26.0" - core-js "^2.5.0" - home-or-tmp "^2.0.0" - lodash "^4.17.4" - mkdirp "^0.5.1" - source-map-support "^0.4.15" - -babel-runtime@^6.22.0, babel-runtime@^6.26.0: - version "6.26.0" - resolved "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz" - integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4= - dependencies: - core-js "^2.4.0" - regenerator-runtime "^0.11.0" - -babel-runtime@^7.0.0-beta.3: - version "7.0.0-beta.3" - resolved "https://registry.npmjs.org/babel-runtime/-/babel-runtime-7.0.0-beta.3.tgz" - integrity sha512-jlzZ8RACjt0QGxq+wqsw5bCQE9RcUyWpw987mDY3GYxTpOQT2xoyNoG++oVCHzr/nACLBIprfVBNvv/If1ZYcg== - dependencies: - core-js "^2.4.0" - regenerator-runtime "^0.11.0" - -babel-template@^6.24.1, babel-template@^6.26.0: - version "6.26.0" - resolved "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz" - integrity sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI= - dependencies: - babel-runtime "^6.26.0" - babel-traverse "^6.26.0" - babel-types "^6.26.0" - babylon "^6.18.0" - lodash "^4.17.4" - -babel-traverse@^6.26.0: - version "6.26.0" - resolved "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz" - integrity sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4= - dependencies: - babel-code-frame "^6.26.0" - babel-messages "^6.23.0" - babel-runtime "^6.26.0" - babel-types "^6.26.0" - babylon "^6.18.0" - debug "^2.6.8" - globals "^9.18.0" - invariant "^2.2.2" - lodash "^4.17.4" - -babel-types@^6.26.0: - version "6.26.0" - resolved "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz" - integrity sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc= - dependencies: - babel-runtime "^6.26.0" - esutils "^2.0.2" - lodash "^4.17.4" - to-fast-properties "^1.0.3" - -babylon@^6.18.0: - version "6.18.0" - resolved "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz" - integrity sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ== - -balanced-match@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz" - integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= - -base64-js@^1.0.2: - version "1.3.1" - resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz" - integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g== - -base@^0.11.1: - version "0.11.2" - resolved "https://registry.npmjs.org/base/-/base-0.11.2.tgz" - integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== - dependencies: - cache-base "^1.0.1" - class-utils "^0.3.5" - component-emitter "^1.2.1" - define-property "^1.0.0" - isobject "^3.0.1" - mixin-deep "^1.2.0" - pascalcase "^0.1.1" - -before-after-hook@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.1.0.tgz" - integrity sha512-IWIbu7pMqyw3EAJHzzHbWa85b6oud/yfKYg5rqB5hNE8CeMi3nX+2C2sj0HswfblST86hpVEOAb9x34NZd6P7A== - -bfj@^6.1.1: - version "6.1.2" - resolved "https://registry.npmjs.org/bfj/-/bfj-6.1.2.tgz" - integrity sha512-BmBJa4Lip6BPRINSZ0BPEIfB1wUY/9rwbwvIHQA1KjX9om29B6id0wnWXq7m3bn5JrUVjeOTnVuhPT1FiHwPGw== - dependencies: - bluebird "^3.5.5" - check-types "^8.0.3" - hoopy "^0.1.4" - tryer "^1.0.1" - -big.js@^3.1.3: - version "3.2.0" - resolved "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz" - integrity sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q== - -big.js@^5.2.2: - version "5.2.2" - resolved "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz" - integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== - -bin-build@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/bin-build/-/bin-build-3.0.0.tgz" - integrity sha512-jcUOof71/TNAI2uM5uoUaDq2ePcVBQ3R/qhxAz1rX7UfvduAL/RXD3jXzvn8cVcDJdGVkiR1shal3OH0ImpuhA== - dependencies: - decompress "^4.0.0" - download "^6.2.2" - execa "^0.7.0" - p-map-series "^1.0.0" - tempfile "^2.0.0" - -bin-check@^4.1.0: - version "4.1.0" - resolved "https://registry.npmjs.org/bin-check/-/bin-check-4.1.0.tgz" - integrity sha512-b6weQyEUKsDGFlACWSIOfveEnImkJyK/FGW6FAG42loyoquvjdtOIqO6yBFzHyqyVVhNgNkQxxx09SFLK28YnA== - dependencies: - execa "^0.7.0" - executable "^4.1.0" - -bin-version-check@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/bin-version-check/-/bin-version-check-4.0.0.tgz" - integrity sha512-sR631OrhC+1f8Cvs8WyVWOA33Y8tgwjETNPyyD/myRBXLkfS/vl74FmH/lFcRl9KY3zwGh7jFhvyk9vV3/3ilQ== - dependencies: - bin-version "^3.0.0" - semver "^5.6.0" - semver-truncate "^1.1.2" - -bin-version@^3.0.0: - version "3.1.0" - resolved "https://registry.npmjs.org/bin-version/-/bin-version-3.1.0.tgz" - integrity sha512-Mkfm4iE1VFt4xd4vH+gx+0/71esbfus2LsnCGe8Pi4mndSPyT+NGES/Eg99jx8/lUGWfu3z2yuB/bt5UB+iVbQ== - dependencies: - execa "^1.0.0" - find-versions "^3.0.0" - -bin-wrapper@^4.0.0, bin-wrapper@^4.0.1: - version "4.1.0" - resolved "https://registry.npmjs.org/bin-wrapper/-/bin-wrapper-4.1.0.tgz" - integrity sha512-hfRmo7hWIXPkbpi0ZltboCMVrU+0ClXR/JgbCKKjlDjQf6igXa7OwdqNcFWQZPZTgiY7ZpzE3+LjjkLiTN2T7Q== - dependencies: - bin-check "^4.1.0" - bin-version-check "^4.0.0" - download "^7.1.0" - import-lazy "^3.1.0" - os-filter-obj "^2.0.0" - pify "^4.0.1" - -binary-extensions@^1.0.0: - version "1.13.1" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" - integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== - -binary-extensions@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz" - integrity sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ== - -bindings@^1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" - integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== - dependencies: - file-uri-to-path "1.0.0" - -bl@^1.0.0: - version "1.2.2" - resolved "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz" - integrity sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA== - dependencies: - readable-stream "^2.3.5" - safe-buffer "^5.1.1" - -bluebird@3.x.x, bluebird@^3.4.6, bluebird@^3.5.0, bluebird@^3.5.5: - version "3.7.2" - resolved "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz" - integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== - -bmp-js@^0.1.0: - version "0.1.0" - resolved "https://registry.npmjs.org/bmp-js/-/bmp-js-0.1.0.tgz" - integrity sha1-4Fpj95amwf8l9Hcex62twUjAcjM= - -bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.4.0: - version "4.11.9" - resolved "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz" - integrity sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw== - -bn.js@^5.1.1: - version "5.1.2" - resolved "https://registry.npmjs.org/bn.js/-/bn.js-5.1.2.tgz" - integrity sha512-40rZaf3bUNKTVYu9sIeeEGOg7g14Yvnj9kH7b50EiwX0Q7A6umbvfI5tvHaOERH0XigqKkfLkFQxzb4e6CIXnA== - -body-parser@1.19.0: - version "1.19.0" - resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz" - integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== - dependencies: - bytes "3.1.0" - content-type "~1.0.4" - debug "2.6.9" - depd "~1.1.2" - http-errors "1.7.2" - iconv-lite "0.4.24" - on-finished "~2.3.0" - qs "6.7.0" - raw-body "2.4.0" - type-is "~1.6.17" - -boolbase@^1.0.0, boolbase@~1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz" - integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= - -brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -braces@^2.3.1, braces@^2.3.2: - version "2.3.2" - resolved "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz" - integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== - dependencies: - arr-flatten "^1.1.0" - array-unique "^0.3.2" - extend-shallow "^2.0.1" - fill-range "^4.0.0" - isobject "^3.0.1" - repeat-element "^1.1.2" - snapdragon "^0.8.1" - snapdragon-node "^2.0.1" - split-string "^3.0.2" - to-regex "^3.0.1" - -braces@~3.0.2: - version "3.0.2" - resolved "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz" - integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== - dependencies: - fill-range "^7.0.1" - -brorand@^1.0.1: - version "1.1.0" - resolved "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz" - integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= - -browserify-aes@^1.0.0, browserify-aes@^1.0.4: - version "1.2.0" - resolved "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz" - integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== - dependencies: - buffer-xor "^1.0.3" - cipher-base "^1.0.0" - create-hash "^1.1.0" - evp_bytestokey "^1.0.3" - inherits "^2.0.1" - safe-buffer "^5.0.1" - -browserify-cipher@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz" - integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w== - dependencies: - browserify-aes "^1.0.4" - browserify-des "^1.0.0" - evp_bytestokey "^1.0.0" - -browserify-des@^1.0.0: - version "1.0.2" - resolved "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz" - integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A== - dependencies: - cipher-base "^1.0.1" - des.js "^1.0.0" - inherits "^2.0.1" - safe-buffer "^5.1.2" - -browserify-rsa@^4.0.0, browserify-rsa@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz" - integrity sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ= - dependencies: - bn.js "^4.1.0" - randombytes "^2.0.1" - -browserify-sign@^4.0.0: - version "4.2.0" - resolved "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.0.tgz" - integrity sha512-hEZC1KEeYuoHRqhGhTy6gWrpJA3ZDjFWv0DE61643ZnOXAKJb3u7yWcrU0mMc9SwAqK1n7myPGndkp0dFG7NFA== - dependencies: - bn.js "^5.1.1" - browserify-rsa "^4.0.1" - create-hash "^1.2.0" - create-hmac "^1.1.7" - elliptic "^6.5.2" - inherits "^2.0.4" - parse-asn1 "^5.1.5" - readable-stream "^3.6.0" - safe-buffer "^5.2.0" - -browserify-zlib@^0.2.0: - version "0.2.0" - resolved "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz" - integrity sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA== - dependencies: - pako "~1.0.5" - -browserslist@^4.0.0, browserslist@^4.11.1, browserslist@^4.6.4, browserslist@^4.8.5, browserslist@^4.9.1: - version "4.11.1" - resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.11.1.tgz" - integrity sha512-DCTr3kDrKEYNw6Jb9HFxVLQNaue8z+0ZfRBRjmCunKDEXEBajKDj2Y+Uelg+Pi29OnvaSGwjOsnRyNEkXzHg5g== - dependencies: - caniuse-lite "^1.0.30001038" - electron-to-chromium "^1.3.390" - node-releases "^1.1.53" - pkg-up "^2.0.0" - -buffer-alloc-unsafe@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz" - integrity sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg== - -buffer-alloc@^1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz" - integrity sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow== - dependencies: - buffer-alloc-unsafe "^1.1.0" - buffer-fill "^1.0.0" - -buffer-crc32@~0.2.3: - version "0.2.13" - resolved "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz" - integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= - -buffer-equal@0.0.1: - version "0.0.1" - resolved "https://registry.npmjs.org/buffer-equal/-/buffer-equal-0.0.1.tgz" - integrity sha1-kbx0sR6kBbyRa8aqkI+q+ltKrEs= - -buffer-fill@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz" - integrity sha1-+PeLdniYiO858gXNY39o5wISKyw= - -buffer-from@^1.0.0: - version "1.1.1" - resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz" - integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== - -buffer-xor@^1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz" - integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= - -buffer@^4.3.0: - version "4.9.2" - resolved "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz" - integrity sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg== - dependencies: - base64-js "^1.0.2" - ieee754 "^1.1.4" - isarray "^1.0.0" - -buffer@^5.1.0, buffer@^5.2.0, buffer@^5.2.1: - version "5.6.0" - resolved "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz" - integrity sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw== - dependencies: - base64-js "^1.0.2" - ieee754 "^1.1.4" - -builtin-status-codes@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz" - integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug= - -bytes@3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz" - integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== - -cacache@^12.0.2: - version "12.0.4" - resolved "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz" - integrity sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ== - dependencies: - bluebird "^3.5.5" - chownr "^1.1.1" - figgy-pudding "^3.5.1" - glob "^7.1.4" - graceful-fs "^4.1.15" - infer-owner "^1.0.3" - lru-cache "^5.1.1" - mississippi "^3.0.0" - mkdirp "^0.5.1" - move-concurrently "^1.0.1" - promise-inflight "^1.0.1" - rimraf "^2.6.3" - ssri "^6.0.1" - unique-filename "^1.1.1" - y18n "^4.0.0" - -cache-base@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz" - integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== - dependencies: - collection-visit "^1.0.0" - component-emitter "^1.2.1" - get-value "^2.0.6" - has-value "^1.0.0" - isobject "^3.0.1" - set-value "^2.0.0" - to-object-path "^0.3.0" - union-value "^1.0.0" - unset-value "^1.0.0" - -cacheable-request@^2.1.1: - version "2.1.4" - resolved "https://registry.npmjs.org/cacheable-request/-/cacheable-request-2.1.4.tgz" - integrity sha1-DYCIAbY0KtM8kd+dC0TcCbkeXD0= - dependencies: - clone-response "1.0.2" - get-stream "3.0.0" - http-cache-semantics "3.8.1" - keyv "3.0.0" - lowercase-keys "1.0.0" - normalize-url "2.0.1" - responselike "1.0.2" - -calipers-gif@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/calipers-gif/-/calipers-gif-2.0.0.tgz" - integrity sha1-te7+wwZKd8bc29W9xRc1oBuv3Dc= - dependencies: - bluebird "3.x.x" - -calipers-jpeg@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/calipers-jpeg/-/calipers-jpeg-2.0.0.tgz" - integrity sha1-BtVqU/YnF92AnLlWz2RCPOaTRls= - dependencies: - bluebird "3.x.x" - -calipers-png@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/calipers-png/-/calipers-png-2.0.0.tgz" - integrity sha1-HQ0g5cGuX3m3TVKGoul/Wbtwtlg= - dependencies: - bluebird "3.x.x" - -calipers-svg@^2.0.0: - version "2.0.1" - resolved "https://registry.npmjs.org/calipers-svg/-/calipers-svg-2.0.1.tgz" - integrity sha512-3PROqHARmj8wWudUC7DzXm1+mSocqgY7jNuehFNHgrUVrKf8o7MqDjS92vJz5LvZsAofJsoAFMajkqwbxBROSQ== - dependencies: - bluebird "3.x.x" - -calipers-webp@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/calipers-webp/-/calipers-webp-2.0.0.tgz" - integrity sha1-4Sbs4vhM1xd5YSv6KyZTzZXOp3o= - dependencies: - bluebird "3.x.x" - -calipers@^2.0.0: - version "2.0.1" - resolved "https://registry.npmjs.org/calipers/-/calipers-2.0.1.tgz" - integrity sha512-AP4Ui2Z8fZf69d8Dx4cfJgPjQHY3m+QXGFCaAGu8pfNQjyajkosS+Kkf1n6pQDMZcelN5h3MdcjweUqxcsS4pg== - dependencies: - bluebird "3.x.x" - -caller-callsite@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz" - integrity sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ= - dependencies: - callsites "^2.0.0" - -caller-path@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz" - integrity sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ= - dependencies: - caller-callsite "^2.0.0" - -callsites@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz" - integrity sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA= - -callsites@^3.0.0: - version "3.1.0" - resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" - integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== - -camel-case@3.0.x: - version "3.0.0" - resolved "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz" - integrity sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M= - dependencies: - no-case "^2.2.0" - upper-case "^1.1.1" - -camelcase-keys@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz" - integrity sha1-MIvur/3ygRkFHvodkyITyRuPkuc= - dependencies: - camelcase "^2.0.0" - map-obj "^1.0.0" - -camelcase@^2.0.0: - version "2.1.1" - resolved "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz" - integrity sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8= - -camelcase@^5.0.0: - version "5.3.1" - resolved "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz" - integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== - -caniuse-api@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz" - integrity sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw== - dependencies: - browserslist "^4.0.0" - caniuse-lite "^1.0.0" - lodash.memoize "^4.1.2" - lodash.uniq "^4.5.0" - -caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30001038, caniuse-lite@^1.0.30001039: - version "1.0.30001041" - resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001041.tgz" - integrity sha512-fqDtRCApddNrQuBxBS7kEiSGdBsgO4wiVw4G/IClfqzfhW45MbTumfN4cuUJGTM0YGFNn97DCXPJ683PS6zwvA== - -caw@^2.0.0, caw@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/caw/-/caw-2.0.1.tgz" - integrity sha512-Cg8/ZSBEa8ZVY9HspcGUYaK63d/bN7rqS3CYCzEGUxuYv6UlmcjzDUz2fCFFHyTvUW5Pk0I+3hkA3iXlIj6guA== - dependencies: - get-proxy "^2.0.0" - isurl "^1.0.0-alpha5" - tunnel-agent "^0.6.0" - url-to-options "^1.0.1" - -chalk@2.4.2, chalk@^2.0.0, chalk@^2.4.1, chalk@^2.4.2: - version "2.4.2" - resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - -chalk@^1.0.0, chalk@^1.1.3: - version "1.1.3" - resolved "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz" - integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= - dependencies: - ansi-styles "^2.2.1" - escape-string-regexp "^1.0.2" - has-ansi "^2.0.0" - strip-ansi "^3.0.0" - supports-color "^2.0.0" - -chalk@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz" - integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -chalk@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz" - integrity sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -chardet@^0.7.0: - version "0.7.0" - resolved "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz" - integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== - -check-types@^8.0.3: - version "8.0.3" - resolved "https://registry.npmjs.org/check-types/-/check-types-8.0.3.tgz" - integrity sha512-YpeKZngUmG65rLudJ4taU7VLkOCTMhNl/u4ctNC56LQS/zJTyNH0Lrtwm1tfTsbLlwvlfsA2d1c8vCf/Kh2KwQ== - -chokidar@^2.1.8: - version "2.1.8" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" - integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg== - dependencies: - anymatch "^2.0.0" - async-each "^1.0.1" - braces "^2.3.2" - glob-parent "^3.1.0" - inherits "^2.0.3" - is-binary-path "^1.0.0" - is-glob "^4.0.0" - normalize-path "^3.0.0" - path-is-absolute "^1.0.0" - readdirp "^2.2.1" - upath "^1.1.1" - optionalDependencies: - fsevents "^1.2.7" - -chokidar@^3.4.0: - version "3.4.1" - resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.4.1.tgz" - integrity sha512-TQTJyr2stihpC4Sya9hs2Xh+O2wf+igjL36Y75xx2WdHuiICcn/XJza46Jwt0eT5hVpQOzo3FpY3cj3RVYLX0g== - dependencies: - anymatch "~3.1.1" - braces "~3.0.2" - glob-parent "~5.1.0" - is-binary-path "~2.1.0" - is-glob "~4.0.1" - normalize-path "~3.0.0" - readdirp "~3.4.0" - optionalDependencies: - fsevents "~2.1.2" - -chownr@^1.1.1: - version "1.1.4" - resolved "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz" - integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== - -chrome-trace-event@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz" - integrity sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ== - dependencies: - tslib "^1.9.0" - -cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: - version "1.0.4" - resolved "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz" - integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== - dependencies: - inherits "^2.0.1" - safe-buffer "^5.0.1" - -circular-dependency-plugin@^5.0.2: - version "5.2.0" - resolved "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-5.2.0.tgz" - integrity sha512-7p4Kn/gffhQaavNfyDFg7LS5S/UT1JAjyGd4UqR2+jzoYF02eDkj0Ec3+48TsIa4zghjLY87nQHIh/ecK9qLdw== - -circular-json@^0.5.9: - version "0.5.9" - resolved "https://registry.npmjs.org/circular-json/-/circular-json-0.5.9.tgz" - integrity sha512-4ivwqHpIFJZBuhN3g/pEcdbnGUywkBblloGbkglyloVjjR3uT6tieI89MVOfbP2tHX5sgb01FuLgAOzebNlJNQ== - -class-utils@^0.3.5: - version "0.3.6" - resolved "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz" - integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== - dependencies: - arr-union "^3.1.0" - define-property "^0.2.5" - isobject "^3.0.0" - static-extend "^0.1.1" - -clean-css@4.2.x: - version "4.2.3" - resolved "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz" - integrity sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA== - dependencies: - source-map "~0.6.0" - -cli-cursor@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz" - integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== - dependencies: - restore-cursor "^3.1.0" - -cli-width@^2.0.0: - version "2.2.0" - resolved "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz" - integrity sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk= - -clipboard-copy@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/clipboard-copy/-/clipboard-copy-3.1.0.tgz" - integrity sha512-Xsu1NddBXB89IUauda5BIq3Zq73UWkjkaQlPQbLNvNsd5WBMnTWPNKYR6HGaySOxGYZ+BKxP2E9X4ElnI3yiPA== - -cliui@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz" - integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA== - dependencies: - string-width "^3.1.0" - strip-ansi "^5.2.0" - wrap-ansi "^5.1.0" - -cliui@^6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz" - integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.0" - wrap-ansi "^6.2.0" - -clone-response@1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz" - integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= - dependencies: - mimic-response "^1.0.0" - -coa@^2.0.2: - version "2.0.2" - resolved "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz" - integrity sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA== - dependencies: - "@types/q" "^1.5.1" - chalk "^2.4.1" - q "^1.1.2" - -collection-visit@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz" - integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= - dependencies: - map-visit "^1.0.0" - object-visit "^1.0.0" - -color-convert@^1.9.0, color-convert@^1.9.1: - version "1.9.3" - resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz" - integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== - dependencies: - color-name "1.1.3" - -color-convert@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz" - integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== - dependencies: - color-name "~1.1.4" - -color-name@1.1.3, color-name@^1.0.0: - version "1.1.3" - resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" - integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= - -color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - -color-string@^1.5.2: - version "1.5.3" - resolved "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz" - integrity sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw== - dependencies: - color-name "^1.0.0" - simple-swizzle "^0.2.2" - -color@^3.0.0: - version "3.1.2" - resolved "https://registry.npmjs.org/color/-/color-3.1.2.tgz" - integrity sha512-vXTJhHebByxZn3lDvDJYw4lR5+uB3vuoHsuYA5AKuxRVn5wzzIfQKGLBmgdVRHKTJYeK5rvJcHnrd0Li49CFpg== - dependencies: - color-convert "^1.9.1" - color-string "^1.5.2" - -colorette@^1.2.1: - version "1.2.1" - resolved "https://registry.npmjs.org/colorette/-/colorette-1.2.1.tgz" - integrity sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw== - -colors@^1.3.3: - version "1.4.0" - resolved "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz" - integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== - -commander@2.17.x: - version "2.17.1" - resolved "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz" - integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg== - -commander@^2.18.0, commander@^2.20.0: - version "2.20.3" - resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz" - integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== - -commander@~2.19.0: - version "2.19.0" - resolved "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz" - integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg== - -commander@~2.8.1: - version "2.8.1" - resolved "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz" - integrity sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ= - dependencies: - graceful-readlink ">= 1.0.0" - -commondir@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz" - integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= - -component-emitter@^1.2.1: - version "1.3.0" - resolved "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz" - integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== - -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" - integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= - -concat-stream@^1.5.0: - version "1.6.2" - resolved "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz" - integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== - dependencies: - buffer-from "^1.0.0" - inherits "^2.0.3" - readable-stream "^2.2.2" - typedarray "^0.0.6" - -config-chain@^1.1.11: - version "1.1.12" - resolved "https://registry.npmjs.org/config-chain/-/config-chain-1.1.12.tgz" - integrity sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA== - dependencies: - ini "^1.3.4" - proto-list "~1.2.1" - -console-browserify@^1.1.0: - version "1.2.0" - resolved "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz" - integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA== - -console-stream@^0.1.1: - version "0.1.1" - resolved "https://registry.npmjs.org/console-stream/-/console-stream-0.1.1.tgz" - integrity sha1-oJX+B7IEZZVfL6/Si11yvM2UnUQ= - -constants-browserify@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz" - integrity sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U= - -content-disposition@0.5.3, content-disposition@^0.5.2: - version "0.5.3" - resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz" - integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== - dependencies: - safe-buffer "5.1.2" - -content-type@~1.0.4: - version "1.0.4" - resolved "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz" - integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== - -convert-source-map@^1.5.1, convert-source-map@^1.7.0: - version "1.7.0" - resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz" - integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== - dependencies: - safe-buffer "~5.1.1" - -cookie-signature@1.0.6: - version "1.0.6" - resolved "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz" - integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= - -cookie@0.4.0: - version "0.4.0" - resolved "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz" - integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== - -copy-concurrently@^1.0.0: - version "1.0.5" - resolved "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz" - integrity sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A== - dependencies: - aproba "^1.1.1" - fs-write-stream-atomic "^1.0.8" - iferr "^0.1.5" - mkdirp "^0.5.1" - rimraf "^2.5.4" - run-queue "^1.0.0" - -copy-descriptor@^0.1.0: - version "0.1.1" - resolved "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz" - integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= - -core-js-compat@^3.6.2: - version "3.6.5" - resolved "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.6.5.tgz" - integrity sha512-7ItTKOhOZbznhXAQ2g/slGg1PJV5zDO/WdkTwi7UEOJmkvsE32PWvx6mKtDjiMpjnR2CNf6BAD6sSxIlv7ptng== - dependencies: - browserslist "^4.8.5" - semver "7.0.0" - -core-js@3: - version "3.6.5" - resolved "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz" - integrity sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA== - -core-js@^2.4.0, core-js@^2.5.0, core-js@^2.5.7: - version "2.6.11" - resolved "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz" - integrity sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg== - -core-util-is@~1.0.0: - version "1.0.2" - resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" - integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= - -cosmiconfig@^5.0.0: - version "5.2.1" - resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz" - integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA== - dependencies: - import-fresh "^2.0.0" - is-directory "^0.3.1" - js-yaml "^3.13.1" - parse-json "^4.0.0" - -crc@^3.8.0: - version "3.8.0" - resolved "https://registry.npmjs.org/crc/-/crc-3.8.0.tgz" - integrity sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ== - dependencies: - buffer "^5.1.0" - -create-ecdh@^4.0.0: - version "4.0.3" - resolved "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz" - integrity sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw== - dependencies: - bn.js "^4.1.0" - elliptic "^6.0.0" - -create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz" - integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== - dependencies: - cipher-base "^1.0.1" - inherits "^2.0.1" - md5.js "^1.3.4" - ripemd160 "^2.0.1" - sha.js "^2.4.0" - -create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: - version "1.1.7" - resolved "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz" - integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== - dependencies: - cipher-base "^1.0.3" - create-hash "^1.1.0" - inherits "^2.0.1" - ripemd160 "^2.0.0" - safe-buffer "^5.0.1" - sha.js "^2.4.8" - -cross-spawn@6.0.5, cross-spawn@^6.0.0: - version "6.0.5" - resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz" - integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== - dependencies: - nice-try "^1.0.4" - path-key "^2.0.1" - semver "^5.5.0" - shebang-command "^1.2.0" - which "^1.2.9" - -cross-spawn@^5.0.1: - version "5.1.0" - resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz" - integrity sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk= - dependencies: - lru-cache "^4.0.1" - shebang-command "^1.2.0" - which "^1.2.9" - -cross-spawn@^7.0.2: - version "7.0.3" - resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== - dependencies: - path-key "^3.1.0" - shebang-command "^2.0.0" - which "^2.0.1" - -crypto-browserify@^3.11.0: - version "3.12.0" - resolved "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz" - integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg== - dependencies: - browserify-cipher "^1.0.0" - browserify-sign "^4.0.0" - create-ecdh "^4.0.0" - create-hash "^1.1.0" - create-hmac "^1.1.0" - diffie-hellman "^5.0.0" - inherits "^2.0.1" - pbkdf2 "^3.0.3" - public-encrypt "^4.0.0" - randombytes "^2.0.0" - randomfill "^1.0.3" - -css-blank-pseudo@^0.1.4: - version "0.1.4" - resolved "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-0.1.4.tgz" - integrity sha512-LHz35Hr83dnFeipc7oqFDmsjHdljj3TQtxGGiNWSOsTLIAubSm4TEz8qCaKFpk7idaQ1GfWscF4E6mgpBysA1w== - dependencies: - postcss "^7.0.5" - -css-color-names@0.0.4, css-color-names@^0.0.4: - version "0.0.4" - resolved "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz" - integrity sha1-gIrcLnnPhHOAabZGyyDsJ762KeA= - -css-declaration-sorter@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz" - integrity sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA== - dependencies: - postcss "^7.0.1" - timsort "^0.3.0" - -css-has-pseudo@^0.10.0: - version "0.10.0" - resolved "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-0.10.0.tgz" - integrity sha512-Z8hnfsZu4o/kt+AuFzeGpLVhFOGO9mluyHBaA2bA8aCGTwah5sT3WV/fTHH8UNZUytOIImuGPrl/prlb4oX4qQ== - dependencies: - postcss "^7.0.6" - postcss-selector-parser "^5.0.0-rc.4" - -css-loader@^0.9.1: - version "0.9.1" - resolved "https://registry.npmjs.org/css-loader/-/css-loader-0.9.1.tgz" - integrity sha1-LhqgDOfjDvLGp6SzAKCAp8l54Nw= - dependencies: - csso "1.3.x" - loader-utils "~0.2.2" - source-map "~0.1.38" - -css-mqpacker@^7.0.0: - version "7.0.0" - resolved "https://registry.npmjs.org/css-mqpacker/-/css-mqpacker-7.0.0.tgz" - integrity sha512-temVrWS+sB4uocE2quhW8ru/KguDmGhCU7zN213KxtDvWOH3WS/ZUStfpF4fdCT7W8fPpFrQdWRFqtFtPPfBLA== - dependencies: - minimist "^1.2.0" - postcss "^7.0.0" - -css-prefers-color-scheme@^3.1.1: - version "3.1.1" - resolved "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-3.1.1.tgz" - integrity sha512-MTu6+tMs9S3EUqzmqLXEcgNRbNkkD/TGFvowpeoWJn5Vfq7FMgsmRQs9X5NXAURiOBmOxm/lLjsDNXDE6k9bhg== - dependencies: - postcss "^7.0.5" - -css-select-base-adapter@^0.1.1: - version "0.1.1" - resolved "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz" - integrity sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w== - -css-select@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz" - integrity sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ== - dependencies: - boolbase "^1.0.0" - css-what "^3.2.1" - domutils "^1.7.0" - nth-check "^1.0.2" - -css-tree@1.0.0-alpha.37: - version "1.0.0-alpha.37" - resolved "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz" - integrity sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg== - dependencies: - mdn-data "2.0.4" - source-map "^0.6.1" - -css-tree@1.0.0-alpha.39: - version "1.0.0-alpha.39" - resolved "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.39.tgz" - integrity sha512-7UvkEYgBAHRG9Nt980lYxjsTrCyHFN53ky3wVsDkiMdVqylqRt+Zc+jm5qw7/qyOvN2dHSYtX0e4MbCCExSvnA== - dependencies: - mdn-data "2.0.6" - source-map "^0.6.1" - -css-what@^3.2.1: - version "3.2.1" - resolved "https://registry.npmjs.org/css-what/-/css-what-3.2.1.tgz" - integrity sha512-WwOrosiQTvyms+Ti5ZC5vGEK0Vod3FTt1ca+payZqvKuGJF+dq7bG63DstxtN0dpm6FxY27a/zS3Wten+gEtGw== - -cssdb@^4.4.0: - version "4.4.0" - resolved "https://registry.npmjs.org/cssdb/-/cssdb-4.4.0.tgz" - integrity sha512-LsTAR1JPEM9TpGhl/0p3nQecC2LJ0kD8X5YARu1hk/9I1gril5vDtMZyNxcEpxxDj34YNck/ucjuoUd66K03oQ== - -cssesc@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz" - integrity sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg== - -cssesc@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz" - integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== - -cssnano-preset-advanced@^4.0.7: - version "4.0.7" - resolved "https://registry.npmjs.org/cssnano-preset-advanced/-/cssnano-preset-advanced-4.0.7.tgz" - integrity sha512-j1O5/DQnaAqEyFFQfC+Z/vRlLXL3LxJHN+lvsfYqr7KgPH74t69+Rsy2yXkovWNaJjZYBpdz2Fj8ab2nH7pZXw== - dependencies: - autoprefixer "^9.4.7" - cssnano-preset-default "^4.0.7" - postcss-discard-unused "^4.0.1" - postcss-merge-idents "^4.0.1" - postcss-reduce-idents "^4.0.2" - postcss-zindex "^4.0.1" - -cssnano-preset-default@^4.0.7: - version "4.0.7" - resolved "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-4.0.7.tgz" - integrity sha512-x0YHHx2h6p0fCl1zY9L9roD7rnlltugGu7zXSKQx6k2rYw0Hi3IqxcoAGF7u9Q5w1nt7vK0ulxV8Lo+EvllGsA== - dependencies: - css-declaration-sorter "^4.0.1" - cssnano-util-raw-cache "^4.0.1" - postcss "^7.0.0" - postcss-calc "^7.0.1" - postcss-colormin "^4.0.3" - postcss-convert-values "^4.0.1" - postcss-discard-comments "^4.0.2" - postcss-discard-duplicates "^4.0.2" - postcss-discard-empty "^4.0.1" - postcss-discard-overridden "^4.0.1" - postcss-merge-longhand "^4.0.11" - postcss-merge-rules "^4.0.3" - postcss-minify-font-values "^4.0.2" - postcss-minify-gradients "^4.0.2" - postcss-minify-params "^4.0.2" - postcss-minify-selectors "^4.0.2" - postcss-normalize-charset "^4.0.1" - postcss-normalize-display-values "^4.0.2" - postcss-normalize-positions "^4.0.2" - postcss-normalize-repeat-style "^4.0.2" - postcss-normalize-string "^4.0.2" - postcss-normalize-timing-functions "^4.0.2" - postcss-normalize-unicode "^4.0.1" - postcss-normalize-url "^4.0.1" - postcss-normalize-whitespace "^4.0.2" - postcss-ordered-values "^4.1.2" - postcss-reduce-initial "^4.0.3" - postcss-reduce-transforms "^4.0.2" - postcss-svgo "^4.0.2" - postcss-unique-selectors "^4.0.1" - -cssnano-util-get-arguments@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz" - integrity sha1-7ToIKZ8h11dBsg87gfGU7UnMFQ8= - -cssnano-util-get-match@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz" - integrity sha1-wOTKB/U4a7F+xeUiULT1lhNlFW0= - -cssnano-util-raw-cache@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz" - integrity sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA== - dependencies: - postcss "^7.0.0" - -cssnano-util-same-parent@^4.0.0: - version "4.0.1" - resolved "https://registry.npmjs.org/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz" - integrity sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q== - -cssnano@^4.1.10: - version "4.1.10" - resolved "https://registry.npmjs.org/cssnano/-/cssnano-4.1.10.tgz" - integrity sha512-5wny+F6H4/8RgNlaqab4ktc3e0/blKutmq8yNlBFXA//nSFFAqAngjNVRzUvCgYROULmZZUoosL/KSoZo5aUaQ== - dependencies: - cosmiconfig "^5.0.0" - cssnano-preset-default "^4.0.7" - is-resolvable "^1.0.0" - postcss "^7.0.0" - -csso@1.3.x: - version "1.3.12" - resolved "https://registry.npmjs.org/csso/-/csso-1.3.12.tgz" - integrity sha1-/GKGlKLTiTiqrEmWdTIY/TEc254= - -csso@^4.0.2: - version "4.0.3" - resolved "https://registry.npmjs.org/csso/-/csso-4.0.3.tgz" - integrity sha512-NL3spysxUkcrOgnpsT4Xdl2aiEiBG6bXswAABQVHcMrfjjBisFOKwLDOmf4wf32aPdcJws1zds2B0Rg+jqMyHQ== - dependencies: - css-tree "1.0.0-alpha.39" - -currently-unhandled@^0.4.1: - version "0.4.1" - resolved "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz" - integrity sha1-mI3zP+qxke95mmE2nddsF635V+o= - dependencies: - array-find-index "^1.0.1" - -cyclist@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz" - integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk= - -debounce-promise@^3.1.2: - version "3.1.2" - resolved "https://registry.npmjs.org/debounce-promise/-/debounce-promise-3.1.2.tgz" - integrity sha512-rZHcgBkbYavBeD9ej6sP56XfG53d51CD4dnaw989YX/nZ/ZJfgRx/9ePKmTNiUiyQvh4mtrMoS3OAWW+yoYtpg== - -debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9: - version "2.6.9" - resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - -debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: - version "4.1.1" - resolved "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz" - integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== - dependencies: - ms "^2.1.1" - -decamelize@^1.1.2, decamelize@^1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz" - integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= - -decode-uri-component@^0.2.0: - version "0.2.0" - resolved "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz" - integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= - -decompress-response@^3.2.0, decompress-response@^3.3.0: - version "3.3.0" - resolved "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz" - integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= - dependencies: - mimic-response "^1.0.0" - -decompress-tar@^4.0.0, decompress-tar@^4.1.0, decompress-tar@^4.1.1: - version "4.1.1" - resolved "https://registry.npmjs.org/decompress-tar/-/decompress-tar-4.1.1.tgz" - integrity sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ== - dependencies: - file-type "^5.2.0" - is-stream "^1.1.0" - tar-stream "^1.5.2" - -decompress-tarbz2@^4.0.0: - version "4.1.1" - resolved "https://registry.npmjs.org/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz" - integrity sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A== - dependencies: - decompress-tar "^4.1.0" - file-type "^6.1.0" - is-stream "^1.1.0" - seek-bzip "^1.0.5" - unbzip2-stream "^1.0.9" - -decompress-targz@^4.0.0: - version "4.1.1" - resolved "https://registry.npmjs.org/decompress-targz/-/decompress-targz-4.1.1.tgz" - integrity sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w== - dependencies: - decompress-tar "^4.1.1" - file-type "^5.2.0" - is-stream "^1.1.0" - -decompress-unzip@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/decompress-unzip/-/decompress-unzip-4.0.1.tgz" - integrity sha1-3qrM39FK6vhVePczroIQ+bSEj2k= - dependencies: - file-type "^3.8.0" - get-stream "^2.2.0" - pify "^2.3.0" - yauzl "^2.4.2" - -decompress@^4.0.0, decompress@^4.2.0: - version "4.2.1" - resolved "https://registry.npmjs.org/decompress/-/decompress-4.2.1.tgz" - integrity sha512-e48kc2IjU+2Zw8cTb6VZcJQ3lgVbS4uuB1TfCHbiZIP/haNXm+SVyhu+87jts5/3ROpd82GSVCoNs/z8l4ZOaQ== - dependencies: - decompress-tar "^4.0.0" - decompress-tarbz2 "^4.0.0" - decompress-targz "^4.0.0" - decompress-unzip "^4.0.1" - graceful-fs "^4.1.10" - make-dir "^1.0.0" - pify "^2.3.0" - strip-dirs "^2.0.0" - -deep-is@^0.1.3: - version "0.1.3" - resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz" - integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= - -deep-scope-analyser@^1.7.0: - version "1.7.0" - resolved "https://registry.npmjs.org/deep-scope-analyser/-/deep-scope-analyser-1.7.0.tgz" - integrity sha512-rl5Dmt2IZkFpZo6XbEY1zG8st2Wpq8Pi/dV2gz8ZF6BDYt3fnor2JNxHwdO1WLo0k6JbmYp0x8MNy8kE4l1NtA== - dependencies: - esrecurse "^4.2.1" - estraverse "^4.2.0" - -define-properties@^1.1.2, define-properties@^1.1.3: - version "1.1.3" - resolved "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz" - integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== - dependencies: - object-keys "^1.0.12" - -define-property@^0.2.5: - version "0.2.5" - resolved "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz" - integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= - dependencies: - is-descriptor "^0.1.0" - -define-property@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz" - integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= - dependencies: - is-descriptor "^1.0.0" - -define-property@^2.0.2: - version "2.0.2" - resolved "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz" - integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== - dependencies: - is-descriptor "^1.0.2" - isobject "^3.0.1" - -depd@~1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz" - integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= - -deprecation@^2.0.0, deprecation@^2.3.1: - version "2.3.1" - resolved "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz" - integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== - -des.js@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz" - integrity sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA== - dependencies: - inherits "^2.0.1" - minimalistic-assert "^1.0.0" - -destroy@~1.0.4: - version "1.0.4" - resolved "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz" - integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= - -detect-file@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz" - integrity sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc= - -detect-indent@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz" - integrity sha1-920GQ1LN9Docts5hnE7jqUdd4gg= - dependencies: - repeating "^2.0.0" - -diffie-hellman@^5.0.0: - version "5.0.3" - resolved "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz" - integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg== - dependencies: - bn.js "^4.1.0" - miller-rabin "^4.0.0" - randombytes "^2.0.0" - -doctrine@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz" - integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== - dependencies: - esutils "^2.0.2" - -dom-serializer@0: - version "0.2.2" - resolved "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz" - integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g== - dependencies: - domelementtype "^2.0.1" - entities "^2.0.0" - -dom-walk@^0.1.0: - version "0.1.2" - resolved "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz" - integrity sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w== - -domain-browser@^1.1.1: - version "1.2.0" - resolved "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz" - integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA== - -domelementtype@1: - version "1.3.1" - resolved "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz" - integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== - -domelementtype@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz" - integrity sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ== - -domutils@^1.7.0: - version "1.7.0" - resolved "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz" - integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg== - dependencies: - dom-serializer "0" - domelementtype "1" - -dot-prop@^5.2.0: - version "5.2.0" - resolved "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz" - integrity sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A== - dependencies: - is-obj "^2.0.0" - -download@^6.2.2: - version "6.2.5" - resolved "https://registry.npmjs.org/download/-/download-6.2.5.tgz" - integrity sha512-DpO9K1sXAST8Cpzb7kmEhogJxymyVUd5qz/vCOSyvwtp2Klj2XcDt5YUuasgxka44SxF0q5RriKIwJmQHG2AuA== - dependencies: - caw "^2.0.0" - content-disposition "^0.5.2" - decompress "^4.0.0" - ext-name "^5.0.0" - file-type "5.2.0" - filenamify "^2.0.0" - get-stream "^3.0.0" - got "^7.0.0" - make-dir "^1.0.0" - p-event "^1.0.0" - pify "^3.0.0" - -download@^7.1.0: - version "7.1.0" - resolved "https://registry.npmjs.org/download/-/download-7.1.0.tgz" - integrity sha512-xqnBTVd/E+GxJVrX5/eUJiLYjCGPwMpdL+jGhGU57BvtcA7wwhtHVbXBeUk51kOpW3S7Jn3BQbN9Q1R1Km2qDQ== - dependencies: - archive-type "^4.0.0" - caw "^2.0.1" - content-disposition "^0.5.2" - decompress "^4.2.0" - ext-name "^5.0.0" - file-type "^8.1.0" - filenamify "^2.0.0" - get-stream "^3.0.0" - got "^8.3.1" - make-dir "^1.2.0" - p-event "^2.1.0" - pify "^3.0.0" - -duplexer3@^0.1.4: - version "0.1.4" - resolved "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz" - integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= - -duplexer@^0.1.1: - version "0.1.1" - resolved "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz" - integrity sha1-rOb/gIwc5mtX0ev5eXessCM0z8E= - -duplexify@^3.4.2, duplexify@^3.6.0: - version "3.7.1" - resolved "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz" - integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g== - dependencies: - end-of-stream "^1.0.0" - inherits "^2.0.1" - readable-stream "^2.0.0" - stream-shift "^1.0.0" - -ee-first@1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz" - integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= - -ejs@^2.6.1: - version "2.7.4" - resolved "https://registry.npmjs.org/ejs/-/ejs-2.7.4.tgz" - integrity sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA== - -electron-to-chromium@^1.3.390: - version "1.3.403" - resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.403.tgz" - integrity sha512-JaoxV4RzdBAZOnsF4dAlZ2ijJW72MbqO5lNfOBHUWiBQl3Rwe+mk2RCUMrRI3rSClLJ8HSNQNqcry12H+0ZjFw== - -elliptic@^6.0.0, elliptic@^6.5.2: - version "6.5.3" - resolved "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz" - integrity sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw== - dependencies: - bn.js "^4.4.0" - brorand "^1.0.1" - hash.js "^1.0.0" - hmac-drbg "^1.0.0" - inherits "^2.0.1" - minimalistic-assert "^1.0.0" - minimalistic-crypto-utils "^1.0.0" - -email-validator@^2.0.4: - version "2.0.4" - resolved "https://registry.npmjs.org/email-validator/-/email-validator-2.0.4.tgz" - integrity sha512-gYCwo7kh5S3IDyZPLZf6hSS0MnZT8QmJFqYvbqlDZSbwdZlY6QZWxJ4i/6UhITOJ4XzyI647Bm2MXKCLqnJ4nQ== - -emoji-regex@^7.0.1: - version "7.0.3" - resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz" - integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== - -emoji-regex@^8.0.0: - version "8.0.0" - resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" - integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== - -emojis-list@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz" - integrity sha1-TapNnbAPmBmIDHn6RXrlsJof04k= - -emojis-list@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz" - integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== - -encodeurl@~1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz" - integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= - -end-of-stream@^1.0.0, end-of-stream@^1.1.0: - version "1.4.4" - resolved "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz" - integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== - dependencies: - once "^1.4.0" - -enhanced-resolve@4.1.0: - version "4.1.0" - resolved "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz" - integrity sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng== - dependencies: - graceful-fs "^4.1.2" - memory-fs "^0.4.0" - tapable "^1.0.0" - -enhanced-resolve@^4.1.0: - version "4.3.0" - resolved "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.3.0.tgz" - integrity sha512-3e87LvavsdxyoCfGusJnrZ5G8SLPOFeHSNpZI/ATL9a5leXo2k0w6MKnbqhdBad9qTobSfB20Ld7UmgoNbAZkQ== - dependencies: - graceful-fs "^4.1.2" - memory-fs "^0.5.0" - tapable "^1.0.0" - -entities@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz" - integrity sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw== - -errno@^0.1.3, errno@~0.1.7: - version "0.1.7" - resolved "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz" - integrity sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg== - dependencies: - prr "~1.0.1" - -error-ex@^1.2.0, error-ex@^1.3.1: - version "1.3.2" - resolved "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz" - integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== - dependencies: - is-arrayish "^0.2.1" - -es-abstract@^1.17.0-next.1, es-abstract@^1.17.2, es-abstract@^1.17.5: - version "1.17.5" - resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz" - integrity sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg== - dependencies: - es-to-primitive "^1.2.1" - function-bind "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.1" - is-callable "^1.1.5" - is-regex "^1.0.5" - object-inspect "^1.7.0" - object-keys "^1.1.1" - object.assign "^4.1.0" - string.prototype.trimleft "^2.1.1" - string.prototype.trimright "^2.1.1" - -es-to-primitive@^1.2.1: - version "1.2.1" - resolved "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz" - integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== - dependencies: - is-callable "^1.1.4" - is-date-object "^1.0.1" - is-symbol "^1.0.2" - -es6-templates@^0.2.3: - version "0.2.3" - resolved "https://registry.npmjs.org/es6-templates/-/es6-templates-0.2.3.tgz" - integrity sha1-XLmsn7He1usSOTQrgdeSu7QHjuQ= - dependencies: - recast "~0.11.12" - through "~2.3.6" - -escape-html@~1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz" - integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= - -escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: - version "1.0.5" - resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" - integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= - -eslint-config-prettier@6.11.0: - version "6.11.0" - resolved "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.11.0.tgz" - integrity sha512-oB8cpLWSAjOVFEJhhyMZh6NOEOtBVziaqdDQ86+qhDHFbZXoRTM7pNSvFRfW/W/L/LrQ38C99J5CGuRBBzBsdA== - dependencies: - get-stdin "^6.0.0" - -eslint-plugin-prettier@3.1.3: - version "3.1.3" - resolved "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.3.tgz" - integrity sha512-+HG5jmu/dN3ZV3T6eCD7a4BlAySdN7mLIbJYo0z1cFQuI+r2DiTJEFeF68ots93PsnrMxbzIZ2S/ieX+mkrBeQ== - dependencies: - prettier-linter-helpers "^1.0.0" - -eslint-scope@^4.0.3: - version "4.0.3" - resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz" - integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg== - dependencies: - esrecurse "^4.1.0" - estraverse "^4.1.1" - -eslint-scope@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz" - integrity sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw== - dependencies: - esrecurse "^4.1.0" - estraverse "^4.1.1" - -eslint-utils@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.0.0.tgz" - integrity sha512-0HCPuJv+7Wv1bACm8y5/ECVfYdfsAm9xmVb7saeFlxjPYALefjhbYoCkBjPdPzGH8wWyTpAez82Fh3VKYEZ8OA== - dependencies: - eslint-visitor-keys "^1.1.0" - -eslint-visitor-keys@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz" - integrity sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A== - -eslint@7.1.0: - version "7.1.0" - resolved "https://registry.npmjs.org/eslint/-/eslint-7.1.0.tgz" - integrity sha512-DfS3b8iHMK5z/YLSme8K5cge168I8j8o1uiVmFCgnnjxZQbCGyraF8bMl7Ju4yfBmCuxD7shOF7eqGkcuIHfsA== - dependencies: - "@babel/code-frame" "^7.0.0" - ajv "^6.10.0" - chalk "^4.0.0" - cross-spawn "^7.0.2" - debug "^4.0.1" - doctrine "^3.0.0" - eslint-scope "^5.0.0" - eslint-utils "^2.0.0" - eslint-visitor-keys "^1.1.0" - espree "^7.0.0" - esquery "^1.2.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" - imurmurhash "^0.1.4" - inquirer "^7.0.0" - 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.14" - minimatch "^3.0.4" - 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" - -espree@^7.0.0: - version "7.0.0" - resolved "https://registry.npmjs.org/espree/-/espree-7.0.0.tgz" - integrity sha512-/r2XEx5Mw4pgKdyb7GNLQNsu++asx/dltf/CI8RFi9oGHxmQFgvLbc5Op4U6i8Oaj+kdslhJtVlEZeAqH5qOTw== - dependencies: - acorn "^7.1.1" - acorn-jsx "^5.2.0" - eslint-visitor-keys "^1.1.0" - -esprima@^4.0.0: - version "4.0.1" - resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz" - integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== - -esprima@~3.1.0: - version "3.1.3" - resolved "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz" - integrity sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM= - -esquery@^1.2.0: - version "1.3.1" - resolved "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz" - integrity sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ== - dependencies: - estraverse "^5.1.0" - -esrecurse@^4.1.0, esrecurse@^4.2.1: - version "4.2.1" - resolved "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz" - integrity sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ== - dependencies: - estraverse "^4.1.0" - -estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: - version "4.3.0" - resolved "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz" - integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== - -estraverse@^5.1.0: - version "5.1.0" - resolved "https://registry.npmjs.org/estraverse/-/estraverse-5.1.0.tgz" - integrity sha512-FyohXK+R0vE+y1nHLoBM7ZTyqRpqAlhdZHCWIWEviFLiGB8b04H6bQs8G+XTthacvT8VuwvteiP7RJSxMs8UEw== - -esutils@^2.0.2: - version "2.0.3" - resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz" - integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== - -etag@~1.8.1: - version "1.8.1" - resolved "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz" - integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= - -events@^3.0.0: - version "3.1.0" - resolved "https://registry.npmjs.org/events/-/events-3.1.0.tgz" - integrity sha512-Rv+u8MLHNOdMjTAFeT3nCjHn2aGlx435FP/sDHNaRhDEMwyI/aB22Kj2qIN8R0cw3z28psEQLYwxVKLsKrMgWg== - -evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz" - integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== - dependencies: - md5.js "^1.3.4" - safe-buffer "^5.1.1" - -execa@^0.10.0: - version "0.10.0" - resolved "https://registry.npmjs.org/execa/-/execa-0.10.0.tgz" - integrity sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw== - dependencies: - cross-spawn "^6.0.0" - get-stream "^3.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" - -execa@^0.7.0: - version "0.7.0" - resolved "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz" - integrity sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c= - dependencies: - cross-spawn "^5.0.1" - get-stream "^3.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" - -execa@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz" - integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== - dependencies: - cross-spawn "^6.0.0" - get-stream "^4.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" - -executable@^4.1.0: - version "4.1.1" - resolved "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz" - integrity sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg== - dependencies: - pify "^2.2.0" - -exif-parser@^0.1.12: - version "0.1.12" - resolved "https://registry.npmjs.org/exif-parser/-/exif-parser-0.1.12.tgz" - integrity sha1-WKnS1ywCwfbwKg70qRZicrd2CSI= - -expand-brackets@^2.1.4: - version "2.1.4" - resolved "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz" - integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= - dependencies: - debug "^2.3.3" - define-property "^0.2.5" - extend-shallow "^2.0.1" - posix-character-classes "^0.1.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - -expand-tilde@^2.0.0, expand-tilde@^2.0.2: - version "2.0.2" - resolved "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz" - integrity sha1-l+gBqgUt8CRU3kawK/YhZCzchQI= - dependencies: - homedir-polyfill "^1.0.1" - -express@^4.16.3: - version "4.17.1" - resolved "https://registry.npmjs.org/express/-/express-4.17.1.tgz" - integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== - dependencies: - accepts "~1.3.7" - array-flatten "1.1.1" - body-parser "1.19.0" - content-disposition "0.5.3" - content-type "~1.0.4" - cookie "0.4.0" - cookie-signature "1.0.6" - debug "2.6.9" - depd "~1.1.2" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - finalhandler "~1.1.2" - fresh "0.5.2" - merge-descriptors "1.0.1" - methods "~1.1.2" - on-finished "~2.3.0" - parseurl "~1.3.3" - path-to-regexp "0.1.7" - proxy-addr "~2.0.5" - qs "6.7.0" - range-parser "~1.2.1" - safe-buffer "5.1.2" - send "0.17.1" - serve-static "1.14.1" - setprototypeof "1.1.1" - statuses "~1.5.0" - type-is "~1.6.18" - utils-merge "1.0.1" - vary "~1.1.2" - -ext-list@^2.0.0: - version "2.2.2" - resolved "https://registry.npmjs.org/ext-list/-/ext-list-2.2.2.tgz" - integrity sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA== - dependencies: - mime-db "^1.28.0" - -ext-name@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/ext-name/-/ext-name-5.0.0.tgz" - integrity sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ== - dependencies: - ext-list "^2.0.0" - sort-keys-length "^1.0.0" - -extend-shallow@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz" - integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= - dependencies: - is-extendable "^0.1.0" - -extend-shallow@^3.0.0, extend-shallow@^3.0.2: - version "3.0.2" - resolved "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz" - integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= - dependencies: - assign-symbols "^1.0.0" - is-extendable "^1.0.1" - -external-editor@^3.0.3: - version "3.1.0" - resolved "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz" - integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== - dependencies: - chardet "^0.7.0" - iconv-lite "^0.4.24" - tmp "^0.0.33" - -extglob@^2.0.4: - version "2.0.4" - resolved "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz" - integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== - dependencies: - array-unique "^0.3.2" - define-property "^1.0.0" - expand-brackets "^2.1.4" - extend-shallow "^2.0.1" - fragment-cache "^0.2.1" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - -fast-deep-equal@^3.1.1: - version "3.1.3" - resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" - integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== - -fast-diff@^1.1.2: - version "1.2.0" - resolved "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz" - integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== - -fast-json-stable-stringify@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz" - integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== - -fast-levenshtein@^2.0.6: - version "2.0.6" - resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz" - integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= - -fastdom@^1.0.8: - version "1.0.9" - resolved "https://registry.npmjs.org/fastdom/-/fastdom-1.0.9.tgz" - integrity sha512-SSp4fbVzu8JkkG01NUX+0iOwe9M5PN3MGIQ84txLf4TkkJG4q30khkzumKgi4hUqO1+jX6wLHfnCPoZ6eSZ6Tg== - dependencies: - strictdom "^1.0.1" - -faster.js@^1.1.0: - version "1.1.1" - resolved "https://registry.npmjs.org/faster.js/-/faster.js-1.1.1.tgz" - integrity sha512-vPThNSLL/E1f7cLHd9yuayxZR82o/Iic4S5ZY45iY5AgBLNIlr3b3c+VpDjoYqjY9a9C/FQVUQy9oTILVP7X0g== - -fastparse@^1.1.1: - version "1.1.2" - resolved "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz" - integrity sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ== - -fd-slicer@~1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz" - integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4= - dependencies: - pend "~1.2.0" - -figgy-pudding@^3.5.1: - version "3.5.2" - resolved "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz" - integrity sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw== - -figures@^1.3.5: - version "1.7.0" - resolved "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz" - integrity sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4= - dependencies: - escape-string-regexp "^1.0.5" - object-assign "^4.1.0" - -figures@^3.0.0: - version "3.2.0" - resolved "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz" - integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== - dependencies: - escape-string-regexp "^1.0.5" - -file-entry-cache@^5.0.1: - 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== - dependencies: - flat-cache "^2.0.1" - -file-loader@^0.8.1: - version "0.8.5" - resolved "https://registry.npmjs.org/file-loader/-/file-loader-0.8.5.tgz" - integrity sha1-knXQMf54DyfUf19K8CvUNxPMFRs= - dependencies: - loader-utils "~0.2.5" - -file-type@5.2.0, file-type@^5.2.0: - version "5.2.0" - resolved "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz" - integrity sha1-LdvqfHP/42No365J3DOMBYwritY= - -file-type@^3.8.0: - version "3.9.0" - resolved "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz" - integrity sha1-JXoHg4TR24CHvESdEH1SpSZyuek= - -file-type@^4.2.0: - version "4.4.0" - resolved "https://registry.npmjs.org/file-type/-/file-type-4.4.0.tgz" - integrity sha1-G2AOX8ofvcboDApwxxyNul95BsU= - -file-type@^6.1.0: - version "6.2.0" - resolved "https://registry.npmjs.org/file-type/-/file-type-6.2.0.tgz" - integrity sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg== - -file-type@^8.1.0: - version "8.1.0" - resolved "https://registry.npmjs.org/file-type/-/file-type-8.1.0.tgz" - integrity sha512-qyQ0pzAy78gVoJsmYeNgl8uH8yKhr1lVhW7JbzJmnlRi0I4R2eEDEJZVKG8agpDnLpacwNbDhLNG/LMdxHD2YQ== - -file-type@^9.0.0: - version "9.0.0" - resolved "https://registry.npmjs.org/file-type/-/file-type-9.0.0.tgz" - integrity sha512-Qe/5NJrgIOlwijpq3B7BEpzPFcgzggOTagZmkXQY4LA6bsXKTUstK7Wp12lEJ/mLKTpvIZxmIuRcLYWT6ov9lw== - -file-uri-to-path@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" - integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== - -filename-reserved-regex@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz" - integrity sha1-q/c9+rc10EVECr/qLZHzieu/oik= - -filenamify@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/filenamify/-/filenamify-2.1.0.tgz" - integrity sha512-ICw7NTT6RsDp2rnYKVd8Fu4cr6ITzGy3+u4vUujPkabyaz+03F24NWEX7fs5fp+kBonlaqPH8fAO2NM+SXt/JA== - dependencies: - filename-reserved-regex "^2.0.0" - strip-outer "^1.0.0" - trim-repeated "^1.0.0" - -filesize@^3.6.1: - version "3.6.1" - resolved "https://registry.npmjs.org/filesize/-/filesize-3.6.1.tgz" - integrity sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg== - -fill-range@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz" - integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= - dependencies: - extend-shallow "^2.0.1" - is-number "^3.0.0" - repeat-string "^1.6.1" - to-regex-range "^2.1.0" - -fill-range@^7.0.1: - version "7.0.1" - resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz" - integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== - dependencies: - to-regex-range "^5.0.1" - -finalhandler@~1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz" - integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== - dependencies: - debug "2.6.9" - encodeurl "~1.0.2" - escape-html "~1.0.3" - on-finished "~2.3.0" - parseurl "~1.3.3" - statuses "~1.5.0" - unpipe "~1.0.0" - -find-cache-dir@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz" - integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ== - dependencies: - commondir "^1.0.1" - make-dir "^2.0.0" - pkg-dir "^3.0.0" - -find-up@^1.0.0: - version "1.1.2" - resolved "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz" - integrity sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8= - dependencies: - path-exists "^2.0.0" - pinkie-promise "^2.0.0" - -find-up@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz" - integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= - dependencies: - locate-path "^2.0.0" - -find-up@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz" - integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== - dependencies: - locate-path "^3.0.0" - -find-up@^4.1.0: - version "4.1.0" - resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz" - integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== - dependencies: - locate-path "^5.0.0" - path-exists "^4.0.0" - -find-versions@^3.0.0: - version "3.2.0" - resolved "https://registry.npmjs.org/find-versions/-/find-versions-3.2.0.tgz" - integrity sha512-P8WRou2S+oe222TOCHitLy8zj+SIsVJh52VP4lvXkaFVnOFFdoWv1H1Jjvel1aI6NCFOAaeAVm8qrI0odiLcww== - dependencies: - semver-regex "^2.0.0" - -findup-sync@3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz" - integrity sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg== - dependencies: - detect-file "^1.0.0" - is-glob "^4.0.0" - micromatch "^3.0.4" - resolve-dir "^1.0.1" - -flat-cache@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz" - integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA== - dependencies: - flatted "^2.0.0" - rimraf "2.6.3" - write "1.0.3" - -flatted@^2.0.0, flatted@^2.0.1: - version "2.0.2" - resolved "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz" - integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== - -flatten@^1.0.2: - version "1.0.3" - resolved "https://registry.npmjs.org/flatten/-/flatten-1.0.3.tgz" - integrity sha512-dVsPA/UwQ8+2uoFe5GHtiBMu48dWLTdsuEd7CKGlZlD78r1TTWBvDuFaFGKCo/ZfEr95Uk56vZoX86OsHkUeIg== - -flush-write-stream@^1.0.0: - version "1.1.1" - resolved "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz" - integrity sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w== - dependencies: - inherits "^2.0.3" - readable-stream "^2.3.6" - -for-in@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz" - integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= - -forwarded@~0.1.2: - version "0.1.2" - resolved "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz" - integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= - -fragment-cache@^0.2.1: - version "0.2.1" - resolved "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz" - integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= - dependencies: - map-cache "^0.2.2" - -fresh@0.5.2: - version "0.5.2" - resolved "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz" - integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= - -from2@^2.1.0, from2@^2.1.1: - version "2.3.0" - resolved "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz" - integrity sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8= - dependencies: - inherits "^2.0.1" - readable-stream "^2.0.0" - -fs-constants@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz" - integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== - -fs-write-stream-atomic@^1.0.8: - version "1.0.10" - resolved "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz" - integrity sha1-tH31NJPvkR33VzHnCp3tAYnbQMk= - dependencies: - graceful-fs "^4.1.2" - iferr "^0.1.5" - imurmurhash "^0.1.4" - readable-stream "1 || 2" - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" - integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= - -fsevents@^1.2.7: - version "1.2.13" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.13.tgz#f325cb0455592428bcf11b383370ef70e3bfcc38" - integrity sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw== - dependencies: - bindings "^1.5.0" - nan "^2.12.1" - -fsevents@~2.1.2: - version "2.1.3" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" - integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== - -function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz" - integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== - -functional-red-black-tree@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz" - integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= - -gensync@^1.0.0-beta.1: - version "1.0.0-beta.1" - resolved "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz" - integrity sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg== - -get-caller-file@^2.0.1: - version "2.0.5" - resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz" - integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== - -get-proxy@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/get-proxy/-/get-proxy-2.1.0.tgz" - integrity sha512-zmZIaQTWnNQb4R4fJUEp/FC51eZsc6EkErspy3xtIYStaq8EB/hDIWipxsal+E8rz0qD7f2sL/NA9Xee4RInJw== - dependencies: - npm-conf "^1.1.0" - -get-stdin@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz" - integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4= - -get-stdin@^6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz" - integrity sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g== - -get-stream@3.0.0, get-stream@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz" - integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ= - -get-stream@^2.2.0: - version "2.3.1" - resolved "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz" - integrity sha1-Xzj5PzRgCWZu4BUKBUFn+Rvdld4= - dependencies: - object-assign "^4.0.1" - pinkie-promise "^2.0.0" - -get-stream@^4.0.0: - version "4.1.0" - resolved "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz" - integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== - dependencies: - pump "^3.0.0" - -get-value@^2.0.3, get-value@^2.0.6: - version "2.0.6" - resolved "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz" - integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= - -glob-all@^3.1.0: - version "3.2.1" - resolved "https://registry.npmjs.org/glob-all/-/glob-all-3.2.1.tgz" - integrity sha512-x877rVkzB3ipid577QOp+eQCR6M5ZyiwrtaYgrX/z3EThaSPFtLDwBXFHc3sH1cG0R0vFYI5SRYeWMMSEyXkUw== - dependencies: - glob "^7.1.2" - yargs "^15.3.1" - -glob-parent@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" - integrity sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4= - dependencies: - is-glob "^3.1.0" - path-dirname "^1.0.0" - -glob-parent@^5.0.0, glob-parent@~5.1.0: - 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== - dependencies: - is-glob "^4.0.1" - -glob@^7.0.5, glob@^7.0.6, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: - version "7.1.6" - resolved "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz" - integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== - 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" - -global-modules@2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz" - integrity sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A== - dependencies: - global-prefix "^3.0.0" - -global-modules@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz" - integrity sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg== - dependencies: - global-prefix "^1.0.1" - is-windows "^1.0.1" - resolve-dir "^1.0.0" - -global-prefix@^1.0.1: - version "1.0.2" - resolved "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz" - integrity sha1-2/dDxsFJklk8ZVVoy2btMsASLr4= - dependencies: - expand-tilde "^2.0.2" - homedir-polyfill "^1.0.1" - ini "^1.3.4" - is-windows "^1.0.1" - which "^1.2.14" - -global-prefix@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz" - integrity sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg== - dependencies: - ini "^1.3.5" - kind-of "^6.0.2" - which "^1.3.1" - -global@~4.3.0: - version "4.3.2" - resolved "https://registry.npmjs.org/global/-/global-4.3.2.tgz" - integrity sha1-52mJJopsdMOJCLEwWxD8DjlOnQ8= - dependencies: - min-document "^2.19.0" - process "~0.5.1" - -globals@^11.1.0: - version "11.12.0" - resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz" - integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== - -globals@^12.1.0: - version "12.4.0" - resolved "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz" - integrity sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg== - dependencies: - type-fest "^0.8.1" - -globals@^9.18.0: - version "9.18.0" - resolved "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz" - integrity sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ== - -gonzales-pe@^4.2.3: - version "4.3.0" - resolved "https://registry.npmjs.org/gonzales-pe/-/gonzales-pe-4.3.0.tgz" - integrity sha512-otgSPpUmdWJ43VXyiNgEYE4luzHCL2pz4wQ0OnDluC6Eg4Ko3Vexy/SrSynglw/eR+OhkzmqFCZa/OFa/RgAOQ== - dependencies: - minimist "^1.2.5" - -got@^7.0.0: - version "7.1.0" - resolved "https://registry.npmjs.org/got/-/got-7.1.0.tgz" - integrity sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw== - dependencies: - decompress-response "^3.2.0" - duplexer3 "^0.1.4" - get-stream "^3.0.0" - is-plain-obj "^1.1.0" - is-retry-allowed "^1.0.0" - is-stream "^1.0.0" - isurl "^1.0.0-alpha5" - lowercase-keys "^1.0.0" - p-cancelable "^0.3.0" - p-timeout "^1.1.1" - safe-buffer "^5.0.1" - timed-out "^4.0.0" - url-parse-lax "^1.0.0" - url-to-options "^1.0.1" - -got@^8.3.1: - version "8.3.2" - resolved "https://registry.npmjs.org/got/-/got-8.3.2.tgz" - integrity sha512-qjUJ5U/hawxosMryILofZCkm3C84PLJS/0grRIpjAwu+Lkxxj5cxeCU25BG0/3mDSpXKTyZr8oh8wIgLaH0QCw== - dependencies: - "@sindresorhus/is" "^0.7.0" - cacheable-request "^2.1.1" - decompress-response "^3.3.0" - duplexer3 "^0.1.4" - get-stream "^3.0.0" - into-stream "^3.1.0" - is-retry-allowed "^1.1.0" - isurl "^1.0.0-alpha5" - lowercase-keys "^1.0.0" - mimic-response "^1.0.0" - p-cancelable "^0.4.0" - p-timeout "^2.0.1" - pify "^3.0.0" - safe-buffer "^5.1.1" - timed-out "^4.0.1" - url-parse-lax "^3.0.0" - url-to-options "^1.0.1" - -graceful-fs@^4.1.10, graceful-fs@^4.1.15, graceful-fs@^4.1.2: - version "4.2.3" - resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz" - integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ== - -graceful-fs@^4.1.11: - version "4.2.6" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" - integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== - -"graceful-readlink@>= 1.0.0": - version "1.0.1" - resolved "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz" - integrity sha1-TK+tdrxi8C+gObL5Tpo906ORpyU= - -gzip-size@^5.0.0: - version "5.1.1" - resolved "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz" - integrity sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA== - dependencies: - duplexer "^0.1.1" - pify "^4.0.1" - -has-ansi@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz" - integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= - dependencies: - ansi-regex "^2.0.0" - -has-flag@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz" - integrity sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo= - -has-flag@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz" - integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= - -has-flag@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" - integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== - -has-symbol-support-x@^1.4.1: - version "1.4.2" - resolved "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz" - integrity sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw== - -has-symbols@^1.0.0, has-symbols@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz" - integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== - -has-to-string-tag-x@^1.2.0: - version "1.4.1" - resolved "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz" - integrity sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw== - dependencies: - has-symbol-support-x "^1.4.1" - -has-value@^0.3.1: - version "0.3.1" - resolved "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz" - integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= - dependencies: - get-value "^2.0.3" - has-values "^0.1.4" - isobject "^2.0.0" - -has-value@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz" - integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= - dependencies: - get-value "^2.0.6" - has-values "^1.0.0" - isobject "^3.0.0" - -has-values@^0.1.4: - version "0.1.4" - resolved "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz" - integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= - -has-values@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz" - integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= - dependencies: - is-number "^3.0.0" - kind-of "^4.0.0" - -has@^1.0.0, has@^1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/has/-/has-1.0.3.tgz" - integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== - dependencies: - function-bind "^1.1.1" - -hash-base@^3.0.0: - version "3.1.0" - resolved "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz" - integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA== - dependencies: - inherits "^2.0.4" - readable-stream "^3.6.0" - safe-buffer "^5.2.0" - -hash.js@^1.0.0, hash.js@^1.0.3: - version "1.1.7" - resolved "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz" - integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== - dependencies: - inherits "^2.0.3" - minimalistic-assert "^1.0.1" - -he@1.2.x: - version "1.2.0" - resolved "https://registry.npmjs.org/he/-/he-1.2.0.tgz" - integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== - -hex-color-regex@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz" - integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ== - -hmac-drbg@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz" - integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= - dependencies: - hash.js "^1.0.3" - minimalistic-assert "^1.0.0" - minimalistic-crypto-utils "^1.0.1" - -home-or-tmp@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz" - integrity sha1-42w/LSyufXRqhX440Y1fMqeILbg= - dependencies: - os-homedir "^1.0.0" - os-tmpdir "^1.0.1" - -homedir-polyfill@^1.0.1: - version "1.0.3" - resolved "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz" - integrity sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA== - dependencies: - parse-passwd "^1.0.0" - -hoopy@^0.1.4: - version "0.1.4" - resolved "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz" - integrity sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ== - -hosted-git-info@^2.1.4: - version "2.8.8" - resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz" - integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg== - -howler@^2.1.2: - version "2.1.3" - resolved "https://registry.npmjs.org/howler/-/howler-2.1.3.tgz" - integrity sha512-PSGbOi1EYgw80C5UQbxtJM7TmzD+giJunIMBYyH3RVzHZx2fZLYBoes0SpVVHi/SFa1GoNtgXj/j6I7NOKYBxQ== - -hsl-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz" - integrity sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4= - -hsla-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/hsla-regex/-/hsla-regex-1.0.0.tgz" - integrity sha1-wc56MWjIxmFAM6S194d/OyJfnDg= - -html-comment-regex@^1.1.0: - version "1.1.2" - resolved "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.2.tgz" - integrity sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ== - -html-loader@^0.5.5: - version "0.5.5" - resolved "https://registry.npmjs.org/html-loader/-/html-loader-0.5.5.tgz" - integrity sha512-7hIW7YinOYUpo//kSYcPB6dCKoceKLmOwjEMmhIobHuWGDVl0Nwe4l68mdG/Ru0wcUxQjVMEoZpkalZ/SE7zog== - dependencies: - es6-templates "^0.2.3" - fastparse "^1.1.1" - html-minifier "^3.5.8" - loader-utils "^1.1.0" - object-assign "^4.1.1" - -html-minifier@^3.5.8: - version "3.5.21" - resolved "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.21.tgz" - integrity sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA== - dependencies: - camel-case "3.0.x" - clean-css "4.2.x" - commander "2.17.x" - he "1.2.x" - param-case "2.1.x" - relateurl "0.2.x" - uglify-js "3.4.x" - -http-cache-semantics@3.8.1: - version "3.8.1" - resolved "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz" - integrity sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w== - -http-errors@1.7.2, http-errors@~1.7.2: - version "1.7.2" - resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz" - integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== - dependencies: - depd "~1.1.2" - inherits "2.0.3" - setprototypeof "1.1.1" - statuses ">= 1.5.0 < 2" - toidentifier "1.0.0" - -https-browserify@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz" - integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= - -iconv-lite@0.4.24, iconv-lite@^0.4.24: - version "0.4.24" - resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz" - integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== - dependencies: - safer-buffer ">= 2.1.2 < 3" - -ieee754@^1.1.4: - version "1.1.13" - resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz" - integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== - -iferr@^0.1.5: - version "0.1.5" - resolved "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz" - integrity sha1-xg7taebY/bazEEofy8ocGS3FtQE= - -ignore-loader@^0.1.2: - version "0.1.2" - resolved "https://registry.npmjs.org/ignore-loader/-/ignore-loader-0.1.2.tgz" - integrity sha1-2B8kA3bQuk8Nd4lyw60lh0EXpGM= - -ignore@^4.0.6: - version "4.0.6" - resolved "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz" - integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== - -imagemin-mozjpeg@^8.0.0: - version "8.0.0" - resolved "https://registry.npmjs.org/imagemin-mozjpeg/-/imagemin-mozjpeg-8.0.0.tgz" - integrity sha512-+EciPiIjCb8JWjQNr1q8sYWYf7GDCNDxPYnkD11TNIjjWNzaV+oTg4DpOPQjl5ZX/KRCPMEgS79zLYAQzLitIA== - dependencies: - execa "^1.0.0" - is-jpg "^2.0.0" - mozjpeg "^6.0.0" - -imagemin-pngquant@^8.0.0: - version "8.0.0" - resolved "https://registry.npmjs.org/imagemin-pngquant/-/imagemin-pngquant-8.0.0.tgz" - integrity sha512-PVq0diOxO+Zyq/zlMCz2Pfu6mVLHgiT1GpW702OwVlnej+NhS6ZQegYi3OFEDW8d7GxouyR5e8R+t53SMciOeg== - dependencies: - execa "^1.0.0" - is-png "^2.0.0" - is-stream "^2.0.0" - ow "^0.13.2" - pngquant-bin "^5.0.0" - -import-fresh@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz" - integrity sha1-2BNVwVYS04bGH53dOSLUMEgipUY= - dependencies: - caller-path "^2.0.0" - resolve-from "^3.0.0" - -import-fresh@^3.0.0: - version "3.2.1" - resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz" - integrity sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ== - dependencies: - parent-module "^1.0.0" - resolve-from "^4.0.0" - -import-lazy@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/import-lazy/-/import-lazy-3.1.0.tgz" - integrity sha512-8/gvXvX2JMn0F+CDlSC4l6kOmVaLOO3XLkksI7CI3Ud95KDYJuYur2b9P/PUt/i/pDAMd/DulQsNbbbmRRsDIQ== - -import-local@2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz" - integrity sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ== - dependencies: - pkg-dir "^3.0.0" - resolve-cwd "^2.0.0" - -imurmurhash@^0.1.4: - version "0.1.4" - resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz" - integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= - -indent-string@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz" - integrity sha1-ji1INIdCEhtKghi3oTfppSBJ3IA= - dependencies: - repeating "^2.0.0" - -indexes-of@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz" - integrity sha1-8w9xbI4r00bHtn0985FVZqfAVgc= - -infer-owner@^1.0.3: - version "1.0.4" - resolved "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz" - integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A== - -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" - integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3: - version "2.0.4" - resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -inherits@2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" - integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE= - -inherits@2.0.3: - version "2.0.3" - resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" - integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= - -ini@^1.3.4, ini@^1.3.5: - version "1.3.5" - resolved "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz" - integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== - -inquirer@^7.0.0: - version "7.1.0" - resolved "https://registry.npmjs.org/inquirer/-/inquirer-7.1.0.tgz" - integrity sha512-5fJMWEmikSYu0nv/flMc475MhGbB7TSPd/2IpFV4I4rMklboCH2rQjYY5kKiYGHqUF9gvaambupcJFFG9dvReg== - dependencies: - ansi-escapes "^4.2.1" - chalk "^3.0.0" - cli-cursor "^3.1.0" - cli-width "^2.0.0" - external-editor "^3.0.3" - figures "^3.0.0" - lodash "^4.17.15" - mute-stream "0.0.8" - run-async "^2.4.0" - rxjs "^6.5.3" - string-width "^4.1.0" - strip-ansi "^6.0.0" - through "^2.3.6" - -interpret@1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz" - integrity sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw== - -into-stream@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/into-stream/-/into-stream-3.1.0.tgz" - integrity sha1-lvsKk2wSur1v8XUqF9BWFqvQlMY= - dependencies: - from2 "^2.1.1" - p-is-promise "^1.1.0" - -invariant@^2.2.2, invariant@^2.2.4: - version "2.2.4" - resolved "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz" - integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== - dependencies: - loose-envify "^1.0.0" - -invert-kv@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz" - integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA== - -ipaddr.js@1.9.1: - version "1.9.1" - resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz" - integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== - -is-absolute-url@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz" - integrity sha1-UFMN+4T8yap9vnhS6Do3uTufKqY= - -is-accessor-descriptor@^0.1.6: - version "0.1.6" - resolved "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz" - integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= - dependencies: - kind-of "^3.0.2" - -is-accessor-descriptor@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz" - integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== - dependencies: - kind-of "^6.0.0" - -is-arrayish@^0.2.1: - version "0.2.1" - resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz" - integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= - -is-arrayish@^0.3.1: - version "0.3.2" - resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz" - integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== - -is-binary-path@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" - integrity sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg= - dependencies: - binary-extensions "^1.0.0" - -is-binary-path@~2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz" - integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== - dependencies: - binary-extensions "^2.0.0" - -is-buffer@^1.1.5: - version "1.1.6" - resolved "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz" - integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== - -is-callable@^1.1.4, is-callable@^1.1.5: - version "1.1.5" - resolved "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz" - integrity sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q== - -is-color-stop@^1.0.0: - version "1.1.0" - resolved "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz" - integrity sha1-z/9HGu5N1cnhWFmPvhKWe1za00U= - dependencies: - css-color-names "^0.0.4" - hex-color-regex "^1.1.0" - hsl-regex "^1.0.0" - hsla-regex "^1.0.0" - rgb-regex "^1.0.1" - rgba-regex "^1.0.0" - -is-data-descriptor@^0.1.4: - version "0.1.4" - resolved "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz" - integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= - dependencies: - kind-of "^3.0.2" - -is-data-descriptor@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz" - integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== - dependencies: - kind-of "^6.0.0" - -is-date-object@^1.0.1: - version "1.0.2" - resolved "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz" - integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== - -is-descriptor@^0.1.0: - version "0.1.6" - resolved "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz" - integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== - dependencies: - is-accessor-descriptor "^0.1.6" - is-data-descriptor "^0.1.4" - kind-of "^5.0.0" - -is-descriptor@^1.0.0, is-descriptor@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz" - integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== - dependencies: - is-accessor-descriptor "^1.0.0" - is-data-descriptor "^1.0.0" - kind-of "^6.0.2" - -is-directory@^0.3.1: - version "0.3.1" - resolved "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz" - integrity sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE= - -is-extendable@^0.1.0, is-extendable@^0.1.1: - version "0.1.1" - resolved "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz" - integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= - -is-extendable@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz" - integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== - dependencies: - is-plain-object "^2.0.4" - -is-extglob@^2.1.0, is-extglob@^2.1.1: - version "2.1.1" - resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" - integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= - -is-finite@^1.0.0: - version "1.1.0" - resolved "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz" - integrity sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w== - -is-fullwidth-code-point@^2.0.0: - 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= - -is-fullwidth-code-point@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz" - integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== - -is-function@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/is-function/-/is-function-1.0.1.tgz" - integrity sha1-Es+5i2W1fdPRk6MSH19uL0N2ArU= - -is-glob@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" - integrity sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo= - dependencies: - is-extglob "^2.1.0" - -is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz" - integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== - dependencies: - is-extglob "^2.1.1" - -is-jpg@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/is-jpg/-/is-jpg-2.0.0.tgz" - integrity sha1-LhmX+m6RZuqsAkLarkQ0A+TvHZc= - -is-natural-number@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/is-natural-number/-/is-natural-number-4.0.1.tgz" - integrity sha1-q5124dtM7VHjXeDHLr7PCfc0zeg= - -is-number@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz" - integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= - dependencies: - kind-of "^3.0.2" - -is-number@^7.0.0: - version "7.0.0" - resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" - integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== - -is-obj@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz" - integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== - -is-object@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/is-object/-/is-object-1.0.1.tgz" - integrity sha1-iVJojF7C/9awPsyF52ngKQMINHA= - -is-plain-obj@^1.0.0, is-plain-obj@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz" - integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= - -is-plain-object@^2.0.3, is-plain-object@^2.0.4: - version "2.0.4" - resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz" - integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== - dependencies: - isobject "^3.0.1" - -is-plain-object@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz" - integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== - -is-png@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/is-png/-/is-png-2.0.0.tgz" - integrity sha512-4KPGizaVGj2LK7xwJIz8o5B2ubu1D/vcQsgOGFEDlpcvgZHto4gBnyd0ig7Ws+67ixmwKoNmu0hYnpo6AaKb5g== - -is-regex@^1.0.5: - version "1.0.5" - resolved "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz" - integrity sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ== - dependencies: - has "^1.0.3" - -is-resolvable@^1.0.0: - version "1.1.0" - resolved "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz" - integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg== - -is-retry-allowed@^1.0.0, is-retry-allowed@^1.1.0: - version "1.2.0" - resolved "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz" - integrity sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg== - -is-stream@^1.0.0, is-stream@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz" - integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= - -is-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz" - integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== - -is-svg@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/is-svg/-/is-svg-3.0.0.tgz" - integrity sha512-gi4iHK53LR2ujhLVVj+37Ykh9GLqYHX6JOVXbLAucaG/Cqw9xwdFOjDM2qeifLs1sF1npXXFvDu0r5HNgCMrzQ== - dependencies: - html-comment-regex "^1.1.0" - -is-symbol@^1.0.2: - version "1.0.3" - resolved "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz" - integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ== - dependencies: - has-symbols "^1.0.1" - -is-utf8@^0.2.0: - version "0.2.1" - resolved "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz" - integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= - -is-windows@^1.0.1, is-windows@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz" - integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== - -is-wsl@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz" - integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= - -isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" - integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= - -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" - integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= - -isobject@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz" - integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= - dependencies: - isarray "1.0.0" - -isobject@^3.0.0, isobject@^3.0.1: - version "3.0.1" - resolved "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz" - integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= - -isurl@^1.0.0-alpha5: - version "1.0.0" - resolved "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz" - integrity sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w== - dependencies: - has-to-string-tag-x "^1.2.0" - is-object "^1.0.1" - -jimp@^0.6.1: - version "0.6.8" - resolved "https://registry.npmjs.org/jimp/-/jimp-0.6.8.tgz" - integrity sha512-F7emeG7Hp61IM8VFbNvWENLTuHe0ghizWPuP4JS9ujx2r5mCVYEd/zdaz6M2M42ZdN41blxPajLWl9FXo7Mr2Q== - dependencies: - "@jimp/custom" "^0.6.8" - "@jimp/plugins" "^0.6.8" - "@jimp/types" "^0.6.8" - core-js "^2.5.7" - regenerator-runtime "^0.13.3" - -jpeg-js@^0.3.4: - version "0.3.7" - resolved "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.3.7.tgz" - integrity sha512-9IXdWudL61npZjvLuVe/ktHiA41iE8qFyLB+4VDTblEsWBzeg8WQTlktdUK4CdncUqtUgUg0bbOmTE2bKBKaBQ== - -js-base64@^2.1.9: - version "2.5.2" - resolved "https://registry.npmjs.org/js-base64/-/js-base64-2.5.2.tgz" - integrity sha512-Vg8czh0Q7sFBSUMWWArX/miJeBWYBPpdU/3M/DKSaekLMqrqVPaedp+5mZhie/r0lgrcaYBfwXatEew6gwgiQQ== - -"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz" - integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== - -js-tokens@^3.0.2: - version "3.0.2" - resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz" - integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= - -js-yaml@^3.13.1, js-yaml@^3.4.2: - version "3.13.1" - resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz" - integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - -jsesc@^1.3.0: - version "1.3.0" - resolved "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz" - integrity sha1-RsP+yMGJKxKwgz25vHYiF226s0s= - -jsesc@^2.5.1: - version "2.5.2" - resolved "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz" - integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== - -jsesc@~0.5.0: - version "0.5.0" - resolved "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz" - integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= - -json-buffer@3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz" - integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= - -json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz" - integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== - -json-schema-traverse@^0.4.1: - 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== - -json-stable-stringify-without-jsonify@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz" - integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= - -json5@^0.5.0, json5@^0.5.1: - version "0.5.1" - resolved "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz" - integrity sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE= - -json5@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz" - integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== - dependencies: - minimist "^1.2.0" - -json5@^2.1.2: - version "2.1.3" - resolved "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz" - integrity sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA== - dependencies: - minimist "^1.2.5" - -keyv@3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/keyv/-/keyv-3.0.0.tgz" - integrity sha512-eguHnq22OE3uVoSYG0LVWNP+4ppamWr9+zWBe1bsNcovIMy6huUJFPgy4mGwCd/rnl3vOLGW1MTlu4c57CT1xA== - dependencies: - json-buffer "3.0.0" - -kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: - version "3.2.2" - resolved "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz" - integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= - dependencies: - is-buffer "^1.1.5" - -kind-of@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz" - integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= - dependencies: - is-buffer "^1.1.5" - -kind-of@^5.0.0: - version "5.1.0" - resolved "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz" - integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== - -kind-of@^6.0.0, kind-of@^6.0.2: - version "6.0.3" - resolved "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz" - integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== - -known-css-properties@^0.11.0: - version "0.11.0" - resolved "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.11.0.tgz" - integrity sha512-bEZlJzXo5V/ApNNa5z375mJC6Nrz4vG43UgcSCrg2OHC+yuB6j0iDSrY7RQ/+PRofFB03wNIIt9iXIVLr4wc7w== - -lcid@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz" - integrity sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA== - dependencies: - invert-kv "^2.0.0" - -leven@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz" - integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== - -levenary@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/levenary/-/levenary-1.1.1.tgz" - integrity sha512-mkAdOIt79FD6irqjYSs4rdbnlT5vRonMEvBVPVb3XmevfS8kgRXwfes0dhPdEtzTWD/1eNE/Bm/G1iRt6DcnQQ== - dependencies: - leven "^3.1.0" - -levn@^0.4.1: - version "0.4.1" - resolved "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz" - integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== - dependencies: - prelude-ls "^1.2.1" - type-check "~0.4.0" - -load-bmfont@^1.3.1, load-bmfont@^1.4.0: - version "1.4.0" - resolved "https://registry.npmjs.org/load-bmfont/-/load-bmfont-1.4.0.tgz" - integrity sha512-kT63aTAlNhZARowaNYcY29Fn/QYkc52M3l6V1ifRcPewg2lvUZDAj7R6dXjOL9D0sict76op3T5+odumDSF81g== - dependencies: - buffer-equal "0.0.1" - mime "^1.3.4" - parse-bmfont-ascii "^1.0.3" - parse-bmfont-binary "^1.0.5" - parse-bmfont-xml "^1.1.4" - phin "^2.9.1" - xhr "^2.0.1" - xtend "^4.0.0" - -load-json-file@^1.0.0: - version "1.1.0" - resolved "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz" - integrity sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA= - dependencies: - graceful-fs "^4.1.2" - parse-json "^2.2.0" - pify "^2.0.0" - pinkie-promise "^2.0.0" - strip-bom "^2.0.0" - -loader-runner@^2.4.0: - version "2.4.0" - resolved "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz" - integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw== - -loader-utils@1.2.3: - version "1.2.3" - resolved "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz" - integrity sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA== - dependencies: - big.js "^5.2.2" - emojis-list "^2.0.0" - json5 "^1.0.1" - -loader-utils@^0.2.5, loader-utils@~0.2.2, loader-utils@~0.2.3, loader-utils@~0.2.5: - version "0.2.17" - resolved "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz" - integrity sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g= - dependencies: - big.js "^3.1.3" - emojis-list "^2.0.0" - json5 "^0.5.0" - object-assign "^4.0.1" - -loader-utils@^1.0.0, loader-utils@^1.1.0, loader-utils@^1.2.3, loader-utils@^1.4.0: - version "1.4.0" - resolved "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz" - integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA== - dependencies: - big.js "^5.2.2" - emojis-list "^3.0.0" - json5 "^1.0.1" - -locate-path@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz" - integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= - dependencies: - p-locate "^2.0.0" - path-exists "^3.0.0" - -locate-path@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz" - integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== - dependencies: - p-locate "^3.0.0" - path-exists "^3.0.0" - -locate-path@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz" - integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== - dependencies: - p-locate "^4.1.0" - -lodash._reinterpolate@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz" - integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= - -lodash.memoize@^4.1.2: - version "4.1.2" - resolved "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz" - integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= - -lodash.template@^4.5.0: - version "4.5.0" - resolved "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz" - integrity sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A== - dependencies: - lodash._reinterpolate "^3.0.0" - lodash.templatesettings "^4.0.0" - -lodash.templatesettings@^4.0.0: - version "4.2.0" - resolved "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz" - integrity sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ== - dependencies: - lodash._reinterpolate "^3.0.0" - -lodash.uniq@^4.5.0: - version "4.5.0" - resolved "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz" - integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= - -lodash@^4.15.0, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.4: - version "4.17.15" - resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz" - integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== - -logalot@^2.0.0, logalot@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/logalot/-/logalot-2.1.0.tgz" - integrity sha1-X46MkNME7fElMJUaVVSruMXj9VI= - dependencies: - figures "^1.3.5" - squeak "^1.0.0" - -logrocket@^1.0.7: - version "1.0.7" - resolved "https://registry.npmjs.org/logrocket/-/logrocket-1.0.7.tgz" - integrity sha512-v6HWEQIsyG+3FkldB7vIAgHh7/qpsiz2Br4bLK5SHBvjqRrHs/Fp+Jr8oiA2GYq0UurAtCu51U8SWft5+OCKtg== - -longest@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz" - integrity sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc= - -loose-envify@^1.0.0: - version "1.4.0" - resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz" - integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== - dependencies: - js-tokens "^3.0.0 || ^4.0.0" - -loud-rejection@^1.0.0: - version "1.6.0" - resolved "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz" - integrity sha1-W0b4AUft7leIcPCG0Eghz5mOVR8= - dependencies: - currently-unhandled "^0.4.1" - signal-exit "^3.0.0" - -lower-case@^1.1.1: - version "1.1.4" - resolved "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz" - integrity sha1-miyr0bno4K6ZOkv31YdcOcQujqw= - -lowercase-keys@1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.0.tgz" - integrity sha1-TjNms55/VFfjXxMkvfb4jQv8cwY= - -lowercase-keys@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz" - integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== - -lpad-align@^1.0.1: - version "1.1.2" - resolved "https://registry.npmjs.org/lpad-align/-/lpad-align-1.1.2.tgz" - integrity sha1-IfYArBwwlcPG5JfuZyce4ISB/p4= - dependencies: - get-stdin "^4.0.1" - indent-string "^2.1.0" - longest "^1.0.0" - meow "^3.3.0" - -lru-cache@^4.0.1: - version "4.1.5" - resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz" - integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== - dependencies: - pseudomap "^1.0.2" - yallist "^2.1.2" - -lru-cache@^5.1.1: - version "5.1.1" - resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz" - integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== - dependencies: - yallist "^3.0.2" - -lru-cache@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" - integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== - dependencies: - yallist "^4.0.0" - -lz-string@^1.4.4: - version "1.4.4" - resolved "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz" - integrity sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY= - -make-dir@^1.0.0, make-dir@^1.2.0: - version "1.3.0" - resolved "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz" - integrity sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ== - dependencies: - pify "^3.0.0" - -make-dir@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz" - integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== - dependencies: - pify "^4.0.1" - semver "^5.6.0" - -map-age-cleaner@^0.1.1: - version "0.1.3" - resolved "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz" - integrity sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w== - dependencies: - p-defer "^1.0.0" - -map-cache@^0.2.2: - version "0.2.2" - resolved "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz" - integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= - -map-obj@^1.0.0, map-obj@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz" - integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0= - -map-visit@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz" - integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= - dependencies: - object-visit "^1.0.0" - -markdown-loader@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/markdown-loader/-/markdown-loader-4.0.0.tgz" - integrity sha512-9BCm8iyLF4AVYtjtybOTg8cTcpWYKsDGWWhsc7XaJlXQiddo3ztbZxLPJ28pmCxFI1BlMkT1wDVav1chPjTpdA== - dependencies: - loader-utils "^1.1.0" - marked "^0.5.0" - -marked@^0.5.0: - version "0.5.2" - resolved "https://registry.npmjs.org/marked/-/marked-0.5.2.tgz" - integrity sha512-fdZvBa7/vSQIZCi4uuwo2N3q+7jJURpMVCcbaX0S1Mg65WZ5ilXvC67MviJAsdjqqgD+CEq4RKo5AYGgINkVAA== - -match-all@^1.2.5: - version "1.2.5" - resolved "https://registry.npmjs.org/match-all/-/match-all-1.2.5.tgz" - integrity sha512-KW4trRDMYbVkAKZ1J655vh0931mk3XM1lIJ480TXUL3KBrOsZ6WpryYJELonvtXC1O4erLYB069uHidLkswbjQ== - -md5.js@^1.3.4: - version "1.3.5" - resolved "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz" - integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== - dependencies: - hash-base "^3.0.0" - inherits "^2.0.1" - safe-buffer "^5.1.2" - -mdn-data@2.0.4: - version "2.0.4" - resolved "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz" - integrity sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA== - -mdn-data@2.0.6: - version "2.0.6" - resolved "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.6.tgz" - integrity sha512-rQvjv71olwNHgiTbfPZFkJtjNMciWgswYeciZhtvWLO8bmX3TnhyA62I6sTWOyZssWHJJjY6/KiWwqQsWWsqOA== - -media-typer@0.3.0: - version "0.3.0" - resolved "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz" - integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= - -mem@^4.0.0: - version "4.3.0" - resolved "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz" - integrity sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w== - dependencies: - map-age-cleaner "^0.1.1" - mimic-fn "^2.0.0" - p-is-promise "^2.0.0" - -memory-fs@^0.4.0, memory-fs@^0.4.1: - version "0.4.1" - resolved "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz" - integrity sha1-OpoguEYlI+RHz7x+i7gO1me/xVI= - dependencies: - errno "^0.1.3" - readable-stream "^2.0.1" - -memory-fs@^0.5.0: - version "0.5.0" - resolved "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz" - integrity sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA== - dependencies: - errno "^0.1.3" - readable-stream "^2.0.1" - -meow@^3.3.0: - version "3.7.0" - resolved "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz" - integrity sha1-cstmi0JSKCkKu/qFaJJYcwioAfs= - dependencies: - camelcase-keys "^2.0.0" - decamelize "^1.1.2" - loud-rejection "^1.0.0" - map-obj "^1.0.1" - minimist "^1.1.3" - normalize-package-data "^2.3.4" - object-assign "^4.0.1" - read-pkg-up "^1.0.1" - redent "^1.0.0" - trim-newlines "^1.0.0" - -merge-descriptors@1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz" - integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= - -methods@~1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz" - integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= - -micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4: - version "3.1.10" - resolved "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz" - integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== - dependencies: - arr-diff "^4.0.0" - array-unique "^0.3.2" - braces "^2.3.1" - define-property "^2.0.2" - extend-shallow "^3.0.2" - extglob "^2.0.4" - fragment-cache "^0.2.1" - kind-of "^6.0.2" - nanomatch "^1.2.9" - object.pick "^1.3.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.2" - -miller-rabin@^4.0.0: - version "4.0.1" - resolved "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz" - integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA== - dependencies: - bn.js "^4.0.0" - brorand "^1.0.1" - -mime-db@1.43.0, mime-db@^1.28.0: - version "1.43.0" - resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz" - integrity sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ== - -mime-types@~2.1.24: - version "2.1.26" - resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz" - integrity sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ== - dependencies: - mime-db "1.43.0" - -mime@1.6.0, mime@^1.3.4: - version "1.6.0" - resolved "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz" - integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== - -mime@^2.4.0: - version "2.4.4" - resolved "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz" - integrity sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA== - -mimic-fn@^2.0.0, mimic-fn@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz" - integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== - -mimic-response@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz" - integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== - -min-document@^2.19.0: - version "2.19.0" - resolved "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz" - integrity sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU= - dependencies: - dom-walk "^0.1.0" - -minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz" - integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== - -minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz" - integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= - -minimatch@^3.0.4: - version "3.0.4" - resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== - dependencies: - brace-expansion "^1.1.7" - -minimist@0.0.8: - version "0.0.8" - resolved "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" - integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= - -minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5: - version "1.2.5" - resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz" - integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== - -mississippi@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz" - integrity sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA== - dependencies: - concat-stream "^1.5.0" - duplexify "^3.4.2" - end-of-stream "^1.1.0" - flush-write-stream "^1.0.0" - from2 "^2.1.0" - parallel-transform "^1.1.0" - pump "^3.0.0" - pumpify "^1.3.3" - stream-each "^1.1.0" - through2 "^2.0.0" - -mixin-deep@^1.2.0: - version "1.3.2" - resolved "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz" - integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== - dependencies: - for-in "^1.0.2" - is-extendable "^1.0.1" - -mkdirp@0.5.1: - version "0.5.1" - resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz" - integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= - dependencies: - minimist "0.0.8" - -mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@~0.5.1: - version "0.5.5" - resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz" - integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== - dependencies: - minimist "^1.2.5" - -move-concurrently@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz" - integrity sha1-viwAX9oy4LKa8fBdfEszIUxwH5I= - dependencies: - aproba "^1.1.1" - copy-concurrently "^1.0.0" - fs-write-stream-atomic "^1.0.8" - mkdirp "^0.5.1" - rimraf "^2.5.4" - run-queue "^1.0.3" - -mozjpeg@^6.0.0: - version "6.0.1" - resolved "https://registry.npmjs.org/mozjpeg/-/mozjpeg-6.0.1.tgz" - integrity sha512-9Z59pJMi8ni+IUvSH5xQwK5tNLw7p3dwDNCZ3o1xE+of3G5Hc/yOz6Ue/YuLiBXU3ZB5oaHPURyPdqfBX/QYJA== - dependencies: - bin-build "^3.0.0" - bin-wrapper "^4.0.0" - logalot "^2.1.0" - -ms@2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" - integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= - -ms@2.1.1: - version "2.1.1" - resolved "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz" - integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== - -ms@^2.1.1: - version "2.1.2" - resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - -mute-stream@0.0.8: - version "0.0.8" - resolved "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz" - integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== - -nan@^2.12.1: - version "2.14.2" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19" - integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ== - -nanoid@^3.1.20: - version "3.1.20" - resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz" - integrity sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw== - -nanomatch@^1.2.9: - version "1.2.13" - resolved "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz" - integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== - dependencies: - arr-diff "^4.0.0" - array-unique "^0.3.2" - define-property "^2.0.2" - extend-shallow "^3.0.2" - fragment-cache "^0.2.1" - is-windows "^1.0.2" - kind-of "^6.0.2" - object.pick "^1.3.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - -natural-compare@^1.4.0: - version "1.4.0" - resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" - integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= - -negotiator@0.6.2: - version "0.6.2" - resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz" - integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== - -neo-async@^2.5.0, neo-async@^2.6.1: - version "2.6.2" - resolved "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz" - integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== - -nice-try@^1.0.4: - version "1.0.5" - resolved "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz" - integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== - -no-case@^2.2.0: - version "2.3.2" - resolved "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz" - integrity sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ== - dependencies: - lower-case "^1.1.1" - -node-fetch@^2.6.1: - version "2.6.1" - resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz" - integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== - -node-libs-browser@^2.2.1: - version "2.2.1" - resolved "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz" - integrity sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q== - dependencies: - assert "^1.1.1" - browserify-zlib "^0.2.0" - buffer "^4.3.0" - console-browserify "^1.1.0" - constants-browserify "^1.0.0" - crypto-browserify "^3.11.0" - domain-browser "^1.1.1" - events "^3.0.0" - https-browserify "^1.0.0" - os-browserify "^0.3.0" - path-browserify "0.0.1" - process "^0.11.10" - punycode "^1.2.4" - querystring-es3 "^0.2.0" - readable-stream "^2.3.3" - stream-browserify "^2.0.1" - stream-http "^2.7.2" - string_decoder "^1.0.0" - timers-browserify "^2.0.4" - tty-browserify "0.0.0" - url "^0.11.0" - util "^0.11.0" - vm-browserify "^1.0.1" - -node-releases@^1.1.53: - version "1.1.53" - resolved "https://registry.npmjs.org/node-releases/-/node-releases-1.1.53.tgz" - integrity sha512-wp8zyQVwef2hpZ/dJH7SfSrIPD6YoJz6BDQDpGEkcA0s3LpAQoxBIYmfIq6QAhC1DhwsyCgTaTTcONwX8qzCuQ== - -normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: - version "2.5.0" - resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz" - integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== - dependencies: - hosted-git-info "^2.1.4" - resolve "^1.10.0" - semver "2 || 3 || 4 || 5" - validate-npm-package-license "^3.0.1" - -normalize-path@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" - integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= - dependencies: - remove-trailing-separator "^1.0.1" - -normalize-path@^3.0.0, normalize-path@~3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz" - integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== - -normalize-range@^0.1.2: - version "0.1.2" - resolved "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz" - integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI= - -normalize-url@2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/normalize-url/-/normalize-url-2.0.1.tgz" - integrity sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw== - dependencies: - prepend-http "^2.0.0" - query-string "^5.0.1" - sort-keys "^2.0.0" - -normalize-url@^3.0.0: - version "3.3.0" - resolved "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz" - integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg== - -npm-conf@^1.1.0: - version "1.1.3" - resolved "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.3.tgz" - integrity sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw== - dependencies: - config-chain "^1.1.11" - pify "^3.0.0" - -npm-run-path@^2.0.0: - version "2.0.2" - resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz" - integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= - dependencies: - path-key "^2.0.0" - -nth-check@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz" - integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg== - dependencies: - boolbase "~1.0.0" - -num2fraction@^1.2.2: - version "1.2.2" - resolved "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz" - integrity sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4= - -object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: - version "4.1.1" - resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" - integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= - -object-copy@^0.1.0: - version "0.1.0" - resolved "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz" - integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= - dependencies: - copy-descriptor "^0.1.0" - define-property "^0.2.5" - kind-of "^3.0.3" - -object-inspect@^1.7.0: - version "1.7.0" - resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz" - integrity sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw== - -object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz" - integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== - -object-visit@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz" - integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= - dependencies: - isobject "^3.0.0" - -object.assign@^4.1.0: - version "4.1.0" - resolved "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz" - integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w== - dependencies: - define-properties "^1.1.2" - function-bind "^1.1.1" - has-symbols "^1.0.0" - object-keys "^1.0.11" - -object.getownpropertydescriptors@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz" - integrity sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg== - dependencies: - define-properties "^1.1.3" - es-abstract "^1.17.0-next.1" - -object.pick@^1.3.0: - version "1.3.0" - resolved "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz" - integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= - dependencies: - isobject "^3.0.1" - -object.values@^1.1.0: - version "1.1.1" - resolved "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz" - integrity sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA== - dependencies: - define-properties "^1.1.3" - es-abstract "^1.17.0-next.1" - function-bind "^1.1.1" - has "^1.0.3" - -omggif@^1.0.9: - version "1.0.10" - resolved "https://registry.npmjs.org/omggif/-/omggif-1.0.10.tgz" - integrity sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw== - -on-finished@~2.3.0: - version "2.3.0" - resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz" - integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= - dependencies: - ee-first "1.1.1" - -once@^1.3.0, once@^1.3.1, once@^1.4.0: - version "1.4.0" - resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= - dependencies: - wrappy "1" - -onetime@^5.1.0: - version "5.1.0" - resolved "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz" - integrity sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q== - dependencies: - mimic-fn "^2.1.0" - -opener@^1.5.1: - version "1.5.1" - resolved "https://registry.npmjs.org/opener/-/opener-1.5.1.tgz" - integrity sha512-goYSy5c2UXE4Ra1xixabeVh1guIX/ZV/YokJksb6q2lubWu6UbvPQ20p542/sFIll1nl8JnCyK9oBaOcCWXwvA== - -optionator@^0.9.1: - version "0.9.1" - resolved "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz" - integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== - dependencies: - deep-is "^0.1.3" - fast-levenshtein "^2.0.6" - levn "^0.4.1" - prelude-ls "^1.2.1" - type-check "^0.4.0" - word-wrap "^1.2.3" - -os-browserify@^0.3.0: - version "0.3.0" - resolved "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz" - integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc= - -os-filter-obj@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/os-filter-obj/-/os-filter-obj-2.0.0.tgz" - integrity sha512-uksVLsqG3pVdzzPvmAHpBK0wKxYItuzZr7SziusRPoz67tGV8rL1szZ6IdeUrbqLjGDwApBtN29eEE3IqGHOjg== - dependencies: - arch "^2.1.0" - -os-homedir@^1.0.0: - version "1.0.2" - resolved "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz" - integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= - -os-locale@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz" - integrity sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q== - dependencies: - execa "^1.0.0" - lcid "^2.0.0" - mem "^4.0.0" - -os-tmpdir@^1.0.1, os-tmpdir@~1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" - integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= - -ow@^0.13.2: - version "0.13.2" - resolved "https://registry.npmjs.org/ow/-/ow-0.13.2.tgz" - integrity sha512-9wvr+q+ZTDRvXDjL6eDOdFe5WUl/wa5sntf9kAolxqSpkBqaIObwLgFCGXSJASFw+YciXnOVtDWpxXa9cqV94A== - dependencies: - type-fest "^0.5.1" - -p-cancelable@^0.3.0: - version "0.3.0" - resolved "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.3.0.tgz" - integrity sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw== - -p-cancelable@^0.4.0: - version "0.4.1" - resolved "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.4.1.tgz" - integrity sha512-HNa1A8LvB1kie7cERyy21VNeHb2CWJJYqyyC2o3klWFfMGlFmWv2Z7sFgZH8ZiaYL95ydToKTFVXgMV/Os0bBQ== - -p-defer@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz" - integrity sha1-n26xgvbJqozXQwBKfU+WsZaw+ww= - -p-event@^1.0.0: - version "1.3.0" - resolved "https://registry.npmjs.org/p-event/-/p-event-1.3.0.tgz" - integrity sha1-jmtPT2XHK8W2/ii3XtqHT5akoIU= - dependencies: - p-timeout "^1.1.1" - -p-event@^2.1.0: - version "2.3.1" - resolved "https://registry.npmjs.org/p-event/-/p-event-2.3.1.tgz" - integrity sha512-NQCqOFhbpVTMX4qMe8PF8lbGtzZ+LCiN7pcNrb/413Na7+TRoe1xkKUzuWa/YEJdGQ0FvKtj35EEbDoVPO2kbA== - dependencies: - p-timeout "^2.0.1" - -p-finally@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz" - integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= - -p-is-promise@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz" - integrity sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4= - -p-is-promise@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz" - integrity sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg== - -p-limit@^1.1.0: - version "1.3.0" - resolved "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz" - integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== - dependencies: - p-try "^1.0.0" - -p-limit@^2.0.0, p-limit@^2.2.0: - version "2.3.0" - resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" - integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== - dependencies: - p-try "^2.0.0" - -p-locate@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz" - integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= - dependencies: - p-limit "^1.1.0" - -p-locate@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz" - integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== - dependencies: - p-limit "^2.0.0" - -p-locate@^4.1.0: - version "4.1.0" - resolved "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz" - integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== - dependencies: - p-limit "^2.2.0" - -p-map-series@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/p-map-series/-/p-map-series-1.0.0.tgz" - integrity sha1-v5j+V1cFZYqeE1G++4WuTB8Hvco= - dependencies: - p-reduce "^1.0.0" - -p-reduce@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/p-reduce/-/p-reduce-1.0.0.tgz" - integrity sha1-GMKw3ZNqRpClKfgjH1ig/bakffo= - -p-timeout@^1.1.1: - version "1.2.1" - resolved "https://registry.npmjs.org/p-timeout/-/p-timeout-1.2.1.tgz" - integrity sha1-XrOzU7f86Z8QGhA4iAuwVOu+o4Y= - dependencies: - p-finally "^1.0.0" - -p-timeout@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/p-timeout/-/p-timeout-2.0.1.tgz" - integrity sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA== - dependencies: - p-finally "^1.0.0" - -p-try@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz" - integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= - -p-try@^2.0.0: - version "2.2.0" - resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz" - integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== - -pako@^1.0.5, pako@~1.0.5: - version "1.0.11" - resolved "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz" - integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== - -parallel-transform@^1.1.0: - version "1.2.0" - resolved "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz" - integrity sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg== - dependencies: - cyclist "^1.0.1" - inherits "^2.0.3" - readable-stream "^2.1.5" - -param-case@2.1.x: - version "2.1.1" - resolved "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz" - integrity sha1-35T9jPZTHs915r75oIWPvHK+Ikc= - dependencies: - no-case "^2.2.0" - -parent-module@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz" - integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== - dependencies: - callsites "^3.0.0" - -parse-asn1@^5.0.0, parse-asn1@^5.1.5: - version "5.1.5" - resolved "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz" - integrity sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ== - dependencies: - asn1.js "^4.0.0" - browserify-aes "^1.0.0" - create-hash "^1.1.0" - evp_bytestokey "^1.0.0" - pbkdf2 "^3.0.3" - safe-buffer "^5.1.1" - -parse-bmfont-ascii@^1.0.3: - version "1.0.6" - resolved "https://registry.npmjs.org/parse-bmfont-ascii/-/parse-bmfont-ascii-1.0.6.tgz" - integrity sha1-Eaw8P/WPfCAgqyJ2kHkQjU36AoU= - -parse-bmfont-binary@^1.0.5: - version "1.0.6" - resolved "https://registry.npmjs.org/parse-bmfont-binary/-/parse-bmfont-binary-1.0.6.tgz" - integrity sha1-0Di0dtPp3Z2x4RoLDlOiJ5K2kAY= - -parse-bmfont-xml@^1.1.4: - version "1.1.4" - resolved "https://registry.npmjs.org/parse-bmfont-xml/-/parse-bmfont-xml-1.1.4.tgz" - integrity sha512-bjnliEOmGv3y1aMEfREMBJ9tfL3WR0i0CKPj61DnSLaoxWR3nLrsQrEbCId/8rF4NyRF0cCqisSVXyQYWM+mCQ== - dependencies: - xml-parse-from-string "^1.0.0" - xml2js "^0.4.5" - -parse-headers@^2.0.0: - version "2.0.3" - resolved "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.3.tgz" - integrity sha512-QhhZ+DCCit2Coi2vmAKbq5RGTRcQUOE2+REgv8vdyu7MnYx2eZztegqtTx99TZ86GTIwqiy3+4nQTWZ2tgmdCA== - -parse-json@^2.2.0: - version "2.2.0" - resolved "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz" - integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck= - dependencies: - error-ex "^1.2.0" - -parse-json@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz" - integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= - dependencies: - error-ex "^1.3.1" - json-parse-better-errors "^1.0.1" - -parse-passwd@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz" - integrity sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY= - -parseurl@~1.3.3: - version "1.3.3" - resolved "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz" - integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== - -pascalcase@^0.1.1: - version "0.1.1" - resolved "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz" - integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= - -path-browserify@0.0.1: - version "0.0.1" - resolved "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz" - integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ== - -path-dirname@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" - integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA= - -path-exists@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz" - integrity sha1-D+tsZPD8UY2adU3V77YscCJ2H0s= - dependencies: - pinkie-promise "^2.0.0" - -path-exists@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz" - integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= - -path-exists@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz" - integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== - -path-is-absolute@^1.0.0, path-is-absolute@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" - integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= - -path-key@^2.0.0, path-key@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz" - integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= - -path-key@^3.1.0: - version "3.1.1" - resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" - integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== - -path-parse@^1.0.6: - version "1.0.6" - resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz" - integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== - -path-to-regexp@0.1.7: - version "0.1.7" - resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz" - integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= - -path-type@^1.0.0: - version "1.1.0" - resolved "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz" - integrity sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE= - dependencies: - graceful-fs "^4.1.2" - pify "^2.0.0" - pinkie-promise "^2.0.0" - -pbkdf2@^3.0.3: - version "3.1.1" - resolved "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.1.tgz" - integrity sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg== - dependencies: - create-hash "^1.1.2" - create-hmac "^1.1.4" - ripemd160 "^2.0.1" - safe-buffer "^5.0.1" - sha.js "^2.4.8" - -pend@~1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz" - integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= - -phin@^2.9.1: - version "2.9.3" - resolved "https://registry.npmjs.org/phin/-/phin-2.9.3.tgz" - integrity sha512-CzFr90qM24ju5f88quFC/6qohjC144rehe5n6DH900lgXmUe86+xCKc10ev56gRKC4/BkHUoG4uSiQgBiIXwDA== - -phonegap-plugin-mobile-accessibility@^1.0.5: - version "1.0.5" - resolved "https://registry.npmjs.org/phonegap-plugin-mobile-accessibility/-/phonegap-plugin-mobile-accessibility-1.0.5.tgz" - integrity sha1-lah1TRJ1CLxuGuJZpTznZYNurAM= - -picomatch@^2.0.4, picomatch@^2.2.1: - version "2.2.2" - resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz" - integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== - -pify@^2.0.0, pify@^2.2.0, pify@^2.3.0: - version "2.3.0" - resolved "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz" - integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= - -pify@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz" - integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= - -pify@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz" - integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== - -pinkie-promise@^2.0.0: - version "2.0.1" - resolved "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz" - integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= - dependencies: - pinkie "^2.0.0" - -pinkie@^2.0.0: - version "2.0.4" - resolved "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz" - integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= - -pixelmatch@^4.0.2: - version "4.0.2" - resolved "https://registry.npmjs.org/pixelmatch/-/pixelmatch-4.0.2.tgz" - integrity sha1-j0fc7FARtHe2fbA8JDvB8wheiFQ= - dependencies: - pngjs "^3.0.0" - -pkg-dir@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz" - integrity sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw== - dependencies: - find-up "^3.0.0" - -pkg-up@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz" - integrity sha1-yBmscoBZpGHKscOImivjxJoATX8= - dependencies: - find-up "^2.1.0" - -pngjs@^3.0.0, pngjs@^3.3.3: - version "3.4.0" - resolved "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz" - integrity sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w== - -pngquant-bin@^5.0.0: - version "5.0.2" - resolved "https://registry.npmjs.org/pngquant-bin/-/pngquant-bin-5.0.2.tgz" - integrity sha512-OLdT+4JZx5BqE1CFJkrvomYV0aSsv6x2Bba+aWaVc0PMfWlE+ZByNKYAdKeIqsM4uvW1HOSEHnf8KcOnykPNxA== - dependencies: - bin-build "^3.0.0" - bin-wrapper "^4.0.1" - execa "^0.10.0" - logalot "^2.0.0" - -posix-character-classes@^0.1.0: - version "0.1.1" - resolved "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz" - integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= - -postcss-assets@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/postcss-assets/-/postcss-assets-5.0.0.tgz" - integrity sha1-9yHQfTOWBftYQU6fac8FQBxU5wk= - dependencies: - assets "^3.0.0" - bluebird "^3.5.0" - postcss "^6.0.10" - postcss-functions "^3.0.0" - -postcss-attribute-case-insensitive@^4.0.1: - version "4.0.2" - resolved "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-4.0.2.tgz" - integrity sha512-clkFxk/9pcdb4Vkn0hAHq3YnxBQ2p0CGD1dy24jN+reBck+EWxMbxSUqN4Yj7t0w8csl87K6p0gxBe1utkJsYA== - dependencies: - postcss "^7.0.2" - postcss-selector-parser "^6.0.2" - -postcss-calc@^7.0.1: - version "7.0.2" - resolved "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.2.tgz" - integrity sha512-rofZFHUg6ZIrvRwPeFktv06GdbDYLcGqh9EwiMutZg+a0oePCCw1zHOEiji6LCpyRcjTREtPASuUqeAvYlEVvQ== - dependencies: - postcss "^7.0.27" - postcss-selector-parser "^6.0.2" - postcss-value-parser "^4.0.2" - -postcss-color-functional-notation@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-2.0.1.tgz" - integrity sha512-ZBARCypjEDofW4P6IdPVTLhDNXPRn8T2s1zHbZidW6rPaaZvcnCS2soYFIQJrMZSxiePJ2XIYTlcb2ztr/eT2g== - dependencies: - postcss "^7.0.2" - postcss-values-parser "^2.0.0" - -postcss-color-gray@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/postcss-color-gray/-/postcss-color-gray-5.0.0.tgz" - integrity sha512-q6BuRnAGKM/ZRpfDascZlIZPjvwsRye7UDNalqVz3s7GDxMtqPY6+Q871liNxsonUw8oC61OG+PSaysYpl1bnw== - dependencies: - "@csstools/convert-colors" "^1.4.0" - postcss "^7.0.5" - postcss-values-parser "^2.0.0" - -postcss-color-hex-alpha@^5.0.3: - version "5.0.3" - resolved "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-5.0.3.tgz" - integrity sha512-PF4GDel8q3kkreVXKLAGNpHKilXsZ6xuu+mOQMHWHLPNyjiUBOr75sp5ZKJfmv1MCus5/DWUGcK9hm6qHEnXYw== - dependencies: - postcss "^7.0.14" - postcss-values-parser "^2.0.1" - -postcss-color-mod-function@^3.0.3: - version "3.0.3" - resolved "https://registry.npmjs.org/postcss-color-mod-function/-/postcss-color-mod-function-3.0.3.tgz" - integrity sha512-YP4VG+xufxaVtzV6ZmhEtc+/aTXH3d0JLpnYfxqTvwZPbJhWqp8bSY3nfNzNRFLgB4XSaBA82OE4VjOOKpCdVQ== - dependencies: - "@csstools/convert-colors" "^1.4.0" - postcss "^7.0.2" - postcss-values-parser "^2.0.0" - -postcss-color-rebeccapurple@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-4.0.1.tgz" - integrity sha512-aAe3OhkS6qJXBbqzvZth2Au4V3KieR5sRQ4ptb2b2O8wgvB3SJBsdG+jsn2BZbbwekDG8nTfcCNKcSfe/lEy8g== - dependencies: - postcss "^7.0.2" - postcss-values-parser "^2.0.0" - -postcss-colormin@^4.0.3: - version "4.0.3" - resolved "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-4.0.3.tgz" - integrity sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw== - dependencies: - browserslist "^4.0.0" - color "^3.0.0" - has "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-convert-values@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz" - integrity sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ== - dependencies: - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-custom-media@^7.0.8: - version "7.0.8" - resolved "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-7.0.8.tgz" - integrity sha512-c9s5iX0Ge15o00HKbuRuTqNndsJUbaXdiNsksnVH8H4gdc+zbLzr/UasOwNG6CTDpLFekVY4672eWdiiWu2GUg== - dependencies: - postcss "^7.0.14" - -postcss-custom-properties@^8.0.11: - version "8.0.11" - resolved "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-8.0.11.tgz" - integrity sha512-nm+o0eLdYqdnJ5abAJeXp4CEU1c1k+eB2yMCvhgzsds/e0umabFrN6HoTy/8Q4K5ilxERdl/JD1LO5ANoYBeMA== - dependencies: - postcss "^7.0.17" - postcss-values-parser "^2.0.1" - -postcss-custom-selectors@^5.1.2: - version "5.1.2" - resolved "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-5.1.2.tgz" - integrity sha512-DSGDhqinCqXqlS4R7KGxL1OSycd1lydugJ1ky4iRXPHdBRiozyMHrdu0H3o7qNOCiZwySZTUI5MV0T8QhCLu+w== - dependencies: - postcss "^7.0.2" - postcss-selector-parser "^5.0.0-rc.3" - -postcss-dir-pseudo-class@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-5.0.0.tgz" - integrity sha512-3pm4oq8HYWMZePJY+5ANriPs3P07q+LW6FAdTlkFH2XqDdP4HeeJYMOzn0HYLhRSjBO3fhiqSwwU9xEULSrPgw== - dependencies: - postcss "^7.0.2" - postcss-selector-parser "^5.0.0-rc.3" - -postcss-discard-comments@^4.0.2: - version "4.0.2" - resolved "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz" - integrity sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg== - dependencies: - postcss "^7.0.0" - -postcss-discard-duplicates@^4.0.2: - version "4.0.2" - resolved "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz" - integrity sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ== - dependencies: - postcss "^7.0.0" - -postcss-discard-empty@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz" - integrity sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w== - dependencies: - postcss "^7.0.0" - -postcss-discard-overridden@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz" - integrity sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg== - dependencies: - postcss "^7.0.0" - -postcss-discard-unused@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-4.0.1.tgz" - integrity sha512-/3vq4LU0bLH2Lj4NYN7BTf2caly0flUB7Xtrk9a5K3yLuXMkHMqMO/x3sDq8W2b1eQFSCyY0IVz2L+0HP8kUUA== - dependencies: - postcss "^7.0.0" - postcss-selector-parser "^3.0.0" - uniqs "^2.0.0" - -postcss-double-position-gradients@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-1.0.0.tgz" - integrity sha512-G+nV8EnQq25fOI8CH/B6krEohGWnF5+3A6H/+JEpOncu5dCnkS1QQ6+ct3Jkaepw1NGVqqOZH6lqrm244mCftA== - dependencies: - postcss "^7.0.5" - postcss-values-parser "^2.0.0" - -postcss-env-function@^2.0.2: - version "2.0.2" - resolved "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-2.0.2.tgz" - integrity sha512-rwac4BuZlITeUbiBq60h/xbLzXY43qOsIErngWa4l7Mt+RaSkT7QBjXVGTcBHupykkblHMDrBFh30zchYPaOUw== - dependencies: - postcss "^7.0.2" - postcss-values-parser "^2.0.0" - -postcss-focus-visible@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-4.0.0.tgz" - integrity sha512-Z5CkWBw0+idJHSV6+Bgf2peDOFf/x4o+vX/pwcNYrWpXFrSfTkQ3JQ1ojrq9yS+upnAlNRHeg8uEwFTgorjI8g== - dependencies: - postcss "^7.0.2" - -postcss-focus-within@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-3.0.0.tgz" - integrity sha512-W0APui8jQeBKbCGZudW37EeMCjDeVxKgiYfIIEo8Bdh5SpB9sxds/Iq8SEuzS0Q4YFOlG7EPFulbbxujpkrV2w== - dependencies: - postcss "^7.0.2" - -postcss-font-variant@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-4.0.0.tgz" - integrity sha512-M8BFYKOvCrI2aITzDad7kWuXXTm0YhGdP9Q8HanmN4EF1Hmcgs1KK5rSHylt/lUJe8yLxiSwWAHdScoEiIxztg== - dependencies: - postcss "^7.0.2" - -postcss-functions@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/postcss-functions/-/postcss-functions-3.0.0.tgz" - integrity sha1-DpTQFERwCkgd4g3k1V+yZAVkJQ4= - dependencies: - glob "^7.1.2" - object-assign "^4.1.1" - postcss "^6.0.9" - postcss-value-parser "^3.3.0" - -postcss-gap-properties@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-2.0.0.tgz" - integrity sha512-QZSqDaMgXCHuHTEzMsS2KfVDOq7ZFiknSpkrPJY6jmxbugUPTuSzs/vuE5I3zv0WAS+3vhrlqhijiprnuQfzmg== - dependencies: - postcss "^7.0.2" - -postcss-image-set-function@^3.0.1: - version "3.0.1" - resolved "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-3.0.1.tgz" - integrity sha512-oPTcFFip5LZy8Y/whto91L9xdRHCWEMs3e1MdJxhgt4jy2WYXfhkng59fH5qLXSCPN8k4n94p1Czrfe5IOkKUw== - dependencies: - postcss "^7.0.2" - postcss-values-parser "^2.0.0" - -postcss-initial@^3.0.0: - version "3.0.2" - resolved "https://registry.npmjs.org/postcss-initial/-/postcss-initial-3.0.2.tgz" - integrity sha512-ugA2wKonC0xeNHgirR4D3VWHs2JcU08WAi1KFLVcnb7IN89phID6Qtg2RIctWbnvp1TM2BOmDtX8GGLCKdR8YA== - dependencies: - lodash.template "^4.5.0" - postcss "^7.0.2" - -postcss-lab-function@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-2.0.1.tgz" - integrity sha512-whLy1IeZKY+3fYdqQFuDBf8Auw+qFuVnChWjmxm/UhHWqNHZx+B99EwxTvGYmUBqe3Fjxs4L1BoZTJmPu6usVg== - dependencies: - "@csstools/convert-colors" "^1.4.0" - postcss "^7.0.2" - postcss-values-parser "^2.0.0" - -postcss-logical@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/postcss-logical/-/postcss-logical-3.0.0.tgz" - integrity sha512-1SUKdJc2vuMOmeItqGuNaC+N8MzBWFWEkAnRnLpFYj1tGGa7NqyVBujfRtgNa2gXR+6RkGUiB2O5Vmh7E2RmiA== - dependencies: - postcss "^7.0.2" - -postcss-media-minmax@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-4.0.0.tgz" - integrity sha512-fo9moya6qyxsjbFAYl97qKO9gyre3qvbMnkOZeZwlsW6XYFsvs2DMGDlchVLfAd8LHPZDxivu/+qW2SMQeTHBw== - dependencies: - postcss "^7.0.2" - -postcss-merge-idents@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-4.0.1.tgz" - integrity sha512-43S/VNdF6II0NZ31YxcvNYq4gfURlPAAsJW/z84avBXQCaP4I4qRHUH18slW/SOlJbcxxCobflPNUApYDddS7A== - dependencies: - cssnano-util-same-parent "^4.0.0" - has "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-merge-longhand@^4.0.11: - version "4.0.11" - resolved "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz" - integrity sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw== - dependencies: - css-color-names "0.0.4" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - stylehacks "^4.0.0" - -postcss-merge-rules@^4.0.3: - version "4.0.3" - resolved "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz" - integrity sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ== - dependencies: - browserslist "^4.0.0" - caniuse-api "^3.0.0" - cssnano-util-same-parent "^4.0.0" - postcss "^7.0.0" - postcss-selector-parser "^3.0.0" - vendors "^1.0.0" - -postcss-minify-font-values@^4.0.2: - version "4.0.2" - resolved "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz" - integrity sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg== - dependencies: - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-minify-gradients@^4.0.2: - version "4.0.2" - resolved "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz" - integrity sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q== - dependencies: - cssnano-util-get-arguments "^4.0.0" - is-color-stop "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-minify-params@^4.0.2: - version "4.0.2" - resolved "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz" - integrity sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg== - dependencies: - alphanum-sort "^1.0.0" - browserslist "^4.0.0" - cssnano-util-get-arguments "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - uniqs "^2.0.0" - -postcss-minify-selectors@^4.0.2: - version "4.0.2" - resolved "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz" - integrity sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g== - dependencies: - alphanum-sort "^1.0.0" - has "^1.0.0" - postcss "^7.0.0" - postcss-selector-parser "^3.0.0" - -postcss-nesting@^7.0.0: - version "7.0.1" - resolved "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-7.0.1.tgz" - integrity sha512-FrorPb0H3nuVq0Sff7W2rnc3SmIcruVC6YwpcS+k687VxyxO33iE1amna7wHuRVzM8vfiYofXSBHNAZ3QhLvYg== - dependencies: - postcss "^7.0.2" - -postcss-normalize-charset@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz" - integrity sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g== - dependencies: - postcss "^7.0.0" - -postcss-normalize-display-values@^4.0.2: - version "4.0.2" - resolved "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz" - integrity sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ== - dependencies: - cssnano-util-get-match "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-positions@^4.0.2: - version "4.0.2" - resolved "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz" - integrity sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA== - dependencies: - cssnano-util-get-arguments "^4.0.0" - has "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-repeat-style@^4.0.2: - version "4.0.2" - resolved "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz" - integrity sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q== - dependencies: - cssnano-util-get-arguments "^4.0.0" - cssnano-util-get-match "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-string@^4.0.2: - version "4.0.2" - resolved "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz" - integrity sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA== - dependencies: - has "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-timing-functions@^4.0.2: - version "4.0.2" - resolved "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz" - integrity sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A== - dependencies: - cssnano-util-get-match "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-unicode@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz" - integrity sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg== - dependencies: - browserslist "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-url@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz" - integrity sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA== - dependencies: - is-absolute-url "^2.0.0" - normalize-url "^3.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-whitespace@^4.0.2: - version "4.0.2" - resolved "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz" - integrity sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA== - dependencies: - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-ordered-values@^4.1.2: - version "4.1.2" - resolved "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz" - integrity sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw== - dependencies: - cssnano-util-get-arguments "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-overflow-shorthand@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-2.0.0.tgz" - integrity sha512-aK0fHc9CBNx8jbzMYhshZcEv8LtYnBIRYQD5i7w/K/wS9c2+0NSR6B3OVMu5y0hBHYLcMGjfU+dmWYNKH0I85g== - dependencies: - postcss "^7.0.2" - -postcss-page-break@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-2.0.0.tgz" - integrity sha512-tkpTSrLpfLfD9HvgOlJuigLuk39wVTbbd8RKcy8/ugV2bNBUW3xU+AIqyxhDrQr1VUj1RmyJrBn1YWrqUm9zAQ== - dependencies: - postcss "^7.0.2" - -postcss-place@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/postcss-place/-/postcss-place-4.0.1.tgz" - integrity sha512-Zb6byCSLkgRKLODj/5mQugyuj9bvAAw9LqJJjgwz5cYryGeXfFZfSXoP1UfveccFmeq0b/2xxwcTEVScnqGxBg== - dependencies: - postcss "^7.0.2" - postcss-values-parser "^2.0.0" - -postcss-preset-env@^6.5.0: - version "6.7.0" - resolved "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-6.7.0.tgz" - integrity sha512-eU4/K5xzSFwUFJ8hTdTQzo2RBLbDVt83QZrAvI07TULOkmyQlnYlpwep+2yIK+K+0KlZO4BvFcleOCCcUtwchg== - dependencies: - autoprefixer "^9.6.1" - browserslist "^4.6.4" - caniuse-lite "^1.0.30000981" - css-blank-pseudo "^0.1.4" - css-has-pseudo "^0.10.0" - css-prefers-color-scheme "^3.1.1" - cssdb "^4.4.0" - postcss "^7.0.17" - postcss-attribute-case-insensitive "^4.0.1" - postcss-color-functional-notation "^2.0.1" - postcss-color-gray "^5.0.0" - postcss-color-hex-alpha "^5.0.3" - postcss-color-mod-function "^3.0.3" - postcss-color-rebeccapurple "^4.0.1" - postcss-custom-media "^7.0.8" - postcss-custom-properties "^8.0.11" - postcss-custom-selectors "^5.1.2" - postcss-dir-pseudo-class "^5.0.0" - postcss-double-position-gradients "^1.0.0" - postcss-env-function "^2.0.2" - postcss-focus-visible "^4.0.0" - postcss-focus-within "^3.0.0" - postcss-font-variant "^4.0.0" - postcss-gap-properties "^2.0.0" - postcss-image-set-function "^3.0.1" - postcss-initial "^3.0.0" - postcss-lab-function "^2.0.1" - postcss-logical "^3.0.0" - postcss-media-minmax "^4.0.0" - postcss-nesting "^7.0.0" - postcss-overflow-shorthand "^2.0.0" - postcss-page-break "^2.0.0" - postcss-place "^4.0.1" - postcss-pseudo-class-any-link "^6.0.0" - postcss-replace-overflow-wrap "^3.0.0" - postcss-selector-matches "^4.0.0" - postcss-selector-not "^4.0.0" - -postcss-pseudo-class-any-link@^6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-6.0.0.tgz" - integrity sha512-lgXW9sYJdLqtmw23otOzrtbDXofUdfYzNm4PIpNE322/swES3VU9XlXHeJS46zT2onFO7V1QFdD4Q9LiZj8mew== - dependencies: - postcss "^7.0.2" - postcss-selector-parser "^5.0.0-rc.3" - -postcss-reduce-idents@^4.0.2: - version "4.0.2" - resolved "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-4.0.2.tgz" - integrity sha512-Tz70Ri10TclPoCtFfftjFVddx3fZGUkr0dEDbIEfbYhFUOFQZZ77TEqRrU0e6TvAvF+Wa5VVzYTpFpq0uwFFzw== - dependencies: - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-reduce-initial@^4.0.3: - version "4.0.3" - resolved "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz" - integrity sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA== - dependencies: - browserslist "^4.0.0" - caniuse-api "^3.0.0" - has "^1.0.0" - postcss "^7.0.0" - -postcss-reduce-transforms@^4.0.2: - version "4.0.2" - resolved "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz" - integrity sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg== - dependencies: - cssnano-util-get-match "^4.0.0" - has "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-replace-overflow-wrap@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-3.0.0.tgz" - integrity sha512-2T5hcEHArDT6X9+9dVSPQdo7QHzG4XKclFT8rU5TzJPDN7RIRTbO9c4drUISOVemLj03aezStHCR2AIcr8XLpw== - dependencies: - postcss "^7.0.2" - -postcss-round-subpixels@^1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/postcss-round-subpixels/-/postcss-round-subpixels-1.2.0.tgz" - integrity sha1-4h1qxZUuGF+b3ACLlPAE/lCdChE= - dependencies: - postcss "^5.0.2" - postcss-value-parser "^3.1.2" - -postcss-selector-matches@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/postcss-selector-matches/-/postcss-selector-matches-4.0.0.tgz" - integrity sha512-LgsHwQR/EsRYSqlwdGzeaPKVT0Ml7LAT6E75T8W8xLJY62CE4S/l03BWIt3jT8Taq22kXP08s2SfTSzaraoPww== - dependencies: - balanced-match "^1.0.0" - postcss "^7.0.2" - -postcss-selector-not@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-4.0.0.tgz" - integrity sha512-W+bkBZRhqJaYN8XAnbbZPLWMvZD1wKTu0UxtFKdhtGjWYmxhkUneoeOhRJKdAE5V7ZTlnbHfCR+6bNwK9e1dTQ== - dependencies: - balanced-match "^1.0.0" - postcss "^7.0.2" - -postcss-selector-parser@^3.0.0: - version "3.1.2" - resolved "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz" - integrity sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA== - dependencies: - dot-prop "^5.2.0" - indexes-of "^1.0.1" - uniq "^1.0.1" - -postcss-selector-parser@^5.0.0, postcss-selector-parser@^5.0.0-rc.3, postcss-selector-parser@^5.0.0-rc.4: - version "5.0.0" - resolved "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz" - integrity sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ== - dependencies: - cssesc "^2.0.0" - indexes-of "^1.0.1" - uniq "^1.0.1" - -postcss-selector-parser@^6.0.2: - version "6.0.2" - resolved "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz" - integrity sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg== - dependencies: - cssesc "^3.0.0" - indexes-of "^1.0.1" - uniq "^1.0.1" - -postcss-svgo@^4.0.2: - version "4.0.2" - resolved "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-4.0.2.tgz" - integrity sha512-C6wyjo3VwFm0QgBy+Fu7gCYOkCmgmClghO+pjcxvrcBKtiKt0uCF+hvbMO1fyv5BMImRK90SMb+dwUnfbGd+jw== - dependencies: - is-svg "^3.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - svgo "^1.0.0" - -postcss-unique-selectors@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz" - integrity sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg== - dependencies: - alphanum-sort "^1.0.0" - postcss "^7.0.0" - uniqs "^2.0.0" - -postcss-unprefix@^2.1.3: - version "2.1.4" - resolved "https://registry.npmjs.org/postcss-unprefix/-/postcss-unprefix-2.1.4.tgz" - integrity sha512-s+muBiGIMx3RvgPTtPBnSrfvIBHJ2Zx16QZf/VDB/sAxdYP6FIzci8d1gLh0+9psu5W6zVtCbU5micNt6Zh3cg== - dependencies: - autoprefixer "^9.4.3" - known-css-properties "^0.11.0" - normalize-range "^0.1.2" - postcss-selector-parser "^5.0.0" - postcss-value-parser "^3.3.1" - pseudo-classes "^1.0.0" - pseudo-elements "^1.1.0" - -postcss-value-parser@^3.0.0, postcss-value-parser@^3.1.2, postcss-value-parser@^3.3.0, postcss-value-parser@^3.3.1: - version "3.3.1" - resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz" - integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ== - -postcss-value-parser@^4.0.2, postcss-value-parser@^4.0.3: - version "4.0.3" - resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.0.3.tgz" - integrity sha512-N7h4pG+Nnu5BEIzyeaaIYWs0LI5XC40OrRh5L60z0QjFsqGWcHcbkBvpe1WYpcIS9yQ8sOi/vIPt1ejQCrMVrg== - -postcss-values-parser@^2.0.0, postcss-values-parser@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/postcss-values-parser/-/postcss-values-parser-2.0.1.tgz" - integrity sha512-2tLuBsA6P4rYTNKCXYG/71C7j1pU6pK503suYOmn4xYrQIzW+opD+7FAFNuGSdZC/3Qfy334QbeMu7MEb8gOxg== - dependencies: - flatten "^1.0.2" - indexes-of "^1.0.1" - uniq "^1.0.1" - -postcss-zindex@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-4.0.1.tgz" - integrity sha512-d/8BlQcUdEugZNRM9AdCA2V4fqREUtn/wcixLN3L6ITgc2P/FMcVVYz8QZkhItWT9NB5qr8wuN2dJCE4/+dlrA== - dependencies: - has "^1.0.0" - postcss "^7.0.0" - uniqs "^2.0.0" - -postcss@>=5.0.0: - version "8.2.6" - resolved "https://registry.npmjs.org/postcss/-/postcss-8.2.6.tgz" - integrity sha512-xpB8qYxgPuly166AGlpRjUdEYtmOWx2iCwGmrv4vqZL9YPVviDVPZPRXxnXr6xPZOdxQ9lp3ZBFCRgWJ7LE3Sg== - dependencies: - colorette "^1.2.1" - nanoid "^3.1.20" - source-map "^0.6.1" - -postcss@^5.0.2: - version "5.2.18" - resolved "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz" - integrity sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg== - dependencies: - chalk "^1.1.3" - js-base64 "^2.1.9" - source-map "^0.5.6" - supports-color "^3.2.3" - -postcss@^6.0.10, postcss@^6.0.9: - version "6.0.23" - resolved "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz" - integrity sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag== - dependencies: - chalk "^2.4.1" - source-map "^0.6.1" - supports-color "^5.4.0" - -postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.17, postcss@^7.0.2, postcss@^7.0.27, postcss@^7.0.5, postcss@^7.0.6: - version "7.0.27" - resolved "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz" - integrity sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ== - dependencies: - chalk "^2.4.2" - source-map "^0.6.1" - supports-color "^6.1.0" - -prelude-ls@^1.2.1: - version "1.2.1" - resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz" - integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== - -prepend-http@^1.0.1: - version "1.0.4" - resolved "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz" - integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw= - -prepend-http@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz" - integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= - -prettier-linter-helpers@^1.0.0: - 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== - dependencies: - fast-diff "^1.1.2" - -prettier@^2.0.4: - version "2.0.4" - resolved "https://registry.npmjs.org/prettier/-/prettier-2.0.4.tgz" - integrity sha512-SVJIQ51spzFDvh4fIbCLvciiDMCrRhlN3mbZvv/+ycjvmF5E73bKdGfU8QDLNmjYJf+lsGnDBC4UUnvTe5OO0w== - -private@^0.1.8, private@~0.1.5: - version "0.1.8" - resolved "https://registry.npmjs.org/private/-/private-0.1.8.tgz" - integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg== - -process-nextick-args@~2.0.0: - version "2.0.1" - resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz" - integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== - -process@^0.11.10: - version "0.11.10" - resolved "https://registry.npmjs.org/process/-/process-0.11.10.tgz" - integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= - -process@~0.5.1: - version "0.5.2" - resolved "https://registry.npmjs.org/process/-/process-0.5.2.tgz" - integrity sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8= - -progress@^2.0.0: - version "2.0.3" - resolved "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz" - integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== - -promise-inflight@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz" - integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM= - -promise-polyfill@^8.1.0: - version "8.1.3" - resolved "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-8.1.3.tgz" - integrity sha512-MG5r82wBzh7pSKDRa9y+vllNHz3e3d4CNj1PQE4BQYxLme0gKYYBm9YENq+UkEikyZ0XbiGWxYlVw3Rl9O/U8g== - -proto-list@~1.2.1: - version "1.2.4" - resolved "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz" - integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk= - -proxy-addr@~2.0.5: - version "2.0.6" - resolved "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz" - integrity sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw== - dependencies: - forwarded "~0.1.2" - ipaddr.js "1.9.1" - -prr@~1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz" - integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY= - -pseudo-classes@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/pseudo-classes/-/pseudo-classes-1.0.0.tgz" - integrity sha1-YKabZzlcNv8RnE0chuGYF4Uga5Y= - -pseudo-elements@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/pseudo-elements/-/pseudo-elements-1.1.0.tgz" - integrity sha1-m6bdisPOHz19NtQ1WqPijQg5Hyg= - -pseudomap@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz" - integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= - -public-encrypt@^4.0.0: - version "4.0.3" - resolved "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz" - integrity sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q== - dependencies: - bn.js "^4.1.0" - browserify-rsa "^4.0.0" - create-hash "^1.1.0" - parse-asn1 "^5.0.0" - randombytes "^2.0.1" - safe-buffer "^5.1.2" - -pump@^2.0.0: - version "2.0.1" - resolved "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz" - integrity sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA== - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - -pump@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz" - integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - -pumpify@^1.3.3: - version "1.5.1" - resolved "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz" - integrity sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ== - dependencies: - duplexify "^3.6.0" - inherits "^2.0.3" - pump "^2.0.0" - -punycode@1.3.2: - version "1.3.2" - resolved "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz" - integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= - -punycode@^1.2.4: - version "1.4.1" - resolved "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz" - integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= - -punycode@^2.1.0: - version "2.1.1" - resolved "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz" - integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== - -q@^1.1.2: - version "1.5.1" - resolved "https://registry.npmjs.org/q/-/q-1.5.1.tgz" - integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= - -qs@6.7.0: - version "6.7.0" - resolved "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz" - integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== - -query-string@^5.0.1: - version "5.1.1" - resolved "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz" - integrity sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw== - dependencies: - decode-uri-component "^0.2.0" - object-assign "^4.1.0" - strict-uri-encode "^1.0.0" - -query-string@^6.8.1: - version "6.12.1" - resolved "https://registry.npmjs.org/query-string/-/query-string-6.12.1.tgz" - integrity sha512-OHj+zzfRMyj3rmo/6G8a5Ifvw3AleL/EbcHMD27YA31Q+cO5lfmQxECkImuNVjcskLcvBRVHNAB3w6udMs1eAA== - dependencies: - decode-uri-component "^0.2.0" - split-on-first "^1.0.0" - strict-uri-encode "^2.0.0" - -querystring-es3@^0.2.0: - version "0.2.1" - resolved "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz" - integrity sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM= - -querystring@0.2.0: - version "0.2.0" - resolved "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz" - integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= - -randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: - version "2.1.0" - resolved "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz" - integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== - dependencies: - safe-buffer "^5.1.0" - -randomfill@^1.0.3: - version "1.0.4" - resolved "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz" - integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw== - dependencies: - randombytes "^2.0.5" - safe-buffer "^5.1.0" - -range-parser@~1.2.1: - version "1.2.1" - resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz" - integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== - -raw-body@2.4.0: - version "2.4.0" - resolved "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz" - integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== - dependencies: - bytes "3.1.0" - http-errors "1.7.2" - iconv-lite "0.4.24" - unpipe "1.0.0" - -read-pkg-up@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz" - integrity sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI= - dependencies: - find-up "^1.0.0" - read-pkg "^1.0.0" - -read-pkg@^1.0.0: - version "1.1.0" - resolved "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz" - integrity sha1-9f+qXs0pyzHAR0vKfXVra7KePyg= - dependencies: - load-json-file "^1.0.0" - normalize-package-data "^2.3.2" - path-type "^1.0.0" - -"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.0, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6: - version "2.3.7" - resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz" - integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.1.1" - util-deprecate "~1.0.1" - -readable-stream@^3.6.0: - version "3.6.0" - resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz" - integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== - dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" - -readdirp@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" - integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ== - dependencies: - graceful-fs "^4.1.11" - micromatch "^3.1.10" - readable-stream "^2.0.2" - -readdirp@~3.4.0: - version "3.4.0" - resolved "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz" - integrity sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ== - dependencies: - picomatch "^2.2.1" - -recast@~0.11.12: - version "0.11.23" - resolved "https://registry.npmjs.org/recast/-/recast-0.11.23.tgz" - integrity sha1-RR/TAEqx5N+bTktmN2sqIZEkYtM= - dependencies: - ast-types "0.9.6" - esprima "~3.1.0" - private "~0.1.5" - source-map "~0.5.0" - -redent@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz" - integrity sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94= - dependencies: - indent-string "^2.1.0" - strip-indent "^1.0.1" - -regenerate-unicode-properties@^8.2.0: - version "8.2.0" - resolved "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz" - integrity sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA== - dependencies: - regenerate "^1.4.0" - -regenerate@^1.4.0: - version "1.4.0" - resolved "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz" - integrity sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg== - -regenerator-runtime@^0.11.0: - version "0.11.1" - resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz" - integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== - -regenerator-runtime@^0.13.3, regenerator-runtime@^0.13.4: - version "0.13.5" - resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz" - integrity sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA== - -regenerator-transform@^0.14.2: - version "0.14.4" - resolved "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.4.tgz" - integrity sha512-EaJaKPBI9GvKpvUz2mz4fhx7WPgvwRLY9v3hlNHWmAuJHI13T4nwKnNvm5RWJzEdnI5g5UwtOww+S8IdoUC2bw== - dependencies: - "@babel/runtime" "^7.8.4" - private "^0.1.8" - -regex-not@^1.0.0, regex-not@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz" - integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== - dependencies: - extend-shallow "^3.0.2" - safe-regex "^1.1.0" - -regexpp@^3.0.0, regexpp@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz" - integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q== - -regexpu-core@^4.7.0: - version "4.7.0" - resolved "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.0.tgz" - integrity sha512-TQ4KXRnIn6tz6tjnrXEkD/sshygKH/j5KzK86X8MkeHyZ8qst/LZ89j3X4/8HEIfHANTFIP/AbXakeRhWIl5YQ== - dependencies: - regenerate "^1.4.0" - regenerate-unicode-properties "^8.2.0" - regjsgen "^0.5.1" - regjsparser "^0.6.4" - unicode-match-property-ecmascript "^1.0.4" - unicode-match-property-value-ecmascript "^1.2.0" - -regjsgen@^0.5.1: - version "0.5.1" - resolved "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.1.tgz" - integrity sha512-5qxzGZjDs9w4tzT3TPhCJqWdCc3RLYwy9J2NB0nm5Lz+S273lvWcpjaTGHsT1dc6Hhfq41uSEOw8wBmxrKOuyg== - -regjsparser@^0.6.4: - version "0.6.4" - resolved "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.4.tgz" - integrity sha512-64O87/dPDgfk8/RQqC4gkZoGyyWFIEUTTh80CU6CWuK5vkCGyekIx+oKcEIYtP/RAxSQltCZHCNu/mdd7fqlJw== - dependencies: - jsesc "~0.5.0" - -relateurl@0.2.x: - version "0.2.7" - resolved "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz" - integrity sha1-VNvzd+UUQKypCkzSdGANP/LYiKk= - -remove-trailing-separator@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" - integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= - -repeat-element@^1.1.2: - version "1.1.3" - resolved "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz" - integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g== - -repeat-string@^1.6.1: - version "1.6.1" - resolved "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz" - integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= - -repeating@^2.0.0: - version "2.0.1" - resolved "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz" - integrity sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo= - dependencies: - is-finite "^1.0.0" - -require-directory@^2.1.1: - version "2.1.1" - resolved "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz" - integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= - -require-main-filename@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz" - integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== - -resolve-cwd@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz" - integrity sha1-AKn3OHVW4nA46uIyyqNypqWbZlo= - dependencies: - resolve-from "^3.0.0" - -resolve-dir@^1.0.0, resolve-dir@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz" - integrity sha1-eaQGRMNivoLybv/nOcm7U4IEb0M= - dependencies: - expand-tilde "^2.0.0" - global-modules "^1.0.0" - -resolve-from@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz" - integrity sha1-six699nWiBvItuZTM17rywoYh0g= - -resolve-from@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz" - integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== - -resolve-url@^0.2.1: - version "0.2.1" - resolved "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz" - integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= - -resolve@^1.10.0, resolve@^1.3.2: - version "1.15.1" - resolved "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz" - integrity sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w== - dependencies: - path-parse "^1.0.6" - -responselike@1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz" - integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= - dependencies: - lowercase-keys "^1.0.0" - -restore-cursor@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz" - integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== - dependencies: - onetime "^5.1.0" - signal-exit "^3.0.2" - -ret@~0.1.10: - version "0.1.15" - resolved "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz" - integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== - -rgb-regex@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz" - integrity sha1-wODWiC3w4jviVKR16O3UGRX+rrE= - -rgba-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz" - integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM= - -rimraf@2.6.3, rimraf@^2.5.4, rimraf@^2.6.3: - version "2.6.3" - resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz" - integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== - dependencies: - glob "^7.1.3" - -ripemd160@^2.0.0, ripemd160@^2.0.1: - version "2.0.2" - resolved "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz" - integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== - dependencies: - hash-base "^3.0.0" - inherits "^2.0.1" - -run-async@^2.4.0: - version "2.4.1" - resolved "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz" - integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== - -run-queue@^1.0.0, run-queue@^1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz" - integrity sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec= - dependencies: - aproba "^1.1.1" - -rusha@^0.8.13: - version "0.8.13" - resolved "https://registry.npmjs.org/rusha/-/rusha-0.8.13.tgz" - integrity sha1-mghOe4YLF7/zAVuSxnpqM2GRUTo= - -rxjs@^6.5.3: - version "6.5.5" - resolved "https://registry.npmjs.org/rxjs/-/rxjs-6.5.5.tgz" - integrity sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ== - dependencies: - tslib "^1.9.0" - -safe-buffer@5.1.2, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - -safe-buffer@^5.2.0: - version "5.2.1" - resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - -safe-regex@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz" - integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4= - dependencies: - ret "~0.1.10" - -"safer-buffer@>= 2.1.2 < 3": - version "2.1.2" - resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== - -sass-unused@^0.3.0: - version "0.3.0" - resolved "https://registry.npmjs.org/sass-unused/-/sass-unused-0.3.0.tgz" - integrity sha512-fGNcUpDeSFwnN+BTQ251iM77Py8awPXc96vSE3TpvMcgbC90IrohonRb4oxWX/KzHpezkmUddS8/t04R+yIB8w== - dependencies: - glob "^7.0.5" - gonzales-pe "^4.2.3" - -sax@>=0.6.0, sax@~1.2.4: - version "1.2.4" - resolved "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz" - integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== - -schema-utils@^0.4.0: - version "0.4.7" - resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz" - integrity sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ== - dependencies: - ajv "^6.1.0" - ajv-keywords "^3.1.0" - -schema-utils@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz" - integrity sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g== - dependencies: - ajv "^6.1.0" - ajv-errors "^1.0.0" - ajv-keywords "^3.1.0" - -schema-utils@^2.6.5: - version "2.6.5" - resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.5.tgz" - integrity sha512-5KXuwKziQrTVHh8j/Uxz+QUbxkaLW9X/86NBlx/gnKgtsZA2GIVMUn17qWhRFwF8jdYb3Dig5hRO/W5mZqy6SQ== - dependencies: - ajv "^6.12.0" - ajv-keywords "^3.4.1" - -seek-bzip@^1.0.5: - version "1.0.5" - resolved "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.5.tgz" - integrity sha1-z+kXyz0nS8/6x5J1ivUxc+sfq9w= - dependencies: - commander "~2.8.1" - -semver-regex@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/semver-regex/-/semver-regex-2.0.0.tgz" - integrity sha512-mUdIBBvdn0PLOeP3TEkMH7HHeUP3GjsXCwKarjv/kGmUFOYg1VqEemKhoQpWMu6X2I8kHeuVdGibLGkVK+/5Qw== - -semver-truncate@^1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/semver-truncate/-/semver-truncate-1.1.2.tgz" - integrity sha1-V/Qd5pcHpicJp+AQS6IRcQnqR+g= - dependencies: - semver "^5.3.0" - -"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.6.0: - version "5.7.1" - resolved "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== - -semver@7.0.0: - version "7.0.0" - resolved "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz" - integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== - -semver@^7.2.1, semver@^7.3.2: - version "7.3.2" - resolved "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz" - integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== - -semver@^7.3.5: - version "7.3.5" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" - integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== - dependencies: - lru-cache "^6.0.0" - -send@0.17.1: - version "0.17.1" - resolved "https://registry.npmjs.org/send/-/send-0.17.1.tgz" - integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== - dependencies: - debug "2.6.9" - depd "~1.1.2" - destroy "~1.0.4" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - fresh "0.5.2" - http-errors "~1.7.2" - mime "1.6.0" - ms "2.1.1" - on-finished "~2.3.0" - range-parser "~1.2.1" - statuses "~1.5.0" - -serialize-javascript@^2.1.2: - version "2.1.2" - resolved "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz" - integrity sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ== - -serve-static@1.14.1: - version "1.14.1" - resolved "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz" - integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== - dependencies: - encodeurl "~1.0.2" - escape-html "~1.0.3" - parseurl "~1.3.3" - send "0.17.1" - -set-blocking@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz" - integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= - -set-value@^2.0.0, set-value@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz" - integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== - dependencies: - extend-shallow "^2.0.1" - is-extendable "^0.1.1" - is-plain-object "^2.0.3" - split-string "^3.0.1" - -setimmediate@^1.0.4: - version "1.0.5" - resolved "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz" - integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= - -setprototypeof@1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz" - integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== - -sha.js@^2.4.0, sha.js@^2.4.8: - version "2.4.11" - resolved "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz" - integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== - dependencies: - inherits "^2.0.1" - safe-buffer "^5.0.1" - -shebang-command@^1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz" - integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= - dependencies: - shebang-regex "^1.0.0" - -shebang-command@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz" - integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== - dependencies: - shebang-regex "^3.0.0" - -shebang-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz" - integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= - -shebang-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" - integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== - -signal-exit@^3.0.0, signal-exit@^3.0.2: - version "3.0.3" - resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz" - integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== - -simple-swizzle@^0.2.2: - version "0.2.2" - resolved "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz" - integrity sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo= - dependencies: - is-arrayish "^0.3.1" - -slash@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz" - integrity sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU= - -slice-ansi@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz" - integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== - dependencies: - ansi-styles "^3.2.0" - astral-regex "^1.0.0" - is-fullwidth-code-point "^2.0.0" - -snapdragon-node@^2.0.1: - version "2.1.1" - resolved "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz" - integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== - dependencies: - define-property "^1.0.0" - isobject "^3.0.0" - snapdragon-util "^3.0.1" - -snapdragon-util@^3.0.1: - version "3.0.1" - resolved "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz" - integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== - dependencies: - kind-of "^3.2.0" - -snapdragon@^0.8.1: - version "0.8.2" - resolved "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz" - integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== - dependencies: - base "^0.11.1" - debug "^2.2.0" - define-property "^0.2.5" - extend-shallow "^2.0.1" - map-cache "^0.2.2" - source-map "^0.5.6" - source-map-resolve "^0.5.0" - use "^3.1.0" - -sort-keys-length@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/sort-keys-length/-/sort-keys-length-1.0.1.tgz" - integrity sha1-nLb09OnkgVWmqgZx7dM2/xR5oYg= - dependencies: - sort-keys "^1.0.0" - -sort-keys@^1.0.0: - version "1.1.2" - resolved "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz" - integrity sha1-RBttTTRnmPG05J6JIK37oOVD+a0= - dependencies: - is-plain-obj "^1.0.0" - -sort-keys@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/sort-keys/-/sort-keys-2.0.0.tgz" - integrity sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg= - dependencies: - is-plain-obj "^1.0.0" - -source-list-map@^2.0.0: - version "2.0.1" - resolved "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz" - integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== - -source-map-resolve@^0.5.0: - version "0.5.3" - resolved "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz" - integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== - dependencies: - atob "^2.1.2" - decode-uri-component "^0.2.0" - resolve-url "^0.2.1" - source-map-url "^0.4.0" - urix "^0.1.0" - -source-map-support@^0.4.15: - version "0.4.18" - resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz" - integrity sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA== - dependencies: - source-map "^0.5.6" - -source-map-support@~0.5.12: - version "0.5.19" - resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz" - integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - -source-map-url@^0.4.0: - version "0.4.0" - resolved "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz" - integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= - -source-map@^0.5.0, source-map@^0.5.6, source-map@^0.5.7, source-map@~0.5.0: - version "0.5.7" - resolved "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz" - integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= - -source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: - version "0.6.1" - resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== - -source-map@~0.1.38: - version "0.1.43" - resolved "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz" - integrity sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y= - dependencies: - amdefine ">=0.0.4" - -spdx-correct@^3.0.0: - version "3.1.0" - resolved "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz" - integrity sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q== - dependencies: - spdx-expression-parse "^3.0.0" - spdx-license-ids "^3.0.0" - -spdx-exceptions@^2.1.0: - version "2.2.0" - resolved "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz" - integrity sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA== - -spdx-expression-parse@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz" - integrity sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg== - dependencies: - spdx-exceptions "^2.1.0" - spdx-license-ids "^3.0.0" - -spdx-license-ids@^3.0.0: - version "3.0.5" - resolved "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz" - integrity sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q== - -split-on-first@^1.0.0: - version "1.1.0" - resolved "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz" - integrity sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw== - -split-string@^3.0.1, split-string@^3.0.2: - version "3.1.0" - resolved "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz" - integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== - dependencies: - extend-shallow "^3.0.0" - -sprintf-js@~1.0.2: - version "1.0.3" - resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz" - integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= - -squeak@^1.0.0: - version "1.3.0" - resolved "https://registry.npmjs.org/squeak/-/squeak-1.3.0.tgz" - integrity sha1-MwRQN7ZDiLVnZ0uEMiplIQc5FsM= - dependencies: - chalk "^1.0.0" - console-stream "^0.1.1" - lpad-align "^1.0.1" - -ssri@^6.0.1: - version "6.0.1" - resolved "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz" - integrity sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA== - dependencies: - figgy-pudding "^3.5.1" - -stable@^0.1.8: - version "0.1.8" - resolved "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz" - integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== - -static-extend@^0.1.1: - version "0.1.2" - resolved "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz" - integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= - dependencies: - define-property "^0.2.5" - object-copy "^0.1.0" - -"statuses@>= 1.5.0 < 2", statuses@~1.5.0: - version "1.5.0" - resolved "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz" - integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= - -stream-browserify@^2.0.1: - version "2.0.2" - resolved "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz" - integrity sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg== - dependencies: - inherits "~2.0.1" - readable-stream "^2.0.2" - -stream-each@^1.1.0: - version "1.2.3" - resolved "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz" - integrity sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw== - dependencies: - end-of-stream "^1.1.0" - stream-shift "^1.0.0" - -stream-http@^2.7.2: - version "2.8.3" - resolved "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz" - integrity sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw== - dependencies: - builtin-status-codes "^3.0.0" - inherits "^2.0.1" - readable-stream "^2.3.6" - to-arraybuffer "^1.0.0" - xtend "^4.0.0" - -stream-shift@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz" - integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== - -strict-uri-encode@^1.0.0: - version "1.1.0" - resolved "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz" - integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM= - -strict-uri-encode@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz" - integrity sha1-ucczDHBChi9rFC3CdLvMWGbONUY= - -strictdom@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/strictdom/-/strictdom-1.0.1.tgz" - integrity sha1-GJ3pFkn3PUTVm4Qy76aO+dJllGA= - -string-replace-webpack-plugin@^0.1.3: - version "0.1.3" - resolved "https://registry.npmjs.org/string-replace-webpack-plugin/-/string-replace-webpack-plugin-0.1.3.tgz" - integrity sha1-c8ZX51nWbP6Arh4M8JGqJW0OcVw= - dependencies: - async "~0.2.10" - loader-utils "~0.2.3" - optionalDependencies: - css-loader "^0.9.1" - file-loader "^0.8.1" - style-loader "^0.8.3" - -string-width@^3.0.0, string-width@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz" - integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== - dependencies: - emoji-regex "^7.0.1" - is-fullwidth-code-point "^2.0.0" - strip-ansi "^5.1.0" - -string-width@^4.1.0, string-width@^4.2.0: - version "4.2.0" - resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz" - integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.0" - -string.prototype.trimend@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz" - integrity sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g== - dependencies: - define-properties "^1.1.3" - es-abstract "^1.17.5" - -string.prototype.trimleft@^2.1.1: - version "2.1.2" - resolved "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz" - integrity sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw== - dependencies: - define-properties "^1.1.3" - es-abstract "^1.17.5" - string.prototype.trimstart "^1.0.0" - -string.prototype.trimright@^2.1.1: - version "2.1.2" - resolved "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz" - integrity sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg== - dependencies: - define-properties "^1.1.3" - es-abstract "^1.17.5" - string.prototype.trimend "^1.0.0" - -string.prototype.trimstart@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz" - integrity sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw== - dependencies: - define-properties "^1.1.3" - es-abstract "^1.17.5" - -string_decoder@^1.0.0, string_decoder@^1.1.1, string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - -strip-ansi@^3.0.0: - version "3.0.1" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz" - integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= - dependencies: - ansi-regex "^2.0.0" - -strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: - version "5.2.0" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz" - integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== - dependencies: - ansi-regex "^4.1.0" - -strip-ansi@^6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz" - integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== - dependencies: - ansi-regex "^5.0.0" - -strip-bom@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz" - integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4= - dependencies: - is-utf8 "^0.2.0" - -strip-dirs@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/strip-dirs/-/strip-dirs-2.1.0.tgz" - integrity sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g== - dependencies: - is-natural-number "^4.0.1" - -strip-eof@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz" - integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= - -strip-indent@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz" - integrity sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI= - dependencies: - get-stdin "^4.0.1" - -strip-json-comments@^3.0.1, strip-json-comments@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.0.tgz" - integrity sha512-e6/d0eBu7gHtdCqFt0xJr642LdToM5/cN4Qb9DbHjVx1CP5RyeM+zH7pbecEmDv/lBqb0QH+6Uqq75rxFPkM0w== - -strip-outer@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz" - integrity sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg== - dependencies: - escape-string-regexp "^1.0.2" - -style-loader@^0.8.3: - version "0.8.3" - resolved "https://registry.npmjs.org/style-loader/-/style-loader-0.8.3.tgz" - integrity sha1-9Pkut9tjdodI8VBlzWcA9aHIU1c= - dependencies: - loader-utils "^0.2.5" - -stylehacks@^4.0.0: - version "4.0.3" - resolved "https://registry.npmjs.org/stylehacks/-/stylehacks-4.0.3.tgz" - integrity sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g== - dependencies: - browserslist "^4.0.0" - postcss "^7.0.0" - postcss-selector-parser "^3.0.0" - -supports-color@6.1.0, supports-color@^6.1.0: - version "6.1.0" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz" - integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== - dependencies: - has-flag "^3.0.0" - -supports-color@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz" - integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= - -supports-color@^3.2.3: - version "3.2.3" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz" - integrity sha1-ZawFBLOVQXHYpklGsq48u4pfVPY= - dependencies: - has-flag "^1.0.0" - -supports-color@^5.3.0, supports-color@^5.4.0: - version "5.5.0" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz" - integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== - dependencies: - has-flag "^3.0.0" - -supports-color@^7.1.0: - version "7.1.0" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz" - integrity sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g== - dependencies: - has-flag "^4.0.0" - -svgo@^1.0.0: - version "1.3.2" - resolved "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz" - integrity sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw== - dependencies: - chalk "^2.4.1" - coa "^2.0.2" - css-select "^2.0.0" - css-select-base-adapter "^0.1.1" - css-tree "1.0.0-alpha.37" - csso "^4.0.2" - js-yaml "^3.13.1" - mkdirp "~0.5.1" - object.values "^1.1.0" - sax "~1.2.4" - stable "^0.1.8" - unquote "~1.1.1" - util.promisify "~1.0.0" - -table@^5.2.3: - version "5.4.6" - resolved "https://registry.npmjs.org/table/-/table-5.4.6.tgz" - integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug== - dependencies: - ajv "^6.10.2" - lodash "^4.17.14" - slice-ansi "^2.1.0" - string-width "^3.0.0" - -tapable@^1.0.0, tapable@^1.1.3: - version "1.1.3" - resolved "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz" - integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== - -tar-stream@^1.5.2: - version "1.6.2" - resolved "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz" - integrity sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A== - dependencies: - bl "^1.0.0" - buffer-alloc "^1.2.0" - end-of-stream "^1.0.0" - fs-constants "^1.0.0" - readable-stream "^2.3.0" - to-buffer "^1.1.1" - xtend "^4.0.0" - -temp-dir@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz" - integrity sha1-CnwOom06Oa+n4OvqnB/AvE2qAR0= - -tempfile@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/tempfile/-/tempfile-2.0.0.tgz" - integrity sha1-awRGhWqbERTRhW/8vlCczLCXcmU= - dependencies: - temp-dir "^1.0.0" - uuid "^3.0.1" - -terser-webpack-plugin@^1.1.0, terser-webpack-plugin@^1.4.3: - version "1.4.3" - resolved "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.3.tgz" - integrity sha512-QMxecFz/gHQwteWwSo5nTc6UaICqN1bMedC5sMtUc7y3Ha3Q8y6ZO0iCR8pq4RJC8Hjf0FEPEHZqcMB/+DFCrA== - dependencies: - cacache "^12.0.2" - find-cache-dir "^2.1.0" - is-wsl "^1.1.0" - schema-utils "^1.0.0" - serialize-javascript "^2.1.2" - source-map "^0.6.1" - terser "^4.1.2" - webpack-sources "^1.4.0" - worker-farm "^1.7.0" - -terser@^4.1.2: - version "4.8.0" - resolved "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz" - integrity sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw== - dependencies: - commander "^2.20.0" - source-map "~0.6.1" - source-map-support "~0.5.12" - -text-table@^0.2.0: - version "0.2.0" - resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz" - integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= - -through2@^2.0.0: - version "2.0.5" - resolved "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz" - integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== - dependencies: - readable-stream "~2.3.6" - xtend "~4.0.1" - -through@^2.3.6, through@^2.3.8, through@~2.3.6: - version "2.3.8" - resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz" - integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= - -timed-out@^4.0.0, timed-out@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz" - integrity sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8= - -timers-browserify@^2.0.4: - version "2.0.11" - resolved "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.11.tgz" - integrity sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ== - dependencies: - setimmediate "^1.0.4" - -timm@^1.6.1: - version "1.6.2" - resolved "https://registry.npmjs.org/timm/-/timm-1.6.2.tgz" - integrity sha512-IH3DYDL1wMUwmIlVmMrmesw5lZD6N+ZOAFWEyLrtpoL9Bcrs9u7M/vyOnHzDD2SMs4irLkVjqxZbHrXStS/Nmw== - -timsort@^0.3.0: - version "0.3.0" - resolved "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz" - integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q= - -tinycolor2@^1.4.1: - version "1.4.1" - resolved "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.1.tgz" - integrity sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g= - -tmp@^0.0.33: - version "0.0.33" - resolved "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz" - integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== - dependencies: - os-tmpdir "~1.0.2" - -to-arraybuffer@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz" - integrity sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M= - -to-buffer@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz" - integrity sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg== - -to-fast-properties@^1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz" - integrity sha1-uDVx+k2MJbguIxsG46MFXeTKGkc= - -to-fast-properties@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz" - integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= - -to-object-path@^0.3.0: - version "0.3.0" - resolved "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz" - integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= - dependencies: - kind-of "^3.0.2" - -to-regex-range@^2.1.0: - version "2.1.1" - resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz" - integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= - dependencies: - is-number "^3.0.0" - repeat-string "^1.6.1" - -to-regex-range@^5.0.1: - 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== - dependencies: - is-number "^7.0.0" - -to-regex@^3.0.1, to-regex@^3.0.2: - version "3.0.2" - resolved "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz" - integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== - dependencies: - define-property "^2.0.2" - extend-shallow "^3.0.2" - regex-not "^1.0.2" - safe-regex "^1.1.0" - -toidentifier@1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz" - integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== - -trim-newlines@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz" - integrity sha1-WIeWa7WCpFA6QetST301ARgVphM= - -trim-repeated@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz" - integrity sha1-42RqLqTokTEr9+rObPsFOAvAHCE= - dependencies: - escape-string-regexp "^1.0.2" - -trim-right@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz" - integrity sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM= - -trim@^0.0.1: - version "0.0.1" - resolved "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz" - integrity sha1-WFhUf2spB1fulczMZm+1AITEYN0= - -tryer@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz" - integrity sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA== - -tslib@^1.8.1, tslib@^1.9.0: - version "1.13.0" - resolved "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz" - integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q== - -tsutils@^3.17.1: - version "3.17.1" - resolved "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz" - integrity sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g== - dependencies: - tslib "^1.8.1" - -tty-browserify@0.0.0: - version "0.0.0" - resolved "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz" - integrity sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY= - -tunnel-agent@^0.6.0: - version "0.6.0" - resolved "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz" - integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= - dependencies: - safe-buffer "^5.0.1" - -type-check@^0.4.0, type-check@~0.4.0: - version "0.4.0" - resolved "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz" - integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== - dependencies: - prelude-ls "^1.2.1" - -type-fest@^0.11.0: - version "0.11.0" - resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz" - integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ== - -type-fest@^0.5.1: - version "0.5.2" - resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.5.2.tgz" - integrity sha512-DWkS49EQKVX//Tbupb9TFa19c7+MK1XmzkrZUR8TAktmE/DizXoaoJV6TZ/tSIPXipqNiRI6CyAe7x69Jb6RSw== - -type-fest@^0.8.1: - version "0.8.1" - resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz" - integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== - -type-is@~1.6.17, type-is@~1.6.18: - version "1.6.18" - resolved "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz" - integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== - dependencies: - media-typer "0.3.0" - mime-types "~2.1.24" - -typedarray@^0.0.6: - version "0.0.6" - resolved "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz" - integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= - -typescript@3.9.3: - version "3.9.3" - resolved "https://registry.npmjs.org/typescript/-/typescript-3.9.3.tgz" - integrity sha512-D/wqnB2xzNFIcoBG9FG8cXRDjiqSTbG2wd8DMZeQyJlP1vfTkIxH4GKveWaEBYySKIg+USu+E+EDIR47SqnaMQ== - -uglify-js@3.4.x: - version "3.4.10" - resolved "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.10.tgz" - integrity sha512-Y2VsbPVs0FIshJztycsO2SfPk7/KAF/T72qzv9u5EpQ4kB2hQoHlhNQTsNyy6ul7lQtqJN/AoWeS23OzEiEFxw== - dependencies: - commander "~2.19.0" - source-map "~0.6.1" - -uglify-template-string-loader@^1.1.0: - version "1.1.1" - resolved "https://registry.npmjs.org/uglify-template-string-loader/-/uglify-template-string-loader-1.1.1.tgz" - integrity sha512-EHJx8m0aIHlwX5xlJd2xPYIFvLrPkVK5X8zpVxSNTmu7KoT2eSg1TNlwZS+JS65+dwJXC4rC5mc+F4UVe2rckw== - -unbzip2-stream@^1.0.9: - version "1.4.1" - resolved "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.1.tgz" - integrity sha512-sgDYfSDPMsA4Hr2/w7vOlrJBlwzmyakk1+hW8ObLvxSp0LA36LcL2XItGvOT3OSblohSdevMuT8FQjLsqyy4sA== - dependencies: - buffer "^5.2.1" - through "^2.3.8" - -unicode-canonical-property-names-ecmascript@^1.0.4: - version "1.0.4" - resolved "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz" - integrity sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ== - -unicode-match-property-ecmascript@^1.0.4: - version "1.0.4" - resolved "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz" - integrity sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg== - dependencies: - unicode-canonical-property-names-ecmascript "^1.0.4" - unicode-property-aliases-ecmascript "^1.0.4" - -unicode-match-property-value-ecmascript@^1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz" - integrity sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ== - -unicode-property-aliases-ecmascript@^1.0.4: - version "1.1.0" - resolved "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz" - integrity sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg== - -union-value@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz" - integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== - dependencies: - arr-union "^3.1.0" - get-value "^2.0.6" - is-extendable "^0.1.1" - set-value "^2.0.1" - -uniq@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz" - integrity sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8= - -uniqs@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz" - integrity sha1-/+3ks2slKQaW5uFl1KWe25mOawI= - -unique-filename@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz" - integrity sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ== - dependencies: - unique-slug "^2.0.0" - -unique-slug@^2.0.0: - version "2.0.2" - resolved "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz" - integrity sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w== - dependencies: - imurmurhash "^0.1.4" - -universal-user-agent@^6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz" - integrity sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w== - -unpipe@1.0.0, unpipe@~1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz" - integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= - -unquote@~1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz" - integrity sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ= - -unset-value@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz" - integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= - dependencies: - has-value "^0.3.1" - isobject "^3.0.0" - -unused-files-webpack-plugin@^3.4.0: - version "3.4.0" - resolved "https://registry.npmjs.org/unused-files-webpack-plugin/-/unused-files-webpack-plugin-3.4.0.tgz" - integrity sha512-cmukKOBdIqaM1pqThY0+jp+mYgCVyzrD8uRbKEucQwIGZcLIRn+gSRiQ7uLjcDd3Zba9NUxVGyYa7lWM4UCGeg== - dependencies: - babel-runtime "^7.0.0-beta.3" - glob-all "^3.1.0" - semver "^5.5.0" - util.promisify "^1.0.0" - warning "^3.0.0" - -upath@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" - integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== - -upper-case@^1.1.1: - version "1.1.3" - resolved "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz" - integrity sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg= - -uri-js@^4.2.2: - version "4.2.2" - resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz" - integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ== - dependencies: - punycode "^2.1.0" - -urix@^0.1.0: - version "0.1.0" - resolved "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz" - integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= - -url-parse-lax@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz" - integrity sha1-evjzA2Rem9eaJy56FKxovAYJ2nM= - dependencies: - prepend-http "^1.0.1" - -url-parse-lax@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz" - integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww= - dependencies: - prepend-http "^2.0.0" - -url-to-options@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz" - integrity sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k= - -url@^0.11.0: - version "0.11.0" - resolved "https://registry.npmjs.org/url/-/url-0.11.0.tgz" - integrity sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE= - dependencies: - punycode "1.3.2" - querystring "0.2.0" - -use@^3.1.0: - version "3.1.1" - resolved "https://registry.npmjs.org/use/-/use-3.1.1.tgz" - integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== - -utif@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/utif/-/utif-2.0.1.tgz" - integrity sha512-Z/S1fNKCicQTf375lIP9G8Sa1H/phcysstNrrSdZKj1f9g58J4NMgb5IgiEZN9/nLMPDwF0W7hdOe9Qq2IYoLg== - dependencies: - pako "^1.0.5" - -util-deprecate@^1.0.1, util-deprecate@~1.0.1: - version "1.0.2" - resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" - integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= - -util.promisify@^1.0.0, util.promisify@~1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz" - integrity sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA== - dependencies: - define-properties "^1.1.3" - es-abstract "^1.17.2" - has-symbols "^1.0.1" - object.getownpropertydescriptors "^2.1.0" - -util@0.10.3: - version "0.10.3" - resolved "https://registry.npmjs.org/util/-/util-0.10.3.tgz" - integrity sha1-evsa/lCAUkZInj23/g7TeTNqwPk= - dependencies: - inherits "2.0.1" - -util@^0.11.0: - version "0.11.1" - resolved "https://registry.npmjs.org/util/-/util-0.11.1.tgz" - integrity sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ== - dependencies: - inherits "2.0.3" - -utils-merge@1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz" - integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= - -uuid@^3.0.1: - version "3.4.0" - resolved "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz" - integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== - -v8-compile-cache@2.0.3: - version "2.0.3" - resolved "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.0.3.tgz" - integrity sha512-CNmdbwQMBjwr9Gsmohvm0pbL954tJrNzf6gWL3K+QMQf00PF7ERGrEiLgjuU3mKreLC2MeGhUsNV9ybTbLgd3w== - -v8-compile-cache@^2.0.3: - version "2.1.0" - resolved "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz" - integrity sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g== - -validate-npm-package-license@^3.0.1: - version "3.0.4" - resolved "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz" - integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== - dependencies: - spdx-correct "^3.0.0" - spdx-expression-parse "^3.0.0" - -vary@~1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz" - integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= - -vendors@^1.0.0: - version "1.0.4" - resolved "https://registry.npmjs.org/vendors/-/vendors-1.0.4.tgz" - integrity sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w== - -vm-browserify@^1.0.1: - version "1.1.2" - resolved "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz" - integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== - -warning@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz" - integrity sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w= - dependencies: - loose-envify "^1.0.0" - -watchpack-chokidar2@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz#38500072ee6ece66f3769936950ea1771be1c957" - integrity sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww== - dependencies: - chokidar "^2.1.8" - -watchpack@^1.6.1: - version "1.7.2" - resolved "https://registry.npmjs.org/watchpack/-/watchpack-1.7.2.tgz" - integrity sha512-ymVbbQP40MFTp+cNMvpyBpBtygHnPzPkHqoIwRRj/0B8KhqQwV8LaKjtbaxF2lK4vl8zN9wCxS46IFCU5K4W0g== - dependencies: - graceful-fs "^4.1.2" - neo-async "^2.5.0" - optionalDependencies: - chokidar "^3.4.0" - watchpack-chokidar2 "^2.0.0" - -webpack-bundle-analyzer@^3.0.3: - version "3.7.0" - resolved "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.7.0.tgz" - integrity sha512-mETdjZ30a3Yf+NTB/wqTgACK7rAYQl5uxKK0WVTNmF0sM3Uv8s3R58YZMW7Rhu0Lk2Rmuhdj5dcH5Q76zCDVdA== - dependencies: - acorn "^7.1.1" - acorn-walk "^7.1.1" - bfj "^6.1.1" - chalk "^2.4.1" - commander "^2.18.0" - ejs "^2.6.1" - express "^4.16.3" - filesize "^3.6.1" - gzip-size "^5.0.0" - lodash "^4.17.15" - mkdirp "^0.5.1" - opener "^1.5.1" - ws "^6.0.0" - -webpack-cli@^3.1.0: - version "3.3.11" - resolved "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.3.11.tgz" - integrity sha512-dXlfuml7xvAFwYUPsrtQAA9e4DOe58gnzSxhgrO/ZM/gyXTBowrsYeubyN4mqGhYdpXMFNyQ6emjJS9M7OBd4g== - dependencies: - chalk "2.4.2" - cross-spawn "6.0.5" - enhanced-resolve "4.1.0" - findup-sync "3.0.0" - global-modules "2.0.0" - import-local "2.0.0" - interpret "1.2.0" - loader-utils "1.2.3" - supports-color "6.1.0" - v8-compile-cache "2.0.3" - yargs "13.2.4" - -webpack-deep-scope-plugin@^1.6.0: - version "1.6.2" - resolved "https://registry.npmjs.org/webpack-deep-scope-plugin/-/webpack-deep-scope-plugin-1.6.2.tgz" - integrity sha512-S5ZM1i7oTIVPIS1z/Fu41tqFzaXpy8vZnwEDC9I7NLj5XD8GGrDZbDXtG5FCGkHPGxtAzF4O21DKZZ76vpBGzw== - dependencies: - deep-scope-analyser "^1.7.0" - -webpack-plugin-replace@^1.1.1: - version "1.2.0" - resolved "https://registry.npmjs.org/webpack-plugin-replace/-/webpack-plugin-replace-1.2.0.tgz" - integrity sha512-1HA3etHpJW55qonJqv84o5w5GY7iqF8fqSHpTWdNwarj1llkkt4jT4QSvYs1hoaU8Lu5akDnq/spHHO5mXwo1w== - -webpack-sources@^1.4.0, webpack-sources@^1.4.1: - version "1.4.3" - resolved "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz" - integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ== - dependencies: - source-list-map "^2.0.0" - source-map "~0.6.1" - -webpack-strip-block@^0.2.0: - version "0.2.0" - resolved "https://registry.npmjs.org/webpack-strip-block/-/webpack-strip-block-0.2.0.tgz" - integrity sha1-xg1KcD4O7uiJXn8avptf6RRoFHA= - dependencies: - loader-utils "^1.1.0" - -webpack@^4.43.0: - version "4.43.0" - resolved "https://registry.npmjs.org/webpack/-/webpack-4.43.0.tgz" - integrity sha512-GW1LjnPipFW2Y78OOab8NJlCflB7EFskMih2AHdvjbpKMeDJqEgSx24cXXXiPS65+WSwVyxtDsJH6jGX2czy+g== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-module-context" "1.9.0" - "@webassemblyjs/wasm-edit" "1.9.0" - "@webassemblyjs/wasm-parser" "1.9.0" - acorn "^6.4.1" - ajv "^6.10.2" - ajv-keywords "^3.4.1" - chrome-trace-event "^1.0.2" - enhanced-resolve "^4.1.0" - eslint-scope "^4.0.3" - json-parse-better-errors "^1.0.2" - loader-runner "^2.4.0" - loader-utils "^1.2.3" - memory-fs "^0.4.1" - micromatch "^3.1.10" - mkdirp "^0.5.3" - neo-async "^2.6.1" - node-libs-browser "^2.2.1" - schema-utils "^1.0.0" - tapable "^1.1.3" - terser-webpack-plugin "^1.4.3" - watchpack "^1.6.1" - webpack-sources "^1.4.1" - -whatwg-fetch@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz" - integrity sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q== - -which-module@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz" - integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= - -which@^1.2.14, which@^1.2.9, which@^1.3.1: - version "1.3.1" - resolved "https://registry.npmjs.org/which/-/which-1.3.1.tgz" - integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== - dependencies: - isexe "^2.0.0" - -which@^2.0.1: - version "2.0.2" - resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz" - integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== - dependencies: - isexe "^2.0.0" - -word-wrap@^1.2.3: - 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== - -worker-farm@^1.7.0: - version "1.7.0" - resolved "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz" - integrity sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw== - dependencies: - errno "~0.1.7" - -worker-loader@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/worker-loader/-/worker-loader-2.0.0.tgz" - integrity sha512-tnvNp4K3KQOpfRnD20m8xltE3eWh89Ye+5oj7wXEEHKac1P4oZ6p9oTj8/8ExqoSBnk9nu5Pr4nKfQ1hn2APJw== - dependencies: - loader-utils "^1.0.0" - schema-utils "^0.4.0" - -wrap-ansi@^5.1.0: - version "5.1.0" - resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz" - integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q== - dependencies: - ansi-styles "^3.2.0" - string-width "^3.0.0" - strip-ansi "^5.0.0" - -wrap-ansi@^6.2.0: - version "6.2.0" - resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz" - integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrappy@1: - version "1.0.2" - resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= - -write@1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/write/-/write-1.0.3.tgz" - integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== - dependencies: - mkdirp "^0.5.1" - -ws@^6.0.0: - version "6.2.1" - resolved "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz" - integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA== - dependencies: - async-limiter "~1.0.0" - -xhr@^2.0.1: - version "2.5.0" - resolved "https://registry.npmjs.org/xhr/-/xhr-2.5.0.tgz" - integrity sha512-4nlO/14t3BNUZRXIXfXe+3N6w3s1KoxcJUUURctd64BLRe67E4gRwp4PjywtDY72fXpZ1y6Ch0VZQRY/gMPzzQ== - dependencies: - global "~4.3.0" - is-function "^1.0.1" - parse-headers "^2.0.0" - xtend "^4.0.0" - -xml-parse-from-string@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/xml-parse-from-string/-/xml-parse-from-string-1.0.1.tgz" - integrity sha1-qQKekp09vN7RafPG4oI42VpdWig= - -xml2js@^0.4.5: - version "0.4.23" - resolved "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz" - integrity sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug== - dependencies: - sax ">=0.6.0" - xmlbuilder "~11.0.0" - -xmlbuilder@~11.0.0: - version "11.0.1" - resolved "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz" - integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== - -xtend@^4.0.0, xtend@~4.0.1: - version "4.0.2" - resolved "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz" - integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== - -y18n@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz" - integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== - -yallist@^2.1.2: - version "2.1.2" - resolved "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz" - integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= - -yallist@^3.0.2: - version "3.1.1" - resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz" - integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== - -yallist@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" - integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== - -yaml-js@^0.1.3: - version "0.1.5" - resolved "https://registry.npmjs.org/yaml-js/-/yaml-js-0.1.5.tgz" - integrity sha1-oBNpAQs1WNiq7SOUYV39B4D9j6w= - -yaml@^1.10.0: - version "1.10.0" - resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.0.tgz" - integrity sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg== - -yargs-parser@^13.1.0: - version "13.1.2" - resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz" - integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg== - dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" - -yargs-parser@^18.1.1: - version "18.1.2" - resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.2.tgz" - integrity sha512-hlIPNR3IzC1YuL1c2UwwDKpXlNFBqD1Fswwh1khz5+d8Cq/8yc/Mn0i+rQXduu8hcrFKvO7Eryk+09NecTQAAQ== - dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" - -yargs@13.2.4: - version "13.2.4" - resolved "https://registry.npmjs.org/yargs/-/yargs-13.2.4.tgz" - integrity sha512-HG/DWAJa1PAnHT9JAhNa8AbAv3FPaiLzioSjCcmuXXhP8MlpHO5vwls4g4j6n30Z74GVQj8Xa62dWVx1QCGklg== - dependencies: - cliui "^5.0.0" - find-up "^3.0.0" - get-caller-file "^2.0.1" - os-locale "^3.1.0" - require-directory "^2.1.1" - require-main-filename "^2.0.0" - set-blocking "^2.0.0" - string-width "^3.0.0" - which-module "^2.0.0" - y18n "^4.0.0" - yargs-parser "^13.1.0" - -yargs@^15.3.1: - version "15.3.1" - resolved "https://registry.npmjs.org/yargs/-/yargs-15.3.1.tgz" - integrity sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA== - dependencies: - cliui "^6.0.0" - decamelize "^1.2.0" - find-up "^4.1.0" - get-caller-file "^2.0.1" - require-directory "^2.1.1" - require-main-filename "^2.0.0" - set-blocking "^2.0.0" - string-width "^4.2.0" - which-module "^2.0.0" - y18n "^4.0.0" - yargs-parser "^18.1.1" - -yarn@^1.22.4: - version "1.22.4" - resolved "https://registry.npmjs.org/yarn/-/yarn-1.22.4.tgz" - integrity sha512-oYM7hi/lIWm9bCoDMEWgffW8aiNZXCWeZ1/tGy0DWrN6vmzjCXIKu2Y21o8DYVBUtiktwKcNoxyGl/2iKLUNGA== - -yauzl@^2.4.2: - version "2.10.0" - resolved "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz" - integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk= - dependencies: - buffer-crc32 "~0.2.3" - fd-slicer "~1.1.0" - -yawn-yaml@^1.5.0: - version "1.5.0" - resolved "https://registry.npmjs.org/yawn-yaml/-/yawn-yaml-1.5.0.tgz" - integrity sha512-sH2zX9K1QiWhWh9U19pye660qlzrEAd5c4ebw/6lqz17LZw7xYi7nqXlBoVLVtc2FZFXDKiJIsvVcKGYbLVyFQ== - dependencies: - js-yaml "^3.4.2" - lodash "^4.17.11" - yaml-js "^0.1.3"