├── .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 | --------------------------------------------------------------------------------