├── .eslintrc.js
├── .github
└── workflows
│ ├── main.yml
│ └── publish.yml
├── .gitignore
├── .vscode
├── extensions.json
├── launch.json
├── settings.json
└── tasks.json
├── LICENSE
├── README.md
├── art
├── goob.fla
├── goob_hq.png
├── highTrustPromoArt.png
├── icon.psd
└── thumbnail
│ ├── thumbnail.png
│ ├── thumbnail.psd
│ ├── thumbnail_github.png
│ └── thumbnail_github.psd
├── data
├── psych_0.6.json
├── psych_0.7.json
├── psych_1.0.json
└── psych_latest.ver
├── dataScraper.go
├── images
└── icon.png
├── package.json
├── src
├── engineData.ts
├── extension.ts
├── extensionHx.ts
└── util.ts
└── tsconfig.json
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | /**@type {import('eslint').Linter.Config} */
2 | // eslint-disable-next-line no-undef
3 | module.exports = {
4 | root: true,
5 | parser: '@typescript-eslint/parser',
6 | plugins: [
7 | '@typescript-eslint',
8 | ],
9 | extends: [
10 | 'eslint:recommended',
11 | 'plugin:@typescript-eslint/recommended',
12 | ],
13 | rules: {
14 | 'semi': [2, "always"],
15 | '@typescript-eslint/no-unused-vars': 0,
16 | '@typescript-eslint/no-explicit-any': 0,
17 | '@typescript-eslint/explicit-module-boundary-types': 0,
18 | '@typescript-eslint/no-non-null-assertion': 0,
19 | }
20 | };
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | push:
5 | branches: [ master ]
6 | pull_request:
7 | branches: [ master ]
8 | workflow_dispatch:
9 |
10 | jobs:
11 | build:
12 | runs-on: ubuntu-latest
13 | steps:
14 | - name: Checkout
15 | uses: actions/checkout@v3
16 |
17 | - name: Setup Node.js
18 | uses: actions/setup-node@v3
19 | with:
20 | node-version: 16
21 |
22 | - name: Install dependencies
23 | run: npm i
24 |
25 | - name: Package VS Code extension
26 | id: package
27 | uses: nhedger/package-vscode-extension@v1
28 |
29 | - name: Publish VS Code extension artifact
30 | uses: actions/upload-artifact@v4
31 | with:
32 | name: funkin-completer-build
33 | path: ${{ steps.package.outputs.packagePath }}
34 |
--------------------------------------------------------------------------------
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------
1 | name: Publish Extension
2 |
3 | on:
4 | push:
5 | tags:
6 | - "*"
7 | workflow_dispatch:
8 |
9 | jobs:
10 | deploy:
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: actions/checkout@v3
14 | - uses: actions/setup-node@v3
15 | with:
16 | node-version: 16
17 | - run: npm i
18 | - name: Publish to Visual Studio Marketplace
19 | uses: HaaLeo/publish-vscode-extension@v1
20 | with:
21 | pat: ${{ secrets.VS_MARKETPLACE_TOKEN }}
22 | registryUrl: https://marketplace.visualstudio.com
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | out/
3 | target/
4 | *.vsix
5 | .vscode/settings.json
6 | .vscode/extensions.json
7 | # is this file important? lmao
8 | package-lock.json
9 | changelog.txt
10 | dataInput.txt
11 | dataOutput.json
12 | changelog
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
3 | // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
4 |
5 | // List of extensions which should be recommended for users of this workspace.
6 | "recommendations": [
7 | "dbaeumer.vscode-eslint"
8 | ]
9 | }
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | // A launch configuration that compiles the extension and then opens it inside a new window
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | {
6 | "version": "0.2.0",
7 | "configurations": [
8 | {
9 | "name": "Run Extension",
10 | "type": "extensionHost",
11 | "request": "launch",
12 | "runtimeExecutable": "${execPath}",
13 | "args": [
14 | "--extensionDevelopmentPath=${workspaceFolder}"
15 | ],
16 | "outFiles": [
17 | "${workspaceFolder}/out/**/*.js"
18 | ],
19 | "preLaunchTask": "npm: watch"
20 | }
21 | ]
22 | }
23 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "editor.insertSpaces": false,
3 | "[plaintext]": {
4 | "editor.wordWrap": "on"
5 | },
6 | "[json]": {
7 | "editor.wordWrap": "on"
8 | }
9 | }
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | // See https://go.microsoft.com/fwlink/?LinkId=733558
2 | // for the documentation about the tasks.json format
3 | {
4 | "version": "2.0.0",
5 | "tasks": [
6 | {
7 | "type": "npm",
8 | "script": "watch",
9 | "problemMatcher": "$tsc-watch",
10 | "isBackground": true,
11 | "presentation": {
12 | "reveal": "never"
13 | },
14 | "group": {
15 | "kind": "build",
16 | "isDefault": true
17 | }
18 | }
19 | ]
20 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache NON-AI License, Version 2.0
2 |
3 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
4 |
5 | 1. Definitions.
6 |
7 | “License” shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
8 |
9 | “Licensor” shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
10 |
11 | “Legal Entity” shall mean the union of the acting entity and all other entities that control, are controlled by,
12 | or are under common control with that entity. For the purposes of this definition, “control” means (i) the power, direct or indirect,
13 | to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%)
14 | or more of the outstanding shares, or (iii) beneficial ownership of such entity.
15 |
16 | “You” (or “Your”) shall mean an individual or Legal Entity exercising permissions granted by this License.
17 |
18 | “Source” form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source,
19 | and configuration files.
20 |
21 | “Object” form shall mean any form resulting from mechanical transformation or translation of a Source form, including
22 | but not limited to compiled object code, generated documentation, and conversions to other media types.
23 |
24 | “Work” shall mean the work of authorship, whether in Source or Object form, made available under the License,
25 | as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
26 |
27 | “Derivative Works” shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and
28 | for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship.
29 | For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name)
30 | to the interfaces of, the Work and Derivative Works thereof.
31 |
32 | “Contribution” shall mean any work of authorship, including the original version of the Work and any modifications or additions to
33 | that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or
34 | by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, “submitted”
35 | means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to
36 | communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of,
37 | the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated
38 | in writing by the copyright owner as “Not a Contribution.”
39 |
40 | “Contributor” shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and
41 | subsequently incorporated within the Work.
42 |
43 | 2. Grant of Copyright License.
44 |
45 | Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive,
46 | no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform,
47 | sublicense, and distribute the Work and such Derivative Works in Source or Object form,
48 | under the following conditions:
49 |
50 | 2.1. You shall not use the Covered Software in the creation of an Artificial Intelligence training dataset,
51 | including but not limited to any use that contributes to the training or development of an AI model or algorithm,
52 | unless You obtain explicit written permission from the Contributor to do so.
53 |
54 | 2.2. You acknowledge that the Covered Software is not intended for use in the creation of an Artificial Intelligence training dataset,
55 | and that the Contributor has no obligation to provide support or assistance for any use that violates this license.
56 |
57 | 3. Grant of Patent License.
58 |
59 | Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge,
60 | royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import,
61 | and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are
62 | necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which
63 | such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit)
64 | alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement,
65 | then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
66 |
67 | 4. Redistribution.
68 |
69 | You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications,
70 | and in Source or Object form, provided that You meet the following conditions:
71 |
72 | 1. You must give any other recipients of the Work or Derivative Works a copy of this License; and
73 |
74 | 2. You must cause any modified files to carry prominent notices stating that You changed the files; and
75 |
76 | 3. You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark,
77 | and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
78 |
79 | 4. If the Work includes a “NOTICE” text file as part of its distribution, then any Derivative Works that You distribute
80 | must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that
81 | do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed
82 | as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works;
83 | or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear.
84 | The contents of the NOTICE file are for informational purposes only and do not modify the License.
85 |
86 | You may add Your own attribution notices within Derivative Works that You distribute,
87 | alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as
88 | modifying the License.
89 | You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use,
90 | reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction,
91 | and distribution of the Work otherwise complies with the conditions stated in this License.
92 |
93 | 5. Submission of Contributions.
94 |
95 | Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor
96 | shall be under the terms and conditions of this License, without any additional terms or conditions.
97 | Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed
98 | with Licensor regarding such Contributions.
99 |
100 | 6. Trademarks.
101 |
102 | This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor,
103 | except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
104 |
105 | 7. Disclaimer of Warranty.
106 |
107 | Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions)
108 | on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation,
109 | any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE.
110 | You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated
111 | with Your exercise of permissions under this License.
112 |
113 | 8. Limitation of Liability.
114 |
115 | In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise,
116 | unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing,
117 | shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages
118 | of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages
119 | for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses),
120 | even if such Contributor has been advised of the possibility of such damages.
121 |
122 | 9. Accepting Warranty or Additional Liability.
123 |
124 | While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support,
125 | warranty, indemnity, or other liability obligations and/or rights consistent with this License.
126 | However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility,
127 | not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by,
128 | or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
129 |
130 | END OF TERMS AND CONDITIONS
131 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Funkin Script AutoComplete
2 | Visual Studio Code AutoCompleter Extension for [Psych Engine](https://github.com/ShadowMario/FNF-PsychEngine) Modding API!
3 |
4 | # Features
5 | ## Lua
6 | - Function/Event/Variable auto completing
7 | - Warnings for deprecated functions
8 | - Hovers with documentation
9 | - API data is downloaded from the GitHub repository instead of being grabbed from local storage.
10 | ## Haxe Scripted Classes (EXPERIMENTAL)
11 | - Functional Haxe completion!
12 | - Completion for Friday Night Funkin v0.3.0 Modding API
13 |
14 | # Installation
15 | [**Latest Release**](https://marketplace.visualstudio.com/items?itemName=Snirozu.funkin-script-autocompleter) | [**Latest Build**](https://nightly.link/Snirozu/Funkin-Script-AutoComplete/workflows/main/master)
16 |
17 | ### Installing VSIX
18 | 1. Download the .vsix file from the latest release
19 | 2. Open **Visual Studio Code** and select **Extensions** from the left bar
20 | 3. At the top of Extensions menu click on three dots and select "**Install From VSIX...**"
21 | 4. In the Install from VSIX window select downloaded .vsix file
22 | 5. Restart Visual Studio Code
23 | 6. Optionally you can change the targetted engine in **File/Preferences/Settings/Extensions/Funkin Script AutoComplete/Lua Engine**
24 |
25 | # Quick Documentation
26 | ## Script Types
27 | * *.lua - Any other Lua based FNF Engine, like Psych Engine
28 | * *.hxc - V-Slice (FNF v0.3.0) Scripts, they should always begin with a upper case (Experimental Mode)
29 |
30 | ## Code Comments
31 | - Enable this extension in a file: `---@funkinScript` (only if `funkinVSCode.enableOnlyOnCertainScripts` setting is on)
32 | - Set FNF engine for this file: `---@funkinEngine={psych|pengine|etc.}`
33 | - Disable deprecated warnings for specific function on this file: `---@diagnostic disable: {funcs}`
34 | - Disable deprecated warnings for specific function on next line: `---@diagnostic disable-next-line: {funcs}`
35 |
36 | Report bugs and contribute to this extension on [Github](https://github.com/Snirozu/Funkin-Script-AutoComplete)
37 |
38 | ## Contributing
39 | Any pull requests are appreciated, feel free to Pull Request any missing engines, functions or variables!
40 | If you want to add another engine to this extension make a json file in data/ called something like "piss-engine_data.json", this data file should be based of any `*_data.json` file
41 | NOTE : If you don't know how to install dependencies use `npm install`
42 |
43 | You should manually add/change `documentation` value (if `documentation` is "Needs documentation", "" or doesn't exist) and a `returns` value (if `returns` is "???" or doesn't exist)
44 |
45 | If a function is deprecated add `"deprecated": "deprecated message here"` to the function
46 |
47 | ## Compiling and Editing the source
48 | 1. Install [npm](https://nodejs.org/en/download/)
49 | 2. Clone this repo with ```git clone https://github.com/Snirozu/Funkin-Script-AutoComplete.git``` (must be in empty folder)
50 | 3. To install all the libraries run ```npm install```
51 | 4. And to compile run ```npm run build```
52 |
--------------------------------------------------------------------------------
/art/goob.fla:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Snirozu/Funkin-Script-AutoComplete/0cab09cdbe253a0094f23fdcb2fe374085ab5ab4/art/goob.fla
--------------------------------------------------------------------------------
/art/goob_hq.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Snirozu/Funkin-Script-AutoComplete/0cab09cdbe253a0094f23fdcb2fe374085ab5ab4/art/goob_hq.png
--------------------------------------------------------------------------------
/art/highTrustPromoArt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Snirozu/Funkin-Script-AutoComplete/0cab09cdbe253a0094f23fdcb2fe374085ab5ab4/art/highTrustPromoArt.png
--------------------------------------------------------------------------------
/art/icon.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Snirozu/Funkin-Script-AutoComplete/0cab09cdbe253a0094f23fdcb2fe374085ab5ab4/art/icon.psd
--------------------------------------------------------------------------------
/art/thumbnail/thumbnail.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Snirozu/Funkin-Script-AutoComplete/0cab09cdbe253a0094f23fdcb2fe374085ab5ab4/art/thumbnail/thumbnail.png
--------------------------------------------------------------------------------
/art/thumbnail/thumbnail.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Snirozu/Funkin-Script-AutoComplete/0cab09cdbe253a0094f23fdcb2fe374085ab5ab4/art/thumbnail/thumbnail.psd
--------------------------------------------------------------------------------
/art/thumbnail/thumbnail_github.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Snirozu/Funkin-Script-AutoComplete/0cab09cdbe253a0094f23fdcb2fe374085ab5ab4/art/thumbnail/thumbnail_github.png
--------------------------------------------------------------------------------
/art/thumbnail/thumbnail_github.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Snirozu/Funkin-Script-AutoComplete/0cab09cdbe253a0094f23fdcb2fe374085ab5ab4/art/thumbnail/thumbnail_github.psd
--------------------------------------------------------------------------------
/data/psych_0.6.json:
--------------------------------------------------------------------------------
1 | {
2 | "functions": {
3 | "makeLuaSprite": {
4 | "returns": "void",
5 | "args": "tag:String, image:String, x:Float, y:Float",
6 | "documentation": "Spawns a Lua Sprite with no animations using the tag `tag`, it will be using the image `image`.png, and will be spawned on position `x`, `y` If you want to make a Black screen with no texture, leave `image` field empty and use (luaSpriteMakeGraphic)[]\n\nIf another Lua Sprite that exists is already using the tag `tag`, it will be removed."
7 | },
8 | "makeAnimatedLuaSprite": {
9 | "returns": "void",
10 | "args": "tag:String, image:String, x:Float, y:Float",
11 | "documentation": "Spawns a Lua Sprite that supports Animations, it will be using the tag `tag`, be using the image `image`.png, the XML `image`.xml, and will be spawned on position `x`, `y`\n\nIf another Lua Sprite that exists is already using the tag `tag`, it will be removed."
12 | },
13 | "addLuaSprite": {
14 | "returns": "void",
15 | "args": "tag:String, front:Bool = false",
16 | "documentation": "Adds a Lua Sprite with the specified tag, either in front or behind the characters."
17 | },
18 | "removeLuaSprite": {
19 | "returns": "void",
20 | "args": "tag:String, destroy:Bool = true",
21 | "documentation": "Removes a Lua Sprite with the specified tag\n* `tag` - The Lua Sprite's tag\n* `destroy` - Specifies if you don't want to use the sprite anymore. Default value is `true` (Set to `false` if you want to re-add it later)"
22 | },
23 | "makeLuaText": {
24 | "returns": "void",
25 | "args": "tag:String, text:String, width:Int, x:Float, y:Float",
26 | "documentation": "Creates a Lua Text object on position `x`, `y` and a width with 'width'"
27 | },
28 | "addLuaText": {
29 | "returns": "void",
30 | "args": "tag:String",
31 | "documentation": "Spawns a Lua Text on the stage"
32 | },
33 | "removeLuaText": {
34 | "returns": "void",
35 | "args": "tag:String, destroy:Bool = true",
36 | "documentation": "Removes a Lua Text off the stage\n* `tag` - The Lua Sprite's tag\n* `destroy` - Specifies if you don't want to use the sprite anymore. Default value is `true` (Set to `false` if you want to re-add it later)"
37 | },
38 | "setTextString": {
39 | "returns": "void",
40 | "args": "tag:String, text:String",
41 | "documentation": "Sets text string at the specified tag"
42 | },
43 | "setTextSize": {
44 | "returns": "void",
45 | "args": "tag:String, size:Int",
46 | "documentation": "Sets text size at the specified tag"
47 | },
48 | "setTextWidth": {
49 | "returns": "void",
50 | "args": "tag:String, width:Float",
51 | "documentation": "Sets text width at the specified tag"
52 | },
53 | "setTextBorder": {
54 | "returns": "void",
55 | "args": "tag:String, size:Int, color:String",
56 | "documentation": "Sets text border at the specified tag"
57 | },
58 | "setTextColor": {
59 | "returns": "void",
60 | "args": "tag:String, color:String",
61 | "documentation": "Sets text color at the specified tag"
62 | },
63 | "setTextFont": {
64 | "returns": "void",
65 | "args": "tag:String, font:String",
66 | "documentation": "Sets text font at the specified tag"
67 | },
68 | "setTextItalic": {
69 | "returns": "void",
70 | "args": "tag:String, italic:Bool",
71 | "documentation": "Sets text as italic or not based on the `italic` parameter"
72 | },
73 | "setTextAlignment": {
74 | "returns": "void",
75 | "args": "tag:String, alignment:String = 'left'",
76 | "documentation": "Sets text alignment at the specified tag `'left'`,`'right'`, or `'center'`"
77 | },
78 | "getTextString": {
79 | "returns": "string",
80 | "args": "tag:String",
81 | "documentation": "Gets text object's text at the specified tag"
82 | },
83 | "getTextSize": {
84 | "returns": "number",
85 | "args": "tag:String",
86 | "documentation": "Gets text object's size at the specified tag"
87 | },
88 | "getTextFont": {
89 | "returns": "string",
90 | "args": "tag:String",
91 | "documentation": "Gets text object's font at the specified tag"
92 | },
93 | "getTextWidth": {
94 | "returns": "number",
95 | "args": "tag:String",
96 | "documentation": "Gets text object's width at the specified tag"
97 | },
98 | "startDialogue": {
99 | "returns": "void",
100 | "args": "dialogueFile:String, song:String = null",
101 | "documentation": "Starts a dialogue loading a file inside `mods/data/your-song-name/`.\n* `dialogueFile` - .TXT file to load\n* `song` - Optional value for a Background music during the dialogue\n\nExample: If i want to load a dialogue file `mods/data/bopeebo/dialogue.txt` using the pause menu song (Breakfast), i should use: `startDialogue('bopeebo/dialogue', 'breakfast')`\n\nNOTE: When the dialogue is ended, it calls `startCountdown()`\n\nNOTE 2: `onNextDialogue(line)` callback is called for every dialogue line passed"
102 | },
103 | "startVideo": {
104 | "returns": "void",
105 | "args": "videoFile:String",
106 | "documentation": "Starts a Video Cutscene\n* `videoFile` - File name of your video, it must be inside `mods/videos/`\n\nExample: If you wanted to start the video `mods/videos/ughCutscene.mp4`, you'd have to use `startVideo('ughCutscene')`"
107 | },
108 | "startCountdown": {
109 | "returns": "void",
110 | "args": "",
111 | "documentation": "In case you forced a countdown stop for doing a pre-song cutscene or something, this starts the countdown again manually."
112 | },
113 | "endSong": {
114 | "returns": "void",
115 | "args": "",
116 | "documentation": "In case you forced a song end for doing a post-song cutscene or something, this ends the song manually."
117 | },
118 | "characterPlayAnim": {
119 | "returns": "void",
120 | "args": "character:String, anim:String, forced:Bool = false",
121 | "documentation": "* `character` - Can be `boyfriend`, `dad` or `gf`\n*`anim` - Animation name to be played\n* `forced` - Can be either `true` or `false`, if set to `true`, it will force the animation to reset if the current animation is the same as the animation to play",
122 | "deprecated": "characterPlayAnim is deprecated! Use playAnim instead"
123 | },
124 | "characterDance": {
125 | "returns": "void",
126 | "args": "character:String",
127 | "documentation": "Makes character do the idle dance\n* `character` - Can be `boyfriend`, `dad` or `gf`"
128 | },
129 | "getCharacterX": {
130 | "returns": "number",
131 | "args": "type:String",
132 | "documentation": "Gets the general X position of a character from the type `type`\n* `type` - Can be `boyfriend`, `dad` or `gf`"
133 | },
134 | "setCharacterX": {
135 | "returns": "void",
136 | "args": "type:String, value:Float",
137 | "documentation": "Sets the general X position of a character from the type `type`, this will also move all precached characters from the same type into the position you want\n* `type` - Can be `boyfriend`, `dad` or `gf`"
138 | },
139 | "getCharacterY": {
140 | "returns": "number",
141 | "args": "type:String",
142 | "documentation": "Gets the general Y position of a character from the type `type`\n* `type` - Can be `boyfriend`, `dad` or `gf`"
143 | },
144 | "setCharacterY": {
145 | "returns": "void",
146 | "args": "type:String, value:Float",
147 | "documentation": "Sets the general Y position of a character from the type `type`, this will also move all precached characters from the same type into the position you want\n* `type` - Can be `boyfriend`, `dad` or `gf`"
148 | },
149 | "addScore": {
150 | "returns": "void",
151 | "args": "value:Int = 0",
152 | "documentation": "Adds `value` to the current song's score and recalculates rating"
153 | },
154 | "setScore": {
155 | "returns": "void",
156 | "args": "value:Int = 0",
157 | "documentation": "Set the current song's score to `value` and recalculates rating"
158 | },
159 | "addMisses": {
160 | "returns": "void",
161 | "args": "value:Int = 0",
162 | "documentation": "Adds `value` to the current song's misses total and recalculates rating"
163 | },
164 | "setMisses": {
165 | "returns": "void",
166 | "args": "value:Int = 0",
167 | "documentation": "Set the current song's misses total to `value` and recalculates rating"
168 | },
169 | "addHits": {
170 | "returns": "void",
171 | "args": "value:Int = 0",
172 | "documentation": "Adds `value` to the current song's notes hit total and recalculates rating"
173 | },
174 | "setHits": {
175 | "returns": "void",
176 | "args": "value:Int = 0",
177 | "documentation": "Set the current song's notes hit total to `value` and recalculates rating"
178 | },
179 | "setRatingPercent": {
180 | "returns": "void",
181 | "args": "value:Float",
182 | "documentation": "Sets the rating percent in case you want to do your own rating calculation.\n* `value` - Should go from `0` to `1` but can actually be whatever value you want, but it's kinda stupid to get out of the base values."
183 | },
184 | "setRatingString": {
185 | "returns": "void",
186 | "args": "value:String",
187 | "documentation": "Sets the rating name to `value` in case you want to do your own rating calculation.",
188 | "deprecated": "setRatingString is deprecated! Use setRatingName instead"
189 | },
190 | "keyJustPressed": {
191 | "returns": "boolean",
192 | "args": "name:String",
193 | "documentation": "Get if the key `name` just got pressed on the current frame.\n\nKeys: `'left'`, `'down'`, `'up'`, `'right'`, `'accept'`, `'back'`, `'pause'`, `'reset'`, `'space'`"
194 | },
195 | "keyPressed": {
196 | "returns": "boolean",
197 | "args": "name:String",
198 | "documentation": "Get if the key `name` is being held on the current frame.\n\nKeys: `'left'`, `'down'`, `'up'`, `'right'`, `'space'`"
199 | },
200 | "keyReleased": {
201 | "returns": "boolean",
202 | "args": "name:String",
203 | "documentation": "Get if the key `name` was released on the current frame.\n\nKeys: `'left'`, `'down'`, `'up'`, `'right'`, `'space'`"
204 | },
205 | "mouseClicked": {
206 | "returns": "boolean",
207 | "args": "name:String",
208 | "documentation": "Get if the mouse button `name` just got pressed on the current frame. leave 'name' blank for left mouse\n\nButtons: `'left'`, `'right'`, `'middle'`"
209 | },
210 | "mousePressed": {
211 | "returns": "boolean",
212 | "args": "name:String",
213 | "documentation": "Get if the mouse button `name` is being held on the current frame.\n\nButtons: `'left'`, `'right'`, `'middle'`"
214 | },
215 | "mouseReleased": {
216 | "returns": "boolean",
217 | "args": "name:String",
218 | "documentation": "Get if the mouse button `name` was released on the current frame.\n\nButtons: `'left'`, `'right'`, `'middle'`"
219 | },
220 | "getMouseX": {
221 | "returns": "number",
222 | "args": "camera:String",
223 | "documentation": "Returns the relative mouse X position on a specific camera\n* `camera` - Can be either `game`, `hud` or `other`"
224 | },
225 | "getMouseY": {
226 | "returns": "number",
227 | "args": "camera:String",
228 | "documentation": "Returns the relative mouse Y position on a specific camera\n* `camera` - Can be either `game`, `hud` or `other`"
229 | },
230 | "triggerEvent": {
231 | "returns": "void",
232 | "args": "name:String, arg1:String, arg2:String",
233 | "documentation": "Triggers an event without you having to chart them.\n* `name` - Event name on Chart Editor\n* `arg1` - Value 1 on Chart Editor\n* `arg2` - Value 2 on Chart Editor"
234 | },
235 | "addLuaScript": {
236 | "returns": "void",
237 | "args": "path:String",
238 | "documentation": "Loads another .lua script.\n* `path` - Path to LUA relative to the base folder"
239 | },
240 | "getColorFromHex": {
241 | "returns": "number",
242 | "args": "color:String",
243 | "documentation": "Get the color decimal ID from an Hexadecimal value (`color`).\n\nExample: To get orange, you should use `getColorFromHex('FF7800')` or `getColorFromHex('0xFFFF7800')`"
244 | },
245 | "getSongPosition": {
246 | "returns": "number",
247 | "args": "",
248 | "documentation": "Returns the current song position. Shortcut to `getPropertyClass('Conductor', 'songPosition')`"
249 | },
250 | "cameraShake": {
251 | "returns": "void",
252 | "args": "camera:String, intensity:Float, duration:Float",
253 | "documentation": "* `camera` - \"game\",\"hud\" or \"other\"\n* `intensity` - How far away should it shake, recommended value is `0.05`\n* `duration` - Time duration for it to shake"
254 | },
255 | "cameraSetTarget": {
256 | "returns": "void",
257 | "args": "target:String",
258 | "documentation": "Makes the camera focus on a specific target\n* `target` - Target can be either `boyfriend` or `dad`"
259 | },
260 | "cameraFlash": {
261 | "returns": "void",
262 | "args": "camera:String, color:String, duration:Float,forced:Bool",
263 | "documentation": "* `camera` - \"game\",\"hud\" or \"other\"\n* `color` - color of flash\n* `duration` - Time duration for it to flash\n* `forced` - restarts flash or not"
264 | },
265 | "cameraFade": {
266 | "returns": "void",
267 | "args": "camera:String, color:String, duration:Float,forced:Bool",
268 | "documentation": "* `camera` - \"game\",\"hud\" or \"other\"\n* `color` - color of fade\n* `duration` - Time duration for it to fade\n* `forced` - restarts fade or not"
269 | },
270 | "debugPrint": {
271 | "returns": "void",
272 | "args": "text1:Dynamic, text2:Dynamic, text3:Dynamic, text4:Dynamic, text5:Dynamic",
273 | "documentation": "* Prints a debug message on the top-left corner of the screen\n* All values are optional\n* You can have up to 5 values to be printed.\n\nExample: `debugPrint(\"Current boyfriend character: \", getProperty(\"boyfriend.curCharacter\"));`\n\nThis will print the following message: `Current boyfriend character: bf`"
274 | },
275 | "close": {
276 | "returns": "void",
277 | "args": "printMessage:Bool",
278 | "documentation": "Stops your script in the next 100 miliseconds.\n* `printMessage` - Wether you want a warning to show on the top-left corner of the screen or not"
279 | },
280 | "makeGraphic": {
281 | "returns": "void",
282 | "args": "obj:String, width:Int, height:Int, color:String",
283 | "documentation": "Used for making an object use a solid color Width x Height frame instead of a texture.\n* `obj` - Lua Sprite tag or Object variable name\n* `width` - Width in pixels of the graphic you want to create\n`height` - Height in pixels of the graphic you want to create\n* `color` - Color string, works the same as `getColorFromHex()`\n\nExample: Use `makeGraphic('testBlackSquare', 1000, 1000, '000000')` to make the Lua Sprite with the tag \"testBlackSquare\" turn into a 1000x1000 black square."
284 | },
285 | "setBlendMode": {
286 | "returns": "void",
287 | "args": "obj:String, blend:String",
288 | "documentation": "Changes the blend mode of a Sprite (Works similar to how Photoshop do it)\n* `obj` - Lua Sprite tag or Object variable name\n* `blend` - Blend mode to use. Example: `add`, `darken`, `normal`.\n\n[List of blend modes](https://api.haxe.org/flash/display/BlendMode.html)"
289 | },
290 | "addAnimationByPrefix": {
291 | "returns": "void",
292 | "args": "tag:String, name:String, prefix:String, framerate:Int = 24, loop:Bool = true",
293 | "documentation": "Adds an animation `name` to the Lua Sprite/Object using the tag/variable `tag`, it will also overwrite another animation using the same name.\n* `obj` - Lua Sprite tag or Object variable name\n* `name` - Animation to be added's name\n* `prefix` - Animation name on the .xml file\n* `framerate` - Optional value, how many frames per second does the animation have, Default value is `24`\n* `loop` - Optional value, should the animation loop? Default value: `true`"
294 | },
295 | "addAnimationByIndices": {
296 | "returns": "void",
297 | "args": "tag:String, name:String, prefix:String, indices:String, framerate:Int = 24",
298 | "documentation": "Adds an animation `name` to the Lua Sprite/Object using the tag/variable `tag` with the specified indices on `indices`, it will also overwrite another animation using the same name.\n* `obj` - Lua Sprite tag or Object variable name\n* `name` - Animation to be added's name\n* `prefix` - Animation name on the .xml file\n* `indices` - What frames the animation should use, must be separated with a comma. Example: `1, 2, 3, 4, 5, 3, 4, 5`\n* `framerate` - Optional value, how many frames per second does the animation have, Default value is `24`"
299 | },
300 | "objectPlayAnimation": {
301 | "returns": "void",
302 | "args": "obj:String, name:String, forced:Bool = false",
303 | "documentation": "Plays animation `name` on a Lua Sprite/Object with the tag/variable `obj`.\n* `obj` - Lua Sprite tag or Object variable name\n* `name` - Animation name to play\n* `forced` - If true, the animation will reset if the current animation is the same as the one you're trying to play. Default value is `false`",
304 | "deprecated": "objectPlayAnimation is deprecated! Use playAnim instead"
305 | },
306 | "getObjectOrder": {
307 | "returns": "number",
308 | "args": "obj:String",
309 | "documentation": "Gets the object's layer position\n* `obj` - Object variable/Lua Sprite tag"
310 | },
311 | "setObjectOrder": {
312 | "returns": "void",
313 | "args": "obj:String, position:Int",
314 | "documentation": "Sets the object's layer position\n* `obj` - Object variable/Lua Sprite tag\n* `position` - New position the object will be in\n\n(note: when referring to characters, you must refer to their group) `boyfriendGroup`, `gfGroup`, `dadGroup`"
315 | },
316 | "removeFromGroup": {
317 | "returns": "void",
318 | "args": "obj:String, index:Int, dontDestroy:Bool = false",
319 | "documentation": "* `obj` - Group/Array variable\n* `index` - Member ID\n* `dontDestroy` - Optional variable. Won't clear member from memory, you will probably never ever use this.\n\nExample: To remove the first spawned note from the group you should use `removeFromGroup('notes', 0)`"
320 | },
321 | "setGraphicSize": {
322 | "returns": "void",
323 | "args": "obj:String, multX:Float, multY:Float = 0",
324 | "documentation": "* `obj` - Object from PlayState or Lua Sprite\n* `multX` - Horizontal multiplier, default value is 1\n* `multY` - Vertical multiplier, default value is 1"
325 | },
326 | "scaleObject": {
327 | "returns": "void",
328 | "args": "obj:String, multX:Float, multY:Float = 0",
329 | "documentation": "Works identically to `setGraphicSize()`"
330 | },
331 | "updateHitbox": {
332 | "returns": "void",
333 | "args": "obj:String",
334 | "documentation": "Use this to update the hitbox in case you change the sprite's scale manually or via a tween.\n* `obj` - Object from PlayState or Lua Sprite"
335 | },
336 | "setScrollFactor": {
337 | "returns": "void",
338 | "args": "obj:String, scrollX:Float, scrollY:Float",
339 | "documentation": "Changes how much a Sprite moves along with the camera.\n* `obj` - Lua Sprite tag or Object variable name\n* `scrollX` - Horizontal movement multiplier\n* `scrollY` - Vertical movement multiplier\n\nNote: Boyfriend/Opponent have a scrollX/scrollY value of 1, while Girlfriend have a scrollX/scrollY value of 0.95, if you're gonna do background elements, it's highly suggested that you make the values something under 1."
340 | },
341 | "setObjectCamera": {
342 | "returns": "void",
343 | "args": "obj:String, camera:String",
344 | "documentation": "Changes on which camera should your object be drawn on\n* `obj` - Lua Sprite tag or Object variable name\n* `camera` - `game`, `hud` or `other`."
345 | },
346 | "addCharacterToList": {
347 | "returns": "void",
348 | "args": "name:String, type:String",
349 | "documentation": "Creates a character for the `'Change Character'` event.\n* `name` - Character name, example: `pico-player`, `mom-car`, `gf`, etc.\n* `type` - Character type, 'boyfriend' for player, 'dad' for opponent, 'gf' for girlfriend."
350 | },
351 | "precacheSound": {
352 | "returns": "void",
353 | "args": "name:String",
354 | "documentation": "* `name` - Asset name, should be located inside `mods/sounds/` or `assets/sounds/`"
355 | },
356 | "precacheImage": {
357 | "returns": "void",
358 | "args": "name:String",
359 | "documentation": "* `name` - Asset name, should be located inside `mods/images/`\n\nNOTE: For now precacheImage is only capable of precaching images inside `mods/`!"
360 | },
361 | "playMusic": {
362 | "returns": "void",
363 | "args": "sound:String, volume:Float = 1, loop:Bool = false",
364 | "documentation": "* `sound` - File name (Should be located in `mods/music/` or `assets/music/`)\n* `volume` - Optional value, volume percent goes from `0` to `1`. Default value: `1`\n* `volume` - Optional value, if the music should loop indefinitely. Default value: `false`"
365 | },
366 | "playSound": {
367 | "returns": "void",
368 | "args": "sound:String, volume:Float = 1, ?tag:String",
369 | "documentation": "* `sound` - File name (Should be located in `mods/sounds/` or `assets/sounds/`)\n* `volume` - Optional value, volume percent goes from `0` to `1`. Default value: `1`\n* OPTIONAL: `tag` - Should only be used if you want to pause, resume, change the time, volume or fade in/out your sound, if you don't want to use it, don't include a third value."
370 | },
371 | "stopSound": {
372 | "returns": "void",
373 | "args": "tag:String",
374 | "documentation": "This function will stop the sound AND remove it permanently!\n* `tag` - Sound tag"
375 | },
376 | "pauseSound": {
377 | "returns": "void",
378 | "args": "tag:String",
379 | "documentation": "* `tag` - Sound tag"
380 | },
381 | "resumeSound": {
382 | "returns": "void",
383 | "args": "tag:String",
384 | "documentation": "* `tag` - Sound tag"
385 | },
386 | "soundFadeIn": {
387 | "returns": "void",
388 | "args": "tag:String, duration:Float, fromValue:Float = 0, toValue:Float = 1",
389 | "documentation": "* `tag` - Sound tag, leave this field empty if you want to do a fade in on the Background music instead\n`duration` - The time it takes for the volume to go from `fromValue` to `toValue`\n* `fromValue` - Starting value. Default value is `0`\n* `toValue` - End value. Default value is `1`"
390 | },
391 | "soundFadeOut": {
392 | "returns": "void",
393 | "args": "tag:String, duration:Float, toValue:Float = 0",
394 | "documentation": "* `tag` - Sound tag, leave this field empty if you want to do a fade out on the Background music instead\n* `duration` - The time it takes for the volume to go from the starting volume to `toValue`\n* `toValue` - End value. Default value is `0`"
395 | },
396 | "soundFadeCancel": {
397 | "returns": "void",
398 | "args": "tag:String",
399 | "documentation": "* `tag` - Sound tag, leave this field empty if you want cancel the Background music's fade in/out instead"
400 | },
401 | "getSoundVolume": {
402 | "returns": "number",
403 | "args": "tag:String",
404 | "documentation": "* `tag` - Sound tag, leave this field empty if you want get the Background music's volume instead"
405 | },
406 | "setSoundVolume": {
407 | "returns": "void",
408 | "args": "tag:String, value:Float",
409 | "documentation": "* `tag` - Sound tag, leave this field empty if you want change the Background music's volume instead\n* `value` - Goes from `0` to `1`."
410 | },
411 | "getSoundTime": {
412 | "returns": "number",
413 | "args": "tag:String",
414 | "documentation": "Gets the current sound's position in miliseconds\n* `tag` - Sound tag"
415 | },
416 | "setSoundTime": {
417 | "returns": "void",
418 | "args": "tag:String, value:Float",
419 | "documentation": "Sets the current sound's position in miliseconds\n* `tag` - Sound tag\n* `value` - New position"
420 | },
421 | "noteTweenX": {
422 | "returns": "void",
423 | "args": "tag:String, note:Int, value:Dynamic, duration:Float, ease:String",
424 | "documentation": "Do a tween on a receptor's X position\n* `tag` - Once the tween is finished, it will do a callback of `onTweenCompleted(tag)`\n* `note` - Opponent's notes are 0, 1, 2 and 3, respectively. Boyfriend's notes are 4, 5, 6, 7 respectively.\n* `value` - Target value on the tween end\n* `duration` - How much time it will take for the tween to end\n* `ease` - The tweening method used, example: `linear`, `circInOut`. Check the link on the note in the first line of this page"
425 | },
426 | "noteTweenY": {
427 | "returns": "void",
428 | "args": "tag:String, note:Int, value:Dynamic, duration:Float, ease:String",
429 | "documentation": "Do a tween on a receptor's Y position\n* `tag` - Once the tween is finished, it will do a callback of `onTweenCompleted(tag)`\n* `note` - Opponent's notes are 0, 1, 2 and 3, respectively. Boyfriend's notes are 4, 5, 6, 7 respectively.\n* `value` - Target value on the tween end\n* `duration` - How much time it will take for the tween to end\n* `ease` - The tweening method used, example: `linear`, `circInOut`. Check the link on the note in the first line of this page"
430 | },
431 | "noteTweenAngle": {
432 | "returns": "void",
433 | "args": "tag:String, note:Int, value:Dynamic, duration:Float, ease:String",
434 | "documentation": "Do a tween on a receptor's Angle value\n* `tag` - Once the tween is finished, it will do a callback of `onTweenCompleted(tag)`\n* `note` - Opponent's notes are 0, 1, 2 and 3, respectively. Boyfriend's notes are 4, 5, 6, 7 respectively.\n* `value` - Target value on the tween end\n* `duration` - How much time it will take for the tween to end\n* `ease` - The tweening method used, example: `linear`, `circInOut`. Check the link on the note in the first line of this page"
435 | },
436 | "noteTweenAlpha": {
437 | "returns": "void",
438 | "args": "tag:String, note:Int, value:Dynamic, duration:Float, ease:String",
439 | "documentation": "Do a tween on a receptor's Alpha value\n* `tag` - Once the tween is finished, it will do a callback of `onTweenCompleted(tag)`\n* `note` - Opponent's notes are 0, 1, 2 and 3, respectively. Boyfriend's notes are 4, 5, 6, 7 respectively.\n* `value` - Target value on the tween end\n* `duration` - How much time it will take for the tween to end\n* `ease` - The tweening method used, example: `linear`, `circInOut`. Check the link on the note in the first line of this page"
440 | },
441 | "doTweenX": {
442 | "returns": "void",
443 | "args": "tag:String, vars:String, value:Dynamic, duration:Float, ease:String",
444 | "documentation": "Do a Tween on an object's X value\n\n**Calling this function will cancel another tween that is using the same tag!**\n* `tag` - Once the tween is finished, it will do a callback of `onTweenCompleted(tag)`\n* `vars` - Variable to tween, example: `boyfriend` for tweening Boyfriend's X position, `boyfriend.scale` for tweening Boyfriend's Scale X\n* `value` - Target value on the tween end\n* `duration` - How much time it will take for the tween to end\n* `ease` - The tweening method used, example: `linear`, `circInOut`. Check the link on the note i've added up here\n\nExample: To do a tween to Boyfriend's Scale X, you should use `doTweenX('bfScaleTweenX', 'boyfriend.scale', 1.5, 1, 'elasticInOut')`, when the tween ends, it will do a callback for `onTweenCompleted('bfScaleTweenX')`"
445 | },
446 | "doTweenY": {
447 | "returns": "void",
448 | "args": "tag:String, vars:String, value:Dynamic, duration:Float, ease:String",
449 | "documentation": "Do a Tween on an object's Y value\n\n**Calling this function will cancel another tween that is using the same tag!**\n* `tag` - Once the tween is finished, it will do a callback of `onTweenCompleted(tag)`\n* `vars` - Variable to tween, example: `boyfriend` for tweening Boyfriend's X position, `boyfriend.scale` for tweening Boyfriend's Scale X\n* `value` - Target value on the tween end\n* `duration` - How much time it will take for the tween to end\n* `ease` - The tweening method used, example: `linear`, `circInOut`. Check the link on the note i've added up here\n\nExample: To do a tween to Boyfriend's Scale X, you should use `doTweenX('bfScaleTweenX', 'boyfriend.scale', 1.5, 1, 'elasticInOut')`, when the tween ends, it will do a callback for `onTweenCompleted('bfScaleTweenX')`"
450 | },
451 | "doTweenAngle": {
452 | "returns": "void",
453 | "args": "tag:String, vars:String, value:Dynamic, duration:Float, ease:String",
454 | "documentation": "Do a Tween on an object's Angle value\n\n**Calling this function will cancel another tween that is using the same tag!**\n* `tag` - Once the tween is finished, it will do a callback of `onTweenCompleted(tag)`\n* `vars` - Variable to tween, example: `boyfriend` for tweening Boyfriend's X position, `boyfriend.scale` for tweening Boyfriend's Scale X\n* `value` - Target value on the tween end\n* `duration` - How much time it will take for the tween to end\n* `ease` - The tweening method used, example: `linear`, `circInOut`. Check the link on the note i've added up here\n\nExample: To do a tween to Boyfriend's Scale X, you should use `doTweenX('bfScaleTweenX', 'boyfriend.scale', 1.5, 1, 'elasticInOut')`, when the tween ends, it will do a callback for `onTweenCompleted('bfScaleTweenX')`"
455 | },
456 | "doTweenAlpha": {
457 | "returns": "void",
458 | "args": "tag:String, vars:String, value:Dynamic, duration:Float, ease:String",
459 | "documentation": "Do a Tween on an object's Alpha value\n\n**Calling this function will cancel another tween that is using the same tag!**\n* `tag` - Once the tween is finished, it will do a callback of `onTweenCompleted(tag)`\n* `vars` - Variable to tween, example: `boyfriend` for tweening Boyfriend's X position, `boyfriend.scale` for tweening Boyfriend's Scale X\n* `value` - Target value on the tween end\n* `duration` - How much time it will take for the tween to end\n* `ease` - The tweening method used, example: `linear`, `circInOut`. Check the link on the note i've added up here\n\nExample: To do a tween to Boyfriend's Scale X, you should use `doTweenX('bfScaleTweenX', 'boyfriend.scale', 1.5, 1, 'elasticInOut')`, when the tween ends, it will do a callback for `onTweenCompleted('bfScaleTweenX')`"
460 | },
461 | "doTweenZoom": {
462 | "returns": "void",
463 | "args": "tag:String, cam:String, value:Dynamic, duration:Float, ease:String",
464 | "documentation": "Do a Tween on a Camera's Zoom\n\n**Calling this function will cancel another tween that is using the same tag!**\n* `tag` - Once the tween is finished, it will do a callback of `onTweenCompleted(tag)`\n* `cam` - Should be `camGame`, `camHUD` or `camOther`\n* `value` - Target value on the tween end\n* `duration` - How much time it will take for the tween to end\n* `ease` - The tweening method used, example: `linear`, `circInOut`. Check the link on the note i've added up here\n\nExample: To do a tween to Boyfriend's Scale X, you should use `doTweenX('bfScaleTweenX', 'boyfriend.scale', 1.5, 1, 'elasticInOut')`, when the tween ends, it will do a callback for `onTweenCompleted('bfScaleTweenX')`"
465 | },
466 | "doTweenColor": {
467 | "returns": "void",
468 | "args": "tag:String, vars:String, targetColor:String, duration:Float, ease:String",
469 | "documentation": "Do a Tween on an object's color\n\n**Calling this function will cancel another tween that is using the same tag!**\n* `tag` - Once the tween is finished, it will do a callback of `onTweenCompleted(tag)`\n* `vars` - Variable to tween, example: `boyfriend` for tweening Boyfriend's X position, `boyfriend.scale` for tweening Boyfriend's Scale X\n* `targetColor` - The color the object will have when the tween ends (Must be in hexadecimal!)\n* `duration` - How much time it will take for the tween to end\n* `ease` - The tweening method used, example: `linear`, `circInOut`. Check the link on the note i've added up here\n\nExample: To tween Boyfriend's color to Red, you should use `doTweenColor('bfColorTween', 'boyfriend', 'FF0000', 1, 'linear')`, when the tween ends, it will do a callback for `onTweenCompleted('bfColorTween')`"
470 | },
471 | "runTimer": {
472 | "returns": "void",
473 | "args": "tag:String, time:Float = 1, loops:Int = 1",
474 | "documentation": "Runs a timer with a determined duration and loops count.\n\n**Calling this function will cancel another timer that is using the same tag!**\n* `tag` - Once the timer is finished, it will do a callback of `onTimerCompleted(tag, loops, loopsLeft)`\n* `time` - Optional value, how much time it takes to finish a loop. Default value is `1`\n* `loops` - Optional value, how much loops should it do, if it's set to `0`, it will repeat indefinitely. Default value is `1`"
475 | },
476 | "cancelTween": {
477 | "returns": "void",
478 | "args": "tag:String",
479 | "documentation": "Cancels a tween using the tag `tag`, if there even is one."
480 | },
481 | "cancelTimer": {
482 | "returns": "void",
483 | "args": "tag:String",
484 | "documentation": "Cancels a timer using the tag `tag`, if there even is one."
485 | },
486 | "getProperty": {
487 | "returns": "?",
488 | "args": "variable:String",
489 | "documentation": "Returns a current variable from PlayState's name.\n\nIt can also be used to get the variable from an object that is inside PlayState or a Lua Sprite.\n\nExample: If you wanted to get the current health's value, you should use `getProperty('health')`.\n\nExample 2: If you'd want to get Boyfriend's current character, you should use `getProperty('boyfriend.curCharacter')`"
490 | },
491 | "getPropertyFromGroup": {
492 | "returns": "?",
493 | "args": "obj:String, index:Int, variable:Dynamic",
494 | "documentation": "Gets a variable from an array/group member on PlayState.\n* `obj` - Group/Array variable\n* `index` - Member ID\n* `variable` - Variable to get the value\n\nExample: To get the next event note's strum Time, you should use `getPropertyFromGroup('eventNotes', 0, 0)`\n\nExample 2: To get the next unspawned note's noteData, you should use `getPropertyFromGroup('unspawnNotes', 0, 'noteData')`"
495 | },
496 | "getPropertyFromClass": {
497 | "returns": "?",
498 | "args": "classVar:String, variable:String",
499 | "documentation": "Works similar to `getProperty()`, but can be used to access a variable inside a Class other than PlayState.\n\nExample: To get how much time has passed since the last frame (in milliseconds), you should use `getPropertyFromClass('flixel.FlxG', 'elapsed')`."
500 | },
501 | "setProperty": {
502 | "returns": "boolean",
503 | "args": "variable:String, value:Dynamic",
504 | "documentation": "Works in the same way as `getProperty()`, but it sets a new value for the variable.\n\nAlso returns the new value of the variable.\n\nExample: To set the player's current health to 100%, you should use `setProperty('health', 2)`"
505 | },
506 | "setPropertyFromGroup": {
507 | "returns": "void",
508 | "args": "obj:String, index:Int, variable:Dynamic, value:Dynamic",
509 | "documentation": "Sets the new value to a variable from an array/group member on PlayState.\n* `obj` - Group/Array variable\n* `index` - Member ID\n* `variable` - Variable to get the value\n* `value` - New value to set"
510 | },
511 | "setPropertyFromClass": {
512 | "returns": "boolean",
513 | "args": "classVar:String, variable:String, value:Dynamic",
514 | "documentation": "Works similar to `setProperty()`, but can be used to access a variable inside a Class other than PlayState.\n\nExample: To make the mouse visible, you should use `getPropertyFromClass('FlxG', 'mouse.visible', true)`."
515 | },
516 | "openCustomSubstate": {
517 | "returns": "void",
518 | "args": "name:String, pauseGame:Bool = false",
519 | "documentation": "Adds a custom substate named `name` to current FlxState"
520 | },
521 | "closeCustomSubstate": {
522 | "returns": "void",
523 | "args": "",
524 | "documentation": "Closes current active substate"
525 | },
526 | "initLuaShader": {
527 | "returns": "void",
528 | "args": "name:String, glslVersion:Int = 120",
529 | "documentation": "Initializes a new GLSL shader. This will get `name`.frag and `name`.vert files"
530 | },
531 | "setSpriteShader": {
532 | "returns": "void",
533 | "args": "obj:String, shader:String",
534 | "documentation": "Sets the shader of some `obj` to `shader`"
535 | },
536 | "removeSpriteShader": {
537 | "returns": "void",
538 | "args": "obj:String",
539 | "documentation": "Removes shader from sprite"
540 | },
541 | "getShaderBool": {
542 | "returns": "boolean",
543 | "args": "obj:String, prop:String",
544 | "documentation": "Gets a boolean field from some shader"
545 | },
546 | "getShaderBoolArray": {
547 | "returns": "array",
548 | "args": "obj:String, prop:String",
549 | "documentation": "Gets a boolean array field from some shader"
550 | },
551 | "getShaderInt": {
552 | "returns": "number",
553 | "args": "obj:String, prop:String",
554 | "documentation": "Gets a integer field from some shader"
555 | },
556 | "getShaderIntArray": {
557 | "returns": "array",
558 | "args": "obj:String, prop:String",
559 | "documentation": "Gets a integer array field from some shader"
560 | },
561 | "getShaderFloat": {
562 | "returns": "number",
563 | "args": "obj:String, prop:String",
564 | "documentation": "Gets a float field from some shader"
565 | },
566 | "getShaderFloatArray": {
567 | "returns": "array",
568 | "args": "obj:String, prop:String",
569 | "documentation": "Gets a float array field from some shader"
570 | },
571 | "setShaderBool": {
572 | "returns": "void",
573 | "args": "obj:String, prop:String, value:Bool",
574 | "documentation": "Sets some shader's boolean field to `value`"
575 | },
576 | "setShaderBoolArray": {
577 | "returns": "void",
578 | "args": "obj:String, prop:String, values:Array",
579 | "documentation": "Sets some shader's boolean array field to `value`"
580 | },
581 | "setShaderInt": {
582 | "returns": "void",
583 | "args": "obj:String, prop:String, value:Int",
584 | "documentation": "Sets some shader's integer field to `value`"
585 | },
586 | "setShaderIntArray": {
587 | "returns": "void",
588 | "args": "obj:String, prop:String, values:Dynamic",
589 | "documentation": "Sets some shader's integer array field to `value`"
590 | },
591 | "setShaderFloat": {
592 | "returns": "void",
593 | "args": "obj:String, prop:String, value:Float",
594 | "documentation": "Sets some shader's float field to `value`"
595 | },
596 | "setShaderFloatArray": {
597 | "returns": "void",
598 | "args": "obj:String, prop:String, values:Dynamic",
599 | "documentation": "Sets some shader's float array field to `value`"
600 | },
601 | "getRunningScripts": {
602 | "returns": "array",
603 | "args": "",
604 | "documentation": "Returns array of all running lua scripts"
605 | },
606 | "callOnLuas": {
607 | "returns": "void",
608 | "args": "?funcName:String, ?args:Array, ignoreStops=false, ignoreSelf=true, ?exclusions:Array",
609 | "documentation": "Calls function on ALL lua scripts"
610 | },
611 | "callScript": {
612 | "returns": "void",
613 | "args": "?luaFile:String, ?funcName:String, ?args:Array",
614 | "documentation": "Calls a function on specific lua script"
615 | },
616 | "getGlobalFromScript": {
617 | "returns": "?",
618 | "args": "?luaFile:String, ?global:String",
619 | "documentation": "Gets a global field from specific lua script"
620 | },
621 | "setGlobalFromScript": {
622 | "returns": "void",
623 | "args": "luaFile:String, global:String, val:Dynamic",
624 | "documentation": "Sets a global field value from specific lua script to `val`"
625 | },
626 | "isRunning": {
627 | "returns": "boolean",
628 | "args": "luaFile:String",
629 | "documentation": "Checks if specific lua script is running"
630 | },
631 | "removeLuaScript": {
632 | "returns": "void",
633 | "args": "luaFile:String, ?ignoreAlreadyRunning:Bool = false",
634 | "documentation": "Removes lua script from active list"
635 | },
636 | "runHaxeCode": {
637 | "returns": "?",
638 | "args": "codeToRun:String",
639 | "documentation": "Runs a haxe code and returns it's value"
640 | },
641 | "addHaxeLibrary": {
642 | "returns": "void",
643 | "args": "libName:String, ?libPackage:String = ''",
644 | "documentation": "Adds a haxe library to all hscript scripts"
645 | },
646 | "loadSong": {
647 | "returns": "void",
648 | "args": "?name:String = null, ?difficultyNum:Int = -1",
649 | "documentation": "Sets current chart to another song"
650 | },
651 | "loadGraphic": {
652 | "returns": "void",
653 | "args": "variable:String, image:String, ?gridX:Int = 0, ?gridY:Int = 0",
654 | "documentation": "Loads a graphic"
655 | },
656 | "loadFrames": {
657 | "returns": "void",
658 | "args": "variable:String, image:String, spriteType:String = \"sparrow\"",
659 | "documentation": "Loads frames from some image"
660 | },
661 | "noteTweenDirection": {
662 | "returns": "void",
663 | "args": "tag:String, note:Int, value:Dynamic, duration:Float, ease:String",
664 | "documentation": "Tweens the rotation of `note` for `duration` seconds"
665 | },
666 | "getScore": {
667 | "returns": "number",
668 | "args": "",
669 | "documentation": "Returns current game score"
670 | },
671 | "getMisses": {
672 | "returns": "number",
673 | "args": "",
674 | "documentation": "Returns current game misses"
675 | },
676 | "getHits": {
677 | "returns": "number",
678 | "args": "",
679 | "documentation": "Returns current game hits"
680 | },
681 | "setHealth": {
682 | "returns": "void",
683 | "args": "value:Float = 0",
684 | "documentation": "Sets player's health ranged from `0` to `1`"
685 | },
686 | "addHealth": {
687 | "returns": "void",
688 | "args": "value:Float = 0",
689 | "documentation": "Sets player's health ranged from `0` to `1`"
690 | },
691 | "getHealth": {
692 | "returns": "number",
693 | "args": "",
694 | "documentation": "Returns player's health ranged from `0` to `1`"
695 | },
696 | "keyboardJustPressed": {
697 | "returns": "boolean",
698 | "args": "name:String",
699 | "documentation": "Checks if specific keyboard key was just pressed"
700 | },
701 | "keyboardPressed": {
702 | "returns": "boolean",
703 | "args": "name:String",
704 | "documentation": "Checks if specific keyboard key is pressed"
705 | },
706 | "keyboardReleased": {
707 | "returns": "boolean",
708 | "args": "name:String",
709 | "documentation": "Checks if specific keyboard key was just released"
710 | },
711 | "anyGamepadJustPressed": {
712 | "returns": "boolean",
713 | "args": "name:String",
714 | "documentation": "Checks if any gamepad specific button was just pressed"
715 | },
716 | "anyGamepadPressed": {
717 | "returns": "boolean",
718 | "args": "name:String",
719 | "documentation": "Checks if any gamepad specific button is pressed"
720 | },
721 | "anyGamepadReleased": {
722 | "returns": "boolean",
723 | "args": "name:String",
724 | "documentation": "Checks if any gamepad specific button was just released"
725 | },
726 | "gamepadAnalogX": {
727 | "returns": "number",
728 | "args": "id:Int, ?leftStick:Bool = true",
729 | "documentation": "Returns the x of `id` analog"
730 | },
731 | "gamepadAnalogY": {
732 | "returns": "number",
733 | "args": "id:Int, ?leftStick:Bool = true",
734 | "documentation": "Returns the y of `id` analog"
735 | },
736 | "gamepadJustPressed": {
737 | "returns": "boolean",
738 | "args": "id:Int, name:String",
739 | "documentation": "Checks if gamepad with id `id` specific button was just pressed"
740 | },
741 | "gamepadPressed": {
742 | "returns": "boolean",
743 | "args": "id:Int, name:String",
744 | "documentation": "Checks if gamepad with id `id` specific button is pressed"
745 | },
746 | "gamepadReleased": {
747 | "returns": "boolean",
748 | "args": "id:Int, name:String",
749 | "documentation": "Checks if gamepad with id `id` specific button was just released"
750 | },
751 | "precacheMusic": {
752 | "returns": "void",
753 | "args": "name:String",
754 | "documentation": "Precaches specific music"
755 | },
756 | "restartSong": {
757 | "returns": "void",
758 | "args": "?skipTransition:Bool = false",
759 | "documentation": "Restarts current song"
760 | },
761 | "exitSong": {
762 | "returns": "void",
763 | "args": "?skipTransition:Bool = false",
764 | "documentation": "Exits current song and goes to Menu"
765 | },
766 | "setRatingName": {
767 | "returns": "void",
768 | "args": "value:String",
769 | "documentation": "Sets the rating message"
770 | },
771 | "setRatingFC": {
772 | "returns": "void",
773 | "args": "value:String",
774 | "documentation": "Sets the FC rating"
775 | },
776 | "getMidpointX": {
777 | "returns": "number",
778 | "args": "variable:String",
779 | "documentation": "Returns the midpoint X of specific FlxSprite hitbox"
780 | },
781 | "getMidpointY": {
782 | "returns": "number",
783 | "args": "variable:String",
784 | "documentation": "Returns the midpoint Y of specific FlxSprite hitbox"
785 | },
786 | "getGraphicMidpointX": {
787 | "returns": "number",
788 | "args": "variable:String",
789 | "documentation": "Returns the midpoint X of specific FlxSprite graphic"
790 | },
791 | "getGraphicMidpointY": {
792 | "returns": "number",
793 | "args": "variable:String",
794 | "documentation": "Returns the midpoint Y of specific FlxSprite graphic"
795 | },
796 | "getScreenPositionX": {
797 | "returns": "number",
798 | "args": "variable:String",
799 | "documentation": "Returns the X position of `variable` on screen"
800 | },
801 | "getScreenPositionY": {
802 | "returns": "number",
803 | "args": "variable:String",
804 | "documentation": "Returns the Y position of `variable` on screen"
805 | },
806 | "addAnimation": {
807 | "returns": "void",
808 | "args": "obj:String, name:String, frames:Array, framerate:Int = 24, loop:Bool = true",
809 | "documentation": "Adds a animation to specific sprite"
810 | },
811 | "addAnimationByIndicesLoop": {
812 | "returns": "void",
813 | "args": "obj:String, name:String, prefix:String, indices:String, framerate:Int = 24",
814 | "documentation": "Adds a animation by indices to specific sprite"
815 | },
816 | "playAnim": {
817 | "returns": "void",
818 | "args": "obj:String, name:String, forced:Bool = false, ?reverse:Bool = false, ?startFrame:Int = 0",
819 | "documentation": "Plays a animation on specifc sprite"
820 | },
821 | "addOffset": {
822 | "returns": "void",
823 | "args": "obj:String, anim:String, x:Float, y:Float",
824 | "documentation": "Adds offset to the animation of specific sprite"
825 | },
826 | "updateHitboxFromGroup": {
827 | "returns": "void",
828 | "args": "group:String, index:Int",
829 | "documentation": "Updates the hitbox of item with index `index` in group `group`"
830 | },
831 | "luaSpriteExists": {
832 | "returns": "boolean",
833 | "args": "tag:String",
834 | "documentation": "Checks if specific lua sprite exists"
835 | },
836 | "luaTextExists": {
837 | "returns": "boolean",
838 | "args": "tag:String",
839 | "documentation": "Checks if specifc lua text object exists"
840 | },
841 | "luaSoundExists": {
842 | "returns": "boolean",
843 | "args": "tag:String",
844 | "documentation": "Checks if specific lua sound exists"
845 | },
846 | "setHealthBarColors": {
847 | "returns": "void",
848 | "args": "leftHex:String, rightHex:String",
849 | "documentation": "Sets the colors player's health bar (in hex)"
850 | },
851 | "setTimeBarColors": {
852 | "returns": "void",
853 | "args": "leftHex:String, rightHex:String",
854 | "documentation": "Sets the colors of time progress bar (in hex)"
855 | },
856 | "screenCenter": {
857 | "returns": "void",
858 | "args": "obj:String, pos:String = 'xy'",
859 | "documentation": "Centers a object on screen"
860 | },
861 | "objectsOverlap": {
862 | "returns": "boolean",
863 | "args": "obj1:String, obj2:String",
864 | "documentation": "Checks if `obj1` overlaps/collides `obj2`"
865 | },
866 | "getPixelColor": {
867 | "returns": "number",
868 | "args": "obj:String, x:Int, y:Int",
869 | "documentation": "Gets the color of `obj` specific pixel"
870 | },
871 | "getRandomInt": {
872 | "returns": "number",
873 | "args": "min:Int, max:Int = FlxMath.MAX_VALUE_INT, exclude:String = ''",
874 | "documentation": "Returns randomly generated integer"
875 | },
876 | "getRandomFloat": {
877 | "returns": "number",
878 | "args": "min:Float, max:Float = 1, exclude:String = ''",
879 | "documentation": "Returns randomly generated float"
880 | },
881 | "getRandomBool": {
882 | "returns": "boolean",
883 | "args": "chance:Float = 50",
884 | "documentation": "Returns `true` with `chance` chance; else `false`"
885 | },
886 | "changePresence": {
887 | "returns": "void",
888 | "args": "details:String, state:Null, ?smallImageKey:String, ?hasStartTimestamp:Bool, ?endTimestamp:Float",
889 | "documentation": "Changes discordrpc presence"
890 | },
891 | "initSaveData": {
892 | "returns": "void",
893 | "args": "name:String, ?folder:String = 'psychenginemods'",
894 | "documentation": "Initializes specific save"
895 | },
896 | "flushSaveData": {
897 | "returns": "void",
898 | "args": "name:String",
899 | "documentation": "Saves specific save"
900 | },
901 | "getDataFromSave": {
902 | "returns": "?",
903 | "args": "name:String, field:String, ?defaultValue:Dynamic = null",
904 | "documentation": "Returns data of specific save"
905 | },
906 | "setDataFromSave": {
907 | "returns": "void",
908 | "args": "name:String, field:String, value:Dynamic",
909 | "documentation": "Sets the data of specifc save to `value`"
910 | },
911 | "checkFileExists": {
912 | "returns": "boolean",
913 | "args": "filename:String, ?absolute:Bool = false",
914 | "documentation": "Checks if specific path exists"
915 | },
916 | "saveFile": {
917 | "returns": "void",
918 | "args": "path:String, content:String, ?absolute:Bool = false",
919 | "documentation": "Saves file with `content` content"
920 | },
921 | "deleteFile": {
922 | "returns": "void",
923 | "args": "path:String, ?ignoreModFolders:Bool = false",
924 | "documentation": "Deletes a path"
925 | },
926 | "getTextFromFile": {
927 | "returns": "string",
928 | "args": "path:String, ?ignoreModFolders:Bool = false",
929 | "documentation": "Returns content of specific file in text"
930 | },
931 | "luaSpriteMakeGraphic": {
932 | "returns": "void",
933 | "args": "tag:String, width:Int, height:Int, color:String",
934 | "documentation": "Why are you using it?",
935 | "deprecated": "luaSpriteMakeGraphic is deprecated! Use makeGraphic instead"
936 | },
937 | "luaSpriteAddAnimationByPrefix": {
938 | "returns": "void",
939 | "args": "tag:String, name:String, prefix:String, framerate:Int = 24, loop:Bool = true",
940 | "documentation": "Why are you using it?",
941 | "deprecated": "luaSpriteAddAnimationByPrefix is deprecated! Use addAnimationByPrefix instead"
942 | },
943 | "luaSpriteAddAnimationByIndices": {
944 | "returns": "void",
945 | "args": "tag:String, name:String, prefix:String, indices:String, framerate:Int = 24",
946 | "documentation": "Why are you using it?",
947 | "deprecated": "luaSpriteAddAnimationByIndices is deprecated! Use addAnimationByIndices instead"
948 | },
949 | "luaSpritePlayAnimation": {
950 | "returns": "void",
951 | "args": "tag:String, name:String, forced:Bool = false",
952 | "documentation": "Why are you using it?",
953 | "deprecated": "luaSpritePlayAnimation is deprecated! Use playAnim instead"
954 | },
955 | "setLuaSpriteCamera": {
956 | "returns": "void",
957 | "args": "tag:String, camera:String = ''",
958 | "documentation": "Why are you using it?",
959 | "deprecated": "setLuaSpriteCamera is deprecated! Use setObjectCamera instead"
960 | },
961 | "setLuaSpriteScrollFactor": {
962 | "returns": "void",
963 | "args": "tag:String, scrollX:Float, scrollY:Float",
964 | "documentation": "Why are you using it?",
965 | "deprecated": "setLuaSpriteScrollFactor is deprecated! Use setScrollFactor instead"
966 | },
967 | "scaleLuaSprite": {
968 | "returns": "void",
969 | "args": "tag:String, x:Float, y:Float",
970 | "documentation": "Why are you using it?",
971 | "deprecated": "scaleLuaSprite is deprecated! Use scaleObject instead"
972 | },
973 | "getPropertyLuaSprite": {
974 | "returns": "?",
975 | "args": "tag:String, variable:String",
976 | "documentation": "Why are you using it?",
977 | "deprecated": "getPropertyLuaSprite is deprecated! Use getProperty instead"
978 | },
979 | "setPropertyLuaSprite": {
980 | "returns": "void",
981 | "args": "tag:String, variable:String, value:Dynamic",
982 | "documentation": "Why are you using it?",
983 | "deprecated": "setPropertyLuaSprite is deprecated! Use setProperty instead"
984 | },
985 | "musicFadeIn": {
986 | "returns": "void",
987 | "args": "duration:Float, fromValue:Float = 0, toValue:Float = 1",
988 | "documentation": "Fades in the volume of music",
989 | "deprecated": "musicFadeIn is deprecated! Use soundFadeIn instead."
990 | },
991 | "musicFadeOut": {
992 | "returns": "void",
993 | "args": "duration:Float, toValue:Float = 0",
994 | "documentation": "Fades out the volume of music",
995 | "deprecated": "musicFadeOut is deprecated! Use soundFadeOut instead."
996 | },
997 | "stringStartsWith": {
998 | "returns": "boolean",
999 | "args": "str:String, start:String",
1000 | "documentation": "Checks if `str` starts with `start`"
1001 | },
1002 | "stringEndsWith": {
1003 | "returns": "boolean",
1004 | "args": "str:String, end:String",
1005 | "documentation": "Checks if `str` ends with `end`"
1006 | },
1007 | "stringSplit": {
1008 | "returns": "array",
1009 | "args": "str:String, split:String",
1010 | "documentation": "Splits string with `split` delimeter to array of strings"
1011 | },
1012 | "stringTrim": {
1013 | "returns": "string",
1014 | "args": "str:String",
1015 | "documentation": "Removes empty characters from the beginning and end of `string`"
1016 | },
1017 | "directoryFileList": {
1018 | "returns": "array",
1019 | "args": "folder:String",
1020 | "documentation": "Returns array of filenames in `folder`"
1021 | },
1022 | "setShaderSampler2D": {
1023 | "returns": "void",
1024 | "args": "obj:String, prop:String, bitmapdataPath:String",
1025 | "documentation": "Sets the shader sampler bitmap to `bitmapdataPath` image"
1026 | }
1027 | },
1028 | "variables": {
1029 | "luaDebugMode": {
1030 | "returns": "boolean",
1031 | "documentation": "Enables debug mode, use `luaDebugMode = true` to enable it. Default value: `false`"
1032 | },
1033 | "luaDeprecatedWarnings": {
1034 | "returns": "boolean",
1035 | "documentation": "Tells you if a function/variable is deprecated (shouldn't be used anymore), only works when Debug mode is on, use `luaDeprecatedWarnings = false` to disable it. Default value: `true`"
1036 | },
1037 | "inChartEditor": {
1038 | "returns": "boolean",
1039 | "documentation": "Tells you if your script is running on Chart Editor's chart playtest."
1040 | },
1041 | "curBpm": {
1042 | "returns": "number",
1043 | "documentation": "Current BPM of the Song, shortcut to `getPropertyFromClass('Conductor', 'bpm')`"
1044 | },
1045 | "bpm": {
1046 | "returns": "number",
1047 | "documentation": "Starting BPM of the Song, shortcut to `getProperty('SONG.bpm')`"
1048 | },
1049 | "scrollSpeed": {
1050 | "returns": "number",
1051 | "documentation": "Starting Scroll speed of the Song, shortcut to `getProperty('SONG.speed')`"
1052 | },
1053 | "crochet": {
1054 | "returns": "number",
1055 | "documentation": "Interval between Beat hits"
1056 | },
1057 | "stepCrochet": {
1058 | "returns": "number",
1059 | "documentation": "Interval between Step hits"
1060 | },
1061 | "songLength": {
1062 | "returns": "number",
1063 | "documentation": "Song duration in milliseconds"
1064 | },
1065 | "songName": {
1066 | "returns": "string",
1067 | "documentation": "Shortcut to `getProperty('SONG.song')`"
1068 | },
1069 | "isStoryMode": {
1070 | "returns": "boolean",
1071 | "documentation": "Shortcut to `getProperty('isStoryMode')`"
1072 | },
1073 | "difficulty": {
1074 | "returns": "number",
1075 | "documentation": "Returns the difficulty ID (Easy = 0, Normal = 1, Hard = 2), Shortcut to `getProperty('storyDifficulty')`"
1076 | },
1077 | "weekRaw": {
1078 | "returns": "number",
1079 | "documentation": "Returns the raw current week number. I doubt you will ever use this, but hey, just in case you do, it's here."
1080 | },
1081 | "week": {
1082 | "returns": "string",
1083 | "documentation": "Returns the properly formatted current week file name."
1084 | },
1085 | "cameraX": {
1086 | "returns": "number",
1087 | "documentation": "Shortcut to `getProperty('camFollowPos.x')`"
1088 | },
1089 | "cameraY": {
1090 | "returns": "number",
1091 | "documentation": "Shortcut to `getProperty('camFollowPos.y')`"
1092 | },
1093 | "screenWidth": {
1094 | "returns": "number",
1095 | "documentation": "Shortcut to `getPropertyFromClass('FlxG', width)`"
1096 | },
1097 | "screenHeight": {
1098 | "returns": "number",
1099 | "documentation": "Shortcut to `getPropertyFromClass('FlxG', height)`"
1100 | },
1101 | "startedCountdown": {
1102 | "returns": "boolean",
1103 | "documentation": "Tells you if the countdown already started"
1104 | },
1105 | "seenCutscene": {
1106 | "returns": "boolean",
1107 | "documentation": "Is set to `true` after `onCreate()` function, Shortcut to `getProperty('seenCutscene')`"
1108 | },
1109 | "curBeat": {
1110 | "returns": "number",
1111 | "documentation": "Current beat number"
1112 | },
1113 | "curStep": {
1114 | "returns": "number",
1115 | "documentation": "Current step number"
1116 | },
1117 | "score": {
1118 | "returns": "number",
1119 | "documentation": "Current score, Shortcut to `getProperty('songScore')`"
1120 | },
1121 | "misses": {
1122 | "returns": "number",
1123 | "documentation": "Current total number of notes missed, Shortcut to `getProperty('songMisses')`"
1124 | },
1125 | "ghostMisses": {
1126 | "returns": "number",
1127 | "documentation": "Current number of Key press misses, Shortcut to `getProperty('ghostMisses')`"
1128 | },
1129 | "hits": {
1130 | "returns": "number",
1131 | "documentation": "Current number of notes hit, Shortcut to `getProperty('songHits')`"
1132 | },
1133 | "rating": {
1134 | "returns": "number",
1135 | "documentation": "Current rating percentage, goes from `0` to `1`. Shortcut to `getProperty('ratingPercent')`"
1136 | },
1137 | "ratingName": {
1138 | "returns": "string",
1139 | "documentation": "Current rating's name. Shortcut to `getProperty('ratingString')`"
1140 | },
1141 | "inGameOver": {
1142 | "returns": "boolean",
1143 | "documentation": "Player is Dead"
1144 | },
1145 | "mustHitSection": {
1146 | "returns": "boolean",
1147 | "documentation": "Tells if the current section is a `Must Hit Section` (from Chart Editor)"
1148 | },
1149 | "altAnim": {
1150 | "returns": "boolean",
1151 | "documentation": "Tells if the current section is a `Alt Animation` Section (from Chart Editor)"
1152 | },
1153 | "gfSection": {
1154 | "returns": "boolean",
1155 | "documentation": "Tells if the current section is a `GF Section` (from Chart Editor)"
1156 | },
1157 | "botPlay": {
1158 | "returns": "boolean",
1159 | "documentation": "Tells if Botplay is enabled. Shortcut to `getProperty('cpuControlled')`"
1160 | },
1161 | "defaultPlayerStrumX0": {
1162 | "returns": "number",
1163 | "documentation": "Player's default left arrow X"
1164 | },
1165 | "defaultPlayerStrumY0": {
1166 | "returns": "number",
1167 | "documentation": "Player's default left arrow Y"
1168 | },
1169 | "defaultOpponentStrumX0": {
1170 | "returns": "number",
1171 | "documentation": "Opponent's default left arrow X"
1172 | },
1173 | "defaultOpponentStrumY0": {
1174 | "returns": "number",
1175 | "documentation": "Opponent's default left arrow Y"
1176 | },
1177 | "defaultPlayerStrumX1": {
1178 | "returns": "number",
1179 | "documentation": "Player's default down arrow X"
1180 | },
1181 | "defaultPlayerStrumY1": {
1182 | "returns": "number",
1183 | "documentation": "Player's default down arrow Y"
1184 | },
1185 | "defaultOpponentStrumX1": {
1186 | "returns": "number",
1187 | "documentation": "Opponent's default down arrow X"
1188 | },
1189 | "defaultOpponentStrumY1": {
1190 | "returns": "number",
1191 | "documentation": "Opponent's default down arrow Y"
1192 | },
1193 | "defaultPlayerStrumX2": {
1194 | "returns": "number",
1195 | "documentation": "Player's default up arrow X"
1196 | },
1197 | "defaultPlayerStrumY2": {
1198 | "returns": "number",
1199 | "documentation": "Player's default up arrow Y"
1200 | },
1201 | "defaultOpponentStrumX2": {
1202 | "returns": "number",
1203 | "documentation": "Opponent's default up arrow X"
1204 | },
1205 | "defaultOpponentStrumY2": {
1206 | "returns": "number",
1207 | "documentation": "Opponent's default up arrow Y"
1208 | },
1209 | "defaultPlayerStrumX3": {
1210 | "returns": "number",
1211 | "documentation": "Player's default right arrow X"
1212 | },
1213 | "defaultPlayerStrumY3": {
1214 | "returns": "number",
1215 | "documentation": "Player's default right arrow Y"
1216 | },
1217 | "defaultOpponentStrumX3": {
1218 | "returns": "number",
1219 | "documentation": "Opponent's default right arrow X"
1220 | },
1221 | "defaultOpponentStrumY3": {
1222 | "returns": "number",
1223 | "documentation": "Opponent's default right arrow Y"
1224 | },
1225 | "defaultBoyfriendX": {
1226 | "returns": "number",
1227 | "documentation": "Player's Default X position, defined by the Stage's JSON file."
1228 | },
1229 | "defaultBoyfriendY": {
1230 | "returns": "number",
1231 | "documentation": "Player's Default Y position, defined by the Stage's JSON file."
1232 | },
1233 | "defaultOpponentX": {
1234 | "returns": "number",
1235 | "documentation": "Opponent's Default X position, defined by the Stage's JSON file."
1236 | },
1237 | "defaultOpponentY": {
1238 | "returns": "number",
1239 | "documentation": "Opponent's Default Y position, defined by the Stage's JSON file."
1240 | },
1241 | "defaultGirlfriendX": {
1242 | "returns": "number",
1243 | "documentation": "Girlfriend's Default X position, defined by the Stage's JSON file."
1244 | },
1245 | "defaultGirlfriendY": {
1246 | "returns": "number",
1247 | "documentation": "Girlfriend's Default Y position, defined by the Stage's JSON file."
1248 | },
1249 | "downscroll": {
1250 | "returns": "boolean",
1251 | "documentation": "Downscroll is enabled. Shortcut to `getPropertyFromClass('ClientPrefs', 'downScroll')`"
1252 | },
1253 | "middlescroll": {
1254 | "returns": "boolean",
1255 | "documentation": "Middlescroll is enabled. Shortcut to `getPropertyFromClass('ClientPrefs', 'middleScroll')`"
1256 | },
1257 | "framerate": {
1258 | "returns": "boolean",
1259 | "documentation": "Current framerate limit. Shortcut to `getPropertyFromClass('ClientPrefs', 'framerate')`"
1260 | },
1261 | "ghostTapping": {
1262 | "returns": "boolean",
1263 | "documentation": "Ghost tapping is enabled. Shortcut to `getPropertyFromClass('ClientPrefs', 'ghostTapping')`"
1264 | },
1265 | "hideHud": {
1266 | "returns": "boolean",
1267 | "documentation": "\"Hide HUD\" is enabled. Shortcut to `getPropertyFromClass('ClientPrefs', 'hideHud')`"
1268 | },
1269 | "hideTime": {
1270 | "returns": "boolean",
1271 | "documentation": "\"Hide Song Length\" is enabled. Shortcut to `getPropertyFromClass('ClientPrefs', 'hideTime')`"
1272 | },
1273 | "cameraZoomOnBeat": {
1274 | "returns": "boolean",
1275 | "documentation": "Shortcut to `getPropertyFromClass('ClientPrefs', 'camZooms')`"
1276 | },
1277 | "flashingLights": {
1278 | "returns": "boolean",
1279 | "documentation": "Shortcut to `getPropertyFromClass('ClientPrefs', 'flashing')`"
1280 | },
1281 | "noteOffset": {
1282 | "returns": "number",
1283 | "documentation": "Represents the note delay in milliseconds (Goes from `0` to `500`). Shortcut to `getPropertyFromClass('ClientPrefs', 'noteOffset')`"
1284 | },
1285 | "lowQuality": {
1286 | "returns": "boolean",
1287 | "documentation": "Shortcut to `getPropertyFromClass('ClientPrefs', 'lowQuality')`"
1288 | },
1289 | "Function_StopLua": {
1290 | "returns": "number",
1291 | "documentation": "Returns 2"
1292 | },
1293 | "Function_Stop": {
1294 | "returns": "number",
1295 | "documentation": "Returns 1"
1296 | },
1297 | "Function_Continue": {
1298 | "returns": "number",
1299 | "documentation": "Returns 0"
1300 | },
1301 | "songPath": {
1302 | "returns": "string",
1303 | "documentation": "Returns path of current song"
1304 | },
1305 | "curStage": {
1306 | "returns": "string",
1307 | "documentation": "Returns the id/name of current stage"
1308 | },
1309 | "difficultyName": {
1310 | "returns": "string",
1311 | "documentation": "The difficulty of current difficulty"
1312 | },
1313 | "difficultyPath": {
1314 | "returns": "string",
1315 | "documentation": "Returns path of current song chart"
1316 | },
1317 | "curDecBeat": {
1318 | "returns": "number",
1319 | "documentation": "Returns current beat in a decimal (float) value"
1320 | },
1321 | "curDecStep": {
1322 | "returns": "number",
1323 | "documentation": "Returns current step in a decimal (float) value"
1324 | },
1325 | "ratingFC": {
1326 | "returns": "string",
1327 | "documentation": "Returns current FC rating"
1328 | },
1329 | "version": {
1330 | "returns": "string",
1331 | "documentation": "Returns the version of engine"
1332 | },
1333 | "healthGainMult": {
1334 | "returns": "number",
1335 | "documentation": "The gain multiplayer of health"
1336 | },
1337 | "healthLossMult": {
1338 | "returns": "number",
1339 | "documentation": "The loss multiplayer of health"
1340 | },
1341 | "playbackRate": {
1342 | "returns": "number",
1343 | "documentation": "Pitch of current song"
1344 | },
1345 | "instakillOnMiss": {
1346 | "returns": "boolean",
1347 | "documentation": "Returns if miss == death"
1348 | },
1349 | "practice": {
1350 | "returns": "boolean",
1351 | "documentation": "Whatever or not practice mode is on"
1352 | },
1353 | "defaultPlayerStrumX": {
1354 | "returns": "number",
1355 | "documentation": "Returns default X position of player strumline"
1356 | },
1357 | "defaultPlayerStrumY": {
1358 | "returns": "number",
1359 | "documentation": "Returns default Y position of player strumline"
1360 | },
1361 | "defaultOpponentStrumX": {
1362 | "returns": "number",
1363 | "documentation": "Returns default X position of opponents strumline"
1364 | },
1365 | "defaultOpponentStrumY": {
1366 | "returns": "number",
1367 | "documentation": "Returns default Y position of opponents strumline"
1368 | },
1369 | "boyfriendName": {
1370 | "returns": "string",
1371 | "documentation": "First name BOYFRIEND last name .xml (but seriously it's current character name of boyfriend)"
1372 | },
1373 | "dadName": {
1374 | "returns": "string",
1375 | "documentation": "Dad`s current character name"
1376 | },
1377 | "gfName": {
1378 | "returns": "string",
1379 | "documentation": "Gf's current character name"
1380 | },
1381 | "timeBarType": {
1382 | "returns": "string",
1383 | "documentation": "Returns the type of timebar"
1384 | },
1385 | "scoreZoom": {
1386 | "returns": "boolean",
1387 | "documentation": "Returns whatever or not score bar should be zoomed when a note is hit"
1388 | },
1389 | "healthBarAlpha": {
1390 | "returns": "number",
1391 | "documentation": "Current alpha value of health bar"
1392 | },
1393 | "noResetButton": {
1394 | "returns": "boolean",
1395 | "documentation": "If true reset button does nothing"
1396 | },
1397 | "shadersEnabled": {
1398 | "returns": "boolean",
1399 | "documentation": "Whetever or not shaders are enabled by player"
1400 | },
1401 | "scriptName": {
1402 | "returns": "string",
1403 | "documentation": "The name/id of this script"
1404 | },
1405 | "currentModDirectory": {
1406 | "returns": "string",
1407 | "documentation": "Returns the path to current mod"
1408 | },
1409 | "buildTarget": {
1410 | "returns": "string",
1411 | "documentation": "The build target, should be `windows`, `linux`, `mac`, `browser`, `android` or `unknown` (useful to block playing a mod on gamaverse)"
1412 | }
1413 | },
1414 | "events": {
1415 | "onCreate": {
1416 | "returns": "void",
1417 | "documentation": "Local function called after registering all lua functions/variables",
1418 | "args": ""
1419 | },
1420 | "onCreatePost": {
1421 | "returns": "void",
1422 | "documentation": "Local function called after registering every main function/variables",
1423 | "args": ""
1424 | },
1425 | "onDestroy": {
1426 | "returns": "void",
1427 | "documentation": "Local function called when this script gets released from the memory",
1428 | "args": ""
1429 | },
1430 | "onTweenCompleted": {
1431 | "returns": "void",
1432 | "documentation": "Local function called after specific `tag` tween is completed",
1433 | "args": "tag:String"
1434 | },
1435 | "onTimerCompleted": {
1436 | "returns": "void",
1437 | "documentation": "Local function called everytime `tag` timer loops/completes",
1438 | "args": "tag:String, loops:Integer, loopsLeft:Integer"
1439 | },
1440 | "onSoundFinished": {
1441 | "returns": "void",
1442 | "documentation": "Local function called after specific sound with `tag` tag finishes",
1443 | "args": "tag:String"
1444 | },
1445 | "onCustomSubstateCreate": {
1446 | "returns": "void",
1447 | "documentation": "Local function called when custom substate is created",
1448 | "args": "name:String"
1449 | },
1450 | "onCustomSubstateCreatePost": {
1451 | "returns": "void",
1452 | "documentation": "Local function called after custom substate `create()` function gets called",
1453 | "args": "name:String"
1454 | },
1455 | "onCustomSubstateUpdate": {
1456 | "returns": "void",
1457 | "documentation": "Local function called when custom substate updates",
1458 | "args": "name:String, elapsed:Float"
1459 | },
1460 | "onCustomSubstateUpdatePost": {
1461 | "returns": "void",
1462 | "documentation": "Local function called after custom substate `update()` function gets called",
1463 | "args": "name:String, elapsed:Float"
1464 | },
1465 | "onCustomSubstateDestroy": {
1466 | "returns": "void",
1467 | "documentation": "Local function called after custom substate gets destroyed",
1468 | "args": "name:String"
1469 | },
1470 | "onGameOverStart": {
1471 | "returns": "void",
1472 | "documentation": "Local function called when gameover screen is created",
1473 | "args": ""
1474 | },
1475 | "onUpdate": {
1476 | "returns": "void",
1477 | "documentation": "Local function called when playstate/gameover screen is updated",
1478 | "args": "elapsed:Float"
1479 | },
1480 | "onGameOverConfirm": {
1481 | "returns": "void",
1482 | "documentation": "Local function called when player presses [[Confirm]] to restart current playstate or go back to menu",
1483 | "args": "isNotGoingToMenu:Float"
1484 | },
1485 | "onUpdatePost": {
1486 | "returns": "void",
1487 | "documentation": "Local function called when playstate/gameover screen `update()` function is called",
1488 | "args": "elapsed:Float"
1489 | },
1490 | "onStartCountdown": {
1491 | "returns": "void",
1492 | "documentation": "Local function called when countdown is started",
1493 | "args": ""
1494 | },
1495 | "onCountdownStarted": {
1496 | "returns": "void",
1497 | "documentation": "Local function called when after countdown",
1498 | "args": ""
1499 | },
1500 | "onCountdownTick": {
1501 | "returns": "void",
1502 | "documentation": "Local function called every countdown tick",
1503 | "args": "swagCounter:Int"
1504 | },
1505 | "onUpdateScore": {
1506 | "returns": "void",
1507 | "documentation": "Local function called when score text gets updated",
1508 | "args": "miss:Boolean"
1509 | },
1510 | "onNextDialogue": {
1511 | "returns": "void",
1512 | "documentation": "Local function called when dialogue goes to another page",
1513 | "args": "dialogueCount:Int"
1514 | },
1515 | "onSkipDialogue": {
1516 | "returns": "void",
1517 | "documentation": "Local function called when dialogue is skipped",
1518 | "args": "dialogueCount:Int"
1519 | },
1520 | "onSongStart": {
1521 | "returns": "void",
1522 | "documentation": "Local function called when song starts",
1523 | "args": ""
1524 | },
1525 | "eventEarlyTrigger": {
1526 | "returns": "number",
1527 | "documentation": "Local function called before event note triggers, must return the miliseconds event delay",
1528 | "args": "event:String"
1529 | },
1530 | "onResume": {
1531 | "returns": "void",
1532 | "documentation": "Local function called after game resumes",
1533 | "args": ""
1534 | },
1535 | "onPause": {
1536 | "returns": "void",
1537 | "documentation": "Local function called after game pauses",
1538 | "args": ""
1539 | },
1540 | "onSpawnNote": {
1541 | "returns": "void",
1542 | "documentation": "Local function called when game note is spawned",
1543 | "args": "membersIndex:Int, noteData:Int, noteType:String, isSustainNote:Bool"
1544 | },
1545 | "onGameOver": {
1546 | "returns": "void",
1547 | "documentation": "Local function called when player fucking dies",
1548 | "args": ""
1549 | },
1550 | "onEvent": {
1551 | "returns": "void",
1552 | "documentation": "Local function called when some event gets triggered",
1553 | "args": "eventName:String, value1:String, value2:String"
1554 | },
1555 | "onMoveCamera": {
1556 | "returns": "void",
1557 | "documentation": "Local function called when camera focuses on specific character, characters: `gf`, `dad`, `boyfriend`",
1558 | "args": "character:String"
1559 | },
1560 | "onEndSong": {
1561 | "returns": "void",
1562 | "documentation": "Local function called when song ends",
1563 | "args": ""
1564 | },
1565 | "onGhostTap": {
1566 | "returns": "void",
1567 | "documentation": "Local function called when player ghost taps",
1568 | "args": "key:Int"
1569 | },
1570 | "onKeyPress": {
1571 | "returns": "void",
1572 | "documentation": "Local function called when player presses some key",
1573 | "args": "key:Int"
1574 | },
1575 | "onKeyRelease": {
1576 | "returns": "void",
1577 | "documentation": "Local function called when player releases some key",
1578 | "args": "key:Int"
1579 | },
1580 | "noteMiss": {
1581 | "returns": "void",
1582 | "documentation": "Local function called when player misses a note, (`badNoteHit()`?)",
1583 | "args": "membersIndex:Int, noteData:Int, noteType:String, isSustainNote:Bool"
1584 | },
1585 | "noteMissPress": {
1586 | "returns": "void",
1587 | "documentation": "Local function called when player misses",
1588 | "args": "direction:Int"
1589 | },
1590 | "opponentNoteHit": {
1591 | "returns": "void",
1592 | "documentation": "Local function called when opponent hits a note",
1593 | "args": "membersIndex:Int, noteData:Int, noteType:String, isSustainNote:Bool"
1594 | },
1595 | "goodNoteHit": {
1596 | "returns": "void",
1597 | "documentation": "Local function called when player hits a note",
1598 | "args": "membersIndex:Int, noteData:Int, noteType:String, isSustainNote:Bool"
1599 | },
1600 | "onStepHit": {
1601 | "returns": "void",
1602 | "documentation": "Local function called every song step",
1603 | "args": ""
1604 | },
1605 | "onBeatHit": {
1606 | "returns": "void",
1607 | "documentation": "Local function called every song beat",
1608 | "args": ""
1609 | },
1610 | "onSectionHit": {
1611 | "returns": "void",
1612 | "documentation": "Local function called when jumping to another section",
1613 | "args": ""
1614 | },
1615 | "onRecalculateRating": {
1616 | "returns": "void",
1617 | "documentation": "Local function called accuracy percent is recalculated/updates",
1618 | "args": ""
1619 | }
1620 | }
1621 | }
--------------------------------------------------------------------------------
/data/psych_latest.ver:
--------------------------------------------------------------------------------
1 | psych_1.0
--------------------------------------------------------------------------------
/dataScraper.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | // using go because python is shit (only because it's syntax) and i couldn't find a better alternative to python than go
4 | // also go is pretty nice language
5 |
6 | import (
7 | "encoding/json"
8 | "fmt"
9 | "log"
10 | "os"
11 | )
12 |
13 | type Data struct {
14 | Functions map[string]Func `json:"functions"`
15 | Variables map[string]Var `json:"variables"`
16 | Events map[string]Event `json:"events"`
17 | }
18 |
19 | type Func struct {
20 | Returns string `json:"returns"`
21 | Args string `json:"args"`
22 | Documentation string `json:"documentation"`
23 | Deprecated string `json:"deprecated,omitempty"` // omitempty will not render empty fields
24 | }
25 |
26 | type Var struct {
27 | Returns string `json:"returns"`
28 | Documentation string `json:"documentation"`
29 | Deprecated string `json:"deprecated,omitempty"`
30 | }
31 |
32 | type Event struct {
33 | Returns string `json:"returns"`
34 | Args string `json:"args"`
35 | Documentation string `json:"documentation"`
36 | Deprecated string `json:"deprecated,omitempty"`
37 | }
38 |
39 | func main() {
40 | jsonData := Data{
41 | Functions: map[string]Func{},
42 | Variables: map[string]Var{},
43 | Events: map[string]Event{},
44 | }
45 |
46 | engineJsonData := Data{
47 | Functions: map[string]Func{},
48 | Variables: map[string]Var{},
49 | Events: map[string]Event{},
50 | }
51 |
52 | engineData, err := os.ReadFile("data/psych_0.7.json")
53 | if err == nil {
54 | // pointer jumpscare
55 | json.Unmarshal(engineData, &engineJsonData)
56 | }
57 |
58 | // i generated this dataInput.txt file using CTRL+SHIFT+F in the psych engines source code and it that window i searched for "Lua_helper.add_callback(lua,"
59 | // there after searching for that query, should be a "Open in editor" button which creates a copyable file, that file is basically dataInput.txt
60 | data, err := os.ReadFile("dataInput.txt")
61 | if err != nil {
62 | log.Fatal(err)
63 | }
64 |
65 | word := ""
66 | wordIndex := 0
67 | name := ""
68 | args := ""
69 | insideCallback := false
70 | insideFunctionArgs := false
71 | for _, cha := range data {
72 | char := string(cha)
73 | if (!insideCallback) {
74 | word = word + char
75 | }
76 | if (insideCallback && rune(cha) > 32) { // that was supposed to be used later but fuck it lol
77 | word = word + char
78 | }
79 |
80 | if (!insideCallback && rune(cha) <= 32) { //clear the word if loop encounters a blank character
81 | word = ""
82 | continue
83 | }
84 |
85 | if (insideCallback) {
86 | if (char == "," && wordIndex == 0) { //if has a comma then jump into args from name
87 | wordIndex = 1
88 | word = ""
89 | continue
90 | }
91 |
92 | if (wordIndex == 0 && rune(cha) > 32 && cha != '\'' && cha != '"') { //wait for the name
93 | name = name + char
94 | continue
95 | }
96 |
97 | if (wordIndex == 1 && !insideFunctionArgs && char == "(") { // begin arguments
98 | insideFunctionArgs = true
99 | continue
100 | }
101 |
102 | if (wordIndex == 1 && insideFunctionArgs && char != ")") { // append to args
103 | args = args + char
104 | continue
105 | }
106 |
107 | if (insideFunctionArgs && char == ")") { //stop on ")" in the "function()" and append it to the data
108 | swagFunc := Func{
109 | Returns: "void?",
110 | Args: args,
111 | Documentation: "No documentation yet.",
112 | }
113 | if fun, exists := engineJsonData.Functions[name]; exists {
114 | if (fun.Returns != "") {
115 | swagFunc.Returns = fun.Returns
116 | }
117 | if (fun.Documentation != "") {
118 | swagFunc.Documentation = fun.Documentation
119 | }
120 | }
121 | jsonData.Functions[name] = swagFunc
122 |
123 | word = ""
124 | insideCallback = false
125 | wordIndex = 0
126 | name = ""
127 | args = ""
128 | insideFunctionArgs = false
129 | continue
130 | }
131 | }
132 |
133 | if (word == "Lua_helper.add_callback(lua,") {
134 | word = ""
135 | insideCallback = true
136 | wordIndex = 0
137 | name = ""
138 | args = ""
139 | insideFunctionArgs = false
140 | continue
141 | }
142 | }
143 |
144 | jsonData.Events = engineJsonData.Events
145 | jsonData.Variables = engineJsonData.Variables
146 |
147 | for key, fun := range engineJsonData.Functions {
148 | if fun2, exists := jsonData.Functions[key]; !exists && fun2.Deprecated == "" {
149 | fun.Deprecated = "Removed from the API."
150 | jsonData.Functions[key] = fun
151 | }
152 | }
153 |
154 | file, _ := json.MarshalIndent(jsonData, "", "\t")
155 | err = os.WriteFile("dataOutput.json", file, 0644)
156 | if err != nil {
157 | log.Fatal("Couldn't save the output data file!")
158 | } else {
159 | fmt.Println("Saved the output data to 'dataOutput.json' file!")
160 | }
161 | }
162 |
--------------------------------------------------------------------------------
/images/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Snirozu/Funkin-Script-AutoComplete/0cab09cdbe253a0094f23fdcb2fe374085ab5ab4/images/icon.png
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "funkin-script-autocompleter",
3 | "displayName": "Funkin Script AutoCompleter",
4 | "description": "AutoCompleter for Friday Night Funkin` scripts",
5 | "version": "2.0.0",
6 | "icon": "images/icon.png",
7 | "publisher": "Snirozu",
8 | "private": true,
9 | "license": "Apache-2.0",
10 | "repository": {
11 | "type": "git",
12 | "url": "https://github.com/Snirozu/Funkin-Script-AutoComplete"
13 | },
14 | "engines": {
15 | "vscode": "^1.32.0"
16 | },
17 | "categories": [
18 | "Snippets",
19 | "Other"
20 | ],
21 | "activationEvents": [
22 | "onStartupFinished"
23 | ],
24 | "main": "./out/extension.js",
25 | "scripts": {
26 | "build": "vsce package",
27 | "publish": "vsce publish",
28 | "vscode:prepublish": "npm run compile",
29 | "compile": "tsc -p ./",
30 | "lint": "eslint . --ext .ts,.tsx",
31 | "watch": "tsc -watch -p ./"
32 | },
33 | "devDependencies": {
34 | "@types/node": "^16.11.7",
35 | "@types/vscode": "^1.32.0",
36 | "@typescript-eslint/eslint-plugin": "^5.30.0",
37 | "@typescript-eslint/parser": "^5.30.0",
38 | "@vscode/vsce": "2.23.0",
39 | "eslint": "^8.13.0",
40 | "typescript": "^4.7.2"
41 | },
42 | "contributes": {
43 | "configuration": {
44 | "title": "Funkin Script AutoComplete",
45 | "properties": {
46 | "funkinVSCode.engine": {
47 | "type": "string",
48 | "default": "psych_latest",
49 | "description": "Which Friday Night Funkin' Engine Lua Script API will this extension autocomplete.\nRequires Restart/Reload to take effect."
50 | },
51 | "funkinVSCode.data": {
52 | "type": "string",
53 | "default": "./data/",
54 | "description": "Path to the local folder containing all of the API Data for the different engines."
55 | },
56 | "funkinVSCode.onlineDataURL": {
57 | "type": "string",
58 | "default": "https://raw.githubusercontent.com/Snirozu/Funkin-Script-AutoComplete/master/data/",
59 | "description": "URL path to the online folder which contains all of the API Data for the different engines."
60 | },
61 | "funkinVSCode.enableOnlyOnCertainScripts": {
62 | "type": "boolean",
63 | "default": false,
64 | "description": "If checked, this extension will only work if a Lua script contains '---@funkinScript' comment"
65 | },
66 | "funkinVSCode.eventDocumentationGeneration": {
67 | "type": "boolean",
68 | "default": false,
69 | "description": "If checked, after entering a event function, placeholder arguments are inserted on top of the function"
70 | },
71 | "funkinVSCode.functionArgumentsGeneration": {
72 | "type": "boolean",
73 | "default": false,
74 | "description": "If checked, after entering a call function, default values of arguments are inserted inside of the function"
75 | },
76 | "funkinVSCode.offlineMode": {
77 | "type": "boolean",
78 | "default": false,
79 | "description": "If checked, the completion data will no longer update with the online iteration"
80 | },
81 | "funkinVSCode.haxeEnabled": {
82 | "type": "boolean",
83 | "default": false,
84 | "description": "If checked, haxe completion will be enabled for V-Slice (EXPERIMENTAL)\nRequires Restart/Reload to take effect."
85 | },
86 | "funkinVSCode.hscriptFileExtension": {
87 | "type": "string",
88 | "default": "hxc",
89 | "description": "The associating file extension for Friday Night Funkin' scripts (EXPERIMENTAL)"
90 | },
91 | "funkinVSCode.haxelibs": {
92 | "type": "array",
93 | "default": [
94 | "funkin https://github.com/FunkinCrew/Funkin main source/",
95 | "funkin.vis https://github.com/FunkinCrew/funkVis",
96 | "flixel",
97 | "flixel-addons",
98 | "flixel-text-input",
99 | "flixel-ui",
100 | "flxanimate",
101 | "format",
102 | "hamcrest",
103 | "haxeui-core",
104 | "haxeui-flixel",
105 | "hscript https://github.com/HaxeFoundation/hscript",
106 | "hxCodec",
107 | "json2object",
108 | "lime",
109 | "openfl",
110 | "polymod",
111 | "thx.semver",
112 | "thx.core",
113 | "FlxPartialSound https://github.com/FunkinCrew/FlxPartialSound.git"
114 | ],
115 | "description": "The Haxelibs you want to use\nRequires VSCode Restart to take effect! (EXPERIMENTAL)"
116 | }
117 | }
118 | },
119 | "languages": [
120 | {
121 | "id": "haxe",
122 | "extensions": [
123 | "hxc"
124 | ]
125 | }
126 | ],
127 | "commands": [
128 | {
129 | "command": "funkinVSCode.clearCache",
130 | "title": "Clear Cache for Engine API Data (EXPERIMENTAL)"
131 | },
132 | {
133 | "command": "funkinVSCode.updatelibs",
134 | "title": "Update Haxelibs for Funkin Scripts (EXPERIMENTAL)"
135 | },
136 | {
137 | "command": "funkinVSCode.updatelibsnofunkin",
138 | "title": "Update Haxelibs for Funkin Scripts (Excluding Funkin Lib) (EXPERIMENTAL)"
139 | },
140 | {
141 | "command": "funkinVSCode.updatefunkin",
142 | "title": "Update Funkin API library"
143 | }
144 | ]
145 | },
146 | "dependencies": {
147 | "@types/needle": "^3.2.0",
148 | "fast-xml-parser": "^4.5.3",
149 | "needle": "^3.2.0"
150 | }
151 | }
152 |
--------------------------------------------------------------------------------
/src/engineData.ts:
--------------------------------------------------------------------------------
1 | import * as vscode from 'vscode';
2 |
3 | import { getLineContentAt } from './util';
4 | import * as needle from 'needle';
5 | import { readFileSync, writeFileSync } from 'fs';
6 | import { dataPath, sendToOutput } from './extension';
7 |
8 | // ========================
9 | // ENGINE
10 | // ========================
11 |
12 | function getLuaEngine(document?: vscode.TextDocument | undefined): string | undefined {
13 | // If document selected
14 | if (document != undefined) {
15 | const text = document.getText();
16 | const index = text.indexOf("---@funkinEngine=");
17 |
18 | // If specified, take the engine
19 | if (index != -1)
20 | return getLineContentAt(text, index).trim().split("=")[1];
21 | }
22 |
23 | return vscode.workspace.getConfiguration().get("funkinVSCode.engine");
24 | }
25 |
26 | // ======================
27 | // DATA
28 | // ======================
29 | export const CACHED: Map = new Map();
30 |
31 | export async function getData(file: string): Promise {
32 | file = file.trim();
33 |
34 | // If cached, skip
35 | if (CACHED.has(file))
36 | return CACHED.get(file);
37 |
38 | let content = await getOnlineData(file);
39 |
40 | // If found, skip
41 | if (content !== undefined)
42 | return content;
43 |
44 | // If not found, read from file
45 | try {
46 | content = readFileSync(dataPath + file, { encoding: 'utf8', flag: 'r' });
47 |
48 | if (file.endsWith('.json'))
49 | content = JSON.parse(content);
50 |
51 | CACHED.set(file, content);
52 | }
53 | catch (err) {
54 | vscode.window.showErrorMessage(err + '');
55 | content = undefined;
56 | }
57 |
58 | return content;
59 | }
60 |
61 | async function getOnlineData(file: string): Promise {
62 | if (vscode.workspace.getConfiguration().get("funkinVSCode.offlineMode"))
63 | return undefined;
64 |
65 | const response = await needle("get", vscode.workspace.getConfiguration().get("funkinVSCode.onlineDataURL") + file);
66 |
67 | // If failed, skip
68 | if (response.statusCode != 200)
69 | return undefined;
70 |
71 | // Write to file
72 | // eslint-disable-next-line @typescript-eslint/no-empty-function
73 | writeFileSync(dataPath + file, response.body);
74 |
75 | const content = file.endsWith(".ver")
76 | ? response.body
77 | : JSON.parse(response.body);
78 |
79 | CACHED.set(file, content);
80 |
81 | return content;
82 | }
83 |
84 | export async function getEngineData(document?: vscode.TextDocument): Promise {
85 |
86 | let engine:any = getLuaEngine(document);
87 |
88 | // If no engine defined
89 | if (!engine)
90 | throw "Extension couldn't find any engines...";
91 |
92 | engine = engine + (engine.endsWith("_latest") ? ".ver" : ".json");
93 |
94 | // Fetch data
95 | let data = await getData(engine);
96 |
97 | if (engine.endsWith(".ver"))
98 | data = await getData(data + ".json");
99 |
100 | return data;
101 | }
102 |
103 | // ===========================
104 | // FUNCTIONS
105 | // ===========================
106 | export async function getFunctions(document?: vscode.TextDocument): Promise {
107 | return (await getEngineData(document)).functions;
108 | }
109 |
110 | export async function getFunction(func: string, document?: vscode.TextDocument): Promise {
111 | const functions = await getFunctions(document);
112 |
113 | if (functions === undefined)
114 | return null;
115 |
116 | if (!Reflect.has(functions, func))
117 | return null;
118 |
119 | const funct = Reflect.get(functions, func);
120 |
121 | return {
122 | name: func,
123 | returns: funct.returns,
124 | args: funct.args,
125 | documentation: funct.documentation,
126 | deprecated: funct.deprecated
127 | };
128 | }
129 |
130 | // ========================
131 | // EVENTS
132 | // ========================
133 | export async function getEvents(document?: vscode.TextDocument): Promise {
134 | return (await getEngineData(document)).events;
135 | }
136 |
137 | export async function getEvent(event: string, document?: vscode.TextDocument) : Promise {
138 | const events = await getEvents(document);
139 |
140 | if (events === undefined)
141 | return null;
142 |
143 | if (!Reflect.has(events, event))
144 | return null;
145 |
146 | const e = Reflect.get(events, event);
147 | return {
148 | name: event,
149 | returns: e.returns,
150 | args: e.args,
151 | documentation: e.documentation,
152 | deprecated: e.deprecated
153 | };
154 | }
155 |
156 | // ===========================
157 | // VARIABLES
158 | // ===========================
159 | export async function getVariables(document?: vscode.TextDocument): Promise {
160 | return (await getEngineData(document)).variables;
161 | }
162 |
163 | export async function getVariable(varia: string, document?: vscode.TextDocument) : Promise {
164 | const variables = await getVariables(document);
165 |
166 | if (variables === undefined)
167 | return null;
168 |
169 | if (!Reflect.has(variables, varia))
170 | return null;
171 |
172 | const varr = Reflect.get(variables, varia);
173 | return {
174 | name: varia,
175 | returns: varr.returns,
176 | documentation: varr.documentation,
177 | deprecated: varr.deprecated
178 | };
179 | }
--------------------------------------------------------------------------------
/src/extension.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable prefer-const */
2 |
3 | //import * as Color from 'color';
4 | import * as vscode from 'vscode';
5 | import * as EngineData from './engineData';
6 | import * as util from './util';
7 | import { existsSync } from 'fs';
8 | import { activateHx } from './extensionHx';
9 |
10 | export let dataPath = "";
11 |
12 | export let diagnosticCollection: vscode.DiagnosticCollection;
13 | export let decorationCollection: vscode.DiagnosticCollection;
14 | let outputChannel: vscode.OutputChannel;
15 |
16 | export async function activate(context: vscode.ExtensionContext) {
17 |
18 | let path = vscode.workspace.getConfiguration().get("funkinVSCode.data") || "./data/";
19 |
20 | dataPath = context.asAbsolutePath(path);
21 |
22 | // If path is not relative
23 | if (!existsSync(dataPath))
24 | dataPath = path;
25 |
26 | diagnosticCollection = vscode.languages.createDiagnosticCollection('funkin_sac_diagnostics');
27 | decorationCollection = vscode.languages.createDiagnosticCollection('funkin_sac_decorations');
28 | outputChannel = vscode.window.createOutputChannel('Funkin VSCode DLogs');
29 | context.subscriptions.push(diagnosticCollection);
30 |
31 | // activate haxe completion
32 | if (vscode.workspace.getConfiguration().get("funkinVSCode.haxeEnabled")) {
33 | activateHx(context);
34 | }
35 |
36 | // ======================
37 | // LUA
38 | // ======================
39 |
40 | //Suggest functions and variables
41 | context.subscriptions.push(vscode.languages.registerCompletionItemProvider('lua', {
42 | //executed every time the user requests tab completion
43 | provideCompletionItems: async function (document, position) {
44 | //list of items to append into the tab completer
45 | let list: vscode.ProviderResult = [];
46 |
47 | if (!isEnabled(document)) {
48 | list.push({
49 | documentation: "Enable FNF Script AutoCompleting in this file",
50 | kind: vscode.CompletionItemKind.Snippet,
51 | label: "---@funkinScript"
52 | });
53 |
54 | return list;
55 | }
56 |
57 | list.push({
58 | detail: "---@funkinEngine={engine}",
59 | documentation: "Set the FNF engine for this script.",
60 | kind: vscode.CompletionItemKind.Snippet,
61 | label: "funkinEngine=",
62 | insertText: new vscode.SnippetString("---@funkinEngine=$0")
63 | });
64 |
65 | //add function
66 | for (const _func in await EngineData.getFunctions(document)) {
67 | const func = await EngineData.getFunction(_func, document);
68 | if (func == null)
69 | continue;
70 |
71 | let markdownString = new vscode.MarkdownString();
72 | if (func.deprecated != null) {
73 | markdownString.appendMarkdown("*@deprecated* **" + func.deprecated + "**\n\n");
74 | // TODO i give up with this shit, help
75 | //func.name = "~~" + func.name + "~~";
76 | }
77 | markdownString.appendMarkdown(func.documentation);
78 |
79 | let labelArgs: Array = [];
80 | let completeArgs: Array = [];
81 | const doInsertArguments = vscode.workspace.getConfiguration().get("funkinVSCode.functionArgumentsGeneration");
82 | const args = getArgArgParts(func.args);
83 | args.forEach((arg, _) => {
84 | labelArgs.push(arg.name);
85 | if (!arg.optional)
86 | completeArgs.push(arg.default);
87 | });
88 |
89 | list.push({
90 | detail: func.returns + " " + func.name + "(" + func.args + ")",
91 | kind: vscode.CompletionItemKind.Function,
92 | label: func.name + "(" + labelArgs.join(", ") + ")",
93 | insertText: new vscode.SnippetString(func.name + "(" + (doInsertArguments ? completeArgs.join(", ") : "") + "$0)"),
94 | command: {
95 | title: "complete",
96 | command: "editor.action.triggerParameterHints"
97 | },
98 | documentation: markdownString
99 | });
100 | }
101 |
102 | //add variables
103 | for (const _varia in await EngineData.getVariables(document)) {
104 | const varia = await EngineData.getVariable(_varia, document);
105 | if (varia == null)
106 | continue;
107 |
108 | let markdownString = new vscode.MarkdownString();
109 | if (varia.deprecated != null) {
110 | markdownString.appendMarkdown("*@deprecated* **" + varia.deprecated + "**\n\n");
111 | }
112 | markdownString.appendMarkdown(varia.documentation);
113 |
114 | list.push({
115 | detail: varia.returns + " " + varia.name,
116 | kind: vscode.CompletionItemKind.Variable,
117 | label: varia.name,
118 | documentation: markdownString
119 | });
120 | }
121 |
122 | return list;
123 | }
124 | }));
125 |
126 | //Word hover event
127 | context.subscriptions.push(vscode.languages.registerHoverProvider("lua", {
128 | provideHover: async function (document, position, token) {
129 | if (!isEnabled(document)) {
130 | return null;
131 | }
132 |
133 | const range = document.getWordRangeAtPosition(position);
134 | const word = document.getText(range);
135 |
136 | const func = await EngineData.getFunction(word, document);
137 | const varia = await EngineData.getVariable(word, document);
138 | const event = await EngineData.getEvent(word, document);
139 |
140 | const markdownString = new vscode.MarkdownString();
141 | let object: any = null;
142 | if (func != null) {
143 | if (func.deprecated != null) {
144 | markdownString.appendMarkdown("*@deprecated* **" + func.deprecated + "**\n\n");
145 | }
146 | markdownString.appendCodeblock("function " + func.name + "(" + func.args + "): " + func.returns);
147 | object = func;
148 | }
149 | if (varia != null) {
150 | if (varia.deprecated != null) {
151 | markdownString.appendMarkdown("*@deprecated* **" + varia.deprecated + "**\n\n");
152 | }
153 | markdownString.appendCodeblock(varia.name + ": " + varia.returns);
154 | object = varia;
155 | }
156 | /*
157 | generate the function comment instead
158 |
159 | if (event != null) {
160 | markdownString.appendCodeblock("event " + event.name + "(" + event.args + "): " + event.returns);
161 | object = event;
162 | }
163 | */
164 | if (event != null) {
165 | markdownString.appendMarkdown("**Engine Event/Trigger Function**\n\n" + event.documentation);
166 | return new vscode.Hover(markdownString);
167 | }
168 | if (object != null) {
169 | markdownString.appendMarkdown(object.documentation);
170 | return new vscode.Hover(markdownString);
171 | }
172 | }
173 | }));
174 |
175 | //Suggest args for functions (broken)
176 | context.subscriptions.push(vscode.languages.registerSignatureHelpProvider("lua", {
177 | provideSignatureHelp: async function (document, position, token) {
178 | if (!isEnabled(document)) {
179 | return null;
180 | }
181 |
182 | let i = document.offsetAt(position);
183 | let lastCharPos: vscode.Position | null = null;
184 | let numArgs = 0;
185 | while (i > 0) {
186 | const bch = document.getText().charAt(i - 1);
187 | const ch = document.getText().charAt(i);
188 | if (ch == ",") {
189 | numArgs++;
190 | }
191 | if (ch.replace(/\s/g, "") != "") {
192 | lastCharPos = document.positionAt(i);
193 | }
194 | if (bch == ";" || bch == ")" || bch == null) {
195 | break;
196 | }
197 | i--;
198 | }
199 | if (lastCharPos == null) {
200 | return;
201 | }
202 |
203 | const range = document.getWordRangeAtPosition(lastCharPos, /[a-zA-Z]+/g);
204 | let word = document.getText(range);
205 | const func = await EngineData.getFunction(word, document);
206 |
207 | if (func == null)
208 | return;
209 |
210 | const provider = new vscode.SignatureHelp;
211 | provider.activeParameter = 0;
212 | provider.activeSignature = numArgs;
213 |
214 | const spltArgs: Array = func.args.split(",");
215 | let _i = 0;
216 | for (let arg in spltArgs) {
217 | provider.signatures.push({
218 | label: spltArgs[arg],
219 | parameters: []
220 | });
221 | //argsString.appendMarkdown((_i == numArgs ? "" : "") + arg + (_i == numArgs ? "" : "") + (_i == spltArgs.length - 1 ? "" : ", "));
222 | _i++;
223 | }
224 |
225 | return provider;
226 | }
227 | }, '(', ','));
228 |
229 | //Suggest event snippets
230 | context.subscriptions.push(vscode.languages.registerCompletionItemProvider('lua', {
231 | async provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext) {
232 | if (!isEnabled(document)) {
233 | return null;
234 | }
235 |
236 | let list: vscode.ProviderResult = [];
237 |
238 | for (const _event in await EngineData.getEvents(document)) {
239 | const event = await EngineData.getEvent(_event, document);
240 | if (event == null)
241 | continue;
242 |
243 | //let daComment = "---\n---" + event.documentation + "\n---";
244 | let daArgs: Array = [];
245 |
246 | const args = getArgArgParts(event.args);
247 | const doAppendComments = vscode.workspace.getConfiguration().get("funkinVSCode.eventDocumentationGeneration");
248 | let daComment: string = doAppendComments && args.length > 0 ? "---" : "";
249 | args.forEach((arg, i) => {
250 | if (doAppendComments) {
251 | daComment += "\n--- @param " + arg.name + " " + arg.type;
252 | }
253 | daArgs.push(arg.name);
254 | });
255 |
256 | if (doAppendComments && args.length > 0) {
257 | daComment += "\n---\n";
258 | }
259 |
260 | const includeCode = (event.returns && (
261 | (event.returns + '').startsWith("array") ||
262 | (event.returns + '').startsWith("bool") ||
263 | event.returns == "float" ||
264 | event.returns == "int" ||
265 | event.returns == "number" ||
266 | event.returns == "string"
267 | )) ? 'return ' : '';
268 |
269 | const snippet = new vscode.CompletionItem("Event: " + event.name + "(" + daArgs.join(", ") + ")");
270 | snippet.detail = event.returns + " " + event.name + "(" + event.args + ")";
271 | snippet.insertText = new vscode.SnippetString(daComment + "function " + event.name + "(" + haxeArgsToLua(event.args) + ")\n\t" + includeCode + "$0\nend");
272 | snippet.documentation = new vscode.MarkdownString(event.documentation);
273 | snippet.command = {
274 | title: "complete",
275 | command: "editor.action.triggerSuggest"
276 | };
277 |
278 | list.push(snippet);
279 | }
280 |
281 | return list;
282 | }
283 | }));
284 |
285 | // Show colors
286 | context.subscriptions.push(vscode.languages.registerColorProvider("lua",
287 | {
288 | // select the locations of colors
289 | provideDocumentColors(document, token) {
290 | let colorsList: vscode.ProviderResult = [];
291 | let i = -1;
292 | let isInType = 0;
293 | let curColorString = "";
294 | let isInString = false;
295 |
296 | let begS: vscode.Position | undefined = undefined;
297 | let endS: vscode.Position;
298 |
299 | while (i++ < document.getText().length - 1) {
300 | const curChar = document.getText().charAt(i);
301 | if (curChar == "'" || curChar == '"') {
302 | if (!isInString) {
303 | isInString = true;
304 | }
305 | else {
306 | endS = document.positionAt(i);
307 |
308 | //let color:Color = new Color(curColorString);
309 |
310 | if (begS != undefined) {
311 | const color = util.hexToVSColor(curColorString);
312 |
313 | if (color != null)
314 | colorsList.push(
315 | new vscode.ColorInformation(new vscode.Range(begS, endS), color)
316 | );
317 | }
318 |
319 | isInString = false;
320 | isInType = 0;
321 | curColorString = "";
322 | }
323 | }
324 |
325 | if (isInString) {
326 | if (curChar == "#") {
327 | begS = document.positionAt(i);
328 | isInType = 1;
329 | }
330 |
331 | if (isInType > 0) {
332 | curColorString += curChar;
333 | }
334 | }
335 | }
336 | return colorsList;
337 | },
338 | // show the color picker
339 | provideColorPresentations(color, context, token) {
340 | return [
341 | new vscode.ColorPresentation(util.rgbaToHex(color.red, color.green, color.blue, color.alpha))
342 | ];
343 | }
344 | }
345 | ));
346 |
347 | context.subscriptions.push(vscode.commands.registerCommand("funkinVSCode.clearCache", _ => {
348 | EngineData.CACHED.clear();
349 | }));
350 |
351 | //deprecated warnings here
352 | //copied from some example lmao
353 | let timeout: NodeJS.Timer | undefined = undefined;
354 |
355 | const warningDecorationType = vscode.window.createTextEditorDecorationType({
356 | textDecoration: "line-through",
357 | light: {
358 | backgroundColor: "#ffff57"
359 | },
360 | dark: {
361 | backgroundColor: "#5e5e29"
362 | }
363 | });
364 |
365 | let activeEditor = vscode.window.activeTextEditor;
366 |
367 | async function updateDecorations() {
368 | if (!activeEditor || activeEditor.document.languageId != "lua") {
369 | return;
370 | }
371 |
372 | decorationCollection.clear();
373 |
374 | const regEx = /[a-zA-Z]+/g;
375 | const text = activeEditor.document.getText();
376 | const decorations: vscode.DecorationOptions[] = [];
377 | let diagnostics: vscode.Diagnostic[] = [];
378 |
379 | let match;
380 | while ((match = regEx.exec(text))) {
381 | const startPos = activeEditor.document.positionAt(match.index);
382 | const endPos = activeEditor.document.positionAt(match.index + match[0].length);
383 |
384 | let doContinue = false;
385 |
386 | if (startPos.line - 1 >= 0) {
387 | let prevLine = activeEditor.document.lineAt(startPos.line - 1).text;
388 | if (prevLine.includes("---@diagnostic disable-next-line:") && prevLine.substring(prevLine.indexOf(":")).includes(match[0]))
389 | doContinue = true;
390 | }
391 |
392 | let firstFileDiagnComment = activeEditor.document.getText().indexOf("---@diagnostic disable:");
393 | if (firstFileDiagnComment >= 0 && activeEditor.document.lineAt(activeEditor.document.positionAt(firstFileDiagnComment).line).text.includes(match[0]))
394 | doContinue = true;
395 |
396 | if (doContinue)
397 | continue;
398 |
399 | const func = await EngineData.getFunction(match[0], activeEditor.document);
400 | const event = await EngineData.getEvent(match[0], activeEditor.document);
401 | const varr = await EngineData.getFunction(match[0], activeEditor.document);
402 |
403 | let deprecatedMsg = null;
404 | if (func != null && func.deprecated != null)
405 | deprecatedMsg = func.deprecated;
406 | if (event != null && event.deprecated != null)
407 | deprecatedMsg = event.deprecated;
408 | if (varr != null && varr.deprecated != null)
409 | deprecatedMsg = varr.deprecated;
410 |
411 | if (deprecatedMsg != null && (varr != null && varr.deprecated != null) && activeEditor.document.getText().charAt(match.index + match[0].length) == "(") {
412 | const decoration: vscode.DecorationOptions = { range: new vscode.Range(startPos, endPos) };
413 | decorations.push(decoration);
414 |
415 | diagnostics.push({
416 | code: match[0],
417 | message: deprecatedMsg,
418 | range: new vscode.Range(startPos, endPos),
419 | severity: vscode.DiagnosticSeverity.Warning
420 | });
421 | }
422 | }
423 | decorationCollection.set(activeEditor.document.uri, diagnostics);
424 | activeEditor.setDecorations(warningDecorationType, decorations);
425 | }
426 |
427 | async function triggerUpdateDecorations(throttle = false) {
428 | if (!activeEditor || !activeEditor.document.fileName.endsWith(".lua")) {
429 | return;
430 | }
431 |
432 | if (activeEditor && !isEnabled(activeEditor.document)) {
433 | decorationCollection.set(activeEditor.document.uri, []);
434 | activeEditor.setDecorations(warningDecorationType, []);
435 | return;
436 | }
437 |
438 | if (timeout) {
439 | clearTimeout(timeout);
440 | timeout = undefined;
441 | }
442 | if (throttle) {
443 | timeout = setTimeout(updateDecorations, 5000);
444 | } else {
445 | await updateDecorations();
446 | }
447 | }
448 |
449 | if (activeEditor) {
450 | triggerUpdateDecorations();
451 | }
452 |
453 | vscode.window.onDidChangeActiveTextEditor(editor => {
454 | activeEditor = editor;
455 | if (editor) {
456 | triggerUpdateDecorations();
457 | }
458 | }, null, context.subscriptions);
459 |
460 | vscode.workspace.onDidChangeTextDocument(event => {
461 | diagnosticCollection.clear();
462 | if (activeEditor && event.document === activeEditor.document) {
463 | triggerUpdateDecorations(true);
464 | }
465 | }, null, context.subscriptions);
466 |
467 | vscode.workspace.onDidChangeConfiguration(event => {
468 | if (event.affectsConfiguration('funkinVSCode.offlineMode') || event.affectsConfiguration('funkinVSCode.onlineDataURL')) {
469 | EngineData.CACHED.clear();
470 | }
471 | }, null, context.subscriptions);
472 |
473 | //https://github.com/microsoft/vscode/issues/187141 // I NEEED ITTTT!!!!!
474 | }
475 |
476 | function isEnabled(document: vscode.TextDocument) {
477 | if (vscode.workspace.getConfiguration().get("funkinVSCode.enableOnlyOnCertainScripts") && document.getText().indexOf("---@funkinScript") == -1) {
478 | return false;
479 | }
480 | return true;
481 | }
482 |
483 | function haxeArgsToLua(str: string) {
484 | let finalString = "";
485 | let i = -1;
486 | let searchedString = "";
487 | while (i++ < str.length) {
488 | searchedString += str.charAt(i);
489 | if (str.charAt(i) == "," || i == str.length - 1) {
490 | const splittedString = searchedString.split(":");
491 | finalString += splittedString[0].trim() + (i == str.length - 1 ? "" : ", ");
492 | searchedString = "";
493 | }
494 | }
495 |
496 | return finalString;
497 | }
498 |
499 | function getArgArgParts(argsString: string): Array {
500 | let args: Array = [];
501 |
502 | // argString = " arg1:String = null"
503 | argsString.split(",").forEach((argString) => {
504 | let arg: SexyArg = {
505 | name: "(the program fucked up)",
506 | type: "nil",
507 | default: "",
508 | optional: false
509 | };
510 |
511 | if (argString.trim() != "") {
512 | const cachSplit1 = argString.split(":");
513 |
514 | arg.name = cachSplit1[0].trim();
515 |
516 | if (cachSplit1[0].startsWith("?")) {
517 | arg.optional = true;
518 | }
519 |
520 | if (cachSplit1.length > 1) {
521 | const cachSplit2 = cachSplit1[1].split("=");
522 | arg.type = cachSplit2[0].trim().toLowerCase();
523 |
524 | if (cachSplit2.length > 1) {
525 | arg.optional = true;
526 | arg.default = cachSplit2[1].trim().toLowerCase();
527 | }
528 | else {
529 | arg.default = getDefaultValue(arg.type);
530 | }
531 | }
532 |
533 | args.push(arg);
534 | }
535 | });
536 |
537 | return args;
538 | }
539 |
540 | function getDefaultValue(type: string): string {
541 | type = type.toLowerCase();
542 | if (type.startsWith("string")) {
543 | return '""';
544 | }
545 | if (type.startsWith("array")) {
546 | return '{}';
547 | }
548 | if (type.startsWith("int")) {
549 | return '0';
550 | }
551 | if (type.startsWith("float")) {
552 | return '0.0';
553 | }
554 | return "nil";
555 | }
556 |
557 | interface SexyArg {
558 | name: string,
559 | type: string,
560 | default: string,
561 | optional: boolean
562 | }
563 |
564 | let hasShownOutput = false;
565 | export function sendToOutput(string: string) {
566 | if (!hasShownOutput) {
567 | outputChannel.show();
568 | hasShownOutput = true;
569 | }
570 | outputChannel.appendLine(string);
571 | }
--------------------------------------------------------------------------------
/src/extensionHx.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable prefer-const */
2 | import * as vscode from 'vscode';
3 | import { XMLParser } from "fast-xml-parser";
4 | import { spawn, spawnSync } from 'child_process';
5 | import path = require('path');
6 | import { characterOffsetToByteOffset } from './util';
7 | import { diagnosticCollection, sendToOutput } from './extension';
8 | import { existsSync } from 'fs';
9 |
10 | export async function activateHx(context: vscode.ExtensionContext) {
11 |
12 | // ======================
13 | // HAXE
14 | // ======================
15 |
16 | const parser = new XMLParser({ ignoreAttributes: false, attributeNamePrefix: "a_", textNodeName: "nval" });
17 |
18 | // tab/space completion
19 | context.subscriptions.push(vscode.languages.registerCompletionItemProvider('haxe', {
20 | provideCompletionItems: async function (document, position) {
21 | if (!document.fileName.endsWith(getHScriptExtension()))
22 | return;
23 |
24 | let list: vscode.CompletionItem[] = [];
25 |
26 | const output = await execCommand(document, position);
27 | if (!output)
28 | return;
29 |
30 | const xml = parser.parse(output);
31 |
32 | let ilst: Array = (xml.il ?? xml.list).i;
33 |
34 | if (ilst)
35 | ilst.forEach((value) => {
36 | let item: vscode.CompletionItem = {
37 | label: value.a_n ?? value.nval + "" // professional way to convert to string
38 | };
39 |
40 | switch (value.a_k) {
41 | case "type":
42 | item.kind = vscode.CompletionItemKind.Class;
43 | if (value.a_p)
44 | item.insertText = value.a_p;
45 | break; //dead ass language needs breaks because why the fuck not?
46 | case "keyword":
47 | item.kind = vscode.CompletionItemKind.Keyword;
48 | break;
49 | case "package":
50 | item.kind = vscode.CompletionItemKind.Folder;
51 | break;
52 | case "member":
53 | item.kind = vscode.CompletionItemKind.Field;
54 | break;
55 | case "method":
56 | item.kind = vscode.CompletionItemKind.Method;
57 | break;
58 | case "var":
59 | item.kind = vscode.CompletionItemKind.Property;
60 | break;
61 | case "literal":
62 | item.kind = vscode.CompletionItemKind.Value;
63 | break;
64 | case "enumabstract":
65 | item.kind = vscode.CompletionItemKind.EnumMember;
66 | break;
67 | case "static":
68 | item.kind = vscode.CompletionItemKind.Variable;
69 | break;
70 | }
71 |
72 | item.detail = value.a_p ?? value.a_t ?? value.t;
73 |
74 | let doc: string | undefined = value.a_d ?? value.d;
75 | if (doc) {
76 | doc = doc.split('\t').join('');
77 | doc = doc.split('\n *').join('\n');
78 | doc = doc.split('\n ').join('\n\n');
79 | if (doc.charAt(0) == "*") {
80 | doc = doc.substring(1);
81 | }
82 | //doc = doc.replace("\t", ""); // replace is broken???
83 | item.documentation = new vscode.MarkdownString(doc);
84 | }
85 |
86 | list.push(item);
87 | });
88 |
89 | return list;
90 | }
91 | }, '.'));
92 |
93 | // word hover event
94 | context.subscriptions.push(vscode.languages.registerHoverProvider("haxe", {
95 | provideHover: async function (document, position, token) {
96 | if (!document.fileName.endsWith(getHScriptExtension()))
97 | return;
98 |
99 | const range = document.getWordRangeAtPosition(position);
100 | if (!range)
101 | return;
102 |
103 | const output = await execCommand(document, position, "type");
104 | if (!output)
105 | return new vscode.Hover("Failed to complete!\nCheck PROBLEMS tab!");
106 |
107 | const xml = parser.parse(output);
108 |
109 | let dick = xml.type.a_d;
110 | if (dick) {
111 | dick = dick.split('\t').join('');
112 | dick = dick.split('\n *').join('\n');
113 | dick = dick.split('\n ').join('\n\n');
114 | if (dick.charAt(0) == "*") {
115 | dick = dick.substring(1);
116 | }
117 | }
118 |
119 | let hoverMd = new vscode.MarkdownString();
120 | hoverMd.appendMarkdown("`" + xml.type.nval + "`\n\n");
121 | if (dick) {
122 | hoverMd.appendMarkdown("---\n\n");
123 | hoverMd.appendMarkdown(dick + "\n\n");
124 | }
125 | return new vscode.Hover(hoverMd);
126 | }
127 | }));
128 |
129 | //jump to definitions of identifiers!
130 | context.subscriptions.push(vscode.languages.registerDefinitionProvider('haxe', {
131 | async provideDefinition(document, position, token) {
132 | if (!document.fileName.endsWith(getHScriptExtension()))
133 | return;
134 |
135 | const range = document.getWordRangeAtPosition(position);
136 | if (!range)
137 | return;
138 |
139 | const output = await execCommand(document, position, "position");
140 | if (!output)
141 | return;
142 |
143 | const xml = parser.parse(output);
144 |
145 | //spaghetti code, please don't look 👉👈
146 | const destSplit = (xml.list.pos as string).replace(getHScriptExtension() + ":", ".hx:").split(".hx:");
147 | const destPath = destSplit[0] + ".hx" + ((xml.list.pos as string).indexOf(getHScriptExtension() + ":") != -1 ? "c" : "");
148 | const destSplat = destSplit[1].replace(" character ", " characters ").split(": characters ");
149 | const destPos = new vscode.Position(Number.parseInt(destSplat[0]) - 1, Number.parseInt(destSplat[1].split("-")[0]) - 1);
150 |
151 | return new vscode.Location(vscode.Uri.file(destPath), destPos);
152 | },
153 | }));
154 |
155 | context.subscriptions.push(vscode.commands.registerCommand("funkinVSCode.updatelibs", _ => {
156 | updateLibs(vscode.window.activeTerminal ?? vscode.window.createTerminal());
157 | }));
158 |
159 | context.subscriptions.push(vscode.commands.registerCommand("funkinVSCode.updatelibsnofunkin", _ => {
160 | updateLibs(vscode.window.activeTerminal ?? vscode.window.createTerminal(), 'funkin');
161 | }));
162 |
163 | context.subscriptions.push(vscode.commands.registerCommand("funkinVSCode.updatefunkin", _ => {
164 | updateLib('funkin', vscode.window.activeTerminal ?? vscode.window.createTerminal());
165 | }));
166 | }
167 |
168 | async function showWarnings(output: string) {
169 | if (!output)
170 | return;
171 |
172 | sendToOutput(output);
173 |
174 | if (output == "Please Install Haxe!") {
175 | const selection = await vscode.window.showErrorMessage(output + "\nTo use .hxc completion you need to install Haxe first!", 'Download Haxe');
176 |
177 | if (selection == "Download Haxe") {
178 | vscode.commands.executeCommand('vscode.open', vscode.Uri.parse('https://haxe.org/download/'));
179 | }
180 | return;
181 | }
182 |
183 | if (output.startsWith("Error: ")) {
184 | if (output.includes(" is not installed")) {
185 | const selection = await vscode.window.showErrorMessage(output, 'Install Library');
186 |
187 | if (selection == "Install Library") {
188 | const terminal = vscode.window.activeTerminal ?? vscode.window.createTerminal();
189 | const lib = output.split("Library ")[1].split(" is not installed")[0];
190 |
191 | updateLib(lib, terminal);
192 | }
193 | }
194 | }
195 |
196 | let warns: Map = new Map();
197 | for (const warn of output.split('\n')) {
198 | let i = -1;
199 | let parseString = "";
200 | let phase = 0;
201 |
202 | let path = "";
203 | let line = 0;
204 | let chars: number[] = [];
205 | let message = "";
206 | let isWarning = false;
207 |
208 | while (++i <= warn.length - 1) {
209 | const char = warn.charAt(i);
210 | const nextChar = warn.charAt(i + 1);
211 |
212 | if (char == ":" && (nextChar == "/" || nextChar == "\\")) {
213 | parseString += char;
214 | continue;
215 | }
216 |
217 | switch (phase) {
218 | case 0: //path
219 | if (char == ":") {
220 | path = parseString;
221 | parseString = "";
222 | phase++;
223 | continue;
224 | }
225 | break;
226 | case 1: //line
227 | if (char == ":") {
228 | line = Number.parseInt(parseString) - 1;
229 | parseString = "";
230 | phase++;
231 | continue;
232 | }
233 | break;
234 | case 2: //characters
235 | if (char == " ")
236 | continue;
237 |
238 | if (parseString == "character" || parseString == "characters") {
239 | parseString = "";
240 | continue;
241 | }
242 |
243 | if (char == "-") {
244 | chars.push(Number.parseInt(parseString) - 1);
245 | parseString = "";
246 | continue;
247 | }
248 |
249 | if (char == ":") {
250 | chars.push(Number.parseInt(parseString) - 1);
251 | parseString = "";
252 | phase++;
253 | continue;
254 | }
255 | break;
256 | case 3:
257 | if (parseString == " Warning :") {
258 | isWarning = true;
259 | parseString = "";
260 | }
261 | }
262 |
263 | parseString += char;
264 | }
265 | message = parseString.trim();
266 |
267 | if (!warns.has(path)) {
268 | warns.set(path, []);
269 | }
270 |
271 | warns.get(path).push({
272 | message: message,
273 | severity: isWarning ? vscode.DiagnosticSeverity.Warning : vscode.DiagnosticSeverity.Error,
274 | range: new vscode.Range(
275 | new vscode.Position(line, chars[0]),
276 | new vscode.Position(line, chars[1])
277 | )
278 | });
279 | }
280 |
281 | diagnosticCollection.clear();
282 | for (const warn of warns) {
283 | diagnosticCollection.set(vscode.Uri.file(warn[0]), warn[1]);
284 | }
285 | }
286 |
287 | async function execCommand(document: vscode.TextDocument, position: vscode.Position, mode?: string) {
288 | await document.save();
289 |
290 | const fileNam = document.uri.path.substring(document.uri.path.length - path.basename(document.uri.path).length);
291 | const fileDir = document.uri.path.substring(document.uri.path.charAt(2) == ":" ? 1 : 0, document.uri.path.length - path.basename(document.uri.path).length);
292 |
293 | if (mode)
294 | mode = "@" + mode;
295 | else
296 | mode = "";
297 |
298 | let libs: Array = [];
299 |
300 | (vscode.workspace.getConfiguration().get("funkinVSCode.haxelibs") as Array).forEach(async lib => {
301 | libs.push('-L', lib.split(" ")[0]);
302 | });
303 |
304 | const testSpawn = spawnSync('haxe', ['--connect', '6000']);
305 |
306 | if (testSpawn.error && testSpawn.error.message.endsWith("ENOENT")) {
307 | return "Please Install Haxe!";
308 | }
309 |
310 | if (testSpawn.output.toString().includes('Couldn\'t connect on')) {
311 | spawn('haxe', ['--wait', '6000']);
312 | }
313 |
314 | let funkinPath = spawnSync('haxelib', ['path', 'funkin']).output.join("").split("\n")[0];
315 | if (funkinPath.startsWith("Error:")) {
316 | const selection = await vscode.window.showErrorMessage("\nTo use the completion you need to setup Funkin directory first!", 'Install Library');
317 |
318 | if (selection == "Install Library") {
319 | vscode.commands.executeCommand('funkinVSCode.updatefunkin');
320 | }
321 | return;
322 | }
323 | const funkinSource = funkinPath.split("\\").join("/").trim();
324 | funkinPath = funkinSource.substring(0, funkinPath.length - 'source'.length - 2);
325 |
326 | //
327 |
328 | //the issue with completion as of now is
329 | // haxeui is not able to load the `module.xml` file for some reason
330 |
331 | let _output = spawnSync('haxe', ['--display', fileNam + '@' + characterOffsetToByteOffset(document.getText(), document.offsetAt(position)) + mode,
332 | '--no-output',
333 | '--cpp', '_',
334 | '--connect', '6000',
335 | '--remap', 'flash:openfl',
336 | '--macro flixel.system.macros.FlxDefines.run()',
337 | '--macro haxe.macro.Compiler.addClassPath("' + funkinPath + '")',
338 | '--macro haxe.macro.Compiler.addClassPath("' + funkinSource + '")',
339 | '-D FLX_KEYBOARD',
340 | '-D FLX_SOUND_TRAY',
341 | '-D FLX_MOUSE',
342 | '-D FLX_SOUND_SYSTEM',
343 | '-D FLX_SAVE',
344 | '-D FLX_JOYSTICK_API',
345 | '-D haxeui_dont_impose_base_class'
346 | ].concat(libs), { cwd: fileDir }).output.join("");
347 |
348 | const mesSplit = _output.split("\n<");
349 | let warnings = mesSplit.shift();
350 | let rpc = mesSplit.join('<');
351 |
352 | if (warnings && warnings.charAt(0) == "<") {
353 | rpc = warnings + rpc;
354 | warnings = undefined;
355 | }
356 |
357 | if (rpc.length > 0 && rpc.charAt(0) != "<") {
358 | rpc = "<" + rpc;
359 | }
360 |
361 | showWarnings(warnings);
362 | if (rpc.length < 1)
363 | return;
364 | return rpc;
365 | }
366 |
367 | function getHScriptExtension(): string {
368 | return "." + vscode.workspace.getConfiguration().get("funkinVSCode.hscriptFileExtension") as string;
369 | }
370 |
371 | function updateLib(lib: string, terminal: vscode.Terminal) {
372 | let swagCommand = "haxelib install " + lib;
373 | (vscode.workspace.getConfiguration().get("funkinVSCode.haxelibs") as Array).forEach(clib => {
374 | const libProps = clib.split(" ");
375 | if (libProps[0] == lib && libProps[1]) {
376 | if (libProps[1].startsWith("http")) {
377 | swagCommand = "haxelib git " + libProps[0] + " " + libProps[1];
378 | if (libProps[2]) {
379 | swagCommand += " " + libProps[2];
380 | if (libProps[3]) {
381 | swagCommand += " " + libProps[3];
382 | if (libProps[4]) {
383 | swagCommand += " " + libProps[4];
384 | }
385 | }
386 | }
387 |
388 | }
389 | else {
390 | if (existsSync(libProps[1])) {
391 | swagCommand = "haxelib dev " + libProps[0] + " " + libProps[1];
392 | }
393 | else {
394 | swagCommand = "haxelib install " + libProps[0] + " " + libProps[1];
395 | }
396 | }
397 | }
398 | });
399 |
400 | terminal.sendText(swagCommand);
401 | }
402 |
403 | function updateLibs(terminal: vscode.Terminal, ignore?: string) {
404 | let commands: Array = [];
405 | (vscode.workspace.getConfiguration().get("funkinVSCode.haxelibs") as Array).forEach(clib => {
406 | const libProps = clib.split(" ");
407 | let swagCommand = "haxelib install " + libProps[0];
408 | if (libProps[1]) {
409 | if (libProps[1].startsWith("http")) {
410 | swagCommand = "haxelib git " + libProps[0] + " " + libProps[1];
411 | if (libProps[2]) {
412 | swagCommand += " " + libProps[2];
413 | if (libProps[3]) {
414 | swagCommand += " " + libProps[3];
415 | if (libProps[4]) {
416 | swagCommand += " " + libProps[4];
417 | }
418 | }
419 | }
420 |
421 | }
422 | else {
423 | if (existsSync(libProps[1])) {
424 | swagCommand = "haxelib dev " + libProps[0] + " " + libProps[1];
425 | }
426 | else {
427 | swagCommand = "haxelib install " + libProps[0] + " " + libProps[1];
428 | }
429 | }
430 | }
431 |
432 | if (libProps[0] != ignore)
433 | commands.push(swagCommand);
434 | });
435 | terminal.sendText(commands.join(";"));
436 | }
--------------------------------------------------------------------------------
/src/util.ts:
--------------------------------------------------------------------------------
1 | import { Color } from "vscode";
2 |
3 | export function getLineContentAt(document: string, index: number) {
4 | if (index <= -1) {
5 | return null;
6 | }
7 | let word = "";
8 | for (let i = index; i < document.length; i++) {
9 | const char = document.charAt(i);
10 | if (char == "\n") {
11 | break;
12 | }
13 | word += char;
14 | }
15 | return word;
16 | }
17 |
18 | // most of the code is from stack overflow... yes i used this fucking website lol
19 | export function rgbaToHex(r: number, g: number, b: number, a: number) {
20 | r*=255; g*=255; b*=255; a*=255;
21 | return "#" + (256 + r).toString(16).slice(1) + ((1 << 24) + (g << 16) | (b << 8) | a).toString(16).slice(1);
22 | }
23 |
24 | export function hexToVSColor(hex: string) {
25 | const r = parseInt(hex.slice(1, 3), 16) / 255;
26 | const g = parseInt(hex.slice(3, 5), 16) / 255;
27 | const b = parseInt(hex.slice(5, 7), 16) / 255;
28 |
29 | if (Number.isNaN(r) || Number.isNaN(g) || Number.isNaN(b))
30 | return null;
31 |
32 | if (hex.length > 7)
33 | return new Color(r, g, b, parseInt(hex.slice(7, 9), 16) / 255);
34 |
35 | return new Color(r, g, b, 1);
36 | }
37 |
38 | export function characterOffsetToByteOffset(string:string, offset: number):number {
39 | if (offset == 0)
40 | return 0;
41 | else if (offset == string.length)
42 | return Buffer.byteLength(string, "utf-8");
43 | else
44 | return Buffer.byteLength(string.substring(0, offset), "utf-8");
45 | }
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "target": "es2020",
5 | "lib": ["es2020"],
6 | "outDir": "out",
7 | "sourceMap": true,
8 | "strict": true,
9 | "rootDir": "src",
10 | "strictNullChecks": false
11 | },
12 | "exclude": [
13 | "node_modules",
14 | ".vscode-test"
15 | ]
16 | }
17 |
--------------------------------------------------------------------------------