├── .editorconfig
├── .github
└── workflows
│ └── ci.yml
├── .gitignore
├── .vscode
├── settings.json
└── tasks.json
├── LICENSE
├── README.md
├── content
└── panorama
│ ├── images
│ └── custom_game
│ │ └── tstl.png
│ ├── layout
│ └── custom_game
│ │ ├── custom_ui_manifest.xml
│ │ └── hud.xml
│ └── styles
│ └── custom_game
│ └── hud.css
├── game
└── scripts
│ └── npc
│ ├── heroes
│ └── meepo.kv
│ └── npc_abilities_custom.txt
├── package-lock.json
├── package.json
├── scripts
├── install.js
├── launch.js
└── utils.js
└── src
├── common
├── events.d.ts
└── general.d.ts
├── panorama
├── hud.ts
├── manifest.ts
└── tsconfig.json
└── vscripts
├── GameMode.ts
├── abilities
└── heroes
│ └── meepo
│ └── earthbind_ts_example.ts
├── addon_game_mode.ts
├── lib
├── dota_ts_adapter.ts
├── timers.d.ts
├── timers.lua
└── tstl-utils.ts
├── modifiers
└── modifier_panic.ts
└── tsconfig.json
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = space
5 | indent_size = 4
6 | end_of_line = lf
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on: [push, pull_request]
4 |
5 | jobs:
6 | main:
7 | name: Main
8 | runs-on: ubuntu-latest
9 |
10 | steps:
11 | - uses: actions/checkout@v2
12 | - name: Use Node.js 18
13 | uses: actions/setup-node@v1
14 | with:
15 | node-version: 18
16 | - run: npm ci --ignore-scripts
17 | - run: npm run build
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
3 | /content/panorama/scripts/custom_game/**/*.js
4 | /game/scripts/vscripts/**/*.lua
5 |
6 | # Dota 2 stuff
7 | *.bin
8 | *.vcss_c
9 | *.vxml_c
10 | *.vtex_c
11 | *.vjs_c
12 | *.vpcf_c
13 | *.vmat_c
14 | *.vpk
15 | *.vmdl_c
16 | *.vmesh_c
17 | *.vsndevts_c
18 | *.vsnd_c
19 | panorama_debugger.cfg
20 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "css.lint.unknownProperties": "ignore"
3 | }
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | // See https://go.microsoft.com/fwlink/?LinkId=733558
3 | // for the documentation about the tasks.json format
4 | "version": "2.0.0",
5 | "tasks": [
6 | {
7 | "label": "Watch",
8 | "type": "npm",
9 | "script": "dev",
10 | "problemMatcher": "$tsc-watch",
11 | "isBackground": true,
12 | "group": {
13 | "kind": "build",
14 | "isDefault": true
15 | }
16 | }
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) ModDota
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ModDota template
2 |
3 | A template for Dota 2 Custom Games built with modern technologies.
4 |
5 | [This tutorial](https://moddota.com/scripting/Typescript/typescript-introduction/) explains how to set up and use the template.
6 |
7 | The template includes:
8 |
9 | - [TypeScript for Panorama](https://moddota.com/panorama/introduction-to-panorama-ui-with-typescript)
10 | - [TypeScript for VScripts](https://typescripttolua.github.io/)
11 | - Simple commands to build and launch your custom game
12 | - [Continuous Integration](#continuous-integration) support
13 |
14 | ## Getting Started
15 |
16 | 1. Clone this repository or, if you're planning to have a repository for your custom game on GitHub, [create a new repository from this template](https://help.github.com/en/github/creating-cloning-and-archiving-repositories/creating-a-repository-from-a-template) and clone it instead.
17 | 2. Open the directory of your custom game and change `name` field in `package.json` file to the name of your addon name.
18 | 3. Open terminal in that directory and run `npm install` to install dependencies. You also should run `npm update` once in a while to get tool updates.
19 |
20 | After that you can press `Ctrl+Shift+B` in VSCode or run `npm run dev` command in terminal to compile your code and watch for changes.
21 |
22 | ## Contents:
23 |
24 | * **[src/common]:** TypeScript .d.ts type declaration files with types that can be shared between Panorama and VScripts
25 | * **[src/vscripts]:** TypeScript code for Dota addon (Lua) vscripts. Compiles lua to game/scripts/vscripts.
26 | * **[src/panorama]:** TypeScript code for panorama UI. Compiles js to content/panorama/scripts/custom_game
27 |
28 | --
29 |
30 | * **[game/*]:** Dota game directory containing files such as npc kv files and compiled lua scripts.
31 | * **[content/*]:** Dota content directory containing panorama sources other than scripts (xml, css, compiled js)
32 |
33 | --
34 |
35 | * **[scripts/*]:** Repository installation scripts
36 |
37 | ## Continuous Integration
38 |
39 | This template includes a [GitHub Actions](https://github.com/features/actions) [workflow](.github/workflows/ci.yml) that builds your custom game on every commit and fails when there are type errors.
40 |
--------------------------------------------------------------------------------
/content/panorama/images/custom_game/tstl.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ModDota/TypeScriptAddonTemplate/cc021e25d9314015fc2338850ef85593e2ffcbde/content/panorama/images/custom_game/tstl.png
--------------------------------------------------------------------------------
/content/panorama/layout/custom_game/custom_ui_manifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/content/panorama/layout/custom_game/hud.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/content/panorama/styles/custom_game/hud.css:
--------------------------------------------------------------------------------
1 | .MainHud {
2 | width: 100%;
3 | height: 100%;
4 | }
5 |
6 | #ExamplePanel {
7 | width: 500px;
8 | height: 500px;
9 | /* Using a background image already in dota */
10 | background-image: url("s2r://panorama/images/loadingscreens/international_2023_ls_3/loadingscreen.vtex");
11 | background-repeat: no-repeat;
12 | background-position: center;
13 | background-size: cover;
14 |
15 | border-style: solid;
16 | border-width: 3px;
17 | border-color: rgb(0, 0, 0);
18 |
19 | horizontal-align: center;
20 | vertical-align: center;
21 | }
22 |
23 | #ExamplePanel #BannerImage {
24 | width: 200px;
25 | height: 200px;
26 | horizontal-align: center;
27 | y: 40px;
28 | }
29 |
30 | #ExamplePanel .ExampleTitle {
31 | horizontal-align: center;
32 | font-size: 30px;
33 | y: 260px;
34 | }
35 |
36 | #ExamplePanel .ExampleParagraph {
37 | y: 320px;
38 | margin-left: 30px;
39 | margin-right: 30px;
40 | }
41 |
42 | #ExamplePanel #CloseButton {
43 | width: 100px;
44 | height: 50px;
45 | background-color: rgb(228, 228, 228);
46 | horizontal-align: center;
47 | vertical-align: bottom;
48 | y: -30px;
49 | }
50 |
51 | #ExamplePanel #CloseButton Label {
52 | horizontal-align: center;
53 | vertical-align: center;
54 | color: black;
55 | }
56 |
57 | #ExamplePanel #CloseButton:hover {
58 | background-color: rgb(0, 135, 245);
59 | }
60 |
61 | #ExamplePanel #CloseButton:hover Label {
62 | color: white;
63 | }
64 |
65 |
66 |
--------------------------------------------------------------------------------
/game/scripts/npc/heroes/meepo.kv:
--------------------------------------------------------------------------------
1 | /// ///
2 | /// ModDota AbilityLuaSpellLibrary spells for Meepo ///
3 | /// ///
4 | /// Last Update: 7.37e ///
5 | /// ///
6 |
7 | "DOTAAbilities"
8 | {
9 | //=================================================================================================================
10 | // Meepo: Earthbind
11 | //=================================================================================================================
12 | "meepo_earthbind_ts_example"
13 | {
14 | // General
15 | //-------------------------------------------------------------------------------------------------------------
16 | "BaseClass" "ability_lua"
17 | "ScriptFile" "abilities/heroes/meepo/earthbind_ts_example.lua"
18 | "AbilityTextureName" "meepo_earthbind"
19 | "AbilityBehavior" "DOTA_ABILITY_BEHAVIOR_AOE | DOTA_ABILITY_BEHAVIOR_POINT | DOTA_ABILITY_BEHAVIOR_IGNORE_BACKSWING"
20 | "SpellImmunityType" "SPELL_IMMUNITY_ENEMIES_NO"
21 | "SpellDispellableType" "SPELL_DISPELLABLE_YES"
22 | "FightRecapLevel" "1"
23 | "AbilitySound" "Hero_Meepo.Earthbind.Cast"
24 |
25 |
26 | // Casting
27 | //-------------------------------------------------------------------------------------------------------------
28 | "AbilityCastRange" "1250"
29 | "AbilityCastPoint" "0.3"
30 |
31 | // Time
32 | //-------------------------------------------------------------------------------------------------------------
33 | "AbilityCooldown" "2"
34 |
35 | // Cost
36 | //-------------------------------------------------------------------------------------------------------------
37 | "AbilityManaCost" "10"
38 |
39 | // Special
40 | //-------------------------------------------------------------------------------------------------------------
41 | "AbilityValues"
42 | {
43 | "duration" "2.0"
44 | "radius"
45 | {
46 | "value" "220"
47 | "affected_by_aoe_increase" "1"
48 | }
49 | "speed" "1200"
50 | "megameepo_net_offset"
51 | {
52 | "value" "160"
53 | "affected_by_aoe_increase" "1"
54 | }
55 | "cooldown" "2"
56 | "LinkedSpecialBonus" "special_bonus_unique_meepo3"
57 | }
58 | "AbilityCastAnimation" "ACT_DOTA_CAST_ABILITY_1"
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/game/scripts/npc/npc_abilities_custom.txt:
--------------------------------------------------------------------------------
1 | #base "heroes/meepo.kv"
2 |
3 | "DOTAAbilities"
4 | {
5 | }
6 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "TypeScriptAddonTemplate",
3 | "lockfileVersion": 3,
4 | "requires": true,
5 | "packages": {
6 | "": {
7 | "hasInstallScript": true,
8 | "devDependencies": {
9 | "@moddota/dota-lua-types": "^4.34.1",
10 | "@moddota/find-steam-app": "^1.1.0",
11 | "@moddota/panorama-types": "^1.30.0",
12 | "fs-extra": "^9.0.0",
13 | "npm-run-all": "^4.1.5",
14 | "typescript": "5.5.2",
15 | "typescript-to-lua": "^1.26.0"
16 | }
17 | },
18 | "node_modules/@moddota/dota-lua-types": {
19 | "version": "4.34.1",
20 | "resolved": "https://registry.npmjs.org/@moddota/dota-lua-types/-/dota-lua-types-4.34.1.tgz",
21 | "integrity": "sha512-S72wqi1RRo8Tju15jz3NfcwmnOFpzQ6qaHFcLFrlok/G8yR4EJ7eceFJJ+kIJOw5VoE5U6+G7ScBC36IAVQ7gA==",
22 | "dev": true,
23 | "dependencies": {
24 | "lua-types": "^2.13.1"
25 | },
26 | "peerDependencies": {
27 | "@typescript-to-lua/language-extensions": "^1.0.0",
28 | "typescript": "^4.0.0 || ^5.0.0"
29 | }
30 | },
31 | "node_modules/@moddota/find-steam-app": {
32 | "version": "1.1.0",
33 | "resolved": "https://registry.npmjs.org/@moddota/find-steam-app/-/find-steam-app-1.1.0.tgz",
34 | "integrity": "sha512-bmh6r3OAI9ic7VQxP45BMdzIaUt8tgQ4Ykafi1iXCM/zFpCrkTiCxylSRLfeJ4ugTB0oz0iX/JyD08j/c6F3Qg==",
35 | "dev": true,
36 | "dependencies": {
37 | "execa": "^1.0.0",
38 | "fs-extra": "^7.0.1",
39 | "p-filter": "^2.1.0",
40 | "tslib": "^1.9.3",
41 | "vdf-extra": "^2.2.2"
42 | }
43 | },
44 | "node_modules/@moddota/find-steam-app/node_modules/fs-extra": {
45 | "version": "7.0.1",
46 | "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz",
47 | "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
48 | "dev": true,
49 | "dependencies": {
50 | "graceful-fs": "^4.1.2",
51 | "jsonfile": "^4.0.0",
52 | "universalify": "^0.1.0"
53 | },
54 | "engines": {
55 | "node": ">=6 <7 || >=8"
56 | }
57 | },
58 | "node_modules/@moddota/panorama-types": {
59 | "version": "1.30.0",
60 | "resolved": "https://registry.npmjs.org/@moddota/panorama-types/-/panorama-types-1.30.0.tgz",
61 | "integrity": "sha512-/1X21DtdzOazR4RZT4vmsVXcXvT7BfmrdEPgWroMV3B8pparfHWTS6ORw2Yko8ea3WIe2u1a8KWehxEXh0ekxw==",
62 | "dev": true,
63 | "dependencies": {
64 | "tslib": "^2.0.3"
65 | },
66 | "peerDependencies": {
67 | "typescript": "^4.0.0 || ^5.0.0"
68 | }
69 | },
70 | "node_modules/@moddota/panorama-types/node_modules/tslib": {
71 | "version": "2.3.1",
72 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
73 | "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==",
74 | "dev": true
75 | },
76 | "node_modules/@typescript-to-lua/language-extensions": {
77 | "version": "1.19.0",
78 | "resolved": "https://registry.npmjs.org/@typescript-to-lua/language-extensions/-/language-extensions-1.19.0.tgz",
79 | "integrity": "sha512-Os5wOKwviTD4LeqI29N0btYOjokSJ97iCf45EOjIABlb5IwNQy7AE/AqZJobRw3ywHH8+KzJUMkEirWPzh2tUA==",
80 | "dev": true
81 | },
82 | "node_modules/ansi-styles": {
83 | "version": "3.2.1",
84 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
85 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
86 | "dev": true,
87 | "dependencies": {
88 | "color-convert": "^1.9.0"
89 | },
90 | "engines": {
91 | "node": ">=4"
92 | }
93 | },
94 | "node_modules/array-buffer-byte-length": {
95 | "version": "1.0.0",
96 | "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz",
97 | "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==",
98 | "dev": true,
99 | "dependencies": {
100 | "call-bind": "^1.0.2",
101 | "is-array-buffer": "^3.0.1"
102 | },
103 | "funding": {
104 | "url": "https://github.com/sponsors/ljharb"
105 | }
106 | },
107 | "node_modules/at-least-node": {
108 | "version": "1.0.0",
109 | "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz",
110 | "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==",
111 | "dev": true,
112 | "engines": {
113 | "node": ">= 4.0.0"
114 | }
115 | },
116 | "node_modules/available-typed-arrays": {
117 | "version": "1.0.5",
118 | "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz",
119 | "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==",
120 | "dev": true,
121 | "engines": {
122 | "node": ">= 0.4"
123 | },
124 | "funding": {
125 | "url": "https://github.com/sponsors/ljharb"
126 | }
127 | },
128 | "node_modules/balanced-match": {
129 | "version": "1.0.2",
130 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
131 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
132 | "dev": true
133 | },
134 | "node_modules/brace-expansion": {
135 | "version": "1.1.11",
136 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
137 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
138 | "dev": true,
139 | "dependencies": {
140 | "balanced-match": "^1.0.0",
141 | "concat-map": "0.0.1"
142 | }
143 | },
144 | "node_modules/call-bind": {
145 | "version": "1.0.2",
146 | "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
147 | "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
148 | "dev": true,
149 | "dependencies": {
150 | "function-bind": "^1.1.1",
151 | "get-intrinsic": "^1.0.2"
152 | },
153 | "funding": {
154 | "url": "https://github.com/sponsors/ljharb"
155 | }
156 | },
157 | "node_modules/chalk": {
158 | "version": "2.4.2",
159 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
160 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
161 | "dev": true,
162 | "dependencies": {
163 | "ansi-styles": "^3.2.1",
164 | "escape-string-regexp": "^1.0.5",
165 | "supports-color": "^5.3.0"
166 | },
167 | "engines": {
168 | "node": ">=4"
169 | }
170 | },
171 | "node_modules/color-convert": {
172 | "version": "1.9.3",
173 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
174 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
175 | "dev": true,
176 | "dependencies": {
177 | "color-name": "1.1.3"
178 | }
179 | },
180 | "node_modules/color-name": {
181 | "version": "1.1.3",
182 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
183 | "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
184 | "dev": true
185 | },
186 | "node_modules/concat-map": {
187 | "version": "0.0.1",
188 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
189 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
190 | "dev": true
191 | },
192 | "node_modules/cross-spawn": {
193 | "version": "6.0.5",
194 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
195 | "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
196 | "dev": true,
197 | "dependencies": {
198 | "nice-try": "^1.0.4",
199 | "path-key": "^2.0.1",
200 | "semver": "^5.5.0",
201 | "shebang-command": "^1.2.0",
202 | "which": "^1.2.9"
203 | },
204 | "engines": {
205 | "node": ">=4.8"
206 | }
207 | },
208 | "node_modules/define-properties": {
209 | "version": "1.2.0",
210 | "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz",
211 | "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==",
212 | "dev": true,
213 | "dependencies": {
214 | "has-property-descriptors": "^1.0.0",
215 | "object-keys": "^1.1.1"
216 | },
217 | "engines": {
218 | "node": ">= 0.4"
219 | },
220 | "funding": {
221 | "url": "https://github.com/sponsors/ljharb"
222 | }
223 | },
224 | "node_modules/end-of-stream": {
225 | "version": "1.4.4",
226 | "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
227 | "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
228 | "dev": true,
229 | "dependencies": {
230 | "once": "^1.4.0"
231 | }
232 | },
233 | "node_modules/enhanced-resolve": {
234 | "version": "5.8.2",
235 | "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.2.tgz",
236 | "integrity": "sha512-F27oB3WuHDzvR2DOGNTaYy0D5o0cnrv8TeI482VM4kYgQd/FT9lUQwuNsJ0oOHtBUq7eiW5ytqzp7nBFknL+GA==",
237 | "dev": true,
238 | "dependencies": {
239 | "graceful-fs": "^4.2.4",
240 | "tapable": "^2.2.0"
241 | },
242 | "engines": {
243 | "node": ">=10.13.0"
244 | }
245 | },
246 | "node_modules/error-ex": {
247 | "version": "1.3.2",
248 | "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
249 | "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
250 | "dev": true,
251 | "dependencies": {
252 | "is-arrayish": "^0.2.1"
253 | }
254 | },
255 | "node_modules/es-abstract": {
256 | "version": "1.21.2",
257 | "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.2.tgz",
258 | "integrity": "sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==",
259 | "dev": true,
260 | "dependencies": {
261 | "array-buffer-byte-length": "^1.0.0",
262 | "available-typed-arrays": "^1.0.5",
263 | "call-bind": "^1.0.2",
264 | "es-set-tostringtag": "^2.0.1",
265 | "es-to-primitive": "^1.2.1",
266 | "function.prototype.name": "^1.1.5",
267 | "get-intrinsic": "^1.2.0",
268 | "get-symbol-description": "^1.0.0",
269 | "globalthis": "^1.0.3",
270 | "gopd": "^1.0.1",
271 | "has": "^1.0.3",
272 | "has-property-descriptors": "^1.0.0",
273 | "has-proto": "^1.0.1",
274 | "has-symbols": "^1.0.3",
275 | "internal-slot": "^1.0.5",
276 | "is-array-buffer": "^3.0.2",
277 | "is-callable": "^1.2.7",
278 | "is-negative-zero": "^2.0.2",
279 | "is-regex": "^1.1.4",
280 | "is-shared-array-buffer": "^1.0.2",
281 | "is-string": "^1.0.7",
282 | "is-typed-array": "^1.1.10",
283 | "is-weakref": "^1.0.2",
284 | "object-inspect": "^1.12.3",
285 | "object-keys": "^1.1.1",
286 | "object.assign": "^4.1.4",
287 | "regexp.prototype.flags": "^1.4.3",
288 | "safe-regex-test": "^1.0.0",
289 | "string.prototype.trim": "^1.2.7",
290 | "string.prototype.trimend": "^1.0.6",
291 | "string.prototype.trimstart": "^1.0.6",
292 | "typed-array-length": "^1.0.4",
293 | "unbox-primitive": "^1.0.2",
294 | "which-typed-array": "^1.1.9"
295 | },
296 | "engines": {
297 | "node": ">= 0.4"
298 | },
299 | "funding": {
300 | "url": "https://github.com/sponsors/ljharb"
301 | }
302 | },
303 | "node_modules/es-set-tostringtag": {
304 | "version": "2.0.1",
305 | "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz",
306 | "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==",
307 | "dev": true,
308 | "dependencies": {
309 | "get-intrinsic": "^1.1.3",
310 | "has": "^1.0.3",
311 | "has-tostringtag": "^1.0.0"
312 | },
313 | "engines": {
314 | "node": ">= 0.4"
315 | }
316 | },
317 | "node_modules/es-to-primitive": {
318 | "version": "1.2.1",
319 | "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
320 | "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
321 | "dev": true,
322 | "dependencies": {
323 | "is-callable": "^1.1.4",
324 | "is-date-object": "^1.0.1",
325 | "is-symbol": "^1.0.2"
326 | },
327 | "engines": {
328 | "node": ">= 0.4"
329 | },
330 | "funding": {
331 | "url": "https://github.com/sponsors/ljharb"
332 | }
333 | },
334 | "node_modules/escape-string-regexp": {
335 | "version": "1.0.5",
336 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
337 | "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
338 | "dev": true,
339 | "engines": {
340 | "node": ">=0.8.0"
341 | }
342 | },
343 | "node_modules/execa": {
344 | "version": "1.0.0",
345 | "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz",
346 | "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==",
347 | "dev": true,
348 | "dependencies": {
349 | "cross-spawn": "^6.0.0",
350 | "get-stream": "^4.0.0",
351 | "is-stream": "^1.1.0",
352 | "npm-run-path": "^2.0.0",
353 | "p-finally": "^1.0.0",
354 | "signal-exit": "^3.0.0",
355 | "strip-eof": "^1.0.0"
356 | },
357 | "engines": {
358 | "node": ">=6"
359 | }
360 | },
361 | "node_modules/for-each": {
362 | "version": "0.3.3",
363 | "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
364 | "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==",
365 | "dev": true,
366 | "dependencies": {
367 | "is-callable": "^1.1.3"
368 | }
369 | },
370 | "node_modules/fs-extra": {
371 | "version": "9.0.0",
372 | "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.0.tgz",
373 | "integrity": "sha512-pmEYSk3vYsG/bF651KPUXZ+hvjpgWYw/Gc7W9NFUe3ZVLczKKWIij3IKpOrQcdw4TILtibFslZ0UmR8Vvzig4g==",
374 | "dev": true,
375 | "dependencies": {
376 | "at-least-node": "^1.0.0",
377 | "graceful-fs": "^4.2.0",
378 | "jsonfile": "^6.0.1",
379 | "universalify": "^1.0.0"
380 | },
381 | "engines": {
382 | "node": ">=10"
383 | }
384 | },
385 | "node_modules/fs-extra/node_modules/jsonfile": {
386 | "version": "6.0.1",
387 | "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz",
388 | "integrity": "sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==",
389 | "dev": true,
390 | "dependencies": {
391 | "graceful-fs": "^4.1.6",
392 | "universalify": "^1.0.0"
393 | }
394 | },
395 | "node_modules/fs-extra/node_modules/universalify": {
396 | "version": "1.0.0",
397 | "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz",
398 | "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==",
399 | "dev": true,
400 | "engines": {
401 | "node": ">= 10.0.0"
402 | }
403 | },
404 | "node_modules/function-bind": {
405 | "version": "1.1.1",
406 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
407 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
408 | "dev": true
409 | },
410 | "node_modules/function.prototype.name": {
411 | "version": "1.1.5",
412 | "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz",
413 | "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==",
414 | "dev": true,
415 | "dependencies": {
416 | "call-bind": "^1.0.2",
417 | "define-properties": "^1.1.3",
418 | "es-abstract": "^1.19.0",
419 | "functions-have-names": "^1.2.2"
420 | },
421 | "engines": {
422 | "node": ">= 0.4"
423 | },
424 | "funding": {
425 | "url": "https://github.com/sponsors/ljharb"
426 | }
427 | },
428 | "node_modules/functions-have-names": {
429 | "version": "1.2.3",
430 | "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
431 | "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
432 | "dev": true,
433 | "funding": {
434 | "url": "https://github.com/sponsors/ljharb"
435 | }
436 | },
437 | "node_modules/get-intrinsic": {
438 | "version": "1.2.1",
439 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz",
440 | "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==",
441 | "dev": true,
442 | "dependencies": {
443 | "function-bind": "^1.1.1",
444 | "has": "^1.0.3",
445 | "has-proto": "^1.0.1",
446 | "has-symbols": "^1.0.3"
447 | },
448 | "funding": {
449 | "url": "https://github.com/sponsors/ljharb"
450 | }
451 | },
452 | "node_modules/get-stream": {
453 | "version": "4.1.0",
454 | "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
455 | "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
456 | "dev": true,
457 | "dependencies": {
458 | "pump": "^3.0.0"
459 | },
460 | "engines": {
461 | "node": ">=6"
462 | }
463 | },
464 | "node_modules/get-symbol-description": {
465 | "version": "1.0.0",
466 | "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz",
467 | "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==",
468 | "dev": true,
469 | "dependencies": {
470 | "call-bind": "^1.0.2",
471 | "get-intrinsic": "^1.1.1"
472 | },
473 | "engines": {
474 | "node": ">= 0.4"
475 | },
476 | "funding": {
477 | "url": "https://github.com/sponsors/ljharb"
478 | }
479 | },
480 | "node_modules/globalthis": {
481 | "version": "1.0.3",
482 | "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz",
483 | "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==",
484 | "dev": true,
485 | "dependencies": {
486 | "define-properties": "^1.1.3"
487 | },
488 | "engines": {
489 | "node": ">= 0.4"
490 | },
491 | "funding": {
492 | "url": "https://github.com/sponsors/ljharb"
493 | }
494 | },
495 | "node_modules/gopd": {
496 | "version": "1.0.1",
497 | "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
498 | "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
499 | "dev": true,
500 | "dependencies": {
501 | "get-intrinsic": "^1.1.3"
502 | },
503 | "funding": {
504 | "url": "https://github.com/sponsors/ljharb"
505 | }
506 | },
507 | "node_modules/graceful-fs": {
508 | "version": "4.2.6",
509 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz",
510 | "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==",
511 | "dev": true
512 | },
513 | "node_modules/has": {
514 | "version": "1.0.3",
515 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
516 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
517 | "dev": true,
518 | "dependencies": {
519 | "function-bind": "^1.1.1"
520 | },
521 | "engines": {
522 | "node": ">= 0.4.0"
523 | }
524 | },
525 | "node_modules/has-bigints": {
526 | "version": "1.0.2",
527 | "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz",
528 | "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==",
529 | "dev": true,
530 | "funding": {
531 | "url": "https://github.com/sponsors/ljharb"
532 | }
533 | },
534 | "node_modules/has-flag": {
535 | "version": "3.0.0",
536 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
537 | "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
538 | "dev": true,
539 | "engines": {
540 | "node": ">=4"
541 | }
542 | },
543 | "node_modules/has-property-descriptors": {
544 | "version": "1.0.0",
545 | "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz",
546 | "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==",
547 | "dev": true,
548 | "dependencies": {
549 | "get-intrinsic": "^1.1.1"
550 | },
551 | "funding": {
552 | "url": "https://github.com/sponsors/ljharb"
553 | }
554 | },
555 | "node_modules/has-proto": {
556 | "version": "1.0.1",
557 | "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz",
558 | "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==",
559 | "dev": true,
560 | "engines": {
561 | "node": ">= 0.4"
562 | },
563 | "funding": {
564 | "url": "https://github.com/sponsors/ljharb"
565 | }
566 | },
567 | "node_modules/has-symbols": {
568 | "version": "1.0.3",
569 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
570 | "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
571 | "dev": true,
572 | "engines": {
573 | "node": ">= 0.4"
574 | },
575 | "funding": {
576 | "url": "https://github.com/sponsors/ljharb"
577 | }
578 | },
579 | "node_modules/has-tostringtag": {
580 | "version": "1.0.0",
581 | "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz",
582 | "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
583 | "dev": true,
584 | "dependencies": {
585 | "has-symbols": "^1.0.2"
586 | },
587 | "engines": {
588 | "node": ">= 0.4"
589 | },
590 | "funding": {
591 | "url": "https://github.com/sponsors/ljharb"
592 | }
593 | },
594 | "node_modules/hosted-git-info": {
595 | "version": "2.8.9",
596 | "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
597 | "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
598 | "dev": true
599 | },
600 | "node_modules/internal-slot": {
601 | "version": "1.0.5",
602 | "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz",
603 | "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==",
604 | "dev": true,
605 | "dependencies": {
606 | "get-intrinsic": "^1.2.0",
607 | "has": "^1.0.3",
608 | "side-channel": "^1.0.4"
609 | },
610 | "engines": {
611 | "node": ">= 0.4"
612 | }
613 | },
614 | "node_modules/is-array-buffer": {
615 | "version": "3.0.2",
616 | "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz",
617 | "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==",
618 | "dev": true,
619 | "dependencies": {
620 | "call-bind": "^1.0.2",
621 | "get-intrinsic": "^1.2.0",
622 | "is-typed-array": "^1.1.10"
623 | },
624 | "funding": {
625 | "url": "https://github.com/sponsors/ljharb"
626 | }
627 | },
628 | "node_modules/is-arrayish": {
629 | "version": "0.2.1",
630 | "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
631 | "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
632 | "dev": true
633 | },
634 | "node_modules/is-bigint": {
635 | "version": "1.0.4",
636 | "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz",
637 | "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==",
638 | "dev": true,
639 | "dependencies": {
640 | "has-bigints": "^1.0.1"
641 | },
642 | "funding": {
643 | "url": "https://github.com/sponsors/ljharb"
644 | }
645 | },
646 | "node_modules/is-boolean-object": {
647 | "version": "1.1.2",
648 | "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz",
649 | "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==",
650 | "dev": true,
651 | "dependencies": {
652 | "call-bind": "^1.0.2",
653 | "has-tostringtag": "^1.0.0"
654 | },
655 | "engines": {
656 | "node": ">= 0.4"
657 | },
658 | "funding": {
659 | "url": "https://github.com/sponsors/ljharb"
660 | }
661 | },
662 | "node_modules/is-callable": {
663 | "version": "1.2.7",
664 | "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
665 | "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
666 | "dev": true,
667 | "engines": {
668 | "node": ">= 0.4"
669 | },
670 | "funding": {
671 | "url": "https://github.com/sponsors/ljharb"
672 | }
673 | },
674 | "node_modules/is-core-module": {
675 | "version": "2.6.0",
676 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.6.0.tgz",
677 | "integrity": "sha512-wShG8vs60jKfPWpF2KZRaAtvt3a20OAn7+IJ6hLPECpSABLcKtFKTTI4ZtH5QcBruBHlq+WsdHWyz0BCZW7svQ==",
678 | "dev": true,
679 | "dependencies": {
680 | "has": "^1.0.3"
681 | }
682 | },
683 | "node_modules/is-date-object": {
684 | "version": "1.0.5",
685 | "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz",
686 | "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==",
687 | "dev": true,
688 | "dependencies": {
689 | "has-tostringtag": "^1.0.0"
690 | },
691 | "engines": {
692 | "node": ">= 0.4"
693 | },
694 | "funding": {
695 | "url": "https://github.com/sponsors/ljharb"
696 | }
697 | },
698 | "node_modules/is-negative-zero": {
699 | "version": "2.0.2",
700 | "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz",
701 | "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==",
702 | "dev": true,
703 | "engines": {
704 | "node": ">= 0.4"
705 | },
706 | "funding": {
707 | "url": "https://github.com/sponsors/ljharb"
708 | }
709 | },
710 | "node_modules/is-number-object": {
711 | "version": "1.0.7",
712 | "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz",
713 | "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==",
714 | "dev": true,
715 | "dependencies": {
716 | "has-tostringtag": "^1.0.0"
717 | },
718 | "engines": {
719 | "node": ">= 0.4"
720 | },
721 | "funding": {
722 | "url": "https://github.com/sponsors/ljharb"
723 | }
724 | },
725 | "node_modules/is-regex": {
726 | "version": "1.1.4",
727 | "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
728 | "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==",
729 | "dev": true,
730 | "dependencies": {
731 | "call-bind": "^1.0.2",
732 | "has-tostringtag": "^1.0.0"
733 | },
734 | "engines": {
735 | "node": ">= 0.4"
736 | },
737 | "funding": {
738 | "url": "https://github.com/sponsors/ljharb"
739 | }
740 | },
741 | "node_modules/is-shared-array-buffer": {
742 | "version": "1.0.2",
743 | "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz",
744 | "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==",
745 | "dev": true,
746 | "dependencies": {
747 | "call-bind": "^1.0.2"
748 | },
749 | "funding": {
750 | "url": "https://github.com/sponsors/ljharb"
751 | }
752 | },
753 | "node_modules/is-stream": {
754 | "version": "1.1.0",
755 | "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
756 | "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=",
757 | "dev": true,
758 | "engines": {
759 | "node": ">=0.10.0"
760 | }
761 | },
762 | "node_modules/is-string": {
763 | "version": "1.0.7",
764 | "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz",
765 | "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==",
766 | "dev": true,
767 | "dependencies": {
768 | "has-tostringtag": "^1.0.0"
769 | },
770 | "engines": {
771 | "node": ">= 0.4"
772 | },
773 | "funding": {
774 | "url": "https://github.com/sponsors/ljharb"
775 | }
776 | },
777 | "node_modules/is-symbol": {
778 | "version": "1.0.4",
779 | "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz",
780 | "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==",
781 | "dev": true,
782 | "dependencies": {
783 | "has-symbols": "^1.0.2"
784 | },
785 | "engines": {
786 | "node": ">= 0.4"
787 | },
788 | "funding": {
789 | "url": "https://github.com/sponsors/ljharb"
790 | }
791 | },
792 | "node_modules/is-typed-array": {
793 | "version": "1.1.10",
794 | "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz",
795 | "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==",
796 | "dev": true,
797 | "dependencies": {
798 | "available-typed-arrays": "^1.0.5",
799 | "call-bind": "^1.0.2",
800 | "for-each": "^0.3.3",
801 | "gopd": "^1.0.1",
802 | "has-tostringtag": "^1.0.0"
803 | },
804 | "engines": {
805 | "node": ">= 0.4"
806 | },
807 | "funding": {
808 | "url": "https://github.com/sponsors/ljharb"
809 | }
810 | },
811 | "node_modules/is-weakref": {
812 | "version": "1.0.2",
813 | "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz",
814 | "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==",
815 | "dev": true,
816 | "dependencies": {
817 | "call-bind": "^1.0.2"
818 | },
819 | "funding": {
820 | "url": "https://github.com/sponsors/ljharb"
821 | }
822 | },
823 | "node_modules/isexe": {
824 | "version": "2.0.0",
825 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
826 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
827 | "dev": true
828 | },
829 | "node_modules/json-parse-better-errors": {
830 | "version": "1.0.2",
831 | "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
832 | "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
833 | "dev": true
834 | },
835 | "node_modules/jsonfile": {
836 | "version": "4.0.0",
837 | "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
838 | "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
839 | "dev": true,
840 | "dependencies": {
841 | "graceful-fs": "^4.1.6"
842 | }
843 | },
844 | "node_modules/load-json-file": {
845 | "version": "4.0.0",
846 | "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz",
847 | "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==",
848 | "dev": true,
849 | "dependencies": {
850 | "graceful-fs": "^4.1.2",
851 | "parse-json": "^4.0.0",
852 | "pify": "^3.0.0",
853 | "strip-bom": "^3.0.0"
854 | },
855 | "engines": {
856 | "node": ">=4"
857 | }
858 | },
859 | "node_modules/lodash": {
860 | "version": "4.17.21",
861 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
862 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
863 | "dev": true
864 | },
865 | "node_modules/lua-types": {
866 | "version": "2.13.1",
867 | "resolved": "https://registry.npmjs.org/lua-types/-/lua-types-2.13.1.tgz",
868 | "integrity": "sha512-rRwtvX6kS+5MpuO3xpvKsnYjdSDDI064Qq1OqX8gY+r+0l7m3dFLiZPDFoHqH22jaBpEvcHcPs6+WD7qkdmFsA==",
869 | "dev": true
870 | },
871 | "node_modules/memorystream": {
872 | "version": "0.3.1",
873 | "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz",
874 | "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==",
875 | "dev": true,
876 | "engines": {
877 | "node": ">= 0.10.0"
878 | }
879 | },
880 | "node_modules/minimatch": {
881 | "version": "3.1.2",
882 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
883 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
884 | "dev": true,
885 | "dependencies": {
886 | "brace-expansion": "^1.1.7"
887 | },
888 | "engines": {
889 | "node": "*"
890 | }
891 | },
892 | "node_modules/nice-try": {
893 | "version": "1.0.5",
894 | "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
895 | "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
896 | "dev": true
897 | },
898 | "node_modules/normalize-package-data": {
899 | "version": "2.5.0",
900 | "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
901 | "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
902 | "dev": true,
903 | "dependencies": {
904 | "hosted-git-info": "^2.1.4",
905 | "resolve": "^1.10.0",
906 | "semver": "2 || 3 || 4 || 5",
907 | "validate-npm-package-license": "^3.0.1"
908 | }
909 | },
910 | "node_modules/npm-run-all": {
911 | "version": "4.1.5",
912 | "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz",
913 | "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==",
914 | "dev": true,
915 | "dependencies": {
916 | "ansi-styles": "^3.2.1",
917 | "chalk": "^2.4.1",
918 | "cross-spawn": "^6.0.5",
919 | "memorystream": "^0.3.1",
920 | "minimatch": "^3.0.4",
921 | "pidtree": "^0.3.0",
922 | "read-pkg": "^3.0.0",
923 | "shell-quote": "^1.6.1",
924 | "string.prototype.padend": "^3.0.0"
925 | },
926 | "bin": {
927 | "npm-run-all": "bin/npm-run-all/index.js",
928 | "run-p": "bin/run-p/index.js",
929 | "run-s": "bin/run-s/index.js"
930 | },
931 | "engines": {
932 | "node": ">= 4"
933 | }
934 | },
935 | "node_modules/npm-run-path": {
936 | "version": "2.0.2",
937 | "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
938 | "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=",
939 | "dev": true,
940 | "dependencies": {
941 | "path-key": "^2.0.0"
942 | },
943 | "engines": {
944 | "node": ">=4"
945 | }
946 | },
947 | "node_modules/object-inspect": {
948 | "version": "1.12.3",
949 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz",
950 | "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==",
951 | "dev": true,
952 | "funding": {
953 | "url": "https://github.com/sponsors/ljharb"
954 | }
955 | },
956 | "node_modules/object-keys": {
957 | "version": "1.1.1",
958 | "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
959 | "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
960 | "dev": true,
961 | "engines": {
962 | "node": ">= 0.4"
963 | }
964 | },
965 | "node_modules/object.assign": {
966 | "version": "4.1.4",
967 | "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz",
968 | "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==",
969 | "dev": true,
970 | "dependencies": {
971 | "call-bind": "^1.0.2",
972 | "define-properties": "^1.1.4",
973 | "has-symbols": "^1.0.3",
974 | "object-keys": "^1.1.1"
975 | },
976 | "engines": {
977 | "node": ">= 0.4"
978 | },
979 | "funding": {
980 | "url": "https://github.com/sponsors/ljharb"
981 | }
982 | },
983 | "node_modules/once": {
984 | "version": "1.4.0",
985 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
986 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
987 | "dev": true,
988 | "dependencies": {
989 | "wrappy": "1"
990 | }
991 | },
992 | "node_modules/p-filter": {
993 | "version": "2.1.0",
994 | "resolved": "https://registry.npmjs.org/p-filter/-/p-filter-2.1.0.tgz",
995 | "integrity": "sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==",
996 | "dev": true,
997 | "dependencies": {
998 | "p-map": "^2.0.0"
999 | },
1000 | "engines": {
1001 | "node": ">=8"
1002 | }
1003 | },
1004 | "node_modules/p-finally": {
1005 | "version": "1.0.0",
1006 | "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
1007 | "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=",
1008 | "dev": true,
1009 | "engines": {
1010 | "node": ">=4"
1011 | }
1012 | },
1013 | "node_modules/p-map": {
1014 | "version": "2.1.0",
1015 | "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz",
1016 | "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==",
1017 | "dev": true,
1018 | "engines": {
1019 | "node": ">=6"
1020 | }
1021 | },
1022 | "node_modules/parse-json": {
1023 | "version": "4.0.0",
1024 | "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
1025 | "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==",
1026 | "dev": true,
1027 | "dependencies": {
1028 | "error-ex": "^1.3.1",
1029 | "json-parse-better-errors": "^1.0.1"
1030 | },
1031 | "engines": {
1032 | "node": ">=4"
1033 | }
1034 | },
1035 | "node_modules/path-key": {
1036 | "version": "2.0.1",
1037 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
1038 | "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
1039 | "dev": true,
1040 | "engines": {
1041 | "node": ">=4"
1042 | }
1043 | },
1044 | "node_modules/path-parse": {
1045 | "version": "1.0.7",
1046 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
1047 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
1048 | "dev": true
1049 | },
1050 | "node_modules/path-type": {
1051 | "version": "3.0.0",
1052 | "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz",
1053 | "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==",
1054 | "dev": true,
1055 | "dependencies": {
1056 | "pify": "^3.0.0"
1057 | },
1058 | "engines": {
1059 | "node": ">=4"
1060 | }
1061 | },
1062 | "node_modules/picomatch": {
1063 | "version": "2.3.1",
1064 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
1065 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
1066 | "dev": true,
1067 | "engines": {
1068 | "node": ">=8.6"
1069 | },
1070 | "funding": {
1071 | "url": "https://github.com/sponsors/jonschlinkert"
1072 | }
1073 | },
1074 | "node_modules/pidtree": {
1075 | "version": "0.3.1",
1076 | "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz",
1077 | "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==",
1078 | "dev": true,
1079 | "bin": {
1080 | "pidtree": "bin/pidtree.js"
1081 | },
1082 | "engines": {
1083 | "node": ">=0.10"
1084 | }
1085 | },
1086 | "node_modules/pify": {
1087 | "version": "3.0.0",
1088 | "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
1089 | "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==",
1090 | "dev": true,
1091 | "engines": {
1092 | "node": ">=4"
1093 | }
1094 | },
1095 | "node_modules/pump": {
1096 | "version": "3.0.0",
1097 | "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
1098 | "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
1099 | "dev": true,
1100 | "dependencies": {
1101 | "end-of-stream": "^1.1.0",
1102 | "once": "^1.3.1"
1103 | }
1104 | },
1105 | "node_modules/read-pkg": {
1106 | "version": "3.0.0",
1107 | "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz",
1108 | "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==",
1109 | "dev": true,
1110 | "dependencies": {
1111 | "load-json-file": "^4.0.0",
1112 | "normalize-package-data": "^2.3.2",
1113 | "path-type": "^3.0.0"
1114 | },
1115 | "engines": {
1116 | "node": ">=4"
1117 | }
1118 | },
1119 | "node_modules/regexp.prototype.flags": {
1120 | "version": "1.5.0",
1121 | "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz",
1122 | "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==",
1123 | "dev": true,
1124 | "dependencies": {
1125 | "call-bind": "^1.0.2",
1126 | "define-properties": "^1.2.0",
1127 | "functions-have-names": "^1.2.3"
1128 | },
1129 | "engines": {
1130 | "node": ">= 0.4"
1131 | },
1132 | "funding": {
1133 | "url": "https://github.com/sponsors/ljharb"
1134 | }
1135 | },
1136 | "node_modules/resolve": {
1137 | "version": "1.20.0",
1138 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
1139 | "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==",
1140 | "dev": true,
1141 | "dependencies": {
1142 | "is-core-module": "^2.2.0",
1143 | "path-parse": "^1.0.6"
1144 | }
1145 | },
1146 | "node_modules/safe-regex-test": {
1147 | "version": "1.0.0",
1148 | "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz",
1149 | "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==",
1150 | "dev": true,
1151 | "dependencies": {
1152 | "call-bind": "^1.0.2",
1153 | "get-intrinsic": "^1.1.3",
1154 | "is-regex": "^1.1.4"
1155 | },
1156 | "funding": {
1157 | "url": "https://github.com/sponsors/ljharb"
1158 | }
1159 | },
1160 | "node_modules/semver": {
1161 | "version": "5.7.2",
1162 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
1163 | "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
1164 | "dev": true,
1165 | "bin": {
1166 | "semver": "bin/semver"
1167 | }
1168 | },
1169 | "node_modules/shebang-command": {
1170 | "version": "1.2.0",
1171 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
1172 | "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
1173 | "dev": true,
1174 | "dependencies": {
1175 | "shebang-regex": "^1.0.0"
1176 | },
1177 | "engines": {
1178 | "node": ">=0.10.0"
1179 | }
1180 | },
1181 | "node_modules/shebang-regex": {
1182 | "version": "1.0.0",
1183 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
1184 | "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
1185 | "dev": true,
1186 | "engines": {
1187 | "node": ">=0.10.0"
1188 | }
1189 | },
1190 | "node_modules/shell-quote": {
1191 | "version": "1.8.1",
1192 | "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz",
1193 | "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==",
1194 | "dev": true,
1195 | "funding": {
1196 | "url": "https://github.com/sponsors/ljharb"
1197 | }
1198 | },
1199 | "node_modules/side-channel": {
1200 | "version": "1.0.4",
1201 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
1202 | "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
1203 | "dev": true,
1204 | "dependencies": {
1205 | "call-bind": "^1.0.0",
1206 | "get-intrinsic": "^1.0.2",
1207 | "object-inspect": "^1.9.0"
1208 | },
1209 | "funding": {
1210 | "url": "https://github.com/sponsors/ljharb"
1211 | }
1212 | },
1213 | "node_modules/signal-exit": {
1214 | "version": "3.0.2",
1215 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
1216 | "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
1217 | "dev": true
1218 | },
1219 | "node_modules/source-map": {
1220 | "version": "0.7.3",
1221 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
1222 | "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
1223 | "dev": true,
1224 | "engines": {
1225 | "node": ">= 8"
1226 | }
1227 | },
1228 | "node_modules/spdx-correct": {
1229 | "version": "3.2.0",
1230 | "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz",
1231 | "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==",
1232 | "dev": true,
1233 | "dependencies": {
1234 | "spdx-expression-parse": "^3.0.0",
1235 | "spdx-license-ids": "^3.0.0"
1236 | }
1237 | },
1238 | "node_modules/spdx-exceptions": {
1239 | "version": "2.3.0",
1240 | "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz",
1241 | "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==",
1242 | "dev": true
1243 | },
1244 | "node_modules/spdx-expression-parse": {
1245 | "version": "3.0.1",
1246 | "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
1247 | "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
1248 | "dev": true,
1249 | "dependencies": {
1250 | "spdx-exceptions": "^2.1.0",
1251 | "spdx-license-ids": "^3.0.0"
1252 | }
1253 | },
1254 | "node_modules/spdx-license-ids": {
1255 | "version": "3.0.13",
1256 | "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz",
1257 | "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==",
1258 | "dev": true
1259 | },
1260 | "node_modules/string.prototype.padend": {
1261 | "version": "3.1.4",
1262 | "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.4.tgz",
1263 | "integrity": "sha512-67otBXoksdjsnXXRUq+KMVTdlVRZ2af422Y0aTyTjVaoQkGr3mxl2Bc5emi7dOQ3OGVVQQskmLEWwFXwommpNw==",
1264 | "dev": true,
1265 | "dependencies": {
1266 | "call-bind": "^1.0.2",
1267 | "define-properties": "^1.1.4",
1268 | "es-abstract": "^1.20.4"
1269 | },
1270 | "engines": {
1271 | "node": ">= 0.4"
1272 | },
1273 | "funding": {
1274 | "url": "https://github.com/sponsors/ljharb"
1275 | }
1276 | },
1277 | "node_modules/string.prototype.trim": {
1278 | "version": "1.2.7",
1279 | "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz",
1280 | "integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==",
1281 | "dev": true,
1282 | "dependencies": {
1283 | "call-bind": "^1.0.2",
1284 | "define-properties": "^1.1.4",
1285 | "es-abstract": "^1.20.4"
1286 | },
1287 | "engines": {
1288 | "node": ">= 0.4"
1289 | },
1290 | "funding": {
1291 | "url": "https://github.com/sponsors/ljharb"
1292 | }
1293 | },
1294 | "node_modules/string.prototype.trimend": {
1295 | "version": "1.0.6",
1296 | "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz",
1297 | "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==",
1298 | "dev": true,
1299 | "dependencies": {
1300 | "call-bind": "^1.0.2",
1301 | "define-properties": "^1.1.4",
1302 | "es-abstract": "^1.20.4"
1303 | },
1304 | "funding": {
1305 | "url": "https://github.com/sponsors/ljharb"
1306 | }
1307 | },
1308 | "node_modules/string.prototype.trimstart": {
1309 | "version": "1.0.6",
1310 | "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz",
1311 | "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==",
1312 | "dev": true,
1313 | "dependencies": {
1314 | "call-bind": "^1.0.2",
1315 | "define-properties": "^1.1.4",
1316 | "es-abstract": "^1.20.4"
1317 | },
1318 | "funding": {
1319 | "url": "https://github.com/sponsors/ljharb"
1320 | }
1321 | },
1322 | "node_modules/strip-bom": {
1323 | "version": "3.0.0",
1324 | "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
1325 | "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
1326 | "dev": true,
1327 | "engines": {
1328 | "node": ">=4"
1329 | }
1330 | },
1331 | "node_modules/strip-eof": {
1332 | "version": "1.0.0",
1333 | "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
1334 | "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=",
1335 | "dev": true,
1336 | "engines": {
1337 | "node": ">=0.10.0"
1338 | }
1339 | },
1340 | "node_modules/supports-color": {
1341 | "version": "5.5.0",
1342 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
1343 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
1344 | "dev": true,
1345 | "dependencies": {
1346 | "has-flag": "^3.0.0"
1347 | },
1348 | "engines": {
1349 | "node": ">=4"
1350 | }
1351 | },
1352 | "node_modules/tapable": {
1353 | "version": "2.2.0",
1354 | "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.0.tgz",
1355 | "integrity": "sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw==",
1356 | "dev": true,
1357 | "engines": {
1358 | "node": ">=6"
1359 | }
1360 | },
1361 | "node_modules/tslib": {
1362 | "version": "1.10.0",
1363 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
1364 | "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==",
1365 | "dev": true
1366 | },
1367 | "node_modules/typed-array-length": {
1368 | "version": "1.0.4",
1369 | "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz",
1370 | "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==",
1371 | "dev": true,
1372 | "dependencies": {
1373 | "call-bind": "^1.0.2",
1374 | "for-each": "^0.3.3",
1375 | "is-typed-array": "^1.1.9"
1376 | },
1377 | "funding": {
1378 | "url": "https://github.com/sponsors/ljharb"
1379 | }
1380 | },
1381 | "node_modules/typescript": {
1382 | "version": "5.5.2",
1383 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.2.tgz",
1384 | "integrity": "sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew==",
1385 | "dev": true,
1386 | "bin": {
1387 | "tsc": "bin/tsc",
1388 | "tsserver": "bin/tsserver"
1389 | },
1390 | "engines": {
1391 | "node": ">=14.17"
1392 | }
1393 | },
1394 | "node_modules/typescript-to-lua": {
1395 | "version": "1.26.0",
1396 | "resolved": "https://registry.npmjs.org/typescript-to-lua/-/typescript-to-lua-1.26.0.tgz",
1397 | "integrity": "sha512-Eg8VytMyHzFmlaB0EyZG+jMyAW1McxP/WMuz2NuCLYwJc73zlQzm62d3RK+3qeU5ivnvFqrDuR7+61gvsuGcsA==",
1398 | "dev": true,
1399 | "dependencies": {
1400 | "@typescript-to-lua/language-extensions": "1.19.0",
1401 | "enhanced-resolve": "^5.8.2",
1402 | "picomatch": "^2.3.1",
1403 | "resolve": "^1.15.1",
1404 | "source-map": "^0.7.3"
1405 | },
1406 | "bin": {
1407 | "tstl": "dist/tstl.js"
1408 | },
1409 | "engines": {
1410 | "node": ">=16.10.0"
1411 | },
1412 | "peerDependencies": {
1413 | "typescript": "5.5.2"
1414 | }
1415 | },
1416 | "node_modules/unbox-primitive": {
1417 | "version": "1.0.2",
1418 | "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz",
1419 | "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==",
1420 | "dev": true,
1421 | "dependencies": {
1422 | "call-bind": "^1.0.2",
1423 | "has-bigints": "^1.0.2",
1424 | "has-symbols": "^1.0.3",
1425 | "which-boxed-primitive": "^1.0.2"
1426 | },
1427 | "funding": {
1428 | "url": "https://github.com/sponsors/ljharb"
1429 | }
1430 | },
1431 | "node_modules/universalify": {
1432 | "version": "0.1.2",
1433 | "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
1434 | "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
1435 | "dev": true,
1436 | "engines": {
1437 | "node": ">= 4.0.0"
1438 | }
1439 | },
1440 | "node_modules/validate-npm-package-license": {
1441 | "version": "3.0.4",
1442 | "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
1443 | "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
1444 | "dev": true,
1445 | "dependencies": {
1446 | "spdx-correct": "^3.0.0",
1447 | "spdx-expression-parse": "^3.0.0"
1448 | }
1449 | },
1450 | "node_modules/vdf-extra": {
1451 | "version": "2.2.2",
1452 | "resolved": "https://registry.npmjs.org/vdf-extra/-/vdf-extra-2.2.2.tgz",
1453 | "integrity": "sha512-wqu6E5PMjPzKQSVsWrbVoLp5Oy4pnxy59f/2xaZa7gbBjI6dYSAcpAqNIyZdixQFrNZrLbh1zkeQWWZ5tsnffA==",
1454 | "dev": true,
1455 | "dependencies": {
1456 | "lodash": "^4.17.10"
1457 | }
1458 | },
1459 | "node_modules/which": {
1460 | "version": "1.3.1",
1461 | "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
1462 | "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
1463 | "dev": true,
1464 | "dependencies": {
1465 | "isexe": "^2.0.0"
1466 | },
1467 | "bin": {
1468 | "which": "bin/which"
1469 | }
1470 | },
1471 | "node_modules/which-boxed-primitive": {
1472 | "version": "1.0.2",
1473 | "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz",
1474 | "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==",
1475 | "dev": true,
1476 | "dependencies": {
1477 | "is-bigint": "^1.0.1",
1478 | "is-boolean-object": "^1.1.0",
1479 | "is-number-object": "^1.0.4",
1480 | "is-string": "^1.0.5",
1481 | "is-symbol": "^1.0.3"
1482 | },
1483 | "funding": {
1484 | "url": "https://github.com/sponsors/ljharb"
1485 | }
1486 | },
1487 | "node_modules/which-typed-array": {
1488 | "version": "1.1.9",
1489 | "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz",
1490 | "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==",
1491 | "dev": true,
1492 | "dependencies": {
1493 | "available-typed-arrays": "^1.0.5",
1494 | "call-bind": "^1.0.2",
1495 | "for-each": "^0.3.3",
1496 | "gopd": "^1.0.1",
1497 | "has-tostringtag": "^1.0.0",
1498 | "is-typed-array": "^1.1.10"
1499 | },
1500 | "engines": {
1501 | "node": ">= 0.4"
1502 | },
1503 | "funding": {
1504 | "url": "https://github.com/sponsors/ljharb"
1505 | }
1506 | },
1507 | "node_modules/wrappy": {
1508 | "version": "1.0.2",
1509 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
1510 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
1511 | "dev": true
1512 | }
1513 | }
1514 | }
1515 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "",
3 | "private": true,
4 | "scripts": {
5 | "postinstall": "node scripts/install.js",
6 | "launch": "node scripts/launch.js",
7 | "build": "run-p build:*",
8 | "build:panorama": "tsc --project src/panorama/tsconfig.json",
9 | "build:vscripts": "tstl --project src/vscripts/tsconfig.json",
10 | "dev": "run-p dev:*",
11 | "dev:panorama": "tsc --project src/panorama/tsconfig.json --watch",
12 | "dev:vscripts": "tstl --project src/vscripts/tsconfig.json --watch"
13 | },
14 | "devDependencies": {
15 | "@moddota/dota-lua-types": "^4.34.1",
16 | "@moddota/find-steam-app": "^1.1.0",
17 | "@moddota/panorama-types": "^1.30.0",
18 | "fs-extra": "^9.0.0",
19 | "npm-run-all": "^4.1.5",
20 | "typescript": "5.5.2",
21 | "typescript-to-lua": "^1.26.0"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/scripts/install.js:
--------------------------------------------------------------------------------
1 | const assert = require("assert");
2 | const fs = require("fs-extra");
3 | const path = require("path");
4 | const { getAddonName, getDotaPath } = require("./utils");
5 |
6 | (async () => {
7 | const dotaPath = await getDotaPath();
8 | if (dotaPath === undefined) {
9 | console.log("No Dota 2 installation found. Addon linking is skipped.");
10 | return;
11 | }
12 |
13 | for (const directoryName of ["game", "content"]) {
14 | const sourcePath = path.resolve(__dirname, "..", directoryName);
15 | assert(fs.existsSync(sourcePath), `Could not find '${sourcePath}'`);
16 |
17 | const targetRoot = path.join(dotaPath, directoryName, "dota_addons");
18 | assert(fs.existsSync(targetRoot), `Could not find '${targetRoot}'`);
19 |
20 | const targetPath = path.join(dotaPath, directoryName, "dota_addons", getAddonName());
21 | if (fs.existsSync(targetPath)) {
22 | const isCorrect = fs.lstatSync(sourcePath).isSymbolicLink() && fs.realpathSync(sourcePath) === targetPath;
23 | if (isCorrect) {
24 | console.log(`Skipping '${sourcePath}' since it is already linked`);
25 | continue;
26 | } else {
27 | throw new Error(`'${targetPath}' is already linked to another directory`);
28 | }
29 | }
30 |
31 | fs.moveSync(sourcePath, targetPath);
32 | fs.symlinkSync(targetPath, sourcePath, "junction");
33 | console.log(`Linked ${sourcePath} <==> ${targetPath}`);
34 | }
35 | })().catch(error => {
36 | console.error(error);
37 | process.exit(1);
38 | });
39 |
--------------------------------------------------------------------------------
/scripts/launch.js:
--------------------------------------------------------------------------------
1 | const { spawn } = require("child_process");
2 | const path = require("path");
3 | const { getAddonName, getDotaPath } = require("./utils");
4 |
5 | (async () => {
6 | const dotaPath = await getDotaPath();
7 | const win64 = path.join(dotaPath, "game", "bin", "win64");
8 |
9 | // You can add any arguments there
10 | // For example `+dota_launch_custom_game ${getAddonName()} dota` would automatically load "dota" map
11 | const args = ["-novid", "-tools", "-addon", getAddonName()];
12 | spawn(path.join(win64, "dota2.exe"), args, { detached: true, cwd: win64 });
13 | })().catch(error => {
14 | console.error(error);
15 | process.exit(1);
16 | });
17 |
--------------------------------------------------------------------------------
/scripts/utils.js:
--------------------------------------------------------------------------------
1 | const { findSteamAppByName, SteamNotFoundError } = require("@moddota/find-steam-app");
2 | const packageJson = require("../package.json");
3 |
4 | module.exports.getAddonName = () => {
5 | if (!/^[a-z][\d_a-z]+$/.test(packageJson.name)) {
6 | throw new Error(
7 | "Addon name may consist only of lowercase characters, digits, and underscores " +
8 | "and should start with a letter. Edit `name` field in `package.json` file.",
9 | );
10 | }
11 |
12 | return packageJson.name;
13 | };
14 |
15 | module.exports.getDotaPath = async () => {
16 | try {
17 | return await findSteamAppByName("dota 2 beta");
18 | } catch (error) {
19 | if (!(error instanceof SteamNotFoundError)) {
20 | throw error;
21 | }
22 | }
23 | };
24 |
--------------------------------------------------------------------------------
/src/common/events.d.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This file contains types for the events you want to send between the UI (Panorama)
3 | * and the server (VScripts).
4 | *
5 | * IMPORTANT:
6 | *
7 | * The dota engine will change the type of event data slightly when it is sent, so on the
8 | * Panorama side your event handlers will have to handle NetworkedData, changes are:
9 | * - Booleans are turned to 0 | 1
10 | * - Arrays are automatically translated to objects when sending them as event. You have
11 | * to change them back into arrays yourself! See 'toArray()' in src/panorama/hud.ts
12 | */
13 |
14 | // To declare an event for use, add it to this table with the type of its data
15 | interface CustomGameEventDeclarations {
16 | example_event: ExampleEventData,
17 | ui_panel_closed: UIPanelClosedEventData
18 | }
19 |
20 | // Define the type of data sent by the example_event event
21 | interface ExampleEventData {
22 | myNumber: number;
23 | myBoolean: boolean;
24 | myString: string;
25 | myArrayOfNumbers: number[]
26 | }
27 |
28 | // This event has no data
29 | interface UIPanelClosedEventData {}
--------------------------------------------------------------------------------
/src/common/general.d.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This file contains some general types related to your game that can be shared between
3 | * front-end (Panorama) and back-end (VScripts). Only put stuff in here you need to share.
4 | */
5 |
6 | interface Color {
7 | r: number,
8 | g: number,
9 | b: number
10 | }
11 |
12 | interface UnitData {
13 | name: string,
14 | level: number
15 | }
--------------------------------------------------------------------------------
/src/panorama/hud.ts:
--------------------------------------------------------------------------------
1 | $.Msg("Hud panorama loaded");
2 |
3 | function OnCloseButtonClicked() {
4 | $.Msg("Example close button clicked");
5 |
6 | // Find panel by id
7 | const examplePanel = $("#ExamplePanel");
8 |
9 | // Remove panel
10 | examplePanel.DeleteAsync(0);
11 |
12 | // Send event to server
13 | GameEvents.SendCustomGameEventToServer("ui_panel_closed", {});
14 | }
15 |
16 | GameEvents.Subscribe("example_event", (data: NetworkedData) => {
17 | const myNumber = data.myNumber;
18 | const myString = data.myString;
19 |
20 | const myBoolean = data.myBoolean; // After sending to client this is now type 0 | 1!
21 |
22 | const myArrayObject = data.myArrayOfNumbers; // After sending this is now an object!
23 |
24 | const myArray = toArray(myArrayObject); // We can turn it back into an array ourselves.
25 |
26 | $.Msg("Received example event", myNumber, myString, myBoolean, myArrayObject, myArray);
27 |
28 | });
29 |
30 | /**
31 | * Turn a table object into an array.
32 | * @param obj The object to transform to an array.
33 | * @returns An array with items of the value type of the original object.
34 | */
35 | function toArray(obj: Record): T[] {
36 | const result = [];
37 |
38 | let key = 1;
39 | while (obj[key]) {
40 | result.push(obj[key]);
41 | key++;
42 | }
43 |
44 | return result;
45 | }
46 |
47 | async function sleep(time: number): Promise {
48 | return new Promise((resolve) => $.Schedule(time, resolve));
49 | }
50 |
--------------------------------------------------------------------------------
/src/panorama/manifest.ts:
--------------------------------------------------------------------------------
1 | $.Msg("ui manifest loaded");
--------------------------------------------------------------------------------
/src/panorama/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "rootDir": ".",
4 | "outDir": "../../content/panorama/scripts/custom_game",
5 | "target": "es2017",
6 | "lib": ["es2017"],
7 | "types": ["@moddota/panorama-types"],
8 | "moduleResolution": "node",
9 | "strict": true
10 | },
11 | "include": ["**/*.ts", "../common/**/*.ts"]
12 | }
13 |
--------------------------------------------------------------------------------
/src/vscripts/GameMode.ts:
--------------------------------------------------------------------------------
1 | import { reloadable } from "./lib/tstl-utils";
2 | import { modifier_panic } from "./modifiers/modifier_panic";
3 |
4 | const heroSelectionTime = 20;
5 |
6 | declare global {
7 | interface CDOTAGameRules {
8 | Addon: GameMode;
9 | }
10 | }
11 |
12 | @reloadable
13 | export class GameMode {
14 | public static Precache(this: void, context: CScriptPrecacheContext) {
15 | PrecacheResource("particle", "particles/units/heroes/hero_meepo/meepo_earthbind_projectile_fx.vpcf", context);
16 | PrecacheResource("soundfile", "soundevents/game_sounds_heroes/game_sounds_meepo.vsndevts", context);
17 | }
18 |
19 | public static Activate(this: void) {
20 | // When the addon activates, create a new instance of this GameMode class.
21 | GameRules.Addon = new GameMode();
22 | }
23 |
24 | constructor() {
25 | this.configure();
26 |
27 | // Register event listeners for dota engine events
28 | ListenToGameEvent("game_rules_state_change", () => this.OnStateChange(), undefined);
29 | ListenToGameEvent("npc_spawned", event => this.OnNpcSpawned(event), undefined);
30 |
31 | // Register event listeners for events from the UI
32 | CustomGameEventManager.RegisterListener("ui_panel_closed", (_, data) => {
33 | print(`Player ${data.PlayerID} has closed their UI panel.`);
34 |
35 | // Respond by sending back an example event
36 | const player = PlayerResource.GetPlayer(data.PlayerID)!;
37 | CustomGameEventManager.Send_ServerToPlayer(player, "example_event", {
38 | myNumber: 42,
39 | myBoolean: true,
40 | myString: "Hello!",
41 | myArrayOfNumbers: [1.414, 2.718, 3.142]
42 | });
43 |
44 | // Also apply the panic modifier to the sending player's hero
45 | const hero = player.GetAssignedHero();
46 | if (hero != undefined) { // Hero didn't spawn yet or dead
47 | hero.AddNewModifier(hero, undefined, modifier_panic.name, { duration: 5 });
48 | }
49 | });
50 | }
51 |
52 | private configure(): void {
53 | GameRules.SetCustomGameTeamMaxPlayers(DotaTeam.GOODGUYS, 3);
54 | GameRules.SetCustomGameTeamMaxPlayers(DotaTeam.BADGUYS, 3);
55 |
56 | GameRules.SetShowcaseTime(0);
57 | GameRules.SetHeroSelectionTime(heroSelectionTime);
58 | }
59 |
60 | public OnStateChange(): void {
61 | const state = GameRules.State_Get();
62 |
63 | // Add 4 bots to lobby in tools
64 | if (IsInToolsMode() && state == GameState.CUSTOM_GAME_SETUP) {
65 | for (let i = 0; i < 4; i++) {
66 | Tutorial.AddBot("npc_dota_hero_lina", "", "", false);
67 | }
68 | }
69 |
70 | if (state === GameState.CUSTOM_GAME_SETUP) {
71 | // Automatically skip setup in tools
72 | if (IsInToolsMode()) {
73 | Timers.CreateTimer(3, () => {
74 | GameRules.FinishCustomGameSetup();
75 | });
76 | }
77 | }
78 |
79 | // Start game once pregame hits
80 | if (state === GameState.PRE_GAME) {
81 | Timers.CreateTimer(0.2, () => this.StartGame());
82 | }
83 | }
84 |
85 | private StartGame(): void {
86 | print("Game starting!");
87 |
88 | // Do some stuff here
89 | }
90 |
91 | // Called on script_reload
92 | public Reload() {
93 | print("Script reloaded!");
94 |
95 | // Do some stuff here
96 | }
97 |
98 | private OnNpcSpawned(event: NpcSpawnedEvent) {
99 | // After a hero unit spawns, apply modifier_panic for 8 seconds
100 | const unit = EntIndexToHScript(event.entindex) as CDOTA_BaseNPC; // Cast to npc since this is the 'npc_spawned' event
101 | // Give all real heroes (not illusions) the meepo_earthbind_ts_example spell
102 | if (unit.IsRealHero()) {
103 | if (!unit.HasAbility("meepo_earthbind_ts_example")) {
104 | // Add lua ability to the unit
105 | unit.AddAbility("meepo_earthbind_ts_example");
106 | }
107 | }
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/src/vscripts/abilities/heroes/meepo/earthbind_ts_example.ts:
--------------------------------------------------------------------------------
1 | import { BaseAbility, registerAbility } from "../../../lib/dota_ts_adapter";
2 |
3 | @registerAbility()
4 | export class meepo_earthbind_ts_example extends BaseAbility {
5 | particle?: ParticleID;
6 |
7 | GetCooldown() {
8 | let cooldown = this.GetSpecialValueFor("cooldown");
9 | if (IsServer()) {
10 | const talent = this.GetCaster().FindAbilityByName("special_bonus_unique_meepo_3");
11 | if (talent) {
12 | cooldown -= talent.GetSpecialValueFor("value");
13 | }
14 | }
15 |
16 | return cooldown;
17 | }
18 |
19 | OnAbilityPhaseStart() {
20 | if (IsServer()) {
21 | this.GetCaster().EmitSound("Hero_Meepo.Earthbind.Cast");
22 | }
23 |
24 | return true;
25 | }
26 |
27 | OnAbilityPhaseInterrupted() {
28 | this.GetCaster().StopSound("Hero_Meepo.Earthbind.Cast");
29 | }
30 |
31 | OnSpellStart() {
32 | const caster = this.GetCaster();
33 | const point = this.GetCursorPosition();
34 | const projectileSpeed = this.GetSpecialValueFor("speed");
35 |
36 | const direction = ((point - caster.GetAbsOrigin()) as Vector).Normalized();
37 | direction.z = 0;
38 | const distance = ((point - caster.GetAbsOrigin()) as Vector).Length();
39 |
40 | const radius = this.GetSpecialValueFor("radius");
41 | this.particle = ParticleManager.CreateParticle(
42 | "particles/units/heroes/hero_meepo/meepo_earthbind_projectile_fx.vpcf",
43 | ParticleAttachment.CUSTOMORIGIN,
44 | caster,
45 | );
46 |
47 | ParticleManager.SetParticleControl(this.particle, 0, caster.GetAbsOrigin());
48 | ParticleManager.SetParticleControl(this.particle, 1, point);
49 | ParticleManager.SetParticleControl(this.particle, 2, Vector(projectileSpeed, 0, 0));
50 |
51 | ProjectileManager.CreateLinearProjectile({
52 | Ability: this,
53 | EffectName: "",
54 | vSpawnOrigin: caster.GetAbsOrigin(),
55 | fDistance: distance,
56 | fStartRadius: radius,
57 | fEndRadius: radius,
58 | Source: caster,
59 | bHasFrontalCone: false,
60 | iUnitTargetTeam: UnitTargetTeam.NONE,
61 | iUnitTargetFlags: UnitTargetFlags.NONE,
62 | iUnitTargetType: UnitTargetType.NONE,
63 | vVelocity: (direction * projectileSpeed) as Vector,
64 | bProvidesVision: true,
65 | iVisionRadius: radius,
66 | iVisionTeamNumber: caster.GetTeamNumber(),
67 | });
68 | }
69 |
70 | OnProjectileHit(_target: CDOTA_BaseNPC, location: Vector) {
71 | const caster = this.GetCaster();
72 | const duration = this.GetSpecialValueFor("duration");
73 | const radius = this.GetSpecialValueFor("radius");
74 |
75 | const units = FindUnitsInRadius(
76 | caster.GetTeamNumber(),
77 | location,
78 | undefined,
79 | radius,
80 | UnitTargetTeam.ENEMY,
81 | UnitTargetType.BASIC | UnitTargetType.HERO,
82 | UnitTargetFlags.NONE,
83 | 0,
84 | false,
85 | );
86 |
87 | for (const unit of units) {
88 | unit.AddNewModifier(caster, this, "modifier_meepo_earthbind", { duration });
89 | unit.EmitSound("Hero_Meepo.Earthbind.Target");
90 | }
91 |
92 | ParticleManager.DestroyParticle(this.particle!, false);
93 | ParticleManager.ReleaseParticleIndex(this.particle!);
94 |
95 | return true;
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/src/vscripts/addon_game_mode.ts:
--------------------------------------------------------------------------------
1 | import "./lib/timers";
2 | import { GameMode } from "./GameMode";
3 |
4 | // Connect GameMode.Activate and GameMode.Precache to the dota engine
5 | Object.assign(getfenv(), {
6 | Activate: GameMode.Activate,
7 | Precache: GameMode.Precache,
8 | });
9 |
10 | if (GameRules.Addon !== undefined) {
11 | // This code is only run after script_reload, not at startup
12 | GameRules.Addon.Reload();
13 | }
14 |
--------------------------------------------------------------------------------
/src/vscripts/lib/dota_ts_adapter.ts:
--------------------------------------------------------------------------------
1 | export interface BaseAbility extends CDOTA_Ability_Lua {}
2 | export class BaseAbility {}
3 |
4 | export interface BaseItem extends CDOTA_Item_Lua {}
5 | export class BaseItem {}
6 |
7 | export interface BaseModifier extends CDOTA_Modifier_Lua {}
8 | export class BaseModifier {
9 | public static apply(
10 | this: T,
11 | target: CDOTA_BaseNPC,
12 | caster?: CDOTA_BaseNPC,
13 | ability?: CDOTABaseAbility,
14 | modifierTable?: object,
15 | ): InstanceType {
16 | return target.AddNewModifier(caster, ability, this.name, modifierTable) as any;
17 | }
18 | }
19 |
20 | export interface BaseModifierMotionHorizontal extends CDOTA_Modifier_Lua_Horizontal_Motion {}
21 | export class BaseModifierMotionHorizontal extends BaseModifier {}
22 |
23 | export interface BaseModifierMotionVertical extends CDOTA_Modifier_Lua_Vertical_Motion {}
24 | export class BaseModifierMotionVertical extends BaseModifier {}
25 |
26 | export interface BaseModifierMotionBoth extends CDOTA_Modifier_Lua_Motion_Both {}
27 | export class BaseModifierMotionBoth extends BaseModifier {}
28 |
29 | // Add standard base classes to prototype chain to make `super.*` work as `self.BaseClass.*`
30 | setmetatable(BaseAbility.prototype, { __index: CDOTA_Ability_Lua ?? C_DOTA_Ability_Lua });
31 | setmetatable(BaseItem.prototype, { __index: CDOTA_Item_Lua ?? C_DOTA_Item_Lua });
32 | setmetatable(BaseModifier.prototype, { __index: CDOTA_Modifier_Lua ?? CDOTA_Modifier_Lua });
33 |
34 | export const registerAbility = (name?: string) => (ability: new () => CDOTA_Ability_Lua | CDOTA_Item_Lua, context: ClassDecoratorContext) => {
35 | if (name !== undefined) {
36 | // @ts-ignore
37 | ability.name = name;
38 | } if (context.name) {
39 | name = context.name;
40 | }else {
41 | throw "Unable to determine name of this ability class!";
42 | }
43 |
44 | const [env] = getFileScope();
45 |
46 | env[name] = {};
47 |
48 | toDotaClassInstance(env[name], ability);
49 |
50 | const originalSpawn = (env[name] as CDOTA_Ability_Lua).Spawn;
51 | env[name].Spawn = function () {
52 | this.____constructor();
53 | if (originalSpawn) {
54 | originalSpawn.call(this);
55 | }
56 | };
57 | };
58 |
59 | export const registerModifier = (name?: string) => (modifier: new () => CDOTA_Modifier_Lua, context: ClassDecoratorContext) => {
60 | if (name !== undefined) {
61 | // @ts-ignore
62 | modifier.name = name;
63 | } if (context.name) {
64 | name = context.name;
65 | }else {
66 | throw "Unable to determine name of this modifier class!";
67 | }
68 |
69 | const [env, source] = getFileScope();
70 | const [fileName] = string.gsub(source, ".*scripts[\\/]vscripts[\\/]", "");
71 |
72 | env[name] = {};
73 |
74 | toDotaClassInstance(env[name], modifier);
75 |
76 | const originalOnCreated = (env[name] as CDOTA_Modifier_Lua).OnCreated;
77 | env[name].OnCreated = function (parameters: any) {
78 | this.____constructor();
79 | if (originalOnCreated !== undefined) {
80 | originalOnCreated.call(this, parameters);
81 | }
82 | };
83 |
84 | let type = LuaModifierMotionType.NONE;
85 | let base = (modifier as any).____super;
86 | while (base) {
87 | if (base === BaseModifierMotionBoth) {
88 | type = LuaModifierMotionType.BOTH;
89 | break;
90 | } else if (base === BaseModifierMotionHorizontal) {
91 | type = LuaModifierMotionType.HORIZONTAL;
92 | break;
93 | } else if (base === BaseModifierMotionVertical) {
94 | type = LuaModifierMotionType.VERTICAL;
95 | break;
96 | }
97 |
98 | base = base.____super;
99 | }
100 |
101 | LinkLuaModifier(name, fileName, type);
102 | };
103 |
104 | /**
105 | * Use to expose top-level functions in entity scripts.
106 | * Usage: registerEntityFunction("OnStartTouch", (trigger: TriggerStartTouchEvent) => { });
107 | */
108 | export function registerEntityFunction(name: string, f: (...args: any[]) => any) {
109 | const [env] = getFileScope();
110 | env[name] = function (this: void, ...args: any[]) {
111 | f(...args);
112 | };
113 | }
114 |
115 | function clearTable(table: object) {
116 | for (const key in table) {
117 | delete (table as any)[key];
118 | }
119 | }
120 |
121 | function getFileScope(): [any, string] {
122 | let level = 1;
123 | while (true) {
124 | const info = debug.getinfo(level, "S");
125 | if (info && info.what === "main") {
126 | return [getfenv(level), info.source!];
127 | }
128 |
129 | level += 1;
130 | }
131 | }
132 |
133 | function toDotaClassInstance(instance: any, table: new () => any) {
134 | let { prototype } = table;
135 | while (prototype) {
136 | for (const key in prototype) {
137 | // Using hasOwnProperty to ignore methods from metatable added by ExtendInstance
138 | // https://github.com/SteamDatabase/GameTracking-Dota2/blob/7edcaa294bdcf493df0846f8bbcd4d47a5c3bd57/game/core/scripts/vscripts/init.lua#L195
139 | if (!instance.hasOwnProperty(key)) {
140 | instance[key] = prototype[key];
141 | }
142 | }
143 |
144 | prototype = getmetatable(prototype);
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/src/vscripts/lib/timers.d.ts:
--------------------------------------------------------------------------------
1 | interface CreateTimerOptions {
2 | callback?: (this: void) => void | number;
3 | endTime?: number;
4 | useGameTime?: boolean;
5 | useOldStyle?: boolean;
6 | }
7 |
8 | type CreateTimerOptionsContext = CreateTimerOptions & {
9 | callback?: (this: TThis) => void | number;
10 | };
11 |
12 | declare interface Timers {
13 | CreateTimer(callback: (this: void) => void | number): string;
14 | CreateTimer(callback: (this: T) => void | number, context: T): string;
15 |
16 | CreateTimer(name: string, options: CreateTimerOptions): string;
17 | CreateTimer(name: string, options: CreateTimerOptionsContext, context: T): string;
18 |
19 | CreateTimer(options: CreateTimerOptions): string;
20 | CreateTimer(options: CreateTimerOptionsContext, context: T): string;
21 |
22 | CreateTimer(delay: number, callback: (this: void) => void | number): string;
23 | CreateTimer(delay: number, callback: (this: T) => void | number, context: T): string;
24 |
25 | RemoveTimer(name: string): void;
26 | RemoveTimers(killAll: boolean): void;
27 | }
28 |
29 | declare var Timers: Timers;
30 |
--------------------------------------------------------------------------------
/src/vscripts/lib/timers.lua:
--------------------------------------------------------------------------------
1 | TIMERS_VERSION = "1.07"
2 |
3 | --[[
4 | 1.07 modified by Perry (fixed stack overflow if lots of timers finish at the same time and removed HandleErrors throwing error outside tools mode)
5 | 1.06 modified by Celireor (now uses binary heap priority queue instead of iteration to determine timer of shortest duration)
6 |
7 | DO NOT MODIFY A REALTIME TIMER TO USE GAMETIME OR VICE VERSA MIDWAY WITHOUT FIRST REMOVING AND RE-ADDING THE TIMER
8 |
9 | -- A timer running every second that starts immediately on the next frame, respects pauses
10 | Timers:CreateTimer(function()
11 | print ("Hello. I'm running immediately and then every second thereafter.")
12 | return 1.0
13 | end
14 | )
15 |
16 | -- The same timer as above with a shorthand call
17 | Timers(function()
18 | print ("Hello. I'm running immediately and then every second thereafter.")
19 | return 1.0
20 | end)
21 |
22 |
23 | -- A timer which calls a function with a table context
24 | Timers:CreateTimer(GameMode.someFunction, GameMode)
25 |
26 | -- A timer running every second that starts 5 seconds in the future, respects pauses
27 | Timers:CreateTimer(5, function()
28 | print ("Hello. I'm running 5 seconds after you called me and then every second thereafter.")
29 | return 1.0
30 | end
31 | )
32 |
33 | -- 10 second delayed, run once using gametime (respect pauses)
34 | Timers:CreateTimer({
35 | endTime = 10, -- when this timer should first execute, you can omit this if you want it to run first on the next frame
36 | callback = function()
37 | print ("Hello. I'm running 10 seconds after when I was started.")
38 | end
39 | })
40 |
41 | -- 10 second delayed, run once regardless of pauses
42 | Timers:CreateTimer({
43 | useGameTime = false,
44 | endTime = 10, -- when this timer should first execute, you can omit this if you want it to run first on the next frame
45 | callback = function()
46 | print ("Hello. I'm running 10 seconds after I was started even if someone paused the game.")
47 | end
48 | })
49 |
50 |
51 | -- A timer running every second that starts after 2 minutes regardless of pauses
52 | Timers:CreateTimer("uniqueTimerString3", {
53 | useGameTime = false,
54 | endTime = 120,
55 | callback = function()
56 | print ("Hello. I'm running after 2 minutes and then every second thereafter.")
57 | return 1
58 | end
59 | })
60 |
61 | ]]
62 |
63 | -- Binary Heap implementation copy-pasted from https://gist.github.com/starwing/1757443a1bd295653c39
64 | -- BinaryHeap[1] always points to the element with the lowest "key" variable
65 | -- API
66 | -- BinaryHeap(key) - Creates a new BinaryHeap with key. The key is the name of the integer variable used to sort objects.
67 | -- BinaryHeap:Insert - Inserts an object into BinaryHeap
68 | -- BinaryHeap:Remove - Removes an object from BinaryHeap
69 |
70 | BinaryHeap = BinaryHeap or {}
71 | BinaryHeap.__index = BinaryHeap
72 |
73 | function BinaryHeap:Insert(item)
74 | local index = #self + 1
75 | local key = self.key
76 | item.index = index
77 | self[index] = item
78 | while index > 1 do
79 | local parent = math.floor(index/2)
80 | if self[parent][key] <= item[key] then
81 | break
82 | end
83 | self[index], self[parent] = self[parent], self[index]
84 | self[index].index = index
85 | self[parent].index = parent
86 | index = parent
87 | end
88 | return item
89 | end
90 |
91 | function BinaryHeap:Remove(item)
92 | local index = item.index
93 | if self[index] ~= item then return end
94 | local key = self.key
95 | local heap_size = #self
96 | if index == heap_size then
97 | self[heap_size] = nil
98 | return
99 | end
100 | self[index] = self[heap_size]
101 | self[index].index = index
102 | self[heap_size] = nil
103 | while true do
104 | local left = index*2
105 | local right = left + 1
106 | if not self[left] then break end
107 | local newindex = right
108 | if self[index][key] >= self[left][key] then
109 | if not self[right] or self[left][key] < self[right][key] then
110 | newindex = left
111 | end
112 | elseif not self[right] or self[index][key] <= self[right][key] then
113 | break
114 | end
115 | self[index], self[newindex] = self[newindex], self[index]
116 | self[index].index = index
117 | self[newindex].index = newindex
118 | index = newindex
119 | end
120 | end
121 |
122 | setmetatable(BinaryHeap, {__call = function(self, key) return setmetatable({key=key}, self) end})
123 |
124 | function table.merge(input1, input2)
125 | for i,v in pairs(input2) do
126 | input1[i] = v
127 | end
128 | return input1
129 | end
130 |
131 |
132 | TIMERS_THINK = 0.01
133 |
134 | if Timers == nil then
135 | print ( '[Timers] creating Timers ['..TIMERS_VERSION..']' )
136 | Timers = {}
137 | setmetatable(Timers, {
138 | __call = function(t, ...)
139 | return t:CreateTimer(...)
140 | end
141 | })
142 | --Timers.__index = Timers
143 | end
144 |
145 | function Timers:start()
146 | self.started = true
147 | Timers = self
148 | self:InitializeTimers()
149 | self.nextTickCallbacks = {}
150 |
151 | local ent = SpawnEntityFromTableSynchronous("info_target", {targetname="timers_lua_thinker"})
152 | ent:SetThink("Think", self, "timers", TIMERS_THINK)
153 | end
154 |
155 | function Timers:Think()
156 | local nextTickCallbacks = table.merge({}, Timers.nextTickCallbacks)
157 | Timers.nextTickCallbacks = {}
158 | for _, cb in ipairs(nextTickCallbacks) do
159 | local status, result = xpcall(cb, debug.traceback)
160 | if not status then
161 | Timers:HandleEventError(result)
162 | end
163 | end
164 | if GameRules:State_Get() >= DOTA_GAMERULES_STATE_POST_GAME then
165 | return
166 | end
167 |
168 | -- Track game time, since the dt passed in to think is actually wall-clock time not simulation time.
169 | local now = GameRules:GetGameTime()
170 |
171 | -- Process timers
172 | self:ExecuteTimers(self.realTimeHeap, Time())
173 | self:ExecuteTimers(self.gameTimeHeap, GameRules:GetGameTime())
174 |
175 | return TIMERS_THINK
176 | end
177 |
178 | function Timers:ExecuteTimers(timerList, now)
179 | --Timers are alr. sorted by end time upon insertion
180 | local currentTimer = timerList[1]
181 |
182 | --Check if timer has finished
183 | while currentTimer and (now >= currentTimer.endTime) do
184 | -- Remove from timers list
185 | timerList:Remove(currentTimer)
186 | Timers.runningTimer = currentTimer
187 | Timers.removeSelf = false
188 |
189 | -- Run the callback
190 | local status, timerResult
191 | if currentTimer.context then
192 | status, timerResult = xpcall(function() return currentTimer.callback(currentTimer.context, currentTimer) end, debug.traceback)
193 | else
194 | status, timerResult = xpcall(function() return currentTimer.callback(currentTimer) end, debug.traceback)
195 | end
196 |
197 | Timers.runningTimer = nil
198 |
199 | -- Make sure it worked
200 | if status then
201 | -- Check if it needs to loop
202 | if timerResult and not Timers.removeSelf then
203 | -- Change its end time
204 |
205 | currentTimer.endTime = currentTimer.endTime + timerResult
206 |
207 | timerList:Insert(currentTimer)
208 | end
209 |
210 | -- Update timer data
211 | --self:UpdateTimerData()
212 | else
213 | -- Nope, handle the error
214 | Timers:HandleEventError(timerResult)
215 | end
216 | --Check next timer in heap
217 | currentTimer = timerList[1]
218 | end
219 | end
220 |
221 | function Timers:HandleEventError(err)
222 | print(err)
223 | --if not IsInToolsMode() then
224 | -- If you want to send errors from inside timers on live servers to your own webserver, do it here
225 | --end
226 | end
227 |
228 | function Timers:CreateTimer(arg1, arg2, context)
229 | local timer
230 | if type(arg1) == "function" then
231 | if arg2 ~= nil then
232 | context = arg2
233 | end
234 | timer = {callback = arg1}
235 | elseif type(arg1) == "table" then
236 | timer = arg1
237 | elseif type(arg1) == "string" then
238 | timer = arg2
239 | timer.name = arg1
240 | elseif type(arg1) == "number" then
241 | timer = {endTime = arg1, callback = arg2}
242 | end
243 | if not timer.callback then
244 | print("Invalid timer created")
245 | return
246 | end
247 |
248 | local now = GameRules:GetGameTime()
249 | local timerHeap = self.gameTimeHeap
250 | if timer.useGameTime ~= nil and timer.useGameTime == false then
251 | now = Time()
252 | timerHeap = self.realTimeHeap
253 | end
254 |
255 | if timer.endTime == nil then
256 | timer.endTime = now
257 | else
258 | timer.endTime = now + timer.endTime
259 | end
260 |
261 | timer.context = context
262 |
263 | timerHeap:Insert(timer)
264 |
265 | return timer
266 | end
267 |
268 | function Timers:NextTick(callback)
269 | table.insert(Timers.nextTickCallbacks, callback)
270 | end
271 |
272 | function Timers:RemoveTimer(name)
273 | local timerHeap = self.gameTimeHeap
274 | local runningTimer = Timers.runningTimer
275 | local hasMatch = runningTimer == name or runningTimer.name == name
276 |
277 | if not hasMatch then return end
278 |
279 | if runningTimer.useGameTime ~= nil and runningTimer.useGameTime == false then
280 | timerHeap = self.realTimeHeap
281 | end
282 |
283 | timerHeap:Remove(runningTimer)
284 | Timers.removeSelf = true
285 | end
286 |
287 | function Timers:InitializeTimers()
288 | self.realTimeHeap = BinaryHeap("endTime")
289 | self.gameTimeHeap = BinaryHeap("endTime")
290 | end
291 |
292 | if not Timers.started then Timers:start() end
293 |
294 | GameRules.Timers = Timers
295 |
--------------------------------------------------------------------------------
/src/vscripts/lib/tstl-utils.ts:
--------------------------------------------------------------------------------
1 | const global = globalThis as typeof globalThis & { reloadCache: Record };
2 | if (global.reloadCache === undefined) {
3 | global.reloadCache = {};
4 | }
5 |
6 | export function reloadable(constructor: T, context: ClassDecoratorContext): T {
7 | const className = context.name;
8 |
9 | if (className === undefined) {
10 | throw "Cannot reload classes without names!";
11 | }
12 |
13 | if (global.reloadCache[className] === undefined) {
14 | global.reloadCache[className] = constructor;
15 | }
16 |
17 | Object.assign(global.reloadCache[className].prototype, constructor.prototype);
18 | return global.reloadCache[className];
19 | }
20 |
--------------------------------------------------------------------------------
/src/vscripts/modifiers/modifier_panic.ts:
--------------------------------------------------------------------------------
1 | import { BaseModifier, registerModifier } from "../lib/dota_ts_adapter";
2 |
3 | // Base speed modifier -- Could be moved to a separate file
4 | class ModifierSpeed extends BaseModifier {
5 | // Declare functions
6 | DeclareFunctions(): ModifierFunction[] {
7 | return [ModifierFunction.MOVESPEED_ABSOLUTE];
8 | }
9 |
10 | GetModifierMoveSpeed_Absolute(): number {
11 | return 300;
12 | }
13 | }
14 |
15 | @registerModifier()
16 | export class modifier_panic extends ModifierSpeed {
17 | // Set state
18 | CheckState(): Partial> {
19 | return {
20 | [ModifierState.COMMAND_RESTRICTED]: true,
21 | };
22 | }
23 |
24 | // Override speed given by Modifier_Speed
25 | GetModifierMoveSpeed_Absolute(): number {
26 | return 540;
27 | }
28 |
29 | // Run when modifier instance is created
30 | OnCreated(): void {
31 | if (IsServer()) {
32 | // Think every 0.3 seconds
33 | this.StartIntervalThink(0.3);
34 | }
35 | }
36 |
37 | // Called when intervalThink is triggered
38 | OnIntervalThink(): void {
39 | const parent = this.GetParent();
40 |
41 | parent.MoveToPosition((parent.GetAbsOrigin() + RandomVector(400)) as Vector);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/vscripts/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "rootDir": ".",
4 | "outDir": "../../game/scripts/vscripts",
5 | "target": "esnext",
6 | "lib": ["esnext"],
7 | "types": ["@moddota/dota-lua-types/normalized"],
8 | "plugins": [{ "transform": "@moddota/dota-lua-types/transformer" }],
9 | "moduleResolution": "node",
10 | "strict": true
11 | },
12 | "tstl": {
13 | "luaTarget": "JIT",
14 | "sourceMapTraceback": true
15 | },
16 | "include": ["**/*.ts", "../common/**/*.ts"]
17 | }
18 |
--------------------------------------------------------------------------------