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.
Start by creating a scripted class file with the .hxc extension (in the mods/mymod/scripts/songs if you want to keep things organized).
// Remember to import each class you want to reference in your script! import funkin.play.song.Song; // Choose a name for your scripted class that will be unique, and make sure to specifically extend the Song class. // This class's functions will override the default behavior for the song. class BallisticSong extends Song { public function new() { // The constructor gets called once, when the game loads. // The constructor takes one parameter, which is the song ID for the song you are applying the script to. super('ballistic'); } // Add override functions here! }
You can then add override functions to perform custom behavior.
Variation-Specific Song Scripts
As of 0.7.4, if in the constructor you were to add an anonymous structure with the field variation as a parameter next to the song id, the song script will only be active for that variation. This prevents many conflicts with base game scripts and other mods that may add a variation, as the game would only call the functions from the variation-specific script. If a song doesn't have a variation-specific script, it will fallback to the default one, if it exists. An example of a variation-specific script can be found for Darnell Erect1
import funkin.play.song.Song; import funkin.save.Save; class DarnellErectSong extends Song { public function new() { super('darnell', { variation: 'erect' }); } // ... }
Custom 'NEW'-Label criteria
Usually, in the Freeplay menu, a song would not have the glowing 'NEW' label. However if you override the function isSongNew, you can have the label appear if a specific criteria is met. An example of this is found in the script for Cocoa (Pico Mix)2 which returns true if the variation is Pico and the player hasn't beaten Cocoa Pico Mix.
// ... // Line 14 public override function isSongNew(currentDifficulty:String, currentVariation:String):Bool { if (currentVariation == 'pico') return !Save.instance.hasBeatenSong(this.id, null, this.variation); return false; } // ...
Custom Alternate Instrumental behavior
Instead of reading from the song's metadata file for possible alternate instrumentals, you can use scripts to lock some of them out of being used until a criteria is met. An example of this is found in the script for Stress3 which locks the pico instrumental behind completing the pico remix, as well as not provide the default instrumental if the song is selected on the pico variation.
// ... // Line 21 public override function listAltInstrumentalIds(difficultyId:String, variationId:String):Array<String> { var results:Array<String> = super.listAltInstrumentalIds(difficultyId, variationId); if (difficultyId == 'easy' || difficultyId == 'normal' || difficultyId == 'hard') { var hasBeatenPicoMix = Save.instance.hasBeatenSong(this.id, null, 'pico'); if (!hasBeatenPicoMix) results = [for (id in results) if (id != 'pico') id]; } return results; } // ...
Hiding the Song from the Freeplay Menu
The most important thing to override here is the function listDifficulties. If you return an empty array, the song will be omitted from the Freeplay menu. An example to this is in the song 2hot4
// ... // Line 51 public function listDifficulties(variationId:String, variationIds:Array<String>, showLocked:Bool):Array<String> { if (showLocked || Save.instance.hasBeatenLevel('weekend1')) { return super.listDifficulties(variationId, variationIds); } // Hide all difficulties if the player has not beaten the week. return []; } // ...
-
https://github.com/FunkinCrew/funkin.assets/blob/main/preload/scripts/songs/darnell-erect.hxc ↩
-
https://github.com/FunkinCrew/funkin.assets/blob/main/preload/scripts/songs/cocoa-pico.hxc ↩
-
https://github.com/FunkinCrew/funkin.assets/blob/main/preload/scripts/songs/stress.hxc ↩
-
https://github.com/FunkinCrew/funkin.assets/blob/main/preload/scripts/songs/2hot.hxc ↩