From 66085ff8673d1512ce0716a31a1a12f6effc23da Mon Sep 17 00:00:00 2001 From: EliteMasterEric <ericmyllyoja@gmail.com> Date: Tue, 12 Mar 2024 21:34:50 -0400 Subject: [PATCH 1/5] Song scripts can now be (optionally) enabled in the Chart Editor playtest --- source/funkin/data/BaseRegistry.hx | 87 ++++++++++++++--- source/funkin/data/song/SongRegistry.hx | 1 + source/funkin/graphics/FunkinSprite.hx | 9 +- source/funkin/play/Countdown.hx | 2 +- source/funkin/play/GitarooPause.hx | 2 +- source/funkin/play/PlayState.hx | 4 +- source/funkin/play/components/PopUpStuff.hx | 6 +- source/funkin/play/song/Song.hx | 93 +++++++++++++++++-- source/funkin/play/stage/Stage.hx | 2 +- .../ui/debug/charting/ChartEditorState.hx | 12 ++- .../handlers/ChartEditorToolboxHandler.hx | 11 +++ source/funkin/ui/freeplay/FreeplayState.hx | 2 +- source/funkin/ui/transition/LoadingState.hx | 10 +- .../funkin/ui/transition/StickerSubState.hx | 2 +- source/funkin/util/tools/StringTools.hx | 27 +++++- 15 files changed, 224 insertions(+), 46 deletions(-) diff --git a/source/funkin/data/BaseRegistry.hx b/source/funkin/data/BaseRegistry.hx index 2df0c18f0..ad028fa94 100644 --- a/source/funkin/data/BaseRegistry.hx +++ b/source/funkin/data/BaseRegistry.hx @@ -1,6 +1,5 @@ package funkin.data; -import openfl.Assets; import funkin.util.assets.DataAssets; import funkin.util.VersionUtil; import haxe.Constraints.Constructible; @@ -19,12 +18,23 @@ typedef EntryConstructorFunction = String->Void; @:generic abstract class BaseRegistry<T:(IRegistryEntry<J> & Constructible<EntryConstructorFunction>), J> { + /** + * The ID of the registry. Used when logging. + */ public final registryId:String; final dataFilePath:String; + /** + * A map of entry IDs to entries. + */ final entries:Map<String, T>; + /** + * A map of entry IDs to scripted class names. + */ + final scriptedEntryIds:Map<String, String>; + /** * The version rule to use when loading entries. * If the entry's version does not match this rule, migration is needed. @@ -37,17 +47,18 @@ abstract class BaseRegistry<T:(IRegistryEntry<J> & Constructible<EntryConstructo * @param registryId A readable ID for this registry, used when logging. * @param dataFilePath The path (relative to `assets/data`) to search for JSON files. */ - public function new(registryId:String, dataFilePath:String, versionRule:thx.semver.VersionRule = null) + public function new(registryId:String, dataFilePath:String, ?versionRule:thx.semver.VersionRule) { this.registryId = registryId; this.dataFilePath = dataFilePath; - this.versionRule = versionRule == null ? "1.0.x" : versionRule; + this.versionRule = versionRule == null ? '1.0.x' : versionRule; this.entries = new Map<String, T>(); + this.scriptedEntryIds = []; } /** - * TODO: Create a `loadEntriesAsync()` function. + * TODO: Create a `loadEntriesAsync(onProgress, onComplete)` function. */ public function loadEntries():Void { @@ -66,7 +77,7 @@ abstract class BaseRegistry<T:(IRegistryEntry<J> & Constructible<EntryConstructo { entry = createScriptedEntry(entryCls); } - catch (e:Dynamic) + catch (e) { log('Failed to create scripted entry (${entryCls})'); continue; @@ -76,6 +87,7 @@ abstract class BaseRegistry<T:(IRegistryEntry<J> & Constructible<EntryConstructo { log('Successfully created scripted entry (${entryCls} = ${entry.id})'); entries.set(entry.id, entry); + scriptedEntryIds.set(entry.id, entryCls); } else { @@ -102,7 +114,7 @@ abstract class BaseRegistry<T:(IRegistryEntry<J> & Constructible<EntryConstructo entries.set(entry.id, entry); } } - catch (e:Dynamic) + catch (e) { // Print the error. trace(' Failed to load entry data: ${entryId}'); @@ -130,6 +142,36 @@ abstract class BaseRegistry<T:(IRegistryEntry<J> & Constructible<EntryConstructo return entries.size(); } + /** + * Return whether the entry ID is known to have an attached script. + * @param id The ID of the entry. + * @return `true` if the entry has an attached script, `false` otherwise. + */ + public function isScriptedEntry(id:String):Bool + { + return scriptedEntryIds.exists(id); + } + + /** + * Return the class name of the scripted entry with the given ID, if it exists. + * @param id The ID of the entry. + * @return The class name, or `null` if it does not exist. + */ + public function getScriptedEntryClassName(id:String):String + { + return scriptedEntryIds.get(id); + } + + /** + * Return whether the registry has successfully parsed an entry with the given ID. + * @param id The ID of the entry. + * @return `true` if the entry exists, `false` otherwise. + */ + public function hasEntry(id:String):Bool + { + return entries.exists(id); + } + /** * Fetch an entry by its ID. * @param id The ID of the entry to fetch. @@ -145,6 +187,11 @@ abstract class BaseRegistry<T:(IRegistryEntry<J> & Constructible<EntryConstructo return 'Registry(' + registryId + ', ${countEntries()} entries)'; } + /** + * Retrieve the data for an entry and parse its Semantic Version. + * @param id The ID of the entry. + * @return The entry's version, or `null` if it does not exist or is invalid. + */ public function fetchEntryVersion(id:String):Null<thx.semver.Version> { var entryStr:String = loadEntryFile(id).contents; @@ -185,6 +232,8 @@ abstract class BaseRegistry<T:(IRegistryEntry<J> & Constructible<EntryConstructo * Read, parse, and validate the JSON data and produce the corresponding data object. * * NOTE: Must be implemented on the implementation class. + * @param id The ID of the entry. + * @return The created entry. */ public abstract function parseEntryData(id:String):Null<J>; @@ -194,6 +243,7 @@ abstract class BaseRegistry<T:(IRegistryEntry<J> & Constructible<EntryConstructo * NOTE: Must be implemented on the implementation class. * @param contents The JSON as a string. * @param fileName An optional file name for error reporting. + * @return The created entry. */ public abstract function parseEntryDataRaw(contents:String, ?fileName:String):Null<J>; @@ -202,6 +252,9 @@ abstract class BaseRegistry<T:(IRegistryEntry<J> & Constructible<EntryConstructo * accounting for old versions of the data. * * NOTE: Extend this function to handle migration. + * @param id The ID of the entry. + * @param version The entry's version (use `fetchEntryVersion(id)`). + * @return The created entry. */ public function parseEntryDataWithMigration(id:String, version:thx.semver.Version):Null<J> { @@ -220,12 +273,17 @@ abstract class BaseRegistry<T:(IRegistryEntry<J> & Constructible<EntryConstructo throw '[${registryId}] Entry ${id} does not support migration to version ${versionRule}.'; } - // Example: - // if (VersionUtil.validateVersion(version, "0.1.x")) { - // return parseEntryData_v0_1_x(id); - // } else { - // super.parseEntryDataWithMigration(id, version); - // } + /* + * An example of what you should override this with: + * + * ```haxe + * if (VersionUtil.validateVersion(version, "0.1.x")) { + * return parseEntryData_v0_1_x(id); + * } else { + * super.parseEntryDataWithMigration(id, version); + * } + * ``` + */ } /** @@ -255,10 +313,15 @@ abstract class BaseRegistry<T:(IRegistryEntry<J> & Constructible<EntryConstructo trace('[${registryId}] Failed to parse entry data: ${id}'); for (error in errors) + { DataError.printError(error); + } } } +/** + * A pair of a file name and its contents. + */ typedef JsonFile = { fileName:String, diff --git a/source/funkin/data/song/SongRegistry.hx b/source/funkin/data/song/SongRegistry.hx index dad287e82..9f811d45e 100644 --- a/source/funkin/data/song/SongRegistry.hx +++ b/source/funkin/data/song/SongRegistry.hx @@ -68,6 +68,7 @@ class SongRegistry extends BaseRegistry<Song, SongMetadata> { log('Successfully created scripted entry (${entryCls} = ${entry.id})'); entries.set(entry.id, entry); + scriptedEntryIds.set(entry.id, entryCls); } else { diff --git a/source/funkin/graphics/FunkinSprite.hx b/source/funkin/graphics/FunkinSprite.hx index f47b4138a..03382f757 100644 --- a/source/funkin/graphics/FunkinSprite.hx +++ b/source/funkin/graphics/FunkinSprite.hx @@ -81,9 +81,10 @@ class FunkinSprite extends FlxSprite */ public function loadTexture(key:String):FunkinSprite { - if (!isTextureCached(key)) FlxG.log.warn('Texture not cached, may experience stuttering! $key'); + var graphicKey:String = Paths.image(key); + if (!isTextureCached(graphicKey)) FlxG.log.warn('Texture not cached, may experience stuttering! $graphicKey'); - loadGraphic(key); + loadGraphic(graphicKey); return this; } @@ -95,7 +96,7 @@ class FunkinSprite extends FlxSprite */ public function loadSparrow(key:String):FunkinSprite { - var graphicKey = Paths.image(key); + var graphicKey:String = Paths.image(key); if (!isTextureCached(graphicKey)) FlxG.log.warn('Texture not cached, may experience stuttering! $graphicKey'); this.frames = Paths.getSparrowAtlas(key); @@ -110,7 +111,7 @@ class FunkinSprite extends FlxSprite */ public function loadPacker(key:String):FunkinSprite { - var graphicKey = Paths.image(key); + var graphicKey:String = Paths.image(key); if (!isTextureCached(graphicKey)) FlxG.log.warn('Texture not cached, may experience stuttering! $graphicKey'); this.frames = Paths.getPackerAtlas(key); diff --git a/source/funkin/play/Countdown.hx b/source/funkin/play/Countdown.hx index 38e8986ef..747565100 100644 --- a/source/funkin/play/Countdown.hx +++ b/source/funkin/play/Countdown.hx @@ -215,7 +215,7 @@ class Countdown if (spritePath == null) return; - var countdownSprite:FunkinSprite = FunkinSprite.create(Paths.image(spritePath)); + var countdownSprite:FunkinSprite = FunkinSprite.create(spritePath); countdownSprite.scrollFactor.set(0, 0); if (isPixelStyle) countdownSprite.setGraphicSize(Std.int(countdownSprite.width * Constants.PIXEL_ART_SCALE)); diff --git a/source/funkin/play/GitarooPause.hx b/source/funkin/play/GitarooPause.hx index 1ed9dcf3b..eae56a9c3 100644 --- a/source/funkin/play/GitarooPause.hx +++ b/source/funkin/play/GitarooPause.hx @@ -28,7 +28,7 @@ class GitarooPause extends MusicBeatState { if (FlxG.sound.music != null) FlxG.sound.music.stop(); - var bg:FunkinSprite = FunkinSprite.create(Paths.image('pauseAlt/pauseBG')); + var bg:FunkinSprite = FunkinSprite.create('pauseAlt/pauseBG'); add(bg); var bf:FunkinSprite = FunkinSprite.createSparrow(0, 30, 'pauseAlt/bfLol'); diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index a6e4b4632..55c54e0fb 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -1416,7 +1416,7 @@ class PlayState extends MusicBeatSubState function initHealthBar():Void { var healthBarYPos:Float = Preferences.downscroll ? FlxG.height * 0.1 : FlxG.height * 0.9; - healthBarBG = FunkinSprite.create(0, healthBarYPos, Paths.image('healthBar')); + healthBarBG = FunkinSprite.create(0, healthBarYPos, 'healthBar'); healthBarBG.screenCenter(X); healthBarBG.scrollFactor.set(0, 0); healthBarBG.zIndex = 800; @@ -1453,7 +1453,7 @@ class PlayState extends MusicBeatSubState function initMinimalMode():Void { // Create the green background. - var menuBG = FunkinSprite.create(Paths.image('menuDesat')); + var menuBG = FunkinSprite.create('menuDesat'); menuBG.color = 0xFF4CAF50; menuBG.setGraphicSize(Std.int(menuBG.width * 1.1)); menuBG.updateHitbox(); diff --git a/source/funkin/play/components/PopUpStuff.hx b/source/funkin/play/components/PopUpStuff.hx index 0fe50f513..105fce2b8 100644 --- a/source/funkin/play/components/PopUpStuff.hx +++ b/source/funkin/play/components/PopUpStuff.hx @@ -25,7 +25,7 @@ class PopUpStuff extends FlxTypedGroup<FlxSprite> if (PlayState.instance.currentStageId.startsWith('school')) ratingPath = "weeb/pixelUI/" + ratingPath + "-pixel"; - var rating:FunkinSprite = FunkinSprite.create(0, 0, Paths.image(ratingPath)); + var rating:FunkinSprite = FunkinSprite.create(0, 0, ratingPath); rating.scrollFactor.set(0.2, 0.2); rating.zIndex = 1000; @@ -76,7 +76,7 @@ class PopUpStuff extends FlxTypedGroup<FlxSprite> pixelShitPart1 = 'weeb/pixelUI/'; pixelShitPart2 = '-pixel'; } - var comboSpr:FunkinSprite = FunkinSprite.create(Paths.image(pixelShitPart1 + 'combo' + pixelShitPart2)); + var comboSpr:FunkinSprite = FunkinSprite.create(pixelShitPart1 + 'combo' + pixelShitPart2); comboSpr.y = FlxG.camera.height * 0.4 + 80; comboSpr.x = FlxG.width * 0.50; // comboSpr.x -= FlxG.camera.scroll.x * 0.2; @@ -124,7 +124,7 @@ class PopUpStuff extends FlxTypedGroup<FlxSprite> var daLoop:Int = 1; for (i in seperatedScore) { - var numScore:FunkinSprite = FunkinSprite.create(0, comboSpr.y, Paths.image(pixelShitPart1 + 'num' + Std.int(i) + pixelShitPart2)); + var numScore:FunkinSprite = FunkinSprite.create(0, comboSpr.y, pixelShitPart1 + 'num' + Std.int(i) + pixelShitPart2); if (PlayState.instance.currentStageId.startsWith('school')) { diff --git a/source/funkin/play/song/Song.hx b/source/funkin/play/song/Song.hx index 3997692c2..1b7740408 100644 --- a/source/funkin/play/song/Song.hx +++ b/source/funkin/play/song/Song.hx @@ -1,6 +1,5 @@ package funkin.play.song; -import flixel.sound.FlxSound; import funkin.audio.VoicesGroup; import funkin.audio.FunkinSound; import funkin.data.IRegistryEntry; @@ -13,9 +12,8 @@ import funkin.data.song.SongData.SongOffsets; import funkin.data.song.SongData.SongTimeChange; import funkin.data.song.SongData.SongTimeFormat; import funkin.data.song.SongRegistry; -import funkin.data.song.SongRegistry; +import funkin.modding.IScriptedClass.IPlayStateScriptedClass; import funkin.modding.events.ScriptEvent; -import funkin.modding.IScriptedClass; import funkin.util.SortUtil; import openfl.utils.Assets; @@ -31,14 +29,44 @@ import openfl.utils.Assets; @:nullSafety class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMetadata> { - public static final DEFAULT_SONGNAME:String = "Unknown"; - public static final DEFAULT_ARTIST:String = "Unknown"; + /** + * The default value for the song's name + */ + public static final DEFAULT_SONGNAME:String = 'Unknown'; + + /** + * The default value for the song's artist + */ + public static final DEFAULT_ARTIST:String = 'Unknown'; + + /** + * The default value for the song's time format + */ public static final DEFAULT_TIMEFORMAT:SongTimeFormat = SongTimeFormat.MILLISECONDS; + + /** + * The default value for the song's divisions + */ public static final DEFAULT_DIVISIONS:Null<Int> = null; + + /** + * The default value for whether the song loops. + */ public static final DEFAULT_LOOPED:Bool = false; - public static final DEFAULT_STAGE:String = "mainStage"; + + /** + * The default value for the song's playable stage. + */ + public static final DEFAULT_STAGE:String = 'mainStage'; + + /** + * The default value for the song's scroll speed. + */ public static final DEFAULT_SCROLLSPEED:Float = 1.0; + /** + * The internal ID of the song. + */ public final id:String; /** @@ -53,6 +81,9 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta final _metadata:Map<String, SongMetadata>; final difficulties:Map<String, SongDifficulty>; + /** + * The list of variations a song has. + */ public var variations(get, never):Array<String>; function get_variations():Array<String> @@ -65,6 +96,9 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta */ public var validScore:Bool = true; + /** + * The readable name of the song. + */ public var songName(get, never):String; function get_songName():String @@ -74,6 +108,9 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta return DEFAULT_SONGNAME; } + /** + * The artist of the song. + */ public var songArtist(get, never):String; function get_songArtist():String @@ -101,7 +138,7 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta { for (vari in _data.playData.songVariations) { - var variMeta = fetchVariationMetadata(id, vari); + var variMeta:Null<SongMetadata> = fetchVariationMetadata(id, vari); if (variMeta != null) _metadata.set(variMeta.variation, variMeta); } } @@ -115,27 +152,62 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta populateDifficulties(); } - @:allow(funkin.play.song.Song) + /** + * Build a song from existing metadata rather than loading it from the `assets` folder. + * Used by the Chart Editor. + * + * @param songId The ID of the song. + * @param metadata The metadata of the song. + * @param variations The list of variations this song has. + * @param charts The chart data for each variation. + * @param includeScript Whether to initialize the scripted class tied to the song, if it exists. + * @param validScore Whether the song is elegible for highscores. + * @return The constructed song object. + */ public static function buildRaw(songId:String, metadata:Array<SongMetadata>, variations:Array<String>, charts:Map<String, SongChartData>, - validScore:Bool = false):Song + includeScript:Bool = true, validScore:Bool = false):Song { - var result:Song = new Song(songId); + @:privateAccess + var result:Null<Song>; + + if (includeScript && SongRegistry.instance.isScriptedEntry(songId)) + { + var songClassName:String = SongRegistry.instance.getScriptedEntryClassName(songId); + + @:privateAccess + result = SongRegistry.instance.createScriptedEntry(songClassName); + } + else + { + @:privateAccess + result = SongRegistry.instance.createEntry(songId); + } + + if (result == null) throw 'ERROR: Could not build Song instance ($songId), is the attached script bad?'; result._metadata.clear(); for (meta in metadata) + { result._metadata.set(meta.variation, meta); + } result.difficulties.clear(); result.populateDifficulties(); for (variation => chartData in charts) + { result.applyChartData(chartData, variation); + } result.validScore = validScore; return result; } + /** + * Retrieve a list of the raw metadata for the song. + * @return The metadata JSON objects for the song's variations. + */ public function getRawMetadata():Array<SongMetadata> { return _metadata.values(); @@ -192,6 +264,7 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta /** * Parse and cache the chart for all difficulties of this song. + * @param force Whether to forcibly clear the list of charts first. */ public function cacheCharts(force:Bool = false):Void { diff --git a/source/funkin/play/stage/Stage.hx b/source/funkin/play/stage/Stage.hx index 9605c6989..56026469a 100644 --- a/source/funkin/play/stage/Stage.hx +++ b/source/funkin/play/stage/Stage.hx @@ -212,7 +212,7 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements else { // Initalize static sprite. - propSprite.loadTexture(Paths.image(dataProp.assetPath)); + propSprite.loadTexture(dataProp.assetPath); // Disables calls to update() for a performance boost. propSprite.active = false; diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index 942f28297..1eb2a0b02 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -602,6 +602,12 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState */ var enabledDebuggerPopup:Bool = true; + /** + * Whether song scripts should be enabled during playtesting. + * You should probably check the box if the song has custom mechanics. + */ + var playtestSongScripts:Bool = true; + // Visuals /** @@ -1396,7 +1402,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState function get_currentSongId():String { - return currentSongName.toLowerKebabCase().replace('.', '').replace(' ', '-'); + return currentSongName.toLowerKebabCase().replace(' ', '-').sanitize(); } var currentSongArtist(get, set):String; @@ -5320,7 +5326,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState var targetSong:Song; try { - targetSong = Song.buildRaw(currentSongId, songMetadata.values(), availableVariations, songChartData, false); + targetSong = Song.buildRaw(currentSongId, songMetadata.values(), availableVariations, songChartData, playtestSongScripts, false); } catch (e) { @@ -5348,7 +5354,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState Paths.setCurrentLevel('week6'); case 'tankmanBattlefield': Paths.setCurrentLevel('week7'); - case 'phillyStreets' | 'phillyBlazin': + case 'phillyStreets' | 'phillyBlazin' | 'phillyBlazin2': Paths.setCurrentLevel('weekend1'); } diff --git a/source/funkin/ui/debug/charting/handlers/ChartEditorToolboxHandler.hx b/source/funkin/ui/debug/charting/handlers/ChartEditorToolboxHandler.hx index 3b32edf5d..8c7b1a8c1 100644 --- a/source/funkin/ui/debug/charting/handlers/ChartEditorToolboxHandler.hx +++ b/source/funkin/ui/debug/charting/handlers/ChartEditorToolboxHandler.hx @@ -318,6 +318,17 @@ class ChartEditorToolboxHandler state.enabledDebuggerPopup = checkboxDebugger.selected; }; + var checkboxSongScripts:Null<CheckBox> = toolbox.findComponent('playtestSongScriptsCheckbox', CheckBox); + + if (checkboxSongScripts == null) + throw 'ChartEditorToolboxHandler.buildToolboxPlaytestPropertiesLayout() - Could not find playtestSongScriptsCheckbox component.'; + + state.playtestSongScripts = checkboxSongScripts.selected; + + checkboxSongScripts.onClick = _ -> { + state.playtestSongScripts = checkboxSongScripts.selected; + }; + return toolbox; } diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index 45f9a4d27..24008b19d 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -227,7 +227,7 @@ class FreeplayState extends MusicBeatSubState trace(FlxG.camera.initialZoom); trace(FlxCamera.defaultZoom); - var pinkBack:FunkinSprite = FunkinSprite.create(Paths.image('freeplay/pinkBack')); + var pinkBack:FunkinSprite = FunkinSprite.create('freeplay/pinkBack'); pinkBack.color = 0xFFffd4e9; // sets it to pink! pinkBack.x -= pinkBack.width; diff --git a/source/funkin/ui/transition/LoadingState.hx b/source/funkin/ui/transition/LoadingState.hx index 5f755872f..e2f89a8b3 100644 --- a/source/funkin/ui/transition/LoadingState.hx +++ b/source/funkin/ui/transition/LoadingState.hx @@ -48,7 +48,7 @@ class LoadingState extends MusicBeatState var bg:FunkinSprite = new FunkinSprite().makeSolidColor(FlxG.width, FlxG.height, 0xFFcaff4d); add(bg); - funkay = FunkinSprite.create(Paths.image('funkay')); + funkay = FunkinSprite.create('funkay'); funkay.setGraphicSize(0, FlxG.height); funkay.updateHitbox(); add(funkay); @@ -389,9 +389,15 @@ class MultiCallback public function getUnfired():Array<Void->Void> return unfired.array(); + /** + * Perform an FlxG.switchState with a nice transition + * @param state + * @param transitionTex + * @param time + */ public static function coolSwitchState(state:NextState, transitionTex:String = "shaderTransitionStuff/coolDots", time:Float = 2) { - var screenShit:FunkinSprite = FunkinSprite.create(Paths.image("shaderTransitionStuff/coolDots")); + var screenShit:FunkinSprite = FunkinSprite.create('shaderTransitionStuff/coolDots'); var screenWipeShit:ScreenWipeShader = new ScreenWipeShader(); screenWipeShit.funnyShit.input = screenShit.pixels; diff --git a/source/funkin/ui/transition/StickerSubState.hx b/source/funkin/ui/transition/StickerSubState.hx index 40fce6f7d..981a30e09 100644 --- a/source/funkin/ui/transition/StickerSubState.hx +++ b/source/funkin/ui/transition/StickerSubState.hx @@ -313,7 +313,7 @@ class StickerSprite extends FunkinSprite public function new(x:Float, y:Float, stickerSet:String, stickerName:String):Void { super(x, y); - loadTexture(Paths.image('transitionSwag/' + stickerSet + '/' + stickerName)); + loadTexture('transitionSwag/' + stickerSet + '/' + stickerName); updateHitbox(); scrollFactor.set(); } diff --git a/source/funkin/util/tools/StringTools.hx b/source/funkin/util/tools/StringTools.hx index 0585ffeae..b15808d00 100644 --- a/source/funkin/util/tools/StringTools.hx +++ b/source/funkin/util/tools/StringTools.hx @@ -13,15 +13,15 @@ class StringTools */ public static function toTitleCase(value:String):String { - var words:Array<String> = value.split(" "); - var result:String = ""; + var words:Array<String> = value.split(' '); + var result:String = ''; for (i in 0...words.length) { var word:String = words[i]; result += word.charAt(0).toUpperCase() + word.substr(1).toLowerCase(); if (i < words.length - 1) { - result += " "; + result += ' '; } } return result; @@ -35,7 +35,7 @@ class StringTools */ public static function toLowerKebabCase(value:String):String { - return value.toLowerCase().replace(' ', "-"); + return value.toLowerCase().replace(' ', '-'); } /** @@ -46,13 +46,30 @@ class StringTools */ public static function toUpperKebabCase(value:String):String { - return value.toUpperCase().replace(' ', "-"); + return value.toUpperCase().replace(' ', '-'); + } + + /** + * The regular expression to sanitize strings. + */ + static final SANTIZE_REGEX:EReg = ~/[^-a-zA-Z0-9]/g; + + /** + * Remove all instances of symbols other than alpha-numeric characters (and dashes)from a string. + * @param value The string to sanitize. + * @return The sanitized string. + */ + public static function sanitize(value:String):String + { + return SANTIZE_REGEX.replace(value, ''); } /** * Parses the string data as JSON and returns the resulting object. * This is here so you can use `string.parseJSON()` when `using StringTools`. * + * TODO: Remove this and replace with `json2object` + * @param value The * @return The parsed object. */ public static function parseJSON(value:String):Dynamic From 43cf1e71e2785ff95c0de779d6af1032140ecdc4 Mon Sep 17 00:00:00 2001 From: EliteMasterEric <ericmyllyoja@gmail.com> Date: Tue, 12 Mar 2024 21:35:55 -0400 Subject: [PATCH 2/5] Disable camera events in the minimal playtest. --- source/funkin/play/event/FocusCameraSongEvent.hx | 3 +++ source/funkin/play/event/ZoomCameraSongEvent.hx | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/source/funkin/play/event/FocusCameraSongEvent.hx b/source/funkin/play/event/FocusCameraSongEvent.hx index 847df4a60..4ea6fa8c0 100644 --- a/source/funkin/play/event/FocusCameraSongEvent.hx +++ b/source/funkin/play/event/FocusCameraSongEvent.hx @@ -57,6 +57,9 @@ class FocusCameraSongEvent extends SongEvent // Does nothing if there is no PlayState camera or stage. if (PlayState.instance == null || PlayState.instance.currentStage == null) return; + // Does nothing if we are minimal mode. + if (PlayState.instance.minimalMode) return; + var posX:Null<Float> = data.getFloat('x'); if (posX == null) posX = 0.0; var posY:Null<Float> = data.getFloat('y'); diff --git a/source/funkin/play/event/ZoomCameraSongEvent.hx b/source/funkin/play/event/ZoomCameraSongEvent.hx index 809130499..3a903a4ff 100644 --- a/source/funkin/play/event/ZoomCameraSongEvent.hx +++ b/source/funkin/play/event/ZoomCameraSongEvent.hx @@ -55,7 +55,10 @@ class ZoomCameraSongEvent extends SongEvent public override function handleEvent(data:SongEventData):Void { // Does nothing if there is no PlayState camera or stage. - if (PlayState.instance == null) return; + if (PlayState.instance == null || PlayState.instance.currentStage == null) return; + + // Does nothing if we are minimal mode. + if (PlayState.instance.minimalMode) return; var zoom:Null<Float> = data.getFloat('zoom'); if (zoom == null) zoom = 1.0; From 541c7b40414a191b0464f57e0cea6e4948d122ec Mon Sep 17 00:00:00 2001 From: EliteMasterEric <ericmyllyoja@gmail.com> Date: Tue, 12 Mar 2024 21:36:32 -0400 Subject: [PATCH 3/5] Update assets submodule --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index fe8c987eb..b71005291 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit fe8c987eb846ceb73b8518879b506111aaccdf80 +Subproject commit b71005291132a09043cabb59511d9316a21039ca From a6fecd05e05d555b1769a43a7c38b28d9d324e1c Mon Sep 17 00:00:00 2001 From: Cameron Taylor <cameron.taylor.ninja@gmail.com> Date: Wed, 13 Mar 2024 18:58:18 -0700 Subject: [PATCH 4/5] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index b71005291..b498f7f75 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit b71005291132a09043cabb59511d9316a21039ca +Subproject commit b498f7f7569af24c25c88836d087f93529c2c6be From b5bc63fecaf6674f60d744de7cf02e82b0536f4f Mon Sep 17 00:00:00 2001 From: Cameron Taylor <cameron.taylor.ninja@gmail.com> Date: Wed, 13 Mar 2024 21:27:09 -0700 Subject: [PATCH 5/5] use isMinimalMode --- source/funkin/play/event/FocusCameraSongEvent.hx | 2 +- source/funkin/play/event/ZoomCameraSongEvent.hx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/funkin/play/event/FocusCameraSongEvent.hx b/source/funkin/play/event/FocusCameraSongEvent.hx index 4ea6fa8c0..625b9cb7a 100644 --- a/source/funkin/play/event/FocusCameraSongEvent.hx +++ b/source/funkin/play/event/FocusCameraSongEvent.hx @@ -58,7 +58,7 @@ class FocusCameraSongEvent extends SongEvent if (PlayState.instance == null || PlayState.instance.currentStage == null) return; // Does nothing if we are minimal mode. - if (PlayState.instance.minimalMode) return; + if (PlayState.instance.isMinimalMode) return; var posX:Null<Float> = data.getFloat('x'); if (posX == null) posX = 0.0; diff --git a/source/funkin/play/event/ZoomCameraSongEvent.hx b/source/funkin/play/event/ZoomCameraSongEvent.hx index 3a903a4ff..d1ce97e40 100644 --- a/source/funkin/play/event/ZoomCameraSongEvent.hx +++ b/source/funkin/play/event/ZoomCameraSongEvent.hx @@ -58,7 +58,7 @@ class ZoomCameraSongEvent extends SongEvent if (PlayState.instance == null || PlayState.instance.currentStage == null) return; // Does nothing if we are minimal mode. - if (PlayState.instance.minimalMode) return; + if (PlayState.instance.isMinimalMode) return; var zoom:Null<Float> = data.getFloat('zoom'); if (zoom == null) zoom = 1.0;