├── .gitignore ├── src ├── 30-scripted-modules │ ├── 30-01-TODO.md │ ├── 30-00-scripted-modules.md │ └── 30-99-custom-modules.temp ├── 21-scripted-classes │ ├── 21-02-video-cutscenes.md │ ├── 21-03-ingame-cutscenes.md │ ├── 21-04-dialogue-cutscenes.md │ ├── 21-06-scripted-stages.md │ ├── 21-08-custom-note-kinds.md │ ├── 21-09-custom-song-events.md │ ├── 21-07-scripted-story-levels.md │ ├── 21-01-scripted-songs.md │ ├── 21-10-scripted-playable-characters.md │ ├── 21-05-scripted-characters.md │ └── 21-00-scripted-classes.md ├── 01-fundamentals │ ├── 01-06-chapter-conclusion.md │ ├── 01-00-fundamentals.md │ ├── 01-04-mod-load-order.md │ ├── 01-05-hot-reloading.md │ ├── 01-02-loading-the-mod-in-game.md │ ├── 01-03-asset-replacement-and-additions.md │ └── 01-01-the-metadata-file.md ├── title-page.md ├── 10-appending-and-merging-files │ ├── 10-00-appending-and-merging-files.md │ ├── 10-01-appending-files.md │ └── 10-02-merging-files.md ├── 04-custom-stages │ ├── 04-02-using-a-stage-in-a-song.md │ ├── 04-00-custom-stages.md │ └── 04-01-creating-a-stage.md ├── 03-custom-characters │ ├── 03-02-using-a-character-in-a-song.md │ ├── 03-00-custom-characters.md │ ├── 03-01-character-assets.md │ ├── 03-03-fixing-character-offsets.md │ └── 03-02-creating-a-character.md ├── 06-custom-notestyles │ ├── 06-02-using-a-notestyle-in-a-song.md │ ├── 06-00-custom-notestyles.md │ └── 06-01-creating-a-notestyle.md ├── 07-custom-sticker-packs │ ├── 07-00-custom-sticker-packs.md │ ├── 07-02-modifying-an-existing-sticker-pack.md │ ├── 07-03-using-a-custom-sticker-pack.md │ └── 07-01-creating-a-new-sticker-pack.md ├── 02-custom-songs-and-custom-levels │ ├── 02-00-custom-songs-and-custom-levels.md │ ├── 02-04-what-are-variations.md │ ├── 02-01-creating-a-chart.md │ ├── 02-02-adding-the-custom-song.md │ ├── 02-03-adding-a-custom-level.md │ └── 02-05-adding-variations-to-existing-songs.md ├── 31-custom-shaders │ └── 31-00-custom-shaders.md ├── 09-migration │ ├── 09-00-migrating-mods-to-newer-versions.md │ ├── 09-02-0.5.0-to-0.6.3.md │ └── 09-01-0.1.0-to-0.5.0.md ├── 05-custom-playable-characters │ ├── 05-00-custom-playable-characters.md │ ├── 05-01-required-assets.md │ └── 05-02-creating-a-playable-character.md ├── 20-using-hscript │ ├── 20-00-using-hscript.md │ └── 20-01-what-is-hscript.md ├── writing-and-contributing-to-this-book.md └── SUMMARY.md ├── book.toml ├── README.md └── .github └── workflows └── deploy.yml /.gitignore: -------------------------------------------------------------------------------- 1 | book 2 | -------------------------------------------------------------------------------- /src/30-scripted-modules/30-01-TODO.md: -------------------------------------------------------------------------------- 1 | # TODO: Write this part. -------------------------------------------------------------------------------- /src/21-scripted-classes/21-02-video-cutscenes.md: -------------------------------------------------------------------------------- 1 | # Creating a Friday Night Funkin' Mod - Video Cutscenes 2 | 3 | This chapter will specifically walk through using scripted Songs to implement a Video Cutscene into a mod. 4 | -------------------------------------------------------------------------------- /src/21-scripted-classes/21-03-ingame-cutscenes.md: -------------------------------------------------------------------------------- 1 | # Creating a Friday Night Funkin' Mod - Ingame Cutscenes 2 | 3 | This chapter will specifically walk through using scripted Songs to implement an Ingame Cutscene into a mod. 4 | -------------------------------------------------------------------------------- /src/01-fundamentals/01-06-chapter-conclusion.md: -------------------------------------------------------------------------------- 1 | # Chapter Conclusion 2 | 3 | This chapter has covered the fundamentals of creating mods for Friday Night Funkin'. In other sections, we will go over how to add different types of custom content. 4 | -------------------------------------------------------------------------------- /src/21-scripted-classes/21-04-dialogue-cutscenes.md: -------------------------------------------------------------------------------- 1 | # Creating a Friday Night Funkin' Mod - Dialogue Cutscenes 2 | 3 | This chapter will specifically walk through using scripted Songs to implement Dialogue Cutscenes into a mod, like those seen in Week 6. 4 | -------------------------------------------------------------------------------- /src/21-scripted-classes/21-06-scripted-stages.md: -------------------------------------------------------------------------------- 1 | # Creating a Friday Night Funkin' Mod - Scripted Stages 2 | 3 | This chapter will walk you through the process of adding a script to a Stage, and giving examples of the kind of custom behavior which can be implemented with this functionality. 4 | -------------------------------------------------------------------------------- /src/21-scripted-classes/21-08-custom-note-kinds.md: -------------------------------------------------------------------------------- 1 | # Creating a Friday Night Funkin' Mod - Scripted Note Kinds 2 | 3 | This chapter will walk you through the process of adding a script to a Note Kind, and giving examples of the kind of custom behavior which can be implemented with this functionality. 4 | -------------------------------------------------------------------------------- /src/21-scripted-classes/21-09-custom-song-events.md: -------------------------------------------------------------------------------- 1 | # Creating a Friday Night Funkin' Mod - Scripted Song Events 2 | 3 | This chapter will walk you through the process of adding a script to a Song Event, and giving examples of the kind of custom behavior which can be implemented with this functionality. 4 | -------------------------------------------------------------------------------- /src/21-scripted-classes/21-07-scripted-story-levels.md: -------------------------------------------------------------------------------- 1 | # Creating a Friday Night Funkin' Mod - Scripted Story Levels 2 | 3 | This chapter will walk you through the process of adding a script to a Story Mode Level, and giving examples of the kind of custom behavior which can be implemented with this functionality. 4 | -------------------------------------------------------------------------------- /src/title-page.md: -------------------------------------------------------------------------------- 1 | # Friday Night Funkin' Modding Documentation 2 | 3 | This is an officially written and maintained documentation resource by The Funkin' Crew Inc. for 4 | modding **Friday Night Funkin'**. 5 | 6 | # Documentation Source Code 7 | The source files from which this book is generated can be found on [GitHub](https://github.com/FunkinCrew/funkin-modding-docs). -------------------------------------------------------------------------------- /book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | authors = ["The Funkin' Crew Inc.", "EliteMasterEric"] 3 | language = "en" 4 | multilingual = false 5 | src = "src" 6 | title = "Friday Night Funkin' Modding Documentation" 7 | 8 | 9 | [output.html] 10 | git-repository-url = "https://github.com/FunkinCrew/funkin-modding-docs/tree/main/" 11 | edit-url-template = "https://github.com/FunkinCrew/funkin-modding-docs/edit/main/{path}" 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Funkin' Modding Docs 2 | 3 | ## [READ THE DOCS HERE](https://funkincrew.github.io/funkin-modding-docs/) 4 | 5 | ## Contributions 6 | 7 | The Funkin' Modding Docs repository is accepting contributions via [Github](https://github.com/FunkinCrew/funkin-modding-docs/pulls). 8 | 9 | ## License 10 | 11 | This work is licensed under CC BY-SA 4.0. To view a copy of this license, visit https://creativecommons.org/licenses/by-sa/4.0/ 12 | -------------------------------------------------------------------------------- /src/10-appending-and-merging-files/10-00-appending-and-merging-files.md: -------------------------------------------------------------------------------- 1 | # Creating a Friday Night Funkin' Mod - Appending And Merging Files 2 | 3 | There will often be times where you want to modify the contents of an existing file. This is relatively straightforward for textures, but for data files, things get more complicated. You often don't want to replace the entire file, but instead just a smaller chunk, or even a single value. Thankfully, Polymod provides a function for doing this. 4 | 5 | -------------------------------------------------------------------------------- /src/04-custom-stages/04-02-using-a-stage-in-a-song.md: -------------------------------------------------------------------------------- 1 | # Using a Stage in a Song 2 | 3 | There are two ways to use your stage in a song once it's implemented. 4 | 5 | 1. Create a new chart. Open the Chart Editor, start a chart, and select the stage from the `Metadata` toolbox before charting. 6 | 2. Edit an existing chart in your mod. Open the `metadata.json` file and check in `playData` for the `stage` key, and set it to your internal ID. 7 | 8 | Once the chart which references your stage is in your mod folder, simply start the game with your mod installed. 9 | -------------------------------------------------------------------------------- /src/03-custom-characters/03-02-using-a-character-in-a-song.md: -------------------------------------------------------------------------------- 1 | # Using a Character in a Song 2 | 3 | There are two ways to use your character in a song once it's implemented. 4 | 5 | 1. Create a new chart. Open the Chart Editor, start a chart, and select the character from the `Metadata` toolbox before charting. 6 | 2. Edit an existing chart in your mod. Open the `metadata.json` file and check in `playData.characters` for the `player`, `girlfriend`, and `opponent` keys. 7 | 8 | Once the chart which references your character is in your mod folder, simply start the game with your mod installed. 9 | -------------------------------------------------------------------------------- /src/04-custom-stages/04-00-custom-stages.md: -------------------------------------------------------------------------------- 1 | # Creating a Friday Night Funkin' Mod - Custom Stages 2 | 3 | This chapter will walk you through the process of creating a functioning, fully compatible Friday Night Funkin' mod, using the game's official systems for loading custom content and scripts. Once your mod is complete, you will be able to place it in the `mods` folder in your game install and use its content in-game without overriding the base game content and still maintain compatibility with other mods. 4 | 5 | This chapter goes over adding new stages to the game, and using them in a song. 6 | -------------------------------------------------------------------------------- /src/30-scripted-modules/30-00-scripted-modules.md: -------------------------------------------------------------------------------- 1 | # Creating a Friday Night Funkin' Mod - Scripted Modules 2 | 3 | This guide will walk you through the process of creating a functioning, fully compatible Friday Night Funkin' mod, using the game's official systems for loading custom content and scripts. Once your mod is complete, you will be able to place it in the `mods` folder in your game install and use its content in-game without overriding the base game content and still maintain compatibility with other mods. 4 | 5 | This chapter will walk you through the process of creating a scripted Module. 6 | -------------------------------------------------------------------------------- /src/06-custom-notestyles/06-02-using-a-notestyle-in-a-song.md: -------------------------------------------------------------------------------- 1 | # Using a Note Style in a Song 2 | 3 | There are currently two ways to use your note style in a song once it's implemented. 4 | 5 | 1. Create a new chart. Open the Chart Editor, start a chart, and then select the `Notestyle` box from the `Metadata` toolbox before charting. 6 | 2. Edit an existing chart in your mod. Open the `metadata.json` file of the song you want to modify and check in `playData` for the `noteStyle` key. 7 | 8 | Once the chart which references your note style is in your mod folder, simply start the game with your mod installed. 9 | -------------------------------------------------------------------------------- /src/06-custom-notestyles/06-00-custom-notestyles.md: -------------------------------------------------------------------------------- 1 | # Creating a Friday Night Funkin' Mod - Custom Note Styles 2 | 3 | This chapter will walk you through the process of creating a functioning, fully compatible Friday Night Funkin' mod, using the game's official systems for loading custom content and scripts. Once your mod is complete, you will be able to place it in the `mods` folder in your game install and use its content in-game without overriding the base game content and still maintain compatibility with other mods. 4 | 5 | This chapter goes over adding new Note Styles to the game, and using them in a song. 6 | -------------------------------------------------------------------------------- /src/07-custom-sticker-packs/07-00-custom-sticker-packs.md: -------------------------------------------------------------------------------- 1 | # Creating a Friday Night Funkin' Mod - Custom Sticker Packs 2 | 3 | This chapter will walk you through the process of creating a functioning, fully compatible Friday Night Funkin' mod, using the game's official systems for loading custom content and scripts. Once your mod is complete, you will be able to place it in the `mods` folder in your game install and use its content in-game without overriding the base game content and still maintain compatibility with other mods. 4 | 5 | This chapter goes over adding new Sticker Packs to the game, and telling the game when to use them for songs. 6 | -------------------------------------------------------------------------------- /src/02-custom-songs-and-custom-levels/02-00-custom-songs-and-custom-levels.md: -------------------------------------------------------------------------------- 1 | # Creating a Friday Night Funkin' Mod - Custom Songs and Custom Levels 2 | 3 | This chapter will walk you through the process of creating a functioning, fully compatible Friday Night Funkin' mod, using the game's official systems for loading custom content and scripts. Once your mod is complete, you will be able to place it in the `mods` folder in your game install and use its content in-game without overriding the base game content and still maintain compatibility with other mods. 4 | 5 | This chapter goes over adding new playable songs and Story Mode levels to the game. 6 | 7 | -------------------------------------------------------------------------------- /src/31-custom-shaders/31-00-custom-shaders.md: -------------------------------------------------------------------------------- 1 | # Creating a Friday Night Funkin' Mod - Scripted Modules 2 | 3 | This guide will walk you through the process of creating a functioning, fully compatible Friday Night Funkin' mod, using the game's official systems for loading custom content and scripts. Once your mod is complete, you will be able to place it in the `mods` folder in your game install and use its content in-game without overriding the base game content and still maintain compatibility with other mods. 4 | 5 | This chapter will walk you through the process of using a custom runtime shader to alter the appearance of a given sprite or camera using GLSL. 6 | -------------------------------------------------------------------------------- /src/01-fundamentals/01-00-fundamentals.md: -------------------------------------------------------------------------------- 1 | # Creating a Friday Night Funkin' Mod - Fundamentals 2 | 3 | This guide will walk you through the process of creating a functioning, fully compatible Friday Night Funkin' mod, using the game's official systems for loading custom content and scripts. Once your mod is complete, you will be able to place it in the `mods` folder in your game install and use its content in-game without overriding the base game content and still maintain compatibility with other mods. 4 | 5 | This chapter in particular goes through a lot of core concepts of modding in Funkin' that will get used in other guides, so be sure to read through it carefully. -------------------------------------------------------------------------------- /src/07-custom-sticker-packs/07-02-modifying-an-existing-sticker-pack.md: -------------------------------------------------------------------------------- 1 | # Modifying an Existing Sticker Pack 2 | 3 | You can use the [JSONPatch](../10-appending-and-merging-files/10-02-merging-files.md) feature to add or remove stickers from an existing sticker pack. 4 | 5 | For example, to add to Boyfriend's standard sticker pack, you can use the following JSON Patch file (placed in `mods/mymod/_merge/data/stickerpacks/standard-bf.json`, use a different file path to patch a different sticker pack): 6 | 7 | ```jsonc 8 | [ 9 | // Add an asset path to the end of the sticker list. 10 | { "op": "add", "path": "/stickers/-", "value": "path/to/custom/sticker" } 11 | ] 12 | ``` 13 | -------------------------------------------------------------------------------- /src/01-fundamentals/01-04-mod-load-order.md: -------------------------------------------------------------------------------- 1 | # Mod Load Order 2 | 3 | You may wonder what happens in the case where multiple mods provide a given file. 4 | 5 | The answer is simple; mod order matters. If you have two mods installed which replace a particular asset, the mod which loads last will get precedence over mods that get loaded earlier, similar to Minecraft's resource pack system. This is evaluated on a per-file basis, so if Mod A replaces Pico and GF and Mod B replaces only GF, and Mod B is loaded after Mod A, you'll see the Pico from Mod A and the Girlfriend from Mod B. 6 | 7 | In the current version of Friday Night Funkin', there is no accessible means of altering mod load order. Mods will load in alphabetical order by default, with dependencies being loaded first. -------------------------------------------------------------------------------- /src/09-migration/09-00-migrating-mods-to-newer-versions.md: -------------------------------------------------------------------------------- 1 | # Creating a Friday Night Funkin' Mod - Migrating Mods to Newer Versions 2 | 3 | Occasionally, in order to make improvements to the modding system for Friday Night Funkin', the team has to make breaking changes to features that older mods were using, causing them to have unexpected beahavior or otherwise no longer function properly. When we do this, we update the API version rule, automatically flagging any older mods and preventing them from being loaded to ensure the stability of the game. 4 | 5 | This chapter will walk you through the process of making older mods compatible with newer versions of Friday Night Funkin'. Once you compete the steps in this guide, your mod should function as expected on newer versions. 6 | -------------------------------------------------------------------------------- /src/03-custom-characters/03-00-custom-characters.md: -------------------------------------------------------------------------------- 1 | # Creating a Friday Night Funkin' Mod - Custom Characters 2 | 3 | This chapter will walk you through the process of creating a functioning, fully compatible Friday Night Funkin' mod, using the game's official systems for loading custom content and scripts. Once your mod is complete, you will be able to place it in the `mods` folder in your game install and use its content in-game without overriding the base game content and still maintain compatibility with other mods. 4 | 5 | This chapter goes over adding new characters to the game, and using them in a level. If you are looking for adding characters to the character select screen, see [Custom Playable Characters](../05-custom-playable-characters/05-00-custom-playable-characters.md). 6 | -------------------------------------------------------------------------------- /src/05-custom-playable-characters/05-00-custom-playable-characters.md: -------------------------------------------------------------------------------- 1 | # Creating a Friday Night Funkin' Mod - Custom Playable Characters 2 | 3 | This chapter will walk you through the process of creating a functioning, fully compatible Friday Night Funkin' mod, using the game's official systems for loading custom content and scripts. Once your mod is complete, you will be able to place it in the `mods` folder in your game install and use its content in-game without overriding the base game content and still maintain compatibility with other mods. 4 | 5 | This chapter goes over adding new playable characters to the game, ensuring that they appear in the Character Select menu, determining whether the character has been unlocked, customizing the appearance of the Freeplay menu, and adding custom remixes to them. -------------------------------------------------------------------------------- /src/01-fundamentals/01-05-hot-reloading.md: -------------------------------------------------------------------------------- 1 | # Hot Reloading 2 | 3 | While developing your mod, you may find it inconvenient to make a change, open the game, encounter an error or visual issue, have to close the game, make another change, then open the game again and start the process over and over in order to achieve the desired results for your custom content. 4 | 5 | Thankfully, there is a better way! **Press F5 to force the game to dump its cache and reload all game data from disk,** then restart the current state with the appropriate changes applied. This lets you, for example: 6 | 7 | - Tweak the positions of stage props, or add new ones, then immediately reload to see them in game. 8 | - Modify the animation offsets for a character and quickly ensure they work. 9 | - Modify a script to resolve an exception and reload to continue testing without closing the game. 10 | -------------------------------------------------------------------------------- /src/01-fundamentals/01-02-loading-the-mod-in-game.md: -------------------------------------------------------------------------------- 1 | # Loading the Mod In-Game 2 | 3 | Now that you have a metadata file, you can start the game! Pro tip, if you run the game from the command line, you can see lots of useful debug messages, like these messages that indicate your mod has loaded! 4 | 5 | ```shell 6 | source/funkin/modding/PolymodHandler.hx:316: Found 5 mods when scanning. 7 | source/funkin/modding/PolymodHandler.hx:118: Attempting to load 5 mods... 8 | ... 9 | source/funkin/modding/PolymodErrorHandler.hx:79: [INFO-] LOADING MOD - Preparing to load mod ../../../../example_mods/introMod 10 | source/funkin/modding/PolymodErrorHandler.hx:79: [INFO-] LOADING MOD - Done loading mod ../../../../example_mods/introMod 11 | ... 12 | source/funkin/modding/PolymodHandler.hx:169: Mod loading complete. We loaded 5 / 5 mods. 13 | ``` 14 | 15 | Neat! But right now, your mod doesn't do anything. 16 | -------------------------------------------------------------------------------- /src/20-using-hscript/20-00-using-hscript.md: -------------------------------------------------------------------------------- 1 | # Creating a Friday Night Funkin' Mod - Using HScript 2 | 3 | This guide will walk you through the process of creating a functioning, fully compatible Friday Night Funkin' mod, using the game's official systems for loading custom content and scripts. Once your mod is complete, you will be able to place it in the `mods` folder in your game install and use its content in-game without overriding the base game content and still maintain compatibility with other mods. 4 | 5 | The previous chapters provides for plenty of functionality, and should be sufficient to make your own custom story weeks complete with custom characters, stages with static and animated props, and songs with custom chart events. However, this does not provide for advanced functionality, such as cutscenes, custom mechanics, or other custom behavior. The following chapters describe how to implement your own custom behavior, and this chapter breaks down important fundamentals on how scripting works and how to resolve common issues encountered while scripting. -------------------------------------------------------------------------------- /src/21-scripted-classes/21-01-scripted-songs.md: -------------------------------------------------------------------------------- 1 | # Creating a Friday Night Funkin' Mod - Scripted Songs 2 | 3 | This chapter will walk you through the process of adding a script to a Song, and giving examples of the kind of custom behavior which can be implemented with this functionality. 4 | 5 | Start by creating a scripted class file with the `.hxc` extension (in the `mods/mymod/scripts/songs` if you want to keep things organized). 6 | 7 | ```haxe 8 | // Remember to import each class you want to reference in your script! 9 | import funkin.play.song.Song; 10 | 11 | // Choose a name for your scripted class that will be unique, and make sure to specifically extend the Song class. 12 | // This class's functions will override the default behavior for the song. 13 | class BallisticSong extends Song { 14 | public function new() { 15 | // The constructor gets called once, when the game loads. 16 | // The constructor takes one parameter, which is the song ID for the song you are applying the script to. 17 | super('ballistic'); 18 | } 19 | 20 | // Add override functions here! 21 | } 22 | ``` 23 | 24 | You can then add override functions to perform custom behavior. 25 | -------------------------------------------------------------------------------- /src/02-custom-songs-and-custom-levels/02-04-what-are-variations.md: -------------------------------------------------------------------------------- 1 | # What Are Variations? 2 | 3 | Variations are groups of difficulties for a given song which share metadata. 4 | 5 | As an example, the song DadBattle has eight separate difficulties (at time of writing). These are Easy, Normal, Hard, Erect (Erect Mix), Nightmare (Erect Mix), Easy (Pico Mix), Normal (Pico Mix), and Hard (Pico Mix). 6 | 7 | These are divided into three variations; Default, Erect, and Pico. Each variation defines information like BPM (and BPM changes), artist, charter, which album the song is from, which stage to use, which character to use in those stages, and which instrumental and vocal track to use. The variation then defines which difficulties it includes, and the chart data for that variation specifies the events for that variation, and the note data and scroll speed for each difficulty. 8 | 9 | This means that, in order to make one of these values different for a specific difficulty or remix (including changing the events), you must put that difficulty into a new variation. 10 | 11 | The `metadata.json` file for a song defines what variations exist for that song; the game then looks for `metadata-.json` and `chart-.json` files for each listed variation. 12 | -------------------------------------------------------------------------------- /src/03-custom-characters/03-01-character-assets.md: -------------------------------------------------------------------------------- 1 | # Character Spritesheet Formats 2 | 3 | The individual sprites of a character's animations must be combined into a spritesheet for the game to use them. Friday Night Funkin' supports one of several formats: 4 | 5 | - `sparrow`: Combines the images into a large sheet, then provides an XML file containing the coordinates of each frame with it. Can be exported directly from Adobe Animate or Flash CS6 using the `Generate Sprite Sheet` option, or can be created from individual frames using [Free Texture Packer](http://free-tex-packer.com/) (note that Free Texture Packer refers to this format as Starling). 6 | 7 | - `packer`: Combines images into a sheet, then provides a TXT file containing the coordinates of each frame. 8 | 9 | - `animateatlas`: Created exclusively when using Adobe Animate, this exports individual symbols into a large sheet, then provides a JSON file with data to split up each symbol, then provides a second JSON to arrange those symbols into animations. Great for performance, especially for characters which were made by rearranging smaller parts. We use the [FlxAnimate](https://github.com/Dot-Stuff/flxanimate) Haxelib for this. 10 | 11 | - `multisparrow`: Allows for different groups of animations to be exported into separate Sparrow spritesheets, then combined together into one character. 12 | -------------------------------------------------------------------------------- /src/07-custom-sticker-packs/07-03-using-a-custom-sticker-pack.md: -------------------------------------------------------------------------------- 1 | # Using a Custom Sticker Pack 2 | 3 | There are two ways the game defines a given sticker pack to be used: 4 | 5 | - Each [Playable Character](../05-custom-playable-characters/05-00-custom-playable-characters.md) defines the `stickerPack` variable, which specifies the sticker pack to be used by songs containing that character. For example, `bf` uses the `standard-bf` sticker pack. You can define a sticker pack to be used for your custom playable character by setting the `stickerPack` value, or modify which sticker pack is used by other playable characters by using [JSONPatch](../10-appending-and-merging-files/10-02-merging-files.md) to modify the `stickerPack` value of that character. 6 | - Each song has a value in its metadata to define which sticker pack is used. Set the `playData.stickerPack` on a song (or use JSONPatch to modify metadata of an existing song) to override which sticker pack it uses. 7 | 8 | The game checks and uses sticker packs in this order: 9 | 10 | - The sticker pack chosen by the song. 11 | - The sticker pack chosen by the playable character. 12 | - The `default` sticker pack (which displays only Boyfriend). If you see only Boyfriend during a sticker transition, then you know neither the song or the playable character defines a sticker pack, and you should fix the issue. -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy 2 | on: 3 | push: 4 | branches: 5 | - main 6 | 7 | jobs: 8 | deploy: 9 | runs-on: ubuntu-latest 10 | permissions: 11 | contents: write # To push a branch 12 | pages: write # To push to a GitHub Pages site 13 | id-token: write # To update the deployment status 14 | steps: 15 | - uses: actions/checkout@v4 16 | with: 17 | fetch-depth: 0 18 | - name: Install latest mdbook 19 | run: | 20 | tag=$(curl 'https://api.github.com/repos/rust-lang/mdbook/releases/latest' | jq -r '.tag_name') 21 | url="https://github.com/rust-lang/mdbook/releases/download/${tag}/mdbook-${tag}-x86_64-unknown-linux-gnu.tar.gz" 22 | mkdir mdbook 23 | curl -sSL $url | tar -xz --directory=./mdbook 24 | echo `pwd`/mdbook >> $GITHUB_PATH 25 | - name: Build Book 26 | run: | 27 | # This assumes your book is in the root of your repository. 28 | # Just add a `cd` here if you need to change to another directory. 29 | mdbook build 30 | - name: Setup Pages 31 | uses: actions/configure-pages@v4 32 | - name: Upload artifact 33 | uses: actions/upload-pages-artifact@v3 34 | with: 35 | # Upload entire repository 36 | path: 'book' 37 | - name: Deploy to GitHub Pages 38 | id: deployment 39 | uses: actions/deploy-pages@v4 40 | -------------------------------------------------------------------------------- /src/02-custom-songs-and-custom-levels/02-01-creating-a-chart.md: -------------------------------------------------------------------------------- 1 | # Creating a Chart 2 | 3 | To create a chart, access the Chart Editor tool. This can be found in-game by accessing the Debug menu from the main menu (this is bound to `~` by default). You can also access it by adding a keybind for "Debug Chart" in the options menu (not bound by default), then pressing the bound key while playing a song. 4 | 5 | From here, you can create a new chart from audio files, import one from an older version of the game, or build a chart from existing in-game chart data. 6 | 7 | Detailed use of the chart editor deserves its own guide, but the basic should be fairly intuitive. For now, let's assume you've made a chart of your favorite song, and want to turn it into a mod people can play. 8 | 9 | ## Dissecting Your FNFC File 10 | 11 | When you save your chart, the game packages it up into a `.fnfc` file, which makes it easy to share with other charters and collaborate. It includes the audio for the song, along with the note data and some metadata files to go with it. 12 | 13 | To add the song to our mod, we need to get that info out. This is fairly easy, because an FNFC file is actually secretly a ZIP file! Rename your `mychart.fnfc` to `mychart.zip`, replacing the file extension so that your operating system recognizes it as a ZIP, and extract it to reveal its contents, which will be something like: 14 | 15 | ``` 16 | -manifest.json 17 | -mychart-metadata.json 18 | -mychart-chart.json 19 | -Inst.ogg 20 | -Voices-bf.ogg 21 | -Voices-pico.ogg 22 | ``` 23 | -------------------------------------------------------------------------------- /src/20-using-hscript/20-01-what-is-hscript.md: -------------------------------------------------------------------------------- 1 | # What is HScript? 2 | 3 | HScript is an interpreted scripting language used by Polymod to provide custom functionality to Friday Night Funkin'. The game detects scripts provided in `.hxc` files, interprets them, and executes them to perform custom functionality in-game. 4 | 5 | Mods are not the only thing that utilizes this functionality; the base game itself uses scripts to play animations and cutscenes, manipulate stage props, and even add custom gameplay, in a manner that allows for faster iteration than would be possible without scripts. 6 | 7 | HScript is extremely similar to Haxe, and provides access to almost all the variables and classes available in the game, with a few notes: 8 | 9 | - Each scripted class must have a unique name from every other scripted class. If your mod has two scripted classes with the same name, or even if two different mods have scripted classes of the same name, one copy will be completely suppressed, which can result in unexpected behavior. 10 | - Private variables are simply accessible, be careful what you mess with. 11 | - Certain classes are blacklisted, to help protect users against malicious scripts. 12 | - `abstract enum`s (a Haxe language feature which provides values which act like an Enum, but are actually some other type like a String or Integer) are inaccessible. Check the code for the `abstract enum` you want to use and find the ACTUAL underlying value, and use that instead. 13 | - `abstract`s (a Haxe language feature which applies additional methods to an existing class) are inaccessible. You will have to find some workaround to interact with these values. -------------------------------------------------------------------------------- /src/07-custom-sticker-packs/07-01-creating-a-new-sticker-pack.md: -------------------------------------------------------------------------------- 1 | # Creating a new Sticker Pack 2 | 3 | ## Sticker Pack Assets 4 | 5 | In order to create your own sticker pack, you will need graphics for the stickers you want to use. The ones included in the base game are generally around 300x300 in size, with some variation. 6 | 7 | ## Sticker Pack Data 8 | 9 | A custom sticker pack requires creating a new JSON file in the `data/stickerpacks` folder. 10 | 11 | Below is the "default" sticker pack JSON file `assets/data/stickerpacks/default.json`[^stickerpacksource] 12 | 13 | ```json 14 | { 15 | "version": "1.0.0", 16 | "name": "Default", 17 | "artist": "PhantomArcade3K", 18 | "stickers": [ 19 | "transitionSwag/stickers-set-1/bfSticker1", 20 | "transitionSwag/stickers-set-1/bfSticker2", 21 | "transitionSwag/stickers-set-1/bfSticker3" 22 | ] 23 | } 24 | ``` 25 | 26 | Let's break it all down. 27 | - `version`: The version number for the Sticker Pack data file format. Leave this at `1.0.0`. 28 | - This will increase if the data file format changes, and tell the game whether additional processing needs to be done for backwards compatibility. 29 | - `name`: The readable name for the sticker pack, used in places like the Chart Editor. 30 | - `author`: The author of the sticker pack, aka the artist who created the assets. 31 | - `stickers`: A list of all the paths for all the stickers to use, as strings. 32 | - You cannot currently specify any additional arguments, such as scale, offsets, texture smoothing, or animations. 33 | 34 | [^stickerpacksource]: -------------------------------------------------------------------------------- /src/01-fundamentals/01-03-asset-replacement-and-additions.md: -------------------------------------------------------------------------------- 1 | # Asset Replacement and Additons 2 | 3 | ## Asset Replacement 4 | 5 | The key thing that Polymod allows you to do is to replace assets. This is done by adding those files to your mods folder in the same location as they would go. 6 | 7 | For example, you can replace Girlfriend's sprites by placing your new sprites in the same location as they are in the `assets` folder, which would be `shared/images/characters/GF_assets.png`. 8 | 9 | In other words, structure your mod like so: 10 | 11 | ``` 12 | -assets 13 | -manifest 14 | -plugins 15 | -mods 16 | |-myMod 17 | |-shared 18 | |-images 19 | |-characters 20 | |-GF_assets.png 21 | |-GF_assets.xml 22 | |-_polymod_meta.json 23 | -Funkin.exe 24 | ``` 25 | 26 | When the game goes to load a character's sprites, it will make a request internally to retrieve the `assets/shared/images/characters/GF_assets.png` file to use for the texture (and the corresponding `XML` to split the image into individual frames). When it does, Polymod intercepts that request and checks if there is a file of that name among the loaded mods, and if so, it will use that instead. 27 | 28 | ## Asset Additions 29 | 30 | Polymod also allows you to add new files to the game. This is notable, as trying to place new files into the `assets` directory doesn't work, the game won't recognize those files. 31 | 32 | The game still needs to get told to load those assets for them to get used, but there are many functions which load all the files in a given folder (such as the Song Registry, the Character Registry, the Stage Registry, etc). We'll look more into those later. -------------------------------------------------------------------------------- /src/02-custom-songs-and-custom-levels/02-02-adding-the-custom-song.md: -------------------------------------------------------------------------------- 1 | # Adding The Custom Song 2 | 3 | At the end of [Creating A Chart](02-01-creating-a-chart.md) we learned that `.fnfc` files were just `.zip` archives, so we can simply rename it and unzip it. Once we have thse files, we just need to put them in the correct spots in our mod folder! 4 | 5 | - The `manifest.json` file can be discarded, our mod won't need it. 6 | - The `metadata.json` and `chart.json` files need to go into the `data/songs/` folder, replacing `` with the internal name for our song. 7 | - The OGG files need to go into `songs/`, again replacing `` with the internal name of our song. 8 | 9 | We'll end up with something like this. 10 | 11 | ``` 12 | -mods 13 | |-myMod 14 | |-data 15 | |-songs 16 | |-mychart 17 | |-mychart-metadata.json 18 | |-mychart-chart.json 19 | |-songs 20 | |-mychart 21 | |-Inst.ogg 22 | |-Voices-bf.ogg 23 | |-Voices-pico.ogg 24 | |-_polymod_meta.json 25 | ``` 26 | 27 | When the game starts, it queries the list of available songs by looking in the `data/songs` folder for `/-metadata.json` files, which it then uses to find the chart file and the requisite song files. Neat! But right now, if you boot up the game, this doesn't do anything. You'll see it mentioned in the logs with no complaints, but it's not playable in Story Mode or Freeplay, what gives? 28 | 29 | ``` 30 | source/funkin/play/song/Song.hx:579: Fetching song metadata for mychart 31 | source/funkin/data/song/SongRegistry.hx:103: Loaded entry data: Song(mychart) 32 | ``` 33 | 34 | The fix is simple; every song must be part of a Story Mode level to appear in Freeplay. -------------------------------------------------------------------------------- /src/21-scripted-classes/21-10-scripted-playable-characters.md: -------------------------------------------------------------------------------- 1 | # Creating a Friday Night Funkin' Mod - Scripted Playable Characters 2 | 3 | This chapter will walk you through the process of adding a script to a Playable Character, and giving examples of the kind of custom behavior which can be implemented with this functionality. 4 | 5 | Start by creating a scripted class file with the `.hxc` extension (in the `mods/mymod/scripts/characters` if you want to keep things organized). 6 | 7 | ```haxe 8 | // Remember to import each class you want to reference in your script! 9 | import funkin.play.character.SparrowCharacter; 10 | 11 | // Choose a name for your scripted class that will be unique, and make sure to specifically extend the correct character class. 12 | // SparrowCharacter is the one to use for XML-based characters. 13 | // This class's functions will override the default behavior for the character. 14 | class WhittyCharacter extends SparrowCharacter { 15 | public function new() { 16 | // The constructor gets called whenever the character is spawned. 17 | // The constructor takes one parameter, which is the song ID for the song you are applying the script to. 18 | super('whitty'); 19 | } 20 | 21 | // Add override functions here! 22 | } 23 | ``` 24 | 25 | You can then add override functions to perform custom behavior. 26 | 27 | ## Custom Unlock Behavior 28 | 29 | The most important thing to override here is the `isUnlocked` function, which lets you define whether the player can access the given character in Freeplay. 30 | 31 | ```haxe 32 | // An example of overriding the `isUnlocked` function. 33 | // This is the logic for Pico, which requires checking the completion status of a given story week. 34 | // You can put any logic you want here. 35 | override function isUnlocked():Bool { 36 | return Save.instance.hasBeatenLevel('weekend1'); 37 | } 38 | ``` 39 | -------------------------------------------------------------------------------- /src/10-appending-and-merging-files/10-01-appending-files.md: -------------------------------------------------------------------------------- 1 | # Appending Files 2 | 3 | Adding values to the end of a data file is pretty straight-forward, but it depends on the file type you want to append to. 4 | 5 | Create a new file in the `_append` folder of your mod, with a path matching the file you want to append to. For example, to append to `assets/data/introText.txt`, you would place your file at `mods/mymod/_append/data/introText.txt` 6 | 7 | ### Appending to TXT Files 8 | 9 | If the file extension of the append file is `.txt`, the contents of the file will be simply appended to the end of the target file. 10 | 11 | ### Appending to CSV/TSV Files 12 | 13 | If the file extension of the append file is `.csv` or `.tsv`, the rows in the sheet will be added to the end of the target sheet. 14 | 15 | ### Appending to XML Files 16 | 17 | TODO: Fill this out. 18 | 19 | ### Appending to JSON Files 20 | 21 | If the file extension of the append file is `.json`, the value will be parsed and naively appended to the target data. 22 | 23 | For example, given the source file `data/mydata.json`: 24 | 25 | ```json 26 | { 27 | "test1": [1, 2, 3], 28 | "test2": { 29 | "foo": "bar" 30 | }, 31 | "test3": "baz" 32 | } 33 | ``` 34 | 35 | We can provide the file `mods/mymod/_append/data/mydata.json`: 36 | 37 | ```json 38 | { 39 | "test4": "hello", 40 | "test2": { 41 | "fizz": "buzz" 42 | } 43 | } 44 | ``` 45 | 46 | And Polymod will mutate it to get this result: 47 | 48 | ```jsonc 49 | { 50 | // Unreferenced values are untouched. 51 | "test1": [1, 2, 3], 52 | // Included values are placed in directly, not merged! 53 | "test2": { 54 | "fizz": "buzz" 55 | }, 56 | "test3": "baz", 57 | // New values are simply included. 58 | "test4": "hello" 59 | } 60 | ``` 61 | 62 | If you want something more particular, see [Merging into JSON Files](./10-02-merging-files.md#merging-into-json-files) for a more powerful and flexible approach. 63 | -------------------------------------------------------------------------------- /src/03-custom-characters/03-03-fixing-character-offsets.md: -------------------------------------------------------------------------------- 1 | # Fixing Character Offsets 2 | 3 | Uh Oh! Upon using your character in a song, you might have noticed that with each note hit, the character has weird offsets which makes it wobble back and forth. Let's fix that. 4 | 5 | ## Accessing the Animation Editor 6 | 7 | To fix offsets for you character, you first have to access the Animation Editor tool. This can be found in-game by accessing the Debug menu from the main menu (this is bound to `~` by default) and selecting "Animation Editor" option. 8 | 9 | Once you have accessed the tool, it might be a little overwhelming at first, but everything is pretty straightforward. 10 | 11 | ## Fixing the Offsets 12 | 13 | The first thing you have to do is click `2` on your keyboard to switch to `Animation Mode` in order to properly fix offsets for each animation. Then, you need to select your character from the `Character` section in the UI box that is located in the top-left corner. 14 | 15 | *HINT:* The best thing to do to speed up your process, it to toggle `Onion Skin` mode by pressing `F`. This will show the previous animation played being half transparent. This can help speeding up the proccess, since you will be able to to properly line up the animation with the previous one. 16 | 17 | The UI will show you all of the possible controls and shortcuts, to make your proccess of fixing the character offsets much easier. 18 | 19 | ## Saving Offsets 20 | 21 | Once you are happy with your result, simply press `ESC` on your keyboard to save the `Character Data` file. 22 | - Currently there is a bug which makes the file saving system not automatically put character's ID in the file name, which you will have to do yourself. Simply name the file the ID of your character followed by `.json`. 23 | 24 | From the ["Creating a Character"](03-02-creating-a-character.md) chapter you will know, that you have to place this character data JSON file in `data/characters`. Then, you can simply use [Hot Reloading](../01-fundamentals/01-05-hot-reloading.md) to check the offsets without restarting the game. -------------------------------------------------------------------------------- /src/21-scripted-classes/21-05-scripted-characters.md: -------------------------------------------------------------------------------- 1 | # Creating a Friday Night Funkin' Mod - Scripted Characters 2 | 3 | This chapter will walk you through the process of adding a script to a Character, and giving examples of the kind of custom behavior which can be implemented with this functionality. 4 | 5 | Start by creating a scripted class file with the `.hxc` extension (in the `mods/mymod/scripts/characters` if you want to keep things organized). 6 | 7 | ```haxe 8 | // Remember to import each class you want to reference in your script! 9 | import funkin.play.character.SparrowCharacter; 10 | 11 | // Choose a name for your scripted class that will be unique, and make sure to specifically extend the correct character class. 12 | // SparrowCharacter is the one to use for XML-based characters. 13 | // This class's functions will override the default behavior for the character. 14 | class WhittyCharacter extends SparrowCharacter { 15 | public function new() { 16 | // The constructor gets called whenever the character is spawned. 17 | // The constructor takes one parameter, which is the song ID for the song you are applying the script to. 18 | super('whitty'); 19 | } 20 | 21 | // Add override functions here! 22 | } 23 | ``` 24 | 25 | You can then add override functions to perform custom behavior. 26 | 27 | ## Character Classes 28 | 29 | If you use a different animation type for a character, you need to specify a different base script class, otherwise the character will fail to load. 30 | 31 | - `funkin.play.character.SparrowCharacter` is used for characters that have Sparrow spritesheet animations. 32 | - `funkin.play.character.MultiSparrowCharacter` is used for characters that have several Sparrow spritesheet animations to combine into one character. 33 | - `funkin.play.character.PackerCharacter` is used for characters that have Packer spritesheet animations. 34 | - `funkin.play.character.AnimateAtlasCharacter` is used for characters that have Adobe Animate texture atlases. 35 | - `funkin.play.character.BaseCharacter` has empty stubs for all the rendering and animation handlers, and is only useful for people who want to reimplement their character's animation system by hand. -------------------------------------------------------------------------------- /src/30-scripted-modules/30-99-custom-modules.temp: -------------------------------------------------------------------------------- 1 | # Creating a Friday Night Funkin' Mod - Custom Modules 2 | 3 | This guide will walk you through the process of creating a functioning, fully compatible Friday Night Funkin' mod, using the game's official systems for loading custom content and scripts. Once your mod is complete, you will be able to place it in the `mods` folder in your game install and use its content in-game without overriding the base game content and still maintain compatibility with other mods. 4 | 5 | This entry goes over making custom modules for your mod 6 | 7 | ## what are modules? 8 | modules are scripts that allow you to interact with the full game with only functions/callbacks, you can use them to alter ui elements in menus, and edit the game feild in the song without having to make a script for the stage/song/character 9 | 10 | ## how modules work 11 | modules are taken care of by the ModuleHandler in the game that takes every class that extends `funkin.modding.module.Module` and handles it by firing its callbacks 12 | 13 | ## where to add the module file 14 | first you need to make a scripts folder if you dont already have one in your mods folder. for where you should add your module file(s) in that folder it doesnt really matter, you can add them in folders to keep your files clean so the next part is optional, make a folder called `modules` in `yourmod/scripts/.`. and then make a file named `yourModule.hxc` in that folder, you dont have to make the first letter upperCase like hx files and offcourse change 'yourModule' to your modules name 15 | 16 | ## how to make a module 17 | to make a module you need some scripting knowledge so you can understand how it works, so in that file that you created make a class with the name of your module (make sure the first letter is uppercase) and make it extend `funkin.modding.module.Module` after that override the classes constructor and in the super call function add the classes name in a string 18 | 19 | ``` 20 | import funkin.modding.module.Module; 21 | 22 | class MyModule extends Module{ 23 | override public function new() 24 | { 25 | super('MyModule'); // <-- your modules name here 26 | } 27 | } 28 | ``` 29 | 30 | ## how to add callbacks to a module 31 | 32 | --add something here 33 | 34 | -------------------------------------------------------------------------------- /src/writing-and-contributing-to-this-book.md: -------------------------------------------------------------------------------- 1 | # Writing And Contributing to This Book 2 | This book is written in Markdown format, and uses [mdbook](https://rust-lang.github.io/mdBook/) for generation. 3 | 4 | The source files from which this book is generated can be found on [GitHub](https://github.com/FunkinCrew/funkin-modding-docs). 5 | 6 | ## Things to Consider 7 | 8 | - How should we handle explaining directory paths? The source code has some of the 9 | assets split into libraries (`preload/`, `songs`, `shared`, etc.) however most people modding don't need to 10 | really mind the source code versions of asset paths. 11 | - Currently [Chapter 10: Appending and merging Files](10-appending-and-merging-files/10-00-appending-and-merging-files.md) renders as "Chapter 5" 12 | 13 | ### Style Guide 14 | 15 | #### Folder/Chapter Structure 16 | 17 | [SUMMARY.md](SUMMARY.md) is how `mdbook` generates the chapters, sub-chapters, of all the markdown files. In there you can see how the book is laid out in terms of it's content. 18 | 19 | Folder structure is entirely independant from content ordering, so we can organize generally how we please as long as [SUMMARY.md](SUMMARY.md) is pointing to the right files. 20 | 21 | Folder names should be `{chapter number}-{chapter title}`, generally despite how long the `chapter title` may end up being 22 | From this guides source: 23 | ```shell 24 | src/ 25 | 01-fundamentals/ 26 | 02-custom-songs-and-custom-levels/ 27 | 03-custom-characters/ 28 | ... // and so on... 29 | ``` 30 | 31 | The main chapter page should be `{chapter number}-00-{chapter title}.md` in the chapter's folder. 32 | From this guides source: 33 | ```shell 34 | src/ 35 | 01-fundamentals/ 36 | 01-00-fundamentals.md 37 | ... 38 | 39 | 02-custom-songs-and-custom-levels/ 40 | 02-00-custom-songs-and-custom-levels.md 41 | ... 42 | ... 43 | ``` 44 | 45 | Then each successive sub-chapter should follow `{chapter number}-{sub-chapter number}-{subchapter title}.md` 46 | From this guides source: 47 | ```shell 48 | src/ 49 | 01-fundamentals/ 50 | 01-00-fundamentals.md 51 | 01-01-the-metadata-file.md 52 | 01-02-loading-the-mod-in-game.md 53 | ... 54 | 55 | 02-custom-songs-and-custom-levels/ 56 | 02-00-custom-songs-and-custom-levels.md 57 | 02-01-creating-a-chart.md 58 | 02-02-adding-the-custom-song.md 59 | 02-03-adding-a-custom-level.md 60 | 03-custom-characters/ 61 | 03-00-custom-characters.md 62 | 03-01-character-assets.md 63 | ... 64 | ... 65 | ``` 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /src/05-custom-playable-characters/05-01-required-assets.md: -------------------------------------------------------------------------------- 1 | # Required Assets 2 | 3 | In order to make a fleshed out custom character that can be used from Character Select in Friday Night Funkin', you need a large number of textures, animations, and audio tracks: 4 | 5 | - A [custom character](../03-custom-characters/03-00-custom-characters.md) 6 | - This requires a set of singing animations for the character. 7 | - At least one custom song that uses that character; this can be either [a new song](../02-custom-songs-and-custom-levels/02-02-adding-the-custom-song.md) or a [variation added to an existing song](../02-custom-songs-and-custom-levels/02-05-adding-variations-to-existing-songs.md). 8 | - This requires an instrumental and split vocal tracks. 9 | - A pixel icon asset to use for that character's icon in the Character Select grid. 10 | - This supports either a static image or a Sparrow spritesheet (which includes the animation to play when the character is selected). 11 | - A namecard asset to use for that character's name above them in the Character Select menu. 12 | - The pixellation effect is done in code so this just needs to be a single static image. 13 | - Animations for the character to use in the Character Select menu. 14 | - This is currently hard-coded to use an Adobe Animate texture atlas and cannot use a Sparrow spritesheet. 15 | - The character needs animations for unlock, idle, slide in, slide out, select, and deselect. 16 | - Assets to use for the character's Girlfriend character to the left of them in the Character Select menu. 17 | - This is currently hard-coded to use an Adobe Animate texture atlas and cannot use a Sparrow spritesheet. 18 | - The character needs animations for unlock, idle, slide in, slide out, select, and deselect. 19 | - Assets for the character to use on the Freeplay menu. 20 | - This is currently hardcoded to use an Adobe Animate texture atlas. 21 | - The character needs animations for leaping in, idle, confirm, and moving to character select. It also optionally has an idle and cartoon animation. 22 | - Assets for the character's Freeplay skin and the backing card. 23 | - This requires a variety of assets but can use Boyfriend's as a fallback. 24 | - NOTE: This is currently hardcoded to BF or Pico. 25 | - Assets for the character's animations in the Results screen. 26 | - Each rank has its own animation and music, but animations can be reused between ranks and results themes can fall back to the default. 27 | - Rank animations are Loss, Good, Great, Excellent, Perfect, and Perfect Gold (the base game uses the same animation for Perfect and Perfect Gold) 28 | - Each also can take its own music, but you can reuse Boyfriend's as a good placeholder. 29 | -------------------------------------------------------------------------------- /src/01-fundamentals/01-01-the-metadata-file.md: -------------------------------------------------------------------------------- 1 | # The Metadata File 2 | 3 | To start, create a new folder within your `mods` folder. This is where your mod's assets and scripts will live. Next, create a new text file, and change its name to `_polymod_meta.json`. Make sure you didn't accidentally name it `_polymod_meta.json.txt`! 4 | 5 | Inside this file, we will put the information the game needs in order to learn about your mod. I recommend doing this with a program like [Visual Studio Code](https://code.visualstudio.com/), it will correct you if you accidentally misplace a comma or something. 6 | 7 | ```json 8 | { 9 | "title": "Intro Mod", 10 | "description": "An introductory mod.", 11 | "contributors": [ 12 | { 13 | "name": "EliteMasterEric" 14 | } 15 | ], 16 | "dependencies": { 17 | "modA": "1.0.0" 18 | }, 19 | "optionalDependencies": { 20 | "modB": "1.3.2" 21 | }, 22 | "api_version": "0.6.3", 23 | "mod_version": "1.0.0", 24 | "license": "Apache-2.0" 25 | } 26 | ``` 27 | 28 | `_polymod_meta.json` has the following fields: 29 | 30 | - `title`: A readable name for the mod. 31 | - `description`: A readable description for the mod. 32 | - `contributors`: A list of Contributor objects. 33 | - `homepage`: A URL where users can learn more about your mod. 34 | - `dependencies`: A map of mod IDs which are mandatory dependencies, along with their version numbers. 35 | - These are the mods which must also be loaded in order for this mod to load. 36 | - If the mod is not included, it will fail. 37 | - The mod list will be reordered such that dependencies load first. 38 | - `optionalDependencies`: A map of mod IDs which are optional dependencies, along with their version numbers. 39 | - These mods do not necessarily need to be installed for this mod to load, but they will still force the mod list to be reordered so that the dependencies load before this mod. 40 | - `api_version`: A version number used to determine if mods are compatible with your copy of Funkin'. Change this to the version number for Friday Night Funkin' that you want to support, preferably the latest one (`0.6.3` at time of writing.). 41 | - `mod_version`: A version number specifically for your mod. Choose any version or leave it at `1.0.0`. 42 | - `license`: The license your mod is distributed under. [Pick one from here](https://opensource.org/licenses) or just leave it as `Apache-2.0`. 43 | 44 | A Contributor has the following fields: 45 | 46 | - `name`: The contributor's name. 47 | - `role`: *(optional)* The role the contributor played, for example "Artist" or "Programmer" 48 | - `email`: *(optional)* A contact email 49 | - `url`: *(optional)* A homepage URL 50 | 51 | Many of these fields are intended to be used in the future by an upcoming Mod Menu interface, which will allow users to organize their mods. -------------------------------------------------------------------------------- /src/09-migration/09-02-0.5.0-to-0.6.3.md: -------------------------------------------------------------------------------- 1 | # Migrating from v0.5.0 to v0.6.3 2 | 3 | Migration from v0.5.0 to v0.6.3 requires some changes to mods. 4 | 5 | # Update the API Version 6 | 7 | Eric accidentally forgot to increment the API version for the game when updating to v0.6.0, and this got fixed with v0.6.3. 8 | This means that mods won't load unless the `api_version` value in the `_polymod_meta.json` file is at least v0.6.3. This allows mod creators to perform testing and ensure their mods are compatible before releasing an update on their distribution platforms of choice. Most mods (especially ones with minimal scripting) will need no changes to work as expected, but developers should probably do some playtesting to be sure. 9 | 10 | # Migrate from hxCodec to hxvlc 11 | 12 | The library used for video playback has changed from hxCodec to hxvlc. This has resulted in improved performance and various bug fixes overall, but breaks mods which interact directly with the video library. This should not break any mods which use the built-in system for cutscenes in Funkin'. 13 | 14 | Make the following changes: 15 | 16 | ```haxe 17 | // BEFORE: Imports from the package `hxcodec` 18 | import hxcodec.flixel.FlxVideoSprite; 19 | 20 | // AFTER: Imports from the package `hxvlc` 21 | import hxvlc.flixel.FlxVideoSprite; 22 | 23 | 24 | // BEFORE: Callback to onTextureSetup 25 | video.bitmap.onTextureSetup.add(() -> { ... }); 26 | 27 | // AFTER: Callback to onFormatSetup 28 | video.bitmap.onFormatSetup.add(() -> { ... }); 29 | 30 | 31 | // BEFORE: Play a video by path. 32 | video.play(videoPath); 33 | 34 | // AFTER: Load a video by path, then play if load was successful. 35 | // You can also run video.load() in advance before playing the video. 36 | if (video.load(videoPath)) video.play(); 37 | ``` 38 | 39 | # Options Menu Changes 40 | 41 | The options menu received a minor refactor internally. The `pages` list was moved to its own class, which changes the code needed to access the "Preferences" menu (mainly done to add custom preferences). 42 | 43 | We would like to standardize the process of adding custom user preferences to mods in the future eventually, but in the meantime you can make the necessary tweaks: 44 | 45 | ```haxe 46 | // BEFORE: Retrieve the value from the page Map. 47 | if (Std.isOfType(currentState, OptionsState)) { 48 | var preferencesPage = currentState.pages.get("preferences"); 49 | 50 | // Create a new option. 51 | prefs.createPrefItemCheckbox(...); 52 | } 53 | 54 | // AFTER: Retrieve the value from the page Map, which is now inside a Codex. 55 | if (Std.isOfType(currentState, OptionsState)) { 56 | var preferencesPage = currentState.optionsCodex.pages.get("preferences"); 57 | 58 | // Create a new option. 59 | prefs.createPrefItemCheckbox(...); 60 | } 61 | ``` 62 | 63 | # Sticker Changes 64 | 65 | v0.6.0 rewrote how stickers get used by the game (and v0.6.3 rewrote it again but better this time). Any existing mods that provided stickers will probably break. 66 | 67 | New or updating mods looking to add, remove, or replace stickers should consult the [custom Sticker Packs documentation](../07-custom-sticker-packs/07-00-custom-sticker-packs.md) 68 | -------------------------------------------------------------------------------- /src/02-custom-songs-and-custom-levels/02-03-adding-a-custom-level.md: -------------------------------------------------------------------------------- 1 | # Adding a Custom Level 2 | 3 | Add a new file, with whatever internal name you like, into your mods folder, under the `data/levels/` directory, with the `.json` file extension. 4 | 5 | *NOTE*: Keep in mind that if your internal name is the same as a week from the base game, or of another mod, they will overlap each other and you may have unexpected results! 6 | 7 | We'll end up with something like this. 8 | 9 | ``` 10 | -mods 11 | |-myMod 12 | |-data 13 | |-levels 14 | |-myweek.json 15 | |-songs 16 | |-mychart 17 | |-mychart-metadata.json 18 | |-mychart-chart.json 19 | |-songs 20 | |-mychart 21 | |-Inst.ogg 22 | |-Voices-bf.ogg 23 | |-Voices-pico.ogg 24 | |-images 25 | |-storymenu 26 | |-titles 27 | |-myweek.png 28 | |-_polymod_meta.json 29 | ``` 30 | 31 | Your custom week's JSON file will look something like the following: 32 | 33 | ```json 34 | { 35 | "version": "1.0.0", 36 | "name": "MY CUSTOM WEEK", 37 | "titleAsset": "storymenu/titles/myweek", 38 | "background": "#F9CF51", 39 | "songs": ["mychart"], 40 | "visible": true, 41 | "props": [ 42 | { 43 | "assetPath": "storymenu/props/dad", 44 | "scale": 1.0, 45 | "offsets": [100, 60], 46 | "animations": [ 47 | { 48 | "name": "idle", 49 | "prefix": "idle0", 50 | "frameRate": 24 51 | } 52 | ] 53 | }, 54 | { 55 | "assetPath": "storymenu/props/bf", 56 | "scale": 1.0, 57 | "offsets": [150, 80], 58 | "animations": [ 59 | { 60 | "name": "idle", 61 | "prefix": "idle0", 62 | "frameRate": 24 63 | }, 64 | { 65 | "name": "confirm", 66 | "prefix": "confirm0", 67 | "frameRate": 24 68 | } 69 | ] 70 | } 71 | ] 72 | } 73 | ``` 74 | 75 | There's a lot of info here! Let's break it down: 76 | 77 | - `version`: The version number for the Level data file format. Leave this at `1.0.0`. 78 | - `name`: The readable name for the Level, as displayed at the top right of the Story Menu. 79 | - `titleAsset`: The asset to use for the level name in the list of level, relative to the `images` folder in your mod folder. 80 | - `background`: The background to use for the level. `#F9CF51` is the classic yellow, but this field takes either a color code OR an image file path (relative to the `images` folder in your mod folder). 81 | - `songs`: A list of song IDs to include in the week. 82 | - `visible`: Whether this story level is visible in the Story Menu. 83 | - `props`: Data for the props to display on the Story Menu when the level is selected. For example, Week 1 will display Daddy Dearest, Boyfriend, and Girlfriend. 84 | 85 | When the game starts, it queries the list of available levels by looking in the `data/levels` folder for JSON files, which it then uses to populate the Story Menu, and then the Freeplay Menu (in non-alphabetical views, songs in Freeplay appear in order by what level they are included in). 86 | 87 | If you want your custom song to only show up in Freeplay, you can just create a custom week and set the `visible` property to false, and the songs will show up in Freeplay! 88 | -------------------------------------------------------------------------------- /src/09-migration/09-01-0.1.0-to-0.5.0.md: -------------------------------------------------------------------------------- 1 | # Migrating from v0.1.0 to v0.5.0 2 | 3 | ## Rewriting JSON merge files 4 | 5 | In v0.5.0, the system for merging into JSON files was fundamentally reworked. Any mods which used this system previously need to refactor these files in order to work properly. 6 | 7 | In your mod's `_merge` folder, look for any `json` files and rewrite their contents. 8 | 9 | ```jsonc 10 | // This is the format used by older versions of the game. 11 | { 12 | "merge": [ 13 | // Set `data.difficulty` to "super_hard" 14 | { 15 | "target": "data.difficulty", 16 | "payload": "super_hard" 17 | }, 18 | // In the second element of the `data.nested.enemies` array, set `weapon` to "minigun" 19 | { 20 | "target": "data.nested.enemies[1].weapon", 21 | "payload": "minigun" 22 | } 23 | ] 24 | } 25 | ``` 26 | 27 | ```jsonc 28 | // This is the format which will be used starting with v0.5.0. 29 | [ 30 | { "op": "replace", "path": "/playData/characters/opponent", "value": "monster" }, // Replace the value of opponent with monster. 31 | { "op": "add", "path": "/playData/characters/girlfriend", "value": "nene" }, // Add a new key girlfriend with the value Nene. 32 | { "op": "add", "path": "/playData/difficulties/1", "value": "funky" }, // Add a new value funky to the difficulty array, after easy 33 | { "op": "add", "path": "/playData/difficulties/-", "value": "expert" }, // Add a new value expert to the end of the difficulty array. 34 | { "op": "remove", "path": "/playData/garbageValue" }, // Remove the key garbageValue from the data entirely 35 | { "op": "test", "path": "/playData/garbageValue", "value": 37 } // Test that a given value is in the JSON. If this operation fails, the patches will be rejected. 36 | ] 37 | ``` 38 | 39 | More information about this new system can be found at [Merging Files](../10-appending-and-merging-files/10-02-merging-files.md). 40 | 41 | ## Removal of Flixel UI 42 | 43 | [Flixel UI](https://github.com/haxeflixel/flixel-ui) is a library used for developing creating UI elements and managing UI events in HaxeFlixel. In the past, this was used to power the UI of the Chart Editor, but the development team regularly found the library to be frustrating to use, and eventually switched to [HaxeUI](https://github.com/haxeui) for most of its user interfaces. 44 | 45 | In Friday Night Funkin' v0.5.0, the last places that the game used this library were refactored, and the game now exclusively uses a combination of manual sprite placement and HaxeUI for its user interfaces. As a result, Flixel UI was removed as a dependency. 46 | 47 | Any mods which utilized functions and classes provided by Flixel UI may need refactoring to compensate. 48 | 49 | ## Updating the API version 50 | 51 | Once all the migration steps above have been performed, the last step is to modify your mod's API version string. In your mod's `_polymod_meta.json` file, locate the `"api_version"` property and set it to `"0.5.0"`. 52 | 53 | ```jsonc 54 | { 55 | // ... 56 | 57 | // Change this value from "0.1.0" to "0.5.0" 58 | "api_version": "0.5.0", 59 | 60 | // ... 61 | } 62 | ``` 63 | 64 | NOTE: For versions of Friday Night Funkin' between v0.3.0 and v0.4.1, the modding system always looked for `"0.1.0"` for the `api_version` and refused to load the mod in all other cases. With the v0.5.0 update, this version will now change to match the game version with every update, but only breaking changes will forcibly disable mods. Those breaking changes will be documented on this page. 65 | -------------------------------------------------------------------------------- /src/02-custom-songs-and-custom-levels/02-05-adding-variations-to-existing-songs.md: -------------------------------------------------------------------------------- 1 | # Adding Variations to Existing Songs 2 | 3 | Through modding, it is possible to add new variations to existing songs. This is great for adding a new difficulty or remix to an existing song (even if that song is from another mod). 4 | 5 | ## Obtaining Required Files 6 | 7 | The first step is to compose a new remix for the song. If you're making a playable character remix, the composer will have to manually make sure the original vocals line up if you want the option to use alternate instrumentals. 8 | 9 | You then have to chart this remix. When you're done, you should have an `Inst.ogg` file, two `Voices` OGG files, a `metadata.json` and a `chart.json`. 10 | 11 | ## Placing the Files 12 | 13 | Next, place the assets in the correct spots in our mod folder! Rename each of the files, adding a variation ID of your choice to the end (so if you're making an erect remixes, rename `Inst.ogg` to `Inst-erect.ogg`): 14 | 15 | ``` 16 | -mods 17 | |-myMod 18 | |-data 19 | |-songs 20 | |-mychart 21 | |-mychart-metadata-erect.json 22 | |-mychart-chart-erect.json 23 | |-songs 24 | |-mychart 25 | |-Inst-erect.ogg 26 | |-Voices-bf-erect.ogg 27 | |-Voices-pico-erect.ogg 28 | |-_polymod_meta.json 29 | ``` 30 | 31 | ## Registering the Variation in the JSON Data 32 | 33 | Each chart includes a `songVariations` array, which lets the game know which variations the song has available so it can query their respective metadata files. In order to get the game to load your custom variation, you need to modify the `metadata.json` file for the song's chart, so the game knows that variation exists. 34 | 35 | ### If the song is from your own mod 36 | 37 | If the base song you're adding the remix to is from your own mod, you can just add the variation to the `metadata.json` for your chart. 38 | 39 | Add your variation ID to the `playData.songVariations` array (creating the key if it doesn't exist). 40 | 41 | ```json 42 | { 43 | "playData": { 44 | "songVariations": ["erect"] // Add your new variation to this array. 45 | // ... 46 | } 47 | // ... 48 | } 49 | ``` 50 | 51 | ### If the song is from the base game, or a different mod 52 | 53 | If the base song you're adding to is from a different mod, you don't want to replace the underlying data in case it changes. You want to instead apply a JSON Patch to the file (which Polymod provides the ability to do). 54 | 55 | Create a `_merge` folder in your mod folder, then create a file within that directory whose path matches the one you want to patch, like so: 56 | 57 | ``` 58 | -mods 59 | |-myMod 60 | |-_merge 61 | |-data 62 | |-songs 63 | |-mychart 64 | |-mychart-metadata.json 65 | ``` 66 | 67 | Then we apply a simple patch, which adds a new value to the `playData.songVariations` array. Edit the JSON file and add these contents: 68 | 69 | ```json 70 | [ 71 | { "op": "add", "path": "/playData/songVariations/-", "value": "erect" } // Add a new value erect to the end of the songVariations array. 72 | ] 73 | ``` 74 | 75 | The patching system is very flexible; it works on any JSON file (base game or provided by another mod) and has support for advanced behavior. See [Merging Files](../10-appending-and-merging-files/10-02-merging-files.md) for more information. 76 | 77 | ## Conclusion 78 | 79 | Now, when you start the game, your additional variation should be available in-game! 80 | 81 | If you created a character remix, make sure the player character for the remix is included in the `ownedCharacters` data for your custom playable character. See [Custom Playable Characters](../05-custom-playable-characters/05-00-custom-playable-characters.md) for more info. 82 | -------------------------------------------------------------------------------- /src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Friday Night Funkin' Modding Documentation 2 | 3 | [Friday Night Funkin' Modding](title-page.md) 4 | 5 | # Getting Started 6 | - [Fundamentals](01-fundamentals/01-00-fundamentals.md) 7 | - [The Metadata File](01-fundamentals/01-01-the-metadata-file.md) 8 | - [Loading the Mod In-Game](01-fundamentals/01-02-loading-the-mod-in-game.md) 9 | - [Asset Replacement and Additons](01-fundamentals/01-03-asset-replacement-and-additions.md) 10 | - [Mod Load Order](01-fundamentals/01-04-mod-load-order.md) 11 | - [Hot Reloading](01-fundamentals/01-05-hot-reloading.md) 12 | - [Chapter Conclusion](01-fundamentals/01-06-chapter-conclusion.md) 13 | 14 | # Custom Assets 15 | - [Custom Songs and Custom Levels](02-custom-songs-and-custom-levels/02-00-custom-songs-and-custom-levels.md) 16 | - [Creating a Chart](02-custom-songs-and-custom-levels/02-01-creating-a-chart.md) 17 | - [Adding the Custom Song](02-custom-songs-and-custom-levels/02-02-adding-the-custom-song.md) 18 | - [Adding a Custom Level](02-custom-songs-and-custom-levels/02-03-adding-a-custom-level.md) 19 | - [What Are Variations?](02-custom-songs-and-custom-levels/02-04-what-are-variations.md) 20 | - [Adding Variations to Existing Songs](02-custom-songs-and-custom-levels/02-05-adding-variations-to-existing-songs.md) 21 | 22 | - [Custom Characters](03-custom-characters/03-00-custom-characters.md) 23 | - [Character Assets](03-custom-characters/03-01-character-assets.md) 24 | - [Creating a Character](03-custom-characters/03-02-creating-a-character.md) 25 | - [Using a Character in a Song](03-custom-characters/03-02-using-a-character-in-a-song.md) 26 | - [Fixing Character Offsets](03-custom-characters/03-03-fixing-character-offsets.md) 27 | 28 | - [Custom Stages](04-custom-stages/04-00-custom-stages.md) 29 | - [Creating a Stage](04-custom-stages/04-01-creating-a-stage.md) 30 | - [Using a Stage in a Song](04-custom-stages/04-02-using-a-stage-in-a-song.md) 31 | 32 | - [Custom Playable Characters](05-custom-playable-characters/05-00-custom-playable-characters.md) 33 | - [Required Assets](05-custom-playable-characters/05-01-required-assets.md) 34 | - [Creating a Playable Character](05-custom-playable-characters/05-02-creating-a-playable-character.md) 35 | 36 | - [Custom Note Styles](06-custom-notestyles/06-00-custom-notestyles.md) 37 | - [Creating a Note Style](06-custom-notestyles/06-01-creating-a-notestyle.md) 38 | - [Using a Note Style in a Song](06-custom-notestyles/06-02-using-a-notestyle-in-a-song.md) 39 | 40 | - [Custom Sticker Packs](07-custom-sticker-packs/07-00-custom-sticker-packs.md) 41 | - [Creating a new Sticker Pack](07-custom-sticker-packs/07-01-creating-a-new-sticker-pack.md) 42 | - [Modifying an Existing Sticker Pack](07-custom-sticker-packs/07-02-modifying-an-existing-sticker-pack.md) 43 | - [Using a Custom Sticker Pack](07-custom-sticker-packs/07-03-using-a-custom-sticker-pack.md) 44 | 45 | # Mod API Systems 46 | 47 | - [Migration](09-migration/09-00-migrating-mods-to-newer-versions.md) 48 | - [v0.1.0 to v0.5.0](09-migration/09-01-0.1.0-to-0.5.0.md) 49 | - [v0.5.0 to v0.6.3](09-migration/09-02-0.5.0-to-0.6.3.md) 50 | 51 | - [Appending And Merging Files](10-appending-and-merging-files/10-00-appending-and-merging-files.md) 52 | - [Appending Files](10-appending-and-merging-files/10-01-appending-files.md) 53 | - [Merging Files](10-appending-and-merging-files/10-02-merging-files.md) 54 | 55 | # HScript 56 | 57 | - [Using HScript](20-using-hscript/20-00-using-hscript.md) 58 | - [What is HScript?](20-using-hscript/20-01-what-is-hscript.md) 59 | - [Scripted Classes](21-scripted-classes/21-00-scripted-classes.md) 60 | - [Scripted Songs](21-scripted-classes/21-01-scripted-songs.md) 61 | 62 | # HScript (Advanced) 63 | 64 | - [WIP: Scripted Modules](30-scripted-modules/30-00-scripted-modules.md) 65 | - [WIP: Custom Shaders](31-custom-shaders/31-00-custom-shaders.md) 66 | 67 | --- 68 | 69 | [Writing And Contributing to This Book](writing-and-contributing-to-this-book.md) 70 | -------------------------------------------------------------------------------- /src/10-appending-and-merging-files/10-02-merging-files.md: -------------------------------------------------------------------------------- 1 | # Merging 2 | 3 | Merging files into a data file is required for more complicated operations, such as inserting data somewhere other than the start of the document, replacing certain data with new data, or even deleting certain data from the document. 4 | 5 | By using `_merge` files, rather than replacing the data files entirely, you make your mod fully compatible with both changes to the base game and to changes made by other mods. Merge files are applied in mod load order, meaning that multiple mods can make changes to the same file without any conflicts! 6 | 7 | ### Merging into TXT Files 8 | **TODO** 9 | 10 | ### Merging into CSV/TSV Files 11 | 12 | CSV and TSV files can be merged as well. In this case, the mod loader will look for any rows in the base file whose first cell matches the same value as those in the merge file, and replace them with the rows from the merge file. 13 | 14 | ### Merging into XML Files 15 | 16 | *NOTE: The behavior of Merging XML files may change significantly in the near future.* 17 | 18 | For XML, you must create an XML document containing the desired, values, with additional information to inform Polymod about where to insert it. 19 | 20 | Say you have a big complicated XML file at `data/stuff.xml` with lots of nodes: 21 | 22 | ```xml 23 | 24 | 25 | 26 | 27 | 28 | 29 | ``` 30 | 31 | And you want it to say this instead: 32 | 33 | ```xml 34 | 35 | 36 | 37 | 38 | 39 | 40 | ``` 41 | 42 | Basically we want to change this one tag from this: 43 | 44 | ```xml 45 | 46 | ``` 47 | 48 | to this: 49 | ```xml 50 | 51 | ``` 52 | 53 | This is the file you would put in `//data/stuff.xml`: 54 | ```xml 55 | 56 | 57 | 58 | 59 | 60 | 61 | ``` 62 | 63 | This file contains both data and merge instructions. The `` child tag tells the mod loader what to do, and will not be included in the final data. The actual payload is just this: 64 | 65 | ```xml 66 | 67 | ``` 68 | 69 | The `` tag instructs the mod loader thus: 70 | 71 | * Look for any tags with the same name as my parent (in this case, ``) 72 | * Look within said tags for a `key` attribute (in this case, one named `"id"`) 73 | * Check if the key's value matches what I'm looking for (in this case, `"difficulty"`) 74 | 75 | As soon as it finds the first match, it stops and merges the payload with the specified tag. Any attributes will be added to the base tag (overwriting any existing attributes with the same name, which in this case changes values from "easy" to just "super_hard", which is what we want). Furthermore, if the payload has child nodes, all of its children will be merged with the target tag as well. 76 | 77 | ### Merging into JSON Files 78 | 79 | Merging into JSON files is done using a [JSON Patch](https://jsonpatch.com/) document. (NOTE: This significantly differs from JSON patch files created for v0.4.1 and earlier, which used a different system that honestly kinda sucked). 80 | 81 | Say we have a JSON data file `data/songs/mysong-metadata.json` like below: 82 | 83 | ```json 84 | { 85 | "version": "2.1.0", 86 | "playData": { 87 | "characters": { 88 | "player": "bf", 89 | "opponent": "dad" 90 | }, 91 | "difficulties": [ 92 | "easy", "normal", "hard" 93 | ], 94 | "garbageValue": 37, 95 | "stage": "mainStage" 96 | } 97 | } 98 | ``` 99 | 100 | We can modify the above data with a document `mods/mymod/_merge/data/songs/mysong-metadata.json`: 101 | 102 | ```jsonc 103 | [ 104 | { "op": "replace", "path": "/playData/characters/opponent", "value": "monster" }, // Replace the value of opponent with monster. 105 | { "op": "add", "path": "/playData/characters/girlfriend", "value": "nene" }, // Add a new key girlfriend with the value Nene. 106 | { "op": "add", "path": "/playData/difficulties/1", "value": "funky" }, // Add a new value funky to the difficulty array, after easy 107 | { "op": "add", "path": "/playData/difficulties/-", "value": "expert" }, // Add a new value expert to the end of the difficulty array. 108 | { "op": "remove", "path": "/playData/garbageValue" }, // Remove the key garbageValue from the data entirely 109 | { "op": "test", "path": "/playData/garbageValue", "value": 37 } // Test that a given value is in the JSON. If this operation fails, the patches will be rejected. 110 | ] 111 | ``` 112 | 113 | The `op`erations supported are `add`, `remove`, `replace`, `move`, `copy`, and `test`. If any operation in a JSON Patch document fails, all of the modifications in that file will be reverted. 114 | 115 | The `add`, `replace`, and `test` operations require a `value` key (which can be any JSON data, including an array or object), and the `move` and `copy` operations require a `from` key, which is a path value indicating where to move or copy from. 116 | 117 | The `path` must be a string of either property names or array indexes (starting at 0), starting with and separated by slashes (`/`). For example, `/playData/characters/opponent`. 118 | 119 | The `path` may also be a JSONPath string, which allows robustly specifying a target path with support for filtering logic. You can read more here: https://goessner.net/articles/JsonPath/ 120 | -------------------------------------------------------------------------------- /src/21-scripted-classes/21-00-scripted-classes.md: -------------------------------------------------------------------------------- 1 | # Scripted Classes 2 | 3 | Funkin's implementation of HScript uses a system of scripted classes. To create a scripted class, create an `.hxc` file in your `mods/mymod/scripts/` folder, and, using Haxe syntax, create a new class which extends the base class for the type of object you are scripting, like so: 4 | 5 | ```haxe 6 | // Remember to import each class you want to reference in your script! 7 | import funkin.play.song.Song; 8 | 9 | // Choose a name for your scripted class that will be unique 10 | // Also specify the class you are extending, we choose Song here. 11 | // This script's behaviors will extend the default behavior of the song. 12 | class BallisticSong extends Song { 13 | public function new() { 14 | // You have to call the super constructor for the class you are extending, which may have different parameters. 15 | // Check the specific documentation for more info. 16 | super('ballistic'); 17 | } 18 | } 19 | ``` 20 | 21 | ## List of Scriptable Classes 22 | 23 | There is a predefined list of classes which the game has set up to be scriptable, and will automatically load and execute when relevant. More of these will be added in the future. 24 | 25 | - `funkin.play.song.Song` for providing unique behavior to custom songs, including playing cutscenes and other stuff. See [Scripted Songs](21-scripted-classes/21-01-scripted-songs.md). 26 | - See also [Video Cutscenes](21-scripted-classes/21-03-video-cutscenes.md), [Ingame Cutscenes](21-scripted-classes/21-04-ingame-cutscenes.md), and [Dialogue Cutscenes](21-scripted-classes/21-05-dialogue-cutscenes.md) 27 | - `funkin.play.character.BaseCharacter` for providing unique behavior to custom characters (such as playing custom animations in certain circumstances). See [Scripted Characters](21-scripted-classes/21-05-scripted-characters.md). 28 | - Note that you need to choose the correct subclass of this class for the animation type of your character! 29 | - `funkin.play.character.SparrowCharacter` is used for characters that have Sparrow spritesheet animations. 30 | - `funkin.play.character.MultiSparrowCharacter` is used for characters that have several Sparrow spritesheet animations to combine into one character. 31 | - `funkin.play.character.PackerCharacter` is used for characters that have Packer spritesheet animations. 32 | - `funkin.play.character.AnimateAtlasCharacter` is used for characters that have Adobe Animate texture atlases. 33 | - `funkin.play.character.BaseCharacter` has empty stubs for all the rendering and animation handlers, and is only useful for people who want to reimplement their character's animation system by hand. 34 | - `funkin.play.stage.Stage` for providing unique behavior to custom stages, such as creating custom moving props and defining when props animate or when sound effects play in sync with the stage. See [Scripted Stages](21-scripted-classes/24-06-scripted-stages.md). 35 | - `funkin.ui.story.Level` for providing unique behavior to levels in Story Mode. See [Scripted Story Levels](21-scripted-classes/25-07-scripted-story-levels.md). 36 | - `funkin.play.notes.notekind.NoteKind` for providing unique visuals and behavior to certain kinds of notes, which can then be placed in the Chart Editor. See [Custom Note Kinds](21-scripted-classes/26-00-custom-note-kinds.md). 37 | - `funkin.play.event.SongEvent` for creating custom Song Events, which you can place in the Chart Editor and which perform game actions when they are reached. See [Custom Song Events](21-scripted-classes/28-00-custom-note-kinds.md) 38 | - `funkin.ui.freeplay.charselect.PlayableCharacter` for providing unique behavior to custom playable characters. See [Scripted Playable Characters](21-scripted-classes/25-10-scripted-playable-characters.md) 39 | - `funkin.ui.freeplay.FreeplayStyle` for defining the sprites and colors used by the Freeplay menu when a given character is selected. See [WIP] 40 | - `funkin.play.notes.notestyle.NoteStyle` for modifying the behavior of custom note styles. See [WIP] 41 | - `funkin.play.cutscene.dialogue.Conversation` for providing unique behavior to custom dialogue conversations. See [Dialogue Cutscenes](21-scripted-classes/21-05-dialogue-cutscenes.md) 42 | - `funkin.play.cutscene.dialogue.DialogueBox` for providing unique behavior to custom dialogue boxes used in conversations. See [Dialogue Cutscenes](21-scripted-classes/21-05-dialogue-cutscenes.md) 43 | - `funkin.play.cutscene.dialogue.Speaker` for providing unique behavior to custom speakers used in conversations. See [Dialogue Cutscenes](21-scripted-classes/21-05-dialogue-cutscenes.md) 44 | - `funkin.ui.freeplay.Album` for defining custom behavior for Freeplay Albums. See [WIP] 45 | 46 | There is also `funkin.modding.module.Module` for custom scripted Modules, which are scripts which receive events everywhere, rather than only in a specific context. See [Scripted Modules](30-scripted-modules/30-00-scripted-modules.md) for more information on how these work. 47 | 48 | There are also scripted classes are also set up to be scriptable, but will only be useful if they're accessed from another script. Expect more of these to be added in the future. 49 | 50 | - `funkin.graphics.FunkinSprite` for basic static or animated sprites. 51 | - `funkin.graphics.adobeanimate.FlxAtlasSprite` for generic sprites which use Adobe Animate texture atlases 52 | - `flixel.group.FlxSpriteGroup` for groups of sprites which are included inside a state and manipulated together 53 | - `funkin.ui.MusicBeatState` for groups of sprites which represents a given game state. Includes additional utilities for handling script events. 54 | - `funkin.ui.MusicBeatSubState` for groups of sprites representing a substate of an existing state 55 | - `flixel.addons.display.FlxRuntimeShader` for custom GLSL shaders 56 | - `funkin.play.stage.Bopper` for sprites which will play an idle animation to the beat of the music when they are part of a Stage. 57 | - `flixel.FlxSprite` for basic static or animated sprites. Use this only if you can't use FunkinSprite. 58 | - `flixel.FlxState` for basic groups of sprites which represent a given game state. Use this only if you can't use MusicBeatState. 59 | - `flixel.FlxSubState` for groups of sprites representing a substate of an existing state 60 | -------------------------------------------------------------------------------- /src/03-custom-characters/03-02-creating-a-character.md: -------------------------------------------------------------------------------- 1 | # Creating a Character 2 | 3 | A custom character requires creating a new JSON file in the `data/characters` folder. Below is an example 4 | of Girlfriend's character data file, from `assets/data/characters/gf.json`[^gfsource] 5 | 6 | ```json 7 | { 8 | "version": "1.0.0", 9 | "name": "Girlfriend", 10 | "renderType": "sparrow", 11 | "assetPath": "characters/GF_assets", 12 | "startingAnimation": "danceRight", 13 | "singTime": 8.0, 14 | "animations": [ 15 | { 16 | "name": "danceLeft", 17 | "prefix": "GF Dancing Beat", 18 | "frameIndices": [30, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], 19 | "offsets": [0, -9] 20 | }, 21 | { 22 | "name": "danceRight", 23 | "prefix": "GF Dancing Beat", 24 | "frameIndices": [ 25 | 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29 26 | ], 27 | "offsets": [0, -9] 28 | }, 29 | { 30 | "name": "sad", 31 | "prefix": "gf sad", 32 | "frameIndices": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], 33 | "offsets": [2, -21] 34 | }, 35 | { 36 | "name": "singLEFT", 37 | "prefix": "GF left note", 38 | "offsets": [0, -19] 39 | }, 40 | { 41 | "name": "singDOWN", 42 | "prefix": "GF Down Note", 43 | "offsets": [0, -20] 44 | }, 45 | { 46 | "name": "singUP", 47 | "prefix": "GF Up Note", 48 | "offsets": [0, 4] 49 | }, 50 | { 51 | "name": "singRIGHT", 52 | "prefix": "GF Right Note", 53 | "offsets": [0, -20] 54 | }, 55 | { 56 | "name": "cheer", 57 | "prefix": "GF Cheer", 58 | "offsets": [0, 0] 59 | }, 60 | { 61 | "name": "combo50", 62 | "prefix": "GF Cheer", 63 | "offsets": [0, 0] 64 | }, 65 | { 66 | "name": "drop70", 67 | "prefix": "gf sad", 68 | "frameIndices": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], 69 | "offsets": [2, -21] 70 | }, 71 | { 72 | "name": "hairBlow", 73 | "prefix": "GF Dancing Beat Hair blowing", 74 | "frameIndices": [0, 1, 2, 3], 75 | "offsets": [45, -8] 76 | }, 77 | { 78 | "name": "hairFall", 79 | "prefix": "GF Dancing Beat Hair Landing", 80 | "frameIndices": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 81 | "offsets": [0, -9] 82 | }, 83 | { 84 | "name": "scared", 85 | "prefix": "GF FEAR", 86 | "offsets": [-2, -17] 87 | } 88 | ] 89 | } 90 | ``` 91 | 92 | The available fields are: 93 | - `version`: The version number for the Character data file format. Leave this at `1.0.0`. 94 | - `name`: The readable name for the character, used in places like the Chart Editor. 95 | - `renderType`: The render type. One of `sparrow`, `packer`, `animateatlas`, `multisparrow`. 96 | - `assetPath`: The main asset path to use for this character, relative to the `images` directory in your mod folder. 97 | - For the `sparrow` asset type, this must point to the path where the `xml` and `png` are located, without the file extension. 98 | - For the `packer` asset type, this must point to the path where the `txt` and `png` are located, without the file extension. 99 | - For the `animateatlas` asset type, this must point to the folder where the `Animation.json` and any spritemaps are located. 100 | - For the `multisparrow` asset type, point to the path where your main Sparrow spritesheet is located. On each animations which uses a different Sparrow spritesheet from the main one, add the `assetPath` key to that specific animation. 101 | - `scale` *(currently buggy)*: Specify the size of the character relative to the original size. For example, `2.0` makes the sprite twice as big. Optional, defaults to `1.0`. 102 | - `healthIcon`: Data for the health icon to display in-game. For example, Boyfriend will obviously use Boyfriend's health icon. Optional, defaults its ID to character's ID. 103 | - `death`: Data for the death screen to use, when the character reaches `0` health. Optional, doesn't default to a specific object. 104 | - `offsets`: The global offset to the character's position, in pixels. Optional, defaults to `[0, 0]`. 105 | - Use an array of two decimal values, the first for horizontal position and the second for vertical position. 106 | - `cameraOffsets`: The amount to offset the camera by while focusing on the character. Optional, default value focuses on the character directly. 107 | - Use an array of two decimal values, the first for horizontal position and the second for vertical position. 108 | - `isPixel`: Specify whether to disable texture smoothing for the character. Optional, defaults to `false`. 109 | - `danceEvery`: The frequency at which the character will play its idle animation, in beats. Optional, defaults to `1`. 110 | - Increasing this number will make the character dance less often. 111 | - `flipX`: Whether to flip the whole sprite horizontally in-game. Useful for characters that could also be played (Pico). Optional, defaults to `false`. 112 | - `startingAnimation`: The animation for the character to play when they are first loaded in. Optional, defaults to `idle`. 113 | - `singTime`: The amount of time, in steps, for a character to keep singing after they let go of a note. Optional, defaults to `8`. 114 | - Decrease this if the character seems to hold their poses for too long after their section is done. 115 | - Increase this if the character resets to the idle animation in the middle of their singing animations. 116 | - `animations`: A list of animation data objects for the character. 117 | 118 | Health Icon data is structured like so: 119 | - `id`: The ID to use for the health icon, defaults to character's ID. 120 | - `scale`: Specify the size of the health icon relative to the original size. For example, `2.0` makes the sprite twice as big. Optional, defaults to `1.0`. 121 | - `flipX`: Whether to flip the whole sprite horizontally in-game. Optional, defaults to `false`. 122 | - `isPixel`: Specify whether to disable texture smoothing for this characters health icon. Optional, defaults to `false`. 123 | - `offsets`: The offset of the health icon, in pixels. Optional, defaults to `[0, 0]`. 124 | - Use an array of two decimal values, the first for horizontal position and the second for vertical position. 125 | 126 | Death data is structured like so: 127 | - `cameraOffsets`: The amount to offset the camera by while focusing on this character as they die. Optional, defaults to `[0, 0]`. 128 | - Default value focuses on the character's graphic midpoint. 129 | - Use an array of two decimal values, the first for horizontal position and the second for vertical position. 130 | - `cameraZoom`: The amount to zoom the camera by while focusing on this character as they die. Optional, defaults to `1`. 131 | - `preTransitionDelay`: The delay between when the character reaches `0` health and when the death animation plays. Optional, defaults to `0`. 132 | 133 | Animation data is structured like so: 134 | - `name`: The internal animation name for the game to use. 135 | - `prefix`: The animation name as specified by your spritesheet. 136 | - For Sparrow or Packer, check inside the data file to see what each set of frames is named, and use that as the prefix, excluding the frame numbers at the end. 137 | - For Animate Atlases, use either the frame label or the symbol name of the animation you want to play. 138 | - `offsets`: Some animations may need their positions to be corrected relative to the idle animation. 139 | - Use an array of two decimal values, the first for horizontal position and the second for vertical position. 140 | - `looped`: Whether to loop this animation in-game. If false, the animation will pause when it ends, until the game commands the character to do something else. 141 | - `flipX`: Whether to flip the sprites of this animation horizontally in-game. 142 | - `flipY`: Whether to flip the sprites of this animation vertically in-game. 143 | - `frameRate`: A frame rate value, defaulting to `24`. 144 | - `frameIndices`: Optionally specify an array of frame numbers (starting at frame 0!) to use from a given prefix for this animation. 145 | - For example, specifying `[0, 1, 2, 3]` will make this animation only use the first 4 frames of the given prefix. 146 | - `assetPath`: For the `multisparrow` asset type specifically. Define a secondary Sparrow spritesheet which will be loaded, and which contains the frames for this animation. 147 | 148 | The animation names the game uses by default are: 149 | - `idle`: For the idle animation. 150 | - `danceLeft` and `danceRight`: Supercedes the idle animation with one that toggles between two animations. 151 | - `singLEFT`, `singDOWN`, `singUP`, `singRIGHT`: The animations for playing notes, when the character is a player or opponent. 152 | - `singLEFTmiss`, `singDOWNmiss`, `singUPmiss`, `singRIGHTmiss`: The animations for missing notes, when the character is a player. 153 | - Adding a new singing animation with the name of an existing animation with `-hold` at the end will play the animation after the first one ends, while the character is still singing. 154 | - As a good example, you can copy the `singLEFT` animation to make a `singLEFT-hold` animation, which has `looped` as true and `frameIndices` as the last few frames of the singing animation. 155 | - Adding a new singing animation with the name of an existing animation with `-end` at the end will play an animation before returning to idle. 156 | - For example, you can define a new `singLEFT-end` animation to cleanly transition into the idle animation. 157 | - You can add other animations by name, but you'll have to play them with a script, or a `Play Animation` song event in the Chart Editor. 158 | 159 | When the game starts, it queries the list of possible characters by searching in the `data/characters` folder for JSON files. This gets used to preload data which is used later when the character is loaded in a stage. 160 | 161 | ## Replacing/Reskinning an Existing Character 162 | 163 | As a short aside, you can create a JSON with the same filename as an existing character (from the base game, or from a mod if your mod loads after it) and it will replace it. This can be used to create more elaborate reskins for characters, such as ones that use a different render type. 164 | 165 | 166 | [^gfsource]: -------------------------------------------------------------------------------- /src/05-custom-playable-characters/05-02-creating-a-playable-character.md: -------------------------------------------------------------------------------- 1 | # Creating a Playable Character 2 | 3 | A custom playable character requires creating a new JSON file in the `data/characters` folder. Below is an example of Pico's character data file, from `assets/data/players/pico.json`[^picosource] 4 | 5 | ```json 6 | { 7 | "version": "1.0.0", 8 | "name": "Pico", 9 | "ownedChars": [ 10 | "pico", 11 | "pico-playable", 12 | "pico-blazin", 13 | "pico-christmas", 14 | "pico-dark" 15 | ], 16 | "showUnownedChars": false, 17 | "unlocked": true, 18 | "freeplayStyle": "pico", 19 | "freeplayDJ": { 20 | "assetPath": "freeplay/freeplay-pico", 21 | "text1": "PICO", 22 | "text2": "GOD DAMN HE DOWN ON THE NUT", 23 | "text3": "ZEBOIM DAMN IMA NUT", 24 | "fistPump": { 25 | "introStartFrame": 0, 26 | "introEndFrame": 4, 27 | 28 | "loopStartFrame": 4, 29 | "loopEndFrame": -1, 30 | 31 | "introBadStartFrame": 0, 32 | "introBadEndFrame": 0, 33 | 34 | "loopBadStartFrame": 0, 35 | "loopBadEndFrame": -1 36 | }, 37 | "charSelect": { 38 | "transitionDelay": 0.45 39 | }, 40 | "animations": [ 41 | { 42 | "name": "intro", 43 | "prefix": "pico dj intro", 44 | "offsets": [631.7, 362.6] 45 | }, 46 | { 47 | "name": "idle", 48 | "prefix": "Pico DJ", 49 | "offsets": [625, 360] 50 | }, 51 | { 52 | "name": "idleEasterEgg", 53 | "prefix": "Pico DJ afk", 54 | "offsets": [625, 360] 55 | }, 56 | { 57 | "name": "confirm", 58 | "prefix": "Pico DJ confirm", 59 | "offsets": [625, 360] 60 | }, 61 | { 62 | "name": "fistPump", 63 | "prefix": "pico cheer", 64 | "offsets": [975, 260] 65 | }, 66 | { 67 | "name": "loss", 68 | "prefix": "Pico DJ loss", 69 | "offsets": [625, 360] 70 | }, 71 | { 72 | "name": "charSelect", 73 | "prefix": "Pico DJ to CS", 74 | "offsets": [625, 360] 75 | } 76 | ] 77 | }, 78 | "charSelect": { 79 | "position": 3, 80 | "gf": { 81 | "assetPath": "charSelect/neneChill", 82 | "animInfoPath": "charSelect/neneAnimInfo", 83 | "visualizer": true 84 | } 85 | }, 86 | "results": { 87 | "music": { 88 | "PERFECT_GOLD": "resultsPERFECT-pico", 89 | "PERFECT": "resultsPERFECT-pico", 90 | "EXCELLENT": "resultsEXCELLENT-pico", 91 | "GREAT": "resultsNORMAL-pico", 92 | "GOOD": "resultsNORMAL-pico", 93 | "SHIT": "resultsSHIT-pico" 94 | }, 95 | "perfectGold": [ 96 | { 97 | "renderType": "animateatlas", 98 | "assetPath": "shared:resultScreen/results-pico/resultsPERFECT", 99 | "zIndex": 500, 100 | "scale": 0.88, 101 | "offsets": [385, 82], 102 | "loopFrame": 91 103 | } 104 | ], 105 | "perfect": [ 106 | { 107 | "renderType": "animateatlas", 108 | "assetPath": "shared:resultScreen/results-pico/resultsPERFECT", 109 | "zIndex": 500, 110 | "scale": 0.88, 111 | "offsets": [385, 82], 112 | "loopFrame": 91 113 | } 114 | ], 115 | "excellent": [ 116 | { 117 | "renderType": "animateatlas", 118 | "assetPath": "shared:resultScreen/results-pico/resultsGREAT", 119 | "zIndex": 500, 120 | "scale": 1.25, 121 | "offsets": [350, 25], 122 | "looped": true, 123 | "loopFrame": 32 124 | } 125 | ], 126 | "great": [ 127 | { 128 | "renderType": "animateatlas", 129 | "assetPath": "shared:resultScreen/results-pico/resultsGREAT", 130 | "zIndex": 500, 131 | "scale": 1.25, 132 | "offsets": [350, 25], 133 | "looped": true, 134 | "loopFrame": 32 135 | } 136 | ], 137 | "good": [ 138 | { 139 | "renderType": "animateatlas", 140 | "assetPath": "shared:resultScreen/results-pico/resultsGOOD", 141 | "zIndex": 500, 142 | "scale": 1.25, 143 | "offsets": [350, 25], 144 | "loopFrame": 41 145 | } 146 | ], 147 | "loss": [ 148 | { 149 | "renderType": "animateatlas", 150 | "assetPath": "shared:resultScreen/results-pico/resultsSHIT", 151 | "zIndex": 500, 152 | "offsets": [-185, -125], 153 | "loopFrame": 0 154 | } 155 | ] 156 | } 157 | } 158 | ``` 159 | 160 | The available fields are: 161 | - `version`: The version number for the Playable Character data file format. Leave this at `1.0.0`. 162 | - `name`: The readable name for the character, used internally. 163 | - `ownedCharacters`: The list of [Characters](../03-custom-characters/03-00-custom-characters.md) this character owns. 164 | - When determining which songs to display in Freeplay, the game checks for any songs where the player character is in this list and displays those. Songs where the player character is in another array are not displayed. 165 | - `showUnownedChars`: If this value is `true`, then songs whose player character is not in any `ownedCharacters` list will be displayed for this character. 166 | - `unlocked`: Whether the character is unlocked. 167 | - Create a scripted class for this playable character and override `isUnlocked():Bool` to make this conditional. See [Scripted Playable Characters](21-scripted-classes\21-10-scripted-playable-characters.md) 168 | - `freeplayStyle`: The ID for a Freeplay style to display. 169 | - You can use `"bf"` here to use Boyfriend's Freeplay style as a default, or create a new JSON file in the `data/ui/freeplay/styles` folder (copy the Pico one and edit that). 170 | - `freeplayDJ`: Data for how the character displays as the DJ in the Freeplay menu. 171 | - `charSelect`: Data for how the character displays in the Character Select menu. 172 | - `results`: Data for how the character displays in the Results screen. 173 | 174 | Freeplay DJ data is structured like so: 175 | - `assetPath`: The folder where the Animate Atlas for this character is located, relative to the `images/` folder. 176 | - Note that Sparrow atlases are not supported for Freeplay animations. 177 | - `animations`: A list of animation data for the character. 178 | - Valid animation names include `intro` `idle` `idleEasterEgg` `confirm` `fistPump` `loss` `charSelect` and ` 179 | - `charSelect`: A structured data object containing: 180 | - `transitionDelay`: A duration (in seconds) between when the character's transition to Character Select starts and the camera starts to fade. 181 | - `fistPump`: A structured data object containing: 182 | - `introStartFrame` The frame number in the `fistPump` animation where the intro animation (which loops until the rank slams down) starts. 183 | - `introEndFrame` The frame number in the `fistPump` animation where the intro animation ends. 184 | - `loopStartFrame` The frame number in the `fistPump` animation where the follow-up animation starts. 185 | - `loopEndFrame` The frame number in the `fistPump` animation where the follow-up animation ends. 186 | - Use `-1` to use the last frame of the specified frame label. 187 | - `introBadStartFrame` The frame number in the `loss` animation where the intro animation starts. 188 | - `introBadEndFrame` The frame number in the `loss` animation where the intro animation ends. 189 | - `loopBadStartFrame` The frame number in the `loss` animation where the follow-up animation starts. 190 | - `loopBadEndFrame` The frame number in the `loss` animation where the follow-up animation ends. 191 | 192 | Character Select data is structured like so: 193 | - `position`: The preferred grid square for the character in the Character Select grid. 194 | - `0` represents the top left, `3` represents the middle left, and `8` represents the bottom right. 195 | - Characters are evaluated alphabetically, and if the slot is already occupied, they will be shifted over until they fit. 196 | - At time of writing (v0.5.1) only 9 total characters can fit in the grid. 197 | - `gf`: (NEW with v0.5.1) A structured data object containing: 198 | - `assetPath`: The folder where the Animate Atlas for this character is located, relative to the `images/` folder. 199 | - Note that Sparrow atlases are not supported. 200 | - `animInfoPath`: A path to a Flash JSFL file describing character sliding movement. 201 | - `visualizer`: Whether the character is hooked up to display a visualizer (like Nene's ABot). 202 | - Check the Nene Character Select FLA to see how to implement this. 203 | 204 | Results data is structured like so: 205 | - `music`: A structured data object containing: 206 | - `PERFECT_GOLD`: The path to a music track in the `music/` folder. Played during the Perfect rank animation with all SICKs. 207 | - `PERFECT`: The path to a music track in the `music/` folder. Played during the Perfect rank animation. 208 | - `EXCELLENT`: The path to a music track in the `music/` folder. Played during the Excellent rank animation. 209 | - `GREAT`: The path to a music track in the `music/` folder. Played during the Great rank animation. 210 | - `GOOD`: The path to a music track in the `music/` folder. Played during the Good rank animation. 211 | - `SHIT`: The path to a music track in the `music/` folder. Played during the Loss rank animation. 212 | - Make sure to include a metadata file in the folder to tell the game what BPM the song is, and how to loop it. 213 | - Include a variation of the track with the suffix `-intro.ogg` to play that track once before playing the main music track. 214 | - `perfect`: An array of animation data structures, describing the animation played when the player gets a Perfect rank. 215 | - Data is in the form of an array of animation objects. 216 | - You can use Sparrow animations or `animateatlas` sprites. Use `renderType` to tell the game which one to use. 217 | - Use `loopFrame` to tell the game which frame to start looping at, or set `looped` to `"false"` to just play the animation once. 218 | - `excellent`: Data for the animation played when the player gets a Excellent rank. 219 | - `great`: Data for the animation played when the player gets a Great rank. 220 | - `good`: Data for the animation played when the player gets a Good rank. 221 | - `loss`: Data for the animation played when the player gets a Loss rank. 222 | - `perfectGold`: (NEW with v0.5.1) Data for the animation played when the player gets a Perfect rank with all SICKs. 223 | - In the base game, this is just the same data as `perfect`, but you can make it different if you like. 224 | 225 | [^picosource]: -------------------------------------------------------------------------------- /src/06-custom-notestyles/06-01-creating-a-notestyle.md: -------------------------------------------------------------------------------- 1 | # Creating a Note Style 2 | 3 | ## Note Style Assets 4 | 5 | In order to create your own note style, you will need assets for a variety of elements: 6 | 7 | - `note`: The general notes for the song. These display in the chart editor as well as in-game as the notes the player must hit. 8 | - `holdNote`: The sprites for the hold notes which require the key to be held rather than pressed. 9 | - `noteStrumline`: The notes on the strumline, which react when the player presses the keys, and provide visual feedback for when notes need to be hit. 10 | - `noteSplash`: The special effect when the player gets `Sick!` rating. 11 | - `holdNoteCover`: The special effect when the player is holding a hold note. 12 | - `countdown`: The assets (both the sprites and audio) for the countdown that happens before the song starts. 13 | - `judgementShit`: The assets for judgements, which display how well you hit a note. 14 | - `comboNumber0`: The assets for the combo counter, which displays when you hit a certain number of notes in a row. 15 | 16 | These assets should be placed in your mod folder, ideally somewhere organized. They don't have to be in any specific location, just one that you can directly reference from your note style's JSON data file. 17 | 18 | ## Note Style Fallback 19 | 20 | Note styles use a recursive fallback system; essentially, you can specify just one or several assets for a note style. If a fallback note style is specified, the parent will be queried for any assets that aren't included. If the parent doesn't have an asset, ITS parent will be referenced, and so on. This allows you to easily make note styles which perform only slight variations on the style they are based on. 21 | 22 | ## Note Style Data 23 | 24 | A custom note style requires creating a new JSON file in the `data/notestyles` folder. 25 | 26 | Below is the "funkin" (aka the default) note style json file `assets/data/notestyles/funkin.json`[^notestylesource] 27 | 28 | ```json 29 | { 30 | "version": "1.1.0", 31 | "name": "Funkin'", 32 | "author": "PhantomArcade", 33 | "fallback": null, 34 | "assets": { 35 | "note": { 36 | "assetPath": "shared:notes", 37 | "scale": 1.0, 38 | "data": { 39 | "left": { "prefix": "noteLeft" }, 40 | "down": { "prefix": "noteDown" }, 41 | "up": { "prefix": "noteUp" }, 42 | "right": { "prefix": "noteRight" } 43 | } 44 | }, 45 | "noteStrumline": { 46 | "assetPath": "shared:noteStrumline", 47 | "scale": 1.55, 48 | "offsets": [0, 0], 49 | "data": { 50 | "leftStatic": { "prefix": "staticLeft0" }, 51 | "leftPress": { "prefix": "pressLeft0" }, 52 | "leftConfirm": { "prefix": "confirmLeft0" }, 53 | "leftConfirmHold": { "prefix": "confirmLeft0" }, 54 | "downStatic": { "prefix": "staticDown0" }, 55 | "downPress": { "prefix": "pressDown0" }, 56 | "downConfirm": { "prefix": "confirmDown0" }, 57 | "downConfirmHold": { "prefix": "confirmDown0" }, 58 | "upStatic": { "prefix": "staticUp0" }, 59 | "upPress": { "prefix": "pressUp0" }, 60 | "upConfirm": { "prefix": "confirmUp0" }, 61 | "upConfirmHold": { "prefix": "confirmUp0" }, 62 | "rightStatic": { "prefix": "staticRight0" }, 63 | "rightPress": { "prefix": "pressRight0" }, 64 | "rightConfirm": { "prefix": "confirmRight0" }, 65 | "rightConfirmHold": { "prefix": "confirmRight0" } 66 | } 67 | }, 68 | "holdNote": { 69 | "assetPath": "NOTE_hold_assets", 70 | "data": {} 71 | }, 72 | "noteSplash": { 73 | "assetPath": "", 74 | "data": { 75 | "enabled": true 76 | } 77 | }, 78 | "holdNoteCover": { 79 | "assetPath": "", 80 | "data": { 81 | "enabled": true 82 | } 83 | }, 84 | "countdownThree": { 85 | "data": { 86 | "audioPath": "shared:gameplay/countdown/funkin/introTHREE" 87 | }, 88 | "assetPath": null 89 | }, 90 | "countdownTwo": { 91 | "data": { 92 | "audioPath": "shared:gameplay/countdown/funkin/introTWO" 93 | }, 94 | "assetPath": "shared:ui/countdown/funkin/ready", 95 | "scale": 1.0, 96 | "isPixel": false 97 | }, 98 | "countdownOne": { 99 | "data": { 100 | "audioPath": "shared:gameplay/countdown/funkin/introONE" 101 | }, 102 | "assetPath": "shared:ui/countdown/funkin/set", 103 | "scale": 1.0, 104 | "isPixel": false 105 | }, 106 | "countdownGo": { 107 | "data": { 108 | "audioPath": "shared:gameplay/countdown/funkin/introGO" 109 | }, 110 | "assetPath": "shared:ui/countdown/funkin/go", 111 | "scale": 1.0, 112 | "isPixel": false 113 | }, 114 | "judgementSick": { 115 | "assetPath": "default:ui/popup/funkin/sick", 116 | "scale": 0.65, 117 | "isPixel": false 118 | }, 119 | "judgementGood": { 120 | "assetPath": "default:ui/popup/funkin/good", 121 | "scale": 0.65, 122 | "isPixel": false 123 | }, 124 | "judgementBad": { 125 | "assetPath": "default:ui/popup/funkin/bad", 126 | "scale": 0.65, 127 | "isPixel": false 128 | }, 129 | "judgementShit": { 130 | "assetPath": "default:ui/popup/funkin/shit", 131 | "scale": 0.65, 132 | "isPixel": false 133 | }, 134 | "comboNumber0": { 135 | "assetPath": "default:ui/popup/funkin/num0", 136 | "isPixel": false, 137 | "scale": 0.45 138 | }, 139 | "comboNumber1": { 140 | "assetPath": "default:ui/popup/funkin/num1", 141 | "isPixel": false, 142 | "scale": 0.45 143 | }, 144 | "comboNumber2": { 145 | "assetPath": "default:ui/popup/funkin/num2", 146 | "isPixel": false, 147 | "scale": 0.45 148 | }, 149 | "comboNumber3": { 150 | "assetPath": "default:ui/popup/funkin/num3", 151 | "isPixel": false, 152 | "scale": 0.45 153 | }, 154 | "comboNumber4": { 155 | "assetPath": "default:ui/popup/funkin/num4", 156 | "isPixel": false, 157 | "scale": 0.45 158 | }, 159 | "comboNumber5": { 160 | "assetPath": "default:ui/popup/funkin/num5", 161 | "isPixel": false, 162 | "scale": 0.45 163 | }, 164 | "comboNumber6": { 165 | "assetPath": "default:ui/popup/funkin/num6", 166 | "isPixel": false, 167 | "scale": 0.45 168 | }, 169 | "comboNumber7": { 170 | "assetPath": "default:ui/popup/funkin/num7", 171 | "isPixel": false, 172 | "scale": 0.45 173 | }, 174 | "comboNumber8": { 175 | "assetPath": "default:ui/popup/funkin/num8", 176 | "isPixel": false, 177 | "scale": 0.45 178 | }, 179 | "comboNumber9": { 180 | "assetPath": "default:ui/popup/funkin/num9", 181 | "isPixel": false, 182 | "scale": 0.45 183 | } 184 | } 185 | } 186 | 187 | ``` 188 | 189 | There is quite a lot to unravel, let's break it all down. 190 | - `version`: The version number for the Note Style data file format. Leave this at `1.0.0`. 191 | - `name`: The readable name for the note style, used in places like the Chart Editor. 192 | - `author`: The author of the note style, aka the artist who created the assets. 193 | - `fallback`: The note style ID to use as a fallback note style. Any assets not included for this note style will use the parent's asset instead. `"funkin"` is the recommended default in order to use the default base game assets for anything that is not specified. 194 | - `assets`: A list of asset data for each of the assets for the note style. 195 | - See [list of assets](#note-style-assets) that you can provide the data for. 196 | 197 | Asset data is structured like so: 198 | - `assetPath`: The main asset path to use for this asset. 199 | - `scale`: Specify the size of the asset relative to the original size. For example, `2.0` makes the sprite twice as big. Defaults to `1.0` if unspecified. 200 | - `offsets`: Some animations may need their positions to be corrected relative to the idle animation. 201 | - Use an array of two decimal values, the first for horizontal position and the second for vertical position. 202 | - `isPixel`: Specify whether to display this asset as being pixel (disabling texture smoothing). Optional, defaults to `false`. 203 | - `data`: The structure of note style asset data objects that depends on the asset you are going to edit. 204 | 205 | Note Style Asset data is structured like so: 206 | - For `note`: List of animation data for the `left`, `down`, `up` and `right` arrows. 207 | - For `noteStrumline`: List of animation data for each direction and it's variations, such as `Static`, `Press`, `Confirm`, `ConfirmHold` replacing `` with it's each and every corresponding direction. 208 | - As you may see from the [example](#note-style-data)[^notestylesource] that was given, animation data for the `confirm` and `confirmHold` match up, however you can make it have unique animations. 209 | - For `holdNote`: Currently has no list of animation data that can be set. 210 | - For `noteSplash`: There is currently no list of animation data available that can be set. It is likely to be added in the future. 211 | - `enabled`: Specify whether to display the asset. Optional, defaults to `true`. 212 | - For `holdNoteCover`: There is currently no list of animation data available that can be set. It is likely to be added in the future. 213 | - `enabled`: Specify whether to display the asset. Optional, defaults to `true`. 214 | 215 | Animation data is structured like so: 216 | - `prefix`: The animation name as specified by your spritesheet. 217 | - For Sparrow or Packer, check inside the data file to see what each set of frames is named, and use that as the prefix, excluding the frame numbers at the end. 218 | - For Animate Atlases, use either the frame label or the symbol name of the animation you want to play. 219 | - `offsets`: Some animations may need their positions to be corrected relative to the idle animation. 220 | - Use an array of two decimal values, the first for horizontal position and the second for vertical position. 221 | - `looped`: Whether to loop this animation in-game. If false, the animation will pause when it ends, until the game commands the asset to do something else. 222 | - `flipX`: Whether to flip the sprites of this animation horizontally in-game. 223 | - `flipY`: Whether to flip the sprites of this animation vertically in-game. 224 | - `frameRate`: A frame rate value, defaulting to `24`. 225 | - `frameIndices`: Optionally specify an array of frame numbers (starting at frame 0!) to use from a given prefix for this animation. 226 | - For example, specifying `[0, 1, 2, 3]` will make this animation only use the first 4 frames of the given prefix. 227 | 228 | [^notestylesource]: -------------------------------------------------------------------------------- /src/04-custom-stages/04-01-creating-a-stage.md: -------------------------------------------------------------------------------- 1 | # Creating a Stage 2 | 3 | ## Stage Assets 4 | 5 | The image assets required for a stage should be placed into the `shared/images/` folder within your mod, ideally somewhere organized like a subfolder with the name of the stage. 6 | 7 | ## Stage Data 8 | 9 | A custom stage requires creating a new JSON file in the `data/stages` folder 10 | 11 | Below is the "Main Stage" json file from Week 1 `assets/data/stages/mainStage.json`[^stagesource] 12 | 13 | ```json 14 | { 15 | "version": "1.0.1", 16 | "name": "Main Stage", 17 | "cameraZoom": 1.1, 18 | "props": [ 19 | { 20 | "zIndex": 10, 21 | "position": [-600, -200], 22 | "scale": [1, 1], 23 | "name": "stageBack", 24 | "assetPath": "stages/mainStage/stageback", 25 | "scroll": [0.9, 0.9] 26 | }, 27 | { 28 | "zIndex": 20, 29 | "position": [-650, 600], 30 | "scale": [1.1, 1.1], 31 | "name": "stageFront", 32 | "assetPath": "stages/mainStage/stagefront", 33 | "scroll": [0.9, 0.9] 34 | }, 35 | { 36 | "zIndex": 30, 37 | "position": [-500, -300], 38 | "scale": [0.9, 0.9], 39 | "name": "stageCurtains", 40 | "assetPath": "stages/mainStage/stagecurtains", 41 | "scroll": [1.3, 1.3] 42 | } 43 | ], 44 | "characters": { 45 | "bf": { 46 | "zIndex": 300, 47 | "position": [989.5, 885], 48 | "cameraOffsets": [-100, -100] 49 | }, 50 | "dad": { 51 | "zIndex": 200, 52 | "position": [335, 885], 53 | "cameraOffsets": [150, -100] 54 | }, 55 | "gf": { 56 | "zIndex": 100, 57 | "cameraOffsets": [0, 0], 58 | "position": [751.5, 787] 59 | } 60 | } 61 | } 62 | 63 | ``` 64 | 65 | The available fields are: 66 | - `version`: The version number for the Stage data file format. Leave this at `1.0.1`. 67 | - `name`: The readable name for the stage, used in places like the Chart Editor. 68 | - `cameraZoom`: The default camera zoom level for the stage. Optional, defaults to `1.0`. 69 | - `props`: An array of props to add to the stage, specifying what sprites should be used and where those sprites should be positioned. 70 | - `characters`: Data on how characters in the stage should be positioned. 71 | 72 | Stage prop data is structured like so: 73 | - `name`: An internal name for this prop. Good for keeping track of things. Keep each prop name unique! 74 | - You can access a stage prop in a script using `PlayState.instance.currentStage.getNamedProp(name)`. 75 | - `assetPath`: The asset used to display the prop. This can be either an image or a color. 76 | - To use an image, specify the path relative to the `images` folder in your mod folder. 77 | - To use a color, specify a hex code. This creates a 1x1 pixel square of the specified color, which you can resize with the `scale` property. 78 | - `zIndex`: A value describing the relative position of the prop. Optional, defaults to `0`. 79 | - Elements with higher values get placed in front of elements with lower values. 80 | - `position`: The horizontal and vertical position of the prop, in pixels. 81 | - `isPixel`: Specify whether to display this image as a pixel prop (disabling texture smoothing). Optional, defaults to `false`. 82 | - `scale`: Specify the size of the prop relative to the original size. For example, `2.0` makes the sprite twice as big. Defaults to `1.0` if unspecified. 83 | - `alpha`: Specify the opacity of the prop, with `1.0` being fully opaque and `0.0` being completely transparent. Optional, defaults to `1.0`. 84 | - `scroll`: Specify how much scroll factor, or how much the prop moves relative to the camera horizontally and vertically. Defaults to `[0.0, 0.0]` if unspecified. 85 | - A value of `[0, 0]` causes the prop to not move at all in relation to the camera. 86 | - A value of `[1, 1]` causes the prop to move 1-to-1 with the camera. Characters usually have a scroll factor of `[1, 1]`. 87 | - A value of `[0, 1]` causes the prop to only move vertically in relation to the camera as focus changes. 88 | - A value of `[0.5, 0.5]` causes the prop to move less relative to props configured to move 1-to-1 89 | - A value of `[2, 2]` is valid and causes the prop to move 2-to-1 as the camera moves. 90 | - `animType`: If the prop you choose is animated, specify `sparrow` or `packer` for which animation type you're using. 91 | - Most of the game's spritesheets are Sparrow v2 sheets exported from Adobe Animate. 92 | - `animations`: A list of animation data objects for the stage prop. 93 | - `startingAnimation`: The animation to play on the prop when the stage starts. If the animation is configured to loop, it will play forever unless a script calls a different one (or `danceEvery` is greater than 0). If unspecified, no animation will play unless a script does so. 94 | - `danceEvery`: If non-zero, this prop will play an animation every X beats of the song. Defaults to `0.0`, or no bopping. 95 | - This tries to play the `idle` animation. 96 | - If `danceLeft` and `danceRight ` animations are specified, the game will alternate between these instead. 97 | - This value supports precision up to `0.25`, where `0.25` plays the animation four times per beat. 98 | 99 | Stage prop animation data is structured like so: 100 | - `name`: The internal animation name for the game to use. 101 | - `prefix`: The animation name as specified by your spritesheet. 102 | - For Sparrow or Packer, check inside the data file to see what each set of frames is named, and use that as the prefix, excluding the frame numbers at the end. 103 | - For Animate Atlases, use either the frame label or the symbol name of the animation you want to play. 104 | - `offsets`: Some animations may need their positions to be corrected relative to the idle animation. 105 | - Use an array of two decimal values, the first for horizontal position and the second for vertical position. 106 | - `looped`: Whether to loop this animation in-game. If false, the animation will pause when it ends, until the game commands the character to do something else. 107 | - `flipX`: Whether to flip the sprites of this animation horizontally in-game. 108 | - `flipY`: Whether to flip the sprites of this animation vertically in-game. 109 | - `frameRate`: A frame rate value, defaulting to `24`. 110 | - `frameIndices`: Optionally specify an array of frame numbers (starting at frame 0!) to use from a given prefix for this animation. 111 | - For example, specifying `[0, 1, 2, 3]` will make this animation only use the first 4 frames of the given prefix. 112 | - `assetPath`: For the `multisparrow` asset type specifically. Define a secondary Sparrow spritesheet which will be loaded, and which contains the frames for this animation. 113 | 114 | Character data is structured like so: 115 | - `bf`: Data about the stage's player character. 116 | - `zIndex`: A value describing the relative position of the player character. Elements with higher values get placed in front of those with lower values. 117 | - `position`: The X and Y position where the character should be positioned, relative to other props in the stage. 118 | - `scale`: The relative scale to display the character at. For example, `0.5` makes the character half as big as the default. 119 | - `cameraOffsets`: When the camera focuses on this character, focus on the character's center, then apply camera offsets to shift the camera focus to the desired spot. 120 | - `dad`: Data about the stage's opponent character. 121 | - `zIndex`: A value describing the relative position of the opponent character. Elements with higher values get placed in front of those with lower values. 122 | - `position`: The X and Y position where the character should be positioned, relative to other props in the stage. 123 | - `scale`: The relative scale to display the character at. For example, `0.5` makes the character half as big as the default. 124 | - `cameraOffsets`: When the camera focuses on this character, focus on the character's center, then apply camera offsets to shift the camera focus to the desired spot. 125 | - `gf`: Data about the stage's background character. 126 | - `zIndex`: A value describing the relative position of the background character. Elements with higher values get placed in front of those with lower values. 127 | - `position`: The X and Y position where the character should be positioned, relative to other props in the stage. 128 | - `scale`: The relative scale to display the character at. For example, `0.5` makes the character half as big as the default. 129 | - `cameraOffsets`: When the camera focuses on this character, focus on the character's center, then apply camera offsets to shift the camera focus to the desired spot. 130 | 131 | 132 | Animation data is structured like so: 133 | - `name`: The internal animation name for the game to use. 134 | - `prefix`: The animation name as specified by your spritesheet. 135 | - For Sparrow or Packer, check inside the data file to see what each set of frames is named, and use that as the prefix, excluding the frame numbers at the end. 136 | - For Animate Atlases, use either the frame label or the symbol name of the animation you want to play. 137 | - `offsets`: Some animations may need their positions to be corrected relative to the idle animation. 138 | - Use an array of two decimal values, the first for horizontal position and the second for vertical position. 139 | - `looped`: Whether to loop this animation in-game. If false, the animation will pause when it ends, until the game commands the character to do something else. 140 | - `flipX`: Whether to flip the sprites of this animation horizontally in-game. 141 | - `flipY`: Whether to flip the sprites of this animation vertically in-game. 142 | - `frameRate`: A frame rate value, defaulting to `24`. 143 | - `frameIndices`: Optionally specify an array of frame numbers (starting at frame 0!) to use from a given prefix for this animation. 144 | - For example, specifying `[0, 1, 2, 3]` will make this animation only use the first 4 frames of the given prefix. 145 | - `assetPath`: For the `multisparrow` asset type specifically. Define a secondary Sparrow spritesheet which will be loaded, and which contains the frames for this animation. 146 | 147 | The animation names the game uses by default are: 148 | - `idle`: For the idle animation. 149 | - `danceLeft` and `danceRight`: Supercedes the idle animation with one that toggles between two animations. 150 | - `singLEFT`, `singDOWN`, `singUP`, `singRIGHT`: The animations for playing notes, when the character is a player or opponent. 151 | - `singLEFTmiss`, `singDOWNmiss`, `singUPmiss`, `singRIGHTmiss`: The animations for missing notes, when the character is a player. 152 | - Adding a new singing animation with the name of an existing animation with `-hold` at the end will play the animation after the first one ends, while the character is still singing. 153 | - As a good example, you can copy the `singLEFT` animation to make a `singLEFT-hold` animation, which has `looped` as true and `frameIndices` as the last few frames of the singing animation. 154 | - Adding a new singing animation with the name of an existing animation with `-end` at the end will play an animation before returning to idle. 155 | - For example, you can define a new `singLEFT-end` animation to cleanly transition into the idle animation. 156 | - You can add other animations by name, but you'll have to play them with a script, or a `Play Animation` song event in the Chart Editor. 157 | 158 | When the game starts, it queries the list of possible characters by searching in the `data/characters` folder for JSON files. This gets used to preload data which is used later when the character is loaded in a stage. 159 | 160 | [^stagesource]: 161 | --------------------------------------------------------------------------------