From d0f81add959c5418e3dbeb5c61fc933afb0dad51 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Mon, 4 Mar 2024 21:19:24 -0500 Subject: [PATCH 01/31] Fix a bug where Chart Editor Playtest destroys the vocals and crashes --- source/funkin/play/PlayState.hx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index d090b4f8a..d7cc1493c 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -2787,14 +2787,19 @@ class PlayState extends MusicBeatSubState // TODO: Uncache the song. } - if (!overrideMusic) + if (overrideMusic) { - // Stop the music. + // Stop the music. Do NOT destroy it, something still references it! FlxG.sound.music.pause(); - if (vocals != null) vocals.stop(); + if (vocals != null) + { + vocals.pause(); + remove(vocals); + } } else { + // Stop and destroy the music. FlxG.sound.music.pause(); if (vocals != null) { From ae5f48c29cf86fa1c9691eb1c6d854a5c1712d6b Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 5 Mar 2024 00:22:29 -0500 Subject: [PATCH 02/31] Fix issues with story mode colors breaking --- source/funkin/ui/story/Level.hx | 11 +++++++++-- source/funkin/ui/story/LevelProp.hx | 14 ++++++++++++-- source/funkin/ui/story/StoryMenuState.hx | 7 ++++--- 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/source/funkin/ui/story/Level.hx b/source/funkin/ui/story/Level.hx index c93ad41a6..b548b7b1e 100644 --- a/source/funkin/ui/story/Level.hx +++ b/source/funkin/ui/story/Level.hx @@ -201,8 +201,15 @@ class Level implements IRegistryEntry if (existingProp != null) { existingProp.propData = propData; - existingProp.x = propData.offsets[0] + FlxG.width * 0.25 * propIndex; - existingProp.visible = true; + if (existingProp.propData == null) + { + existingProp.visible = false; + } + else + { + existingProp.visible = true; + existingProp.x = propData.offsets[0] + FlxG.width * 0.25 * propIndex; + } } else { diff --git a/source/funkin/ui/story/LevelProp.hx b/source/funkin/ui/story/LevelProp.hx index 5af383de9..b126f0243 100644 --- a/source/funkin/ui/story/LevelProp.hx +++ b/source/funkin/ui/story/LevelProp.hx @@ -11,11 +11,11 @@ class LevelProp extends Bopper function set_propData(value:LevelPropData):LevelPropData { // Only reset the prop if the asset path has changed. - if (propData == null || value.assetPath != this.propData.assetPath) + if (propData == null || value?.assetPath != propData?.assetPath) { this.visible = (value != null); this.propData = value; - danceEvery = this.propData.danceEvery; + danceEvery = this.propData?.danceEvery ?? 0; applyData(); } @@ -35,6 +35,16 @@ class LevelProp extends Bopper function applyData():Void { + if (propData == null) + { + this.visible = false; + return; + } + else + { + this.visible = true; + } + var isAnimated:Bool = propData.animations.length > 0; if (isAnimated) { diff --git a/source/funkin/ui/story/StoryMenuState.hx b/source/funkin/ui/story/StoryMenuState.hx index ba1d2ed21..8b477feee 100644 --- a/source/funkin/ui/story/StoryMenuState.hx +++ b/source/funkin/ui/story/StoryMenuState.hx @@ -141,10 +141,10 @@ class StoryMenuState extends MusicBeatState persistentUpdate = persistentDraw = true; - updateData(); - rememberSelection(); + updateData(); + // Explicitly define the background color. this.bgColor = FlxColor.BLACK; @@ -403,7 +403,8 @@ class StoryMenuState extends MusicBeatState function hasModdedLevels():Bool { - return LevelRegistry.instance.listModdedLevelIds().length > 0; + return false; + // return LevelRegistry.instance.listModdedLevelIds().length > 0; } /** From 9212ea9c90dad5902a9e4c4a93b162897647e0d5 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 5 Mar 2024 02:29:44 -0500 Subject: [PATCH 03/31] Script fixes for 2hot explosions breaking Polymod --- assets | 2 +- hmm.json | 2 +- source/funkin/data/level/LevelData.hx | 22 +++- source/funkin/modding/PolymodHandler.hx | 130 ++++++++++++++++-------- source/funkin/ui/story/Level.hx | 51 ++++++++-- 5 files changed, 152 insertions(+), 55 deletions(-) diff --git a/assets b/assets index 55c602f2a..14b86f436 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 55c602f2adbbd84de541ea86e5e646c4d2a1df0b +Subproject commit 14b86f4369fddf61eb76116139eb33fa8f6e92d0 diff --git a/hmm.json b/hmm.json index 836b01c9b..cbd5fea30 100644 --- a/hmm.json +++ b/hmm.json @@ -146,7 +146,7 @@ "name": "polymod", "type": "git", "dir": null, - "ref": "d5a3b8995f64d20b95f844454e8c3b38c3d3a9fa", + "ref": "be712450e5d3ba446008884921bb56873b299a64", "url": "https://github.com/larsiusprime/polymod" }, { diff --git a/source/funkin/data/level/LevelData.hx b/source/funkin/data/level/LevelData.hx index 843389cae..f5e58ae16 100644 --- a/source/funkin/data/level/LevelData.hx +++ b/source/funkin/data/level/LevelData.hx @@ -17,7 +17,7 @@ typedef LevelData = var version:String; /** - * The title of the week, as seen in the top corner. + * The title of the level, as seen in the top corner. */ var name:String; @@ -27,21 +27,35 @@ typedef LevelData = @:jcustomparse(funkin.data.DataParse.stringNotEmpty) var titleAsset:String; + /** + * The props to display over the colored background. + * In the base game this is usually Boyfriend and the opponent. + */ @:default([]) var props:Array; - @:default(["bopeebo"]) + + /** + * The list of song IDs included in this level. + */ + @:default(['bopeebo']) var songs:Array; - @:default("#F9CF51") + + /** + * The background for the level behind the props. + */ + @:default('#F9CF51') @:optional var background:String; } +/** + * Data for a single prop for a story mode level. + */ typedef LevelPropData = { /** * The image to use for the prop. May optionally be a sprite sheet. */ - // @:jcustomparse(funkin.data.DataParse.stringNotEmpty) var assetPath:String; /** diff --git a/source/funkin/modding/PolymodHandler.hx b/source/funkin/modding/PolymodHandler.hx index 889f63073..b1c6b511a 100644 --- a/source/funkin/modding/PolymodHandler.hx +++ b/source/funkin/modding/PolymodHandler.hx @@ -1,24 +1,25 @@ package funkin.modding; -import funkin.util.macro.ClassMacro; -import funkin.modding.module.ModuleHandler; -import funkin.data.song.SongData; -import funkin.data.stage.StageData; -import polymod.Polymod; -import polymod.backends.PolymodAssets.PolymodAssetType; -import polymod.format.ParseRules.TextFileFormat; -import funkin.data.event.SongEventRegistry; -import funkin.data.stage.StageRegistry; -import funkin.util.FileUtil; -import funkin.data.level.LevelRegistry; -import funkin.data.notestyle.NoteStyleRegistry; import funkin.data.dialogue.ConversationRegistry; import funkin.data.dialogue.DialogueBoxRegistry; import funkin.data.dialogue.SpeakerRegistry; +import funkin.data.event.SongEventRegistry; +import funkin.data.level.LevelRegistry; +import funkin.data.notestyle.NoteStyleRegistry; +import funkin.data.song.SongRegistry; +import funkin.data.stage.StageRegistry; +import funkin.modding.module.ModuleHandler; import funkin.play.character.CharacterData.CharacterDataParser; import funkin.save.Save; -import funkin.data.song.SongRegistry; +import funkin.util.FileUtil; +import funkin.util.macro.ClassMacro; +import polymod.backends.PolymodAssets.PolymodAssetType; +import polymod.format.ParseRules.TextFileFormat; +import polymod.Polymod; +/** + * A class for interacting with Polymod, the atomic modding framework for Haxe. + */ class PolymodHandler { /** @@ -27,16 +28,33 @@ class PolymodHandler * Bug fixes increment the patch version, new features increment the minor version. * Changes that break old mods increment the major version. */ - static final API_VERSION:String = "0.1.0"; + static final API_VERSION:String = '0.1.0'; /** * Where relative to the executable that mods are located. */ - static final MOD_FOLDER:String = #if (REDIRECT_ASSETS_FOLDER && macos) "../../../../../../../example_mods" #elseif REDIRECT_ASSETS_FOLDER "../../../../example_mods" #else "mods" #end; + static final MOD_FOLDER:String = + #if (REDIRECT_ASSETS_FOLDER && macos) + '../../../../../../../example_mods' + #elseif REDIRECT_ASSETS_FOLDER + '../../../../example_mods' + #else + 'mods' + #end; - static final CORE_FOLDER:Null = #if (REDIRECT_ASSETS_FOLDER && macos) "../../../../../../../assets" #elseif REDIRECT_ASSETS_FOLDER "../../../../assets" #else null #end; + static final CORE_FOLDER:Null = + #if (REDIRECT_ASSETS_FOLDER && macos) + '../../../../../../../assets' + #elseif REDIRECT_ASSETS_FOLDER + '../../../../assets' + #else + null + #end; - public static function createModRoot() + /** + * If the mods folder doesn't exist, create it. + */ + public static function createModRoot():Void { FileUtil.createDirIfNotExists(MOD_FOLDER); } @@ -44,40 +62,44 @@ class PolymodHandler /** * Loads the game with ALL mods enabled with Polymod. */ - public static function loadAllMods() + public static function loadAllMods():Void { // Create the mod root if it doesn't exist. createModRoot(); - trace("Initializing Polymod (using all mods)..."); + trace('Initializing Polymod (using all mods)...'); loadModsById(getAllModIds()); } /** * Loads the game with configured mods enabled with Polymod. */ - public static function loadEnabledMods() + public static function loadEnabledMods():Void { // Create the mod root if it doesn't exist. createModRoot(); - trace("Initializing Polymod (using configured mods)..."); + trace('Initializing Polymod (using configured mods)...'); loadModsById(Save.instance.enabledModIds); } /** * Loads the game without any mods enabled with Polymod. */ - public static function loadNoMods() + public static function loadNoMods():Void { // Create the mod root if it doesn't exist. createModRoot(); // We still need to configure the debug print calls etc. - trace("Initializing Polymod (using no mods)..."); + trace('Initializing Polymod (using no mods)...'); loadModsById([]); } - public static function loadModsById(ids:Array) + /** + * Load all the mods with the given ids. + * @param ids The ORDERED list of mod ids to load. + */ + public static function loadModsById(ids:Array):Void { if (ids.length == 0) { @@ -90,7 +112,7 @@ class PolymodHandler buildImports(); - var loadedModList = polymod.Polymod.init( + var loadedModList:Array = polymod.Polymod.init( { // Root directory for all mods. modRoot: MOD_FOLDER, @@ -142,30 +164,40 @@ class PolymodHandler } #if debug - var fileList = Polymod.listModFiles(PolymodAssetType.IMAGE); + var fileList:Array = Polymod.listModFiles(PolymodAssetType.IMAGE); trace('Installed mods have replaced ${fileList.length} images.'); for (item in fileList) + { trace(' * $item'); + } fileList = Polymod.listModFiles(PolymodAssetType.TEXT); trace('Installed mods have added/replaced ${fileList.length} text files.'); for (item in fileList) + { trace(' * $item'); + } fileList = Polymod.listModFiles(PolymodAssetType.AUDIO_MUSIC); trace('Installed mods have replaced ${fileList.length} music files.'); for (item in fileList) + { trace(' * $item'); + } fileList = Polymod.listModFiles(PolymodAssetType.AUDIO_SOUND); trace('Installed mods have replaced ${fileList.length} sound files.'); for (item in fileList) + { trace(' * $item'); + } fileList = Polymod.listModFiles(PolymodAssetType.AUDIO_GENERIC); trace('Installed mods have replaced ${fileList.length} generic audio files.'); for (item in fileList) + { trace(' * $item'); + } #end } @@ -183,21 +215,21 @@ class PolymodHandler for (cls in ClassMacro.listClassesInPackage('polymod')) { if (cls == null) continue; - var className = Type.getClassName(cls); + var className:String = Type.getClassName(cls); Polymod.blacklistImport(className); } } static function buildParseRules():polymod.format.ParseRules { - var output = polymod.format.ParseRules.getDefault(); + var output:polymod.format.ParseRules = polymod.format.ParseRules.getDefault(); // Ensure TXT files have merge support. - output.addType("txt", TextFileFormat.LINES); + output.addType('txt', TextFileFormat.LINES); // Ensure script files have merge support. - output.addType("hscript", TextFileFormat.PLAINTEXT); - output.addType("hxs", TextFileFormat.PLAINTEXT); - output.addType("hxc", TextFileFormat.PLAINTEXT); - output.addType("hx", TextFileFormat.PLAINTEXT); + output.addType('hscript', TextFileFormat.PLAINTEXT); + output.addType('hxs', TextFileFormat.PLAINTEXT); + output.addType('hxc', TextFileFormat.PLAINTEXT); + output.addType('hx', TextFileFormat.PLAINTEXT); // You can specify the format of a specific file, with file extension. // output.addFile("data/introText.txt", TextFileFormat.LINES) @@ -208,17 +240,21 @@ class PolymodHandler { return { assetLibraryPaths: [ - "default" => "preload", "shared" => "shared", "songs" => "songs", "tutorial" => "tutorial", "week1" => "week1", "week2" => "week2", - "week3" => "week3", "week4" => "week4", "week5" => "week5", "week6" => "week6", "week7" => "week7", "weekend1" => "weekend1", + 'default' => 'preload', 'shared' => 'shared', 'songs' => 'songs', 'tutorial' => 'tutorial', 'week1' => 'week1', 'week2' => 'week2', + 'week3' => 'week3', 'week4' => 'week4', 'week5' => 'week5', 'week6' => 'week6', 'week7' => 'week7', 'weekend1' => 'weekend1', ], coreAssetRedirect: CORE_FOLDER, } } + /** + * Retrieve a list of metadata for ALL installed mods, including disabled mods. + * @return An array of mod metadata + */ public static function getAllMods():Array { trace('Scanning the mods folder...'); - var modMetadata = Polymod.scan( + var modMetadata:Array = Polymod.scan( { modRoot: MOD_FOLDER, apiVersionRule: API_VERSION, @@ -228,17 +264,25 @@ class PolymodHandler return modMetadata; } + /** + * Retrieve a list of ALL mod IDs, including disabled mods. + * @return An array of mod IDs + */ public static function getAllModIds():Array { - var modIds = [for (i in getAllMods()) i.id]; + var modIds:Array = [for (i in getAllMods()) i.id]; return modIds; } + /** + * Retrieve a list of metadata for all enabled mods. + * @return An array of mod metadata + */ public static function getEnabledMods():Array { - var modIds = Save.instance.enabledModIds; - var modMetadata = getAllMods(); - var enabledMods = []; + var modIds:Array = Save.instance.enabledModIds; + var modMetadata:Array = getAllMods(); + var enabledMods:Array = []; for (item in modMetadata) { if (modIds.indexOf(item.id) != -1) @@ -249,7 +293,11 @@ class PolymodHandler return enabledMods; } - public static function forceReloadAssets() + /** + * Clear and reload from disk all data assets. + * Useful for "hot reloading" for fast iteration! + */ + public static function forceReloadAssets():Void { // Forcibly clear scripts so that scripts can be edited. ModuleHandler.clearModuleCache(); diff --git a/source/funkin/ui/story/Level.hx b/source/funkin/ui/story/Level.hx index b548b7b1e..626fb8e52 100644 --- a/source/funkin/ui/story/Level.hx +++ b/source/funkin/ui/story/Level.hx @@ -51,6 +51,7 @@ class Level implements IRegistryEntry /** * Retrieve the title of the level for display on the menu. + * @return Title of the level as a string */ public function getTitle():String { @@ -58,16 +59,21 @@ class Level implements IRegistryEntry return _data.name; } + /** + * Construct the title graphic for the level. + * @return The constructed graphic as a sprite. + */ public function buildTitleGraphic():FlxSprite { - var result = new FlxSprite().loadGraphic(Paths.image(_data.titleAsset)); + var result:FlxSprite = new FlxSprite().loadGraphic(Paths.image(_data.titleAsset)); return result; } /** * Get the list of songs in this level, as an array of names, for display on the menu. - * @return Array + * @param difficulty The difficulty of the level being displayed + * @return The display names of the songs in this level */ public function getSongDisplayNames(difficulty:String):Array { @@ -88,7 +94,9 @@ class Level implements IRegistryEntry /** * Whether this level is unlocked. If not, it will be greyed out on the menu and have a lock icon. - * TODO: Change this behavior in a later release. + * Override this in a script. + * @default `true` + * @return Whether this level is unlocked */ public function isUnlocked():Bool { @@ -97,6 +105,9 @@ class Level implements IRegistryEntry /** * Whether this level is visible. If not, it will not be shown on the menu at all. + * Override this in a script. + * @default `true` + * @return Whether this level is visible in the menu */ public function isVisible():Bool { @@ -106,6 +117,7 @@ class Level implements IRegistryEntry /** * Build a sprite for the background of the level. * Can be overriden by ScriptedLevel. Not used if `isBackgroundSimple` returns true. + * @return The constructed sprite */ public function buildBackground():FlxSprite { @@ -124,6 +136,7 @@ class Level implements IRegistryEntry /** * Returns true if the background is a solid color. * If you have a ScriptedLevel with a fancy background, you may want to override this to false. + * @return Whether the background is a simple color */ public function isBackgroundSimple():Bool { @@ -133,30 +146,36 @@ class Level implements IRegistryEntry /** * Returns true if the background is a solid color. * If you have a ScriptedLevel with a fancy background, you may want to override this to false. + * @return The background as a simple color. May not be valid if `isBackgroundSimple` returns false. */ public function getBackgroundColor():FlxColor { return FlxColor.fromString(_data.background); } + /** + * The list of difficulties the player can select from for this level. + * @return The difficulty IDs. + */ public function getDifficulties():Array { var difficulties:Array = []; - var songList = getSongs(); + var songList:Array = getSongs(); var firstSongId:String = songList[0]; var firstSong:Song = SongRegistry.instance.fetchEntry(firstSongId); if (firstSong != null) { - // Don't display alternate characters in Story Mode. - for (difficulty in firstSong.listDifficulties([Constants.DEFAULT_VARIATION, "erect"])) + // Don't display alternate characters in Story Mode. Only show `default` and `erect` variations. + for (difficulty in firstSong.listDifficulties([Constants.DEFAULT_VARIATION, 'erect'])) { difficulties.push(difficulty); } } + // Sort in a specific order! Fall back to alphabetical. difficulties.sort(SortUtil.defaultsThenAlphabetically.bind(Constants.DEFAULT_DIFFICULTY_LIST)); // Filter to only include difficulties that are present in all songs @@ -169,7 +188,7 @@ class Level implements IRegistryEntry for (difficulty in difficulties) { - if (!song.hasDifficulty(difficulty, [Constants.DEFAULT_VARIATION, "erect"])) + if (!song.hasDifficulty(difficulty, [Constants.DEFAULT_VARIATION, 'erect'])) { difficulties.remove(difficulty); } @@ -181,6 +200,11 @@ class Level implements IRegistryEntry return difficulties; } + /** + * Build the props for display over the colored background. + * @param existingProps The existing prop sprites, if any. + * @return The constructed prop sprites + */ public function buildProps(?existingProps:Array):Array { var props:Array = existingProps == null ? [] : [for (x in existingProps) x]; @@ -189,11 +213,13 @@ class Level implements IRegistryEntry var hiddenProps:Array = props.splice(_data.props.length - 1, props.length - 1); for (hiddenProp in hiddenProps) + { hiddenProp.visible = false; + } for (propIndex in 0..._data.props.length) { - var propData = _data.props[propIndex]; + var propData:LevelPropData = _data.props[propIndex]; // Attempt to reuse the `LevelProp` object. // This prevents animations from resetting. @@ -224,6 +250,10 @@ class Level implements IRegistryEntry return props; } + /** + * Called when the level is destroyed. + * TODO: Document when this gets called + */ public function destroy():Void {} public function toString():String @@ -231,6 +261,11 @@ class Level implements IRegistryEntry return 'Level($id)'; } + /** + * Retrieve and parse the JSON data for a level by ID. + * @param id The ID of the level + * @return The parsed level data, or null if not found or invalid + */ static function _fetchData(id:String):Null { return LevelRegistry.instance.parseEntryDataWithMigration(id, LevelRegistry.instance.fetchEntryVersion(id)); From 6db5d10a55932cb817d429b950313d4578096554 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Tue, 5 Mar 2024 19:35:51 -0500 Subject: [PATCH 04/31] jenny assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 14b86f436..69ebdb6a7 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 14b86f4369fddf61eb76116139eb33fa8f6e92d0 +Subproject commit 69ebdb6a7aa57b6762ce509243679ab959615120 From 059e1c0e13c9560edbe3454e6cf7ae9be8b3886a Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 5 Mar 2024 21:48:04 -0500 Subject: [PATCH 05/31] Implement onNoteIncoming script event. --- assets | 2 +- source/funkin/modding/IScriptedClass.hx | 28 ++++++++++--------- .../modding/events/ScriptEventDispatcher.hx | 16 +++++++++-- .../funkin/modding/events/ScriptEventType.hx | 7 +++++ source/funkin/modding/module/Module.hx | 4 ++- source/funkin/play/PlayState.hx | 13 +++++++-- source/funkin/play/character/BaseCharacter.hx | 2 +- source/funkin/play/notes/Strumline.hx | 7 +++++ source/funkin/play/song/Song.hx | 4 ++- source/funkin/play/stage/Bopper.hx | 4 ++- source/funkin/play/stage/Stage.hx | 4 ++- .../ui/debug/charting/ChartEditorState.hx | 2 +- .../ui/haxeui/components/CharacterPlayer.hx | 8 +++++- 13 files changed, 76 insertions(+), 25 deletions(-) diff --git a/assets b/assets index 69ebdb6a7..518349369 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 69ebdb6a7aa57b6762ce509243679ab959615120 +Subproject commit 518349369ea3237504e8100c901313185bb5b80f diff --git a/source/funkin/modding/IScriptedClass.hx b/source/funkin/modding/IScriptedClass.hx index b009aea41..5f2ff2b9e 100644 --- a/source/funkin/modding/IScriptedClass.hx +++ b/source/funkin/modding/IScriptedClass.hx @@ -56,7 +56,20 @@ interface IStateStageProp extends IScriptedClass */ interface INoteScriptedClass extends IScriptedClass { - public function onNoteHit(event:NoteScriptEvent):Void; + /** + * Called when a note enters the field of view and approaches the strumline. + */ + public function onNoteIncoming(event:NoteScriptEvent):Void; + + /** + * Called when EITHER player hits a note. + * Query the note attached to the event to determine if it was hit by the player or CPU. + */ + public function onNoteHit(event:HitNoteScriptEvent):Void; + + /** + * Called when EITHER player (usually the player) misses a note. + */ public function onNoteMiss(event:NoteScriptEvent):Void; } @@ -73,7 +86,7 @@ interface INoteScriptedClass extends IScriptedClass /** * Defines a set of callbacks available to scripted classes that involve the lifecycle of the Play State. */ -interface IPlayStateScriptedClass extends IScriptedClass +interface IPlayStateScriptedClass extends INoteScriptedClass { /** * Called when the game is paused. @@ -113,17 +126,6 @@ interface IPlayStateScriptedClass extends IScriptedClass */ public function onSongRetry(event:ScriptEvent):Void; - /** - * Called when EITHER player hits a note. - * Query the note attached to the event to determine if it was hit by the player or CPU. - */ - public function onNoteHit(event:NoteScriptEvent):Void; - - /** - * Called when EITHER player (usually the player) misses a note. - */ - public function onNoteMiss(event:NoteScriptEvent):Void; - /** * Called when the player presses a key when no note is on the strumline. */ diff --git a/source/funkin/modding/events/ScriptEventDispatcher.hx b/source/funkin/modding/events/ScriptEventDispatcher.hx index f5d797ea4..fd58d0fad 100644 --- a/source/funkin/modding/events/ScriptEventDispatcher.hx +++ b/source/funkin/modding/events/ScriptEventDispatcher.hx @@ -71,17 +71,29 @@ class ScriptEventDispatcher } } - if (Std.isOfType(target, IPlayStateScriptedClass)) + if (Std.isOfType(target, INoteScriptedClass)) { - var t:IPlayStateScriptedClass = cast(target, IPlayStateScriptedClass); + var t:INoteScriptedClass = cast(target, INoteScriptedClass); switch (event.type) { + case NOTE_INCOMING: + t.onNoteIncoming(cast event); + return; case NOTE_HIT: t.onNoteHit(cast event); return; case NOTE_MISS: t.onNoteMiss(cast event); return; + default: // Continue; + } + } + + if (Std.isOfType(target, IPlayStateScriptedClass)) + { + var t:IPlayStateScriptedClass = cast(target, IPlayStateScriptedClass); + switch (event.type) + { case NOTE_GHOST_MISS: t.onNoteGhostMiss(cast event); return; diff --git a/source/funkin/modding/events/ScriptEventType.hx b/source/funkin/modding/events/ScriptEventType.hx index e06b5ad24..eeeb8ef29 100644 --- a/source/funkin/modding/events/ScriptEventType.hx +++ b/source/funkin/modding/events/ScriptEventType.hx @@ -63,6 +63,13 @@ enum abstract ScriptEventType(String) from String to String */ var SONG_STEP_HIT = 'STEP_HIT'; + /** + * Called when a note comes on screen and starts approaching the strumline. + * + * This event is not cancelable. + */ + var NOTE_INCOMING = 'NOTE_INCOMING'; + /** * Called when a character hits a note. * Important information such as judgement/timing, note data, player/opponent, etc. are all provided. diff --git a/source/funkin/modding/module/Module.hx b/source/funkin/modding/module/Module.hx index f50c9936a..be9b7146b 100644 --- a/source/funkin/modding/module/Module.hx +++ b/source/funkin/modding/module/Module.hx @@ -83,7 +83,9 @@ class Module implements IPlayStateScriptedClass implements IStateChangingScripte public function onGameOver(event:ScriptEvent) {} - public function onNoteHit(event:NoteScriptEvent) {} + public function onNoteIncoming(event:NoteScriptEvent) {} + + public function onNoteHit(event:HitNoteScriptEvent) {} public function onNoteMiss(event:NoteScriptEvent) {} diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index e7ad326d2..5564c46c2 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -1614,8 +1614,10 @@ class PlayState extends MusicBeatSubState var noteStyle:NoteStyle = NoteStyleRegistry.instance.fetchEntry(noteStyleId); if (noteStyle == null) noteStyle = NoteStyleRegistry.instance.fetchDefault(); - playerStrumline = new Strumline(noteStyle, true); + playerStrumline = new Strumline(noteStyle, !isBotPlayMode); + playerStrumline.onNoteIncoming.add(onStrumlineNoteIncoming); opponentStrumline = new Strumline(noteStyle, false); + opponentStrumline.onNoteIncoming.add(onStrumlineNoteIncoming); add(playerStrumline); add(opponentStrumline); @@ -1751,6 +1753,13 @@ class PlayState extends MusicBeatSubState opponentStrumline.applyNoteData(opponentNoteData); } + function onStrumlineNoteIncoming(noteSprite:NoteSprite):Void + { + var event:NoteScriptEvent = new NoteScriptEvent(NOTE_INCOMING, noteSprite, 0, false); + + dispatchEvent(event); + } + /** * Prepares to start the countdown. * Ends any running cutscenes, creates the strumlines, and starts the countdown. @@ -1942,7 +1951,7 @@ class PlayState extends MusicBeatSubState // Call an event to allow canceling the note hit. // NOTE: This is what handles the character animations! - var event:NoteScriptEvent = new NoteScriptEvent(NOTE_HIT, note, 0, true); + var event:NoteScriptEvent = new HitNoteScriptEvent(note, 0.0, 0, 'perfect', 0); dispatchEvent(event); // Calling event.cancelEvent() skips all the other logic! Neat! diff --git a/source/funkin/play/character/BaseCharacter.hx b/source/funkin/play/character/BaseCharacter.hx index cf5311bdc..d39f19b76 100644 --- a/source/funkin/play/character/BaseCharacter.hx +++ b/source/funkin/play/character/BaseCharacter.hx @@ -485,7 +485,7 @@ class BaseCharacter extends Bopper * Every time a note is hit, check if the note is from the same strumline. * If it is, then play the sing animation. */ - public override function onNoteHit(event:NoteScriptEvent) + public override function onNoteHit(event:HitNoteScriptEvent) { super.onNoteHit(event); diff --git a/source/funkin/play/notes/Strumline.hx b/source/funkin/play/notes/Strumline.hx index 190aa3ee0..1ba5dcfc5 100644 --- a/source/funkin/play/notes/Strumline.hx +++ b/source/funkin/play/notes/Strumline.hx @@ -1,5 +1,6 @@ package funkin.play.notes; +import flixel.util.FlxSignal.FlxTypedSignal; import flixel.FlxG; import funkin.play.notes.notestyle.NoteStyle; import flixel.group.FlxSpriteGroup; @@ -49,6 +50,8 @@ class Strumline extends FlxSpriteGroup public var holdNotes:FlxTypedSpriteGroup; + public var onNoteIncoming:FlxTypedSignalVoid>; + var strumlineNotes:FlxTypedSpriteGroup; var noteSplashes:FlxTypedSpriteGroup; var noteHoldCovers:FlxTypedSpriteGroup; @@ -106,6 +109,8 @@ class Strumline extends FlxSpriteGroup this.refresh(); + this.onNoteIncoming = new FlxTypedSignalVoid>(); + for (i in 0...KEY_COUNT) { var child:StrumlineNote = new StrumlineNote(noteStyle, isPlayer, DIRECTIONS[i]); @@ -311,6 +316,8 @@ class Strumline extends FlxSpriteGroup } nextNoteIndex = noteIndex + 1; // Increment the nextNoteIndex rather than splicing the array, because splicing is slow. + + onNoteIncoming.dispatch(noteSprite); } // Update rendering of notes. diff --git a/source/funkin/play/song/Song.hx b/source/funkin/play/song/Song.hx index 61f83d1ed..3997692c2 100644 --- a/source/funkin/play/song/Song.hx +++ b/source/funkin/play/song/Song.hx @@ -364,7 +364,9 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry Date: Tue, 5 Mar 2024 22:27:07 -0500 Subject: [PATCH 06/31] Implement a botplay checkbox in the chart editor --- assets | 2 +- source/funkin/play/PlayState.hx | 71 +++++++++++++++++-- source/funkin/play/notes/Strumline.hx | 4 ++ .../ui/debug/charting/ChartEditorState.hx | 6 ++ .../handlers/ChartEditorToolboxHandler.hx | 9 +++ source/funkin/ui/freeplay/FreeplayState.hx | 8 +-- 6 files changed, 88 insertions(+), 12 deletions(-) diff --git a/assets b/assets index 69ebdb6a7..51b02f0d4 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 69ebdb6a7aa57b6762ce509243679ab959615120 +Subproject commit 51b02f0d47e5b34bf8589065c092953c10c5040d diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index e7ad326d2..46c09090d 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -111,6 +111,11 @@ typedef PlayStateParams = * @default `false` */ ?practiceMode:Bool, + /** + * Whether the song should start in Bot Play Mode. + * @default `false` + */ + ?botPlayMode:Bool, /** * Whether the song should be in minimal mode. * @default `false` @@ -282,6 +287,12 @@ class PlayState extends MusicBeatSubState */ public var isPracticeMode:Bool = false; + /** + * Whether the game is currently in Bot Play Mode. + * If true, player will not lose gain or lose score from notes. + */ + public var isBotPlayMode:Bool = false; + /** * Whether the player has dropped below zero health, * and we are just waiting for an animation to play out before transitioning. @@ -566,6 +577,7 @@ class PlayState extends MusicBeatSubState if (params.targetDifficulty != null) currentDifficulty = params.targetDifficulty; if (params.targetVariation != null) currentVariation = params.targetVariation; isPracticeMode = params.practiceMode ?? false; + isBotPlayMode = params.botPlayMode ?? false; isMinimalMode = params.minimalMode ?? false; startTimestamp = params.startTimestamp ?? 0.0; playbackRate = params.playbackRate ?? 1.0; @@ -1614,7 +1626,7 @@ class PlayState extends MusicBeatSubState var noteStyle:NoteStyle = NoteStyleRegistry.instance.fetchEntry(noteStyleId); if (noteStyle == null) noteStyle = NoteStyleRegistry.instance.fetchDefault(); - playerStrumline = new Strumline(noteStyle, true); + playerStrumline = new Strumline(noteStyle, !isBotPlayMode); opponentStrumline = new Strumline(noteStyle, false); add(playerStrumline); add(opponentStrumline); @@ -1876,7 +1888,14 @@ class PlayState extends MusicBeatSubState function updateScoreText():Void { // TODO: Add functionality for modules to update the score text. - scoreText.text = 'Score:' + songScore; + if (isBotPlayMode) + { + scoreText.text = 'Bot Play Enabled'; + } + else + { + scoreText.text = 'Score:' + songScore; + } } /** @@ -1884,7 +1903,14 @@ class PlayState extends MusicBeatSubState */ function updateHealthBar():Void { - healthLerp = FlxMath.lerp(healthLerp, health, 0.15); + if (isBotPlayMode) + { + healthLerp = Constants.HEALTH_MAX; + } + else + { + healthLerp = FlxMath.lerp(healthLerp, health, 0.15); + } } /** @@ -1928,13 +1954,16 @@ class PlayState extends MusicBeatSubState if (Conductor.instance.songPosition > hitWindowEnd) { - if (note.hasMissed) continue; + if (note.hasMissed || note.hasBeenHit) continue; note.tooEarly = false; note.mayHit = false; note.hasMissed = true; - if (note.holdNoteSprite != null) note.holdNoteSprite.missedNote = true; + if (note.holdNoteSprite != null) + { + note.holdNoteSprite.missedNote = true; + } } else if (Conductor.instance.songPosition > hitWindowCenter) { @@ -2021,10 +2050,38 @@ class PlayState extends MusicBeatSubState if (Conductor.instance.songPosition > hitWindowEnd) { + if (note.hasMissed || note.hasBeenHit) continue; note.tooEarly = false; note.mayHit = false; note.hasMissed = true; - if (note.holdNoteSprite != null) note.holdNoteSprite.missedNote = true; + if (note.holdNoteSprite != null) + { + note.holdNoteSprite.missedNote = true; + } + } + else if (isBotPlayMode && Conductor.instance.songPosition > hitWindowCenter) + { + if (note.hasBeenHit) continue; + + // We call onHitNote to play the proper animations, + // but not goodNoteHit! This means zero score and zero notes hit for the results screen! + + // Call an event to allow canceling the note hit. + // NOTE: This is what handles the character animations! + var event:NoteScriptEvent = new HitNoteScriptEvent(note, 0.0, 0, 'perfect', 0); + dispatchEvent(event); + + // Calling event.cancelEvent() skips all the other logic! Neat! + if (event.eventCanceled) continue; + + // Command the bot to hit the note on time. + // NOTE: This is what handles the strumline and cleaning up the note itself! + playerStrumline.hitNote(note); + + if (note.holdNoteSprite != null) + { + playerStrumline.playNoteHoldCover(note.holdNoteSprite); + } } else if (Conductor.instance.songPosition > hitWindowStart) { @@ -2069,7 +2126,7 @@ class PlayState extends MusicBeatSubState if (holdNote == null || !holdNote.alive) continue; // While the hold note is being hit, and there is length on the hold note... - if (holdNote.hitNote && !holdNote.missedNote && holdNote.sustainLength > 0) + if (!isBotPlayMode && holdNote.hitNote && !holdNote.missedNote && holdNote.sustainLength > 0) { // Grant the player health. health += Constants.HEALTH_HOLD_BONUS_PER_SECOND * elapsed; diff --git a/source/funkin/play/notes/Strumline.hx b/source/funkin/play/notes/Strumline.hx index 190aa3ee0..efe1c707a 100644 --- a/source/funkin/play/notes/Strumline.hx +++ b/source/funkin/play/notes/Strumline.hx @@ -38,6 +38,10 @@ class Strumline extends FlxSpriteGroup return FlxG.height / 0.45; } + /** + * Whether this strumline is controlled by the player's inputs. + * False means it's controlled by the opponent or Bot Play. + */ public var isPlayer:Bool; /** diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index 78e73bf27..85a2396b9 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -592,6 +592,11 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState */ var playtestPracticeMode:Bool = false; + /** + * If true, playtesting a chart will make the computer do it for you! + */ + var playtestBotPlayMode:Bool = false; + /** * Enables or disables the "debugger" popup that appears when you run into a flixel error. */ @@ -5359,6 +5364,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState targetDifficulty: selectedDifficulty, targetVariation: selectedVariation, practiceMode: playtestPracticeMode, + botPlayMode: playtestBotPlayMode, minimalMode: minimal, startTimestamp: startTimestamp, playbackRate: playbackRate, diff --git a/source/funkin/ui/debug/charting/handlers/ChartEditorToolboxHandler.hx b/source/funkin/ui/debug/charting/handlers/ChartEditorToolboxHandler.hx index f32cc2bfb..3b32edf5d 100644 --- a/source/funkin/ui/debug/charting/handlers/ChartEditorToolboxHandler.hx +++ b/source/funkin/ui/debug/charting/handlers/ChartEditorToolboxHandler.hx @@ -299,6 +299,15 @@ class ChartEditorToolboxHandler state.playtestStartTime = checkboxStartTime.selected; }; + var checkboxBotPlay:Null = toolbox.findComponent('playtestBotPlayCheckbox', CheckBox); + if (checkboxBotPlay == null) throw 'ChartEditorToolboxHandler.buildToolboxPlaytestPropertiesLayout() - Could not find playtestBotPlayCheckbox component.'; + + checkboxBotPlay.selected = state.playtestBotPlayMode; + + checkboxBotPlay.onClick = _ -> { + state.playtestBotPlayMode = checkboxBotPlay.selected; + }; + var checkboxDebugger:Null = toolbox.findComponent('playtestDebuggerCheckbox', CheckBox); if (checkboxDebugger == null) throw 'ChartEditorToolboxHandler.buildToolboxPlaytestPropertiesLayout() - Could not find playtestDebuggerCheckbox component.'; diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index 50f85571b..45f9a4d27 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -1143,12 +1143,12 @@ class FreeplayState extends MusicBeatSubState targetSong: targetSong, targetDifficulty: targetDifficulty, targetVariation: targetVariation, - // TODO: Make this an option! - // startTimestamp: 0.0, - // TODO: Make this an option! - // playbackRate: 0.5, practiceMode: false, minimalMode: false, + // TODO: Make these an option! It's currently only accessible via chart editor. + // startTimestamp: 0.0, + // playbackRate: 0.5, + // botPlayMode: true, }, true); }); } From 607b5757fdc6f558c75eedfd1b7fee8d478a1e85 Mon Sep 17 00:00:00 2001 From: FabsTheFabs Date: Wed, 6 Mar 2024 05:05:03 +0000 Subject: [PATCH 07/31] added decimal point to waveform duration --- .../debug/charting/toolboxes/ChartEditorOffsetsToolbox.hx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/funkin/ui/debug/charting/toolboxes/ChartEditorOffsetsToolbox.hx b/source/funkin/ui/debug/charting/toolboxes/ChartEditorOffsetsToolbox.hx index af1d75444..bd2fd8ba3 100644 --- a/source/funkin/ui/debug/charting/toolboxes/ChartEditorOffsetsToolbox.hx +++ b/source/funkin/ui/debug/charting/toolboxes/ChartEditorOffsetsToolbox.hx @@ -272,19 +272,19 @@ class ChartEditorOffsetsToolbox extends ChartEditorBaseToolbox // waveformPlayer.waveform.forceUpdate = true; waveformPlayer.waveform.waveformData = playerVoice?.waveformData; // Set the width and duration to render the full waveform, with the clipRect applied we only render a segment of it. - waveformPlayer.waveform.duration = (playerVoice?.length ?? 1000) / Constants.MS_PER_SEC; + waveformPlayer.waveform.duration = (playerVoice?.length ?? 1000.0) / Constants.MS_PER_SEC; // Build opponent waveform. // waveformOpponent.waveform.forceUpdate = true; // note: if song only has one set of vocals (Vocals.ogg/mp3) then this is null and crashes charting editor // so we null check waveformOpponent.waveform.waveformData = opponentVoice?.waveformData; - waveformOpponent.waveform.duration = (opponentVoice?.length ?? 1000) / Constants.MS_PER_SEC; + waveformOpponent.waveform.duration = (opponentVoice?.length ?? 1000.0) / Constants.MS_PER_SEC; // Build instrumental waveform. // waveformInstrumental.waveform.forceUpdate = true; waveformInstrumental.waveform.waveformData = chartEditorState.audioInstTrack.waveformData; - waveformInstrumental.waveform.duration = (instTrack?.length ?? 1000) / Constants.MS_PER_SEC; + waveformInstrumental.waveform.duration = (instTrack?.lenth ?? 1000.0) / Constants.MS_PER_SEC; addOffsetsToAudioPreview(); } From 6bc1eb7278dab1e01f32125026c33cf1cc67a6ea Mon Sep 17 00:00:00 2001 From: FabsTheFabs Date: Wed, 6 Mar 2024 05:06:57 +0000 Subject: [PATCH 08/31] typo oops --- .../ui/debug/charting/toolboxes/ChartEditorOffsetsToolbox.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/funkin/ui/debug/charting/toolboxes/ChartEditorOffsetsToolbox.hx b/source/funkin/ui/debug/charting/toolboxes/ChartEditorOffsetsToolbox.hx index bd2fd8ba3..46721edfa 100644 --- a/source/funkin/ui/debug/charting/toolboxes/ChartEditorOffsetsToolbox.hx +++ b/source/funkin/ui/debug/charting/toolboxes/ChartEditorOffsetsToolbox.hx @@ -284,7 +284,7 @@ class ChartEditorOffsetsToolbox extends ChartEditorBaseToolbox // Build instrumental waveform. // waveformInstrumental.waveform.forceUpdate = true; waveformInstrumental.waveform.waveformData = chartEditorState.audioInstTrack.waveformData; - waveformInstrumental.waveform.duration = (instTrack?.lenth ?? 1000.0) / Constants.MS_PER_SEC; + waveformInstrumental.waveform.duration = (instTrack?.length ?? 1000.0) / Constants.MS_PER_SEC; addOffsetsToAudioPreview(); } From 67ede14685ad6199cc84cdab6230603c9bfb71cf Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Wed, 6 Mar 2024 02:17:45 -0500 Subject: [PATCH 09/31] raindrop assets --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 095e91fb3..bd718379f 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 095e91fb33dad70a5b51e37542e335cedd025d09 +Subproject commit bd718379f18ea9562f5260a5e41e20720ebe9e9b From a516e9199f714a2be5488d2ee4d063c3430f3c80 Mon Sep 17 00:00:00 2001 From: Mike Welsh Date: Tue, 5 Mar 2024 23:21:57 -0800 Subject: [PATCH 10/31] Remove DynamicTools; fix pause menu on HTML5 Calls intended for `ArrayTools.clone` were being routed to `DynamicTools.clone` due to the order of `using` statements in `imports.hx`. This caused the pause menu to break due to arrays becoming fubar (missing length property). Using `DynamicTools` is a little dangerous, so remove it in favor of calling `Reflect.copy` directly. --- source/funkin/import.hx | 1 - .../funkin/ui/debug/charting/ChartEditorState.hx | 4 ++-- source/funkin/util/tools/DynamicTools.hx | 14 -------------- 3 files changed, 2 insertions(+), 17 deletions(-) delete mode 100644 source/funkin/util/tools/DynamicTools.hx diff --git a/source/funkin/import.hx b/source/funkin/import.hx index 02055d4ed..250de99cb 100644 --- a/source/funkin/import.hx +++ b/source/funkin/import.hx @@ -13,7 +13,6 @@ using Lambda; using StringTools; using funkin.util.tools.ArraySortTools; using funkin.util.tools.ArrayTools; -using funkin.util.tools.DynamicTools; using funkin.util.tools.FloatTools; using funkin.util.tools.Int64Tools; using funkin.util.tools.IntTools; diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index b9e412b36..0e1aa4f4d 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -4530,14 +4530,14 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState { // Create an event and place it in the chart. // TODO: Figure out configuring event data. - var newEventData:SongEventData = new SongEventData(cursorSnappedMs, eventKindToPlace, eventDataToPlace.clone()); + var newEventData:SongEventData = new SongEventData(cursorSnappedMs, eventKindToPlace, Reflect.copy(eventDataToPlace)); performCommand(new AddEventsCommand([newEventData], FlxG.keys.pressed.CONTROL)); } else { // Create a note and place it in the chart. - var newNoteData:SongNoteData = new SongNoteData(cursorSnappedMs, cursorColumn, 0, noteKindToPlace.clone()); + var newNoteData:SongNoteData = new SongNoteData(cursorSnappedMs, cursorColumn, 0, Reflect.copy(noteKindToPlace)); performCommand(new AddNotesCommand([newNoteData], FlxG.keys.pressed.CONTROL)); diff --git a/source/funkin/util/tools/DynamicTools.hx b/source/funkin/util/tools/DynamicTools.hx deleted file mode 100644 index 47501ea22..000000000 --- a/source/funkin/util/tools/DynamicTools.hx +++ /dev/null @@ -1,14 +0,0 @@ -package funkin.util.tools; - -class DynamicTools -{ - /** - * Creates a full clone of the input `Dynamic`. Only guaranteed to work on anonymous structures. - * @param input The `Dynamic` to clone. - * @return A clone of the input `Dynamic`. - */ - public static function clone(input:Dynamic):Dynamic - { - return Reflect.copy(input); - } -} From b2d3fe17d7b7af1e0d21786d357f12815a1aa498 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Wed, 6 Mar 2024 02:56:10 -0500 Subject: [PATCH 11/31] fun lil pitch effect on result scren --- source/funkin/play/ResultState.hx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/funkin/play/ResultState.hx b/source/funkin/play/ResultState.hx index f77c3fc6b..a78d61583 100644 --- a/source/funkin/play/ResultState.hx +++ b/source/funkin/play/ResultState.hx @@ -347,6 +347,10 @@ class ResultState extends MusicBeatSubState if (controls.PAUSE) { + FlxTween.tween(FlxG.sound.music, {volume: 0}, 0.8); + FlxTween.tween(FlxG.sound.music, {pitch: 3}, 0.1, {onComplete: _ -> { + FlxTween.tween(FlxG.sound.music, {pitch: 0.5}, 0.4); + }}); if (params.storyMode) { openSubState(new funkin.ui.transition.StickerSubState(null, (sticker) -> new StoryMenuState(sticker))); From 7a9bff248e3351f051884ef01d7e3b083491000a Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 6 Mar 2024 12:24:25 -0500 Subject: [PATCH 12/31] Fix an issue with array.clone() on HTML5 --- source/funkin/import.hx | 2 +- source/funkin/util/tools/DynamicAccessTools.hx | 16 ++++++++++++++++ source/funkin/util/tools/DynamicTools.hx | 14 -------------- 3 files changed, 17 insertions(+), 15 deletions(-) create mode 100644 source/funkin/util/tools/DynamicAccessTools.hx delete mode 100644 source/funkin/util/tools/DynamicTools.hx diff --git a/source/funkin/import.hx b/source/funkin/import.hx index 02055d4ed..66c3470ff 100644 --- a/source/funkin/import.hx +++ b/source/funkin/import.hx @@ -13,7 +13,7 @@ using Lambda; using StringTools; using funkin.util.tools.ArraySortTools; using funkin.util.tools.ArrayTools; -using funkin.util.tools.DynamicTools; +using funkin.util.tools.DynamicAccessTools; using funkin.util.tools.FloatTools; using funkin.util.tools.Int64Tools; using funkin.util.tools.IntTools; diff --git a/source/funkin/util/tools/DynamicAccessTools.hx b/source/funkin/util/tools/DynamicAccessTools.hx new file mode 100644 index 000000000..1c83ce039 --- /dev/null +++ b/source/funkin/util/tools/DynamicAccessTools.hx @@ -0,0 +1,16 @@ +package funkin.util.tools; + +import haxe.DynamicAccess; + +class DynamicAccessTools +{ + /** + * Creates a full clone of the input `DynamicAccess`. + * @param input The `Dynamic` to clone. + * @return A clone of the input `Dynamic`. + */ + public static function clone(input:DynamicAccess):DynamicAccess + { + return Reflect.copy(input); + } +} diff --git a/source/funkin/util/tools/DynamicTools.hx b/source/funkin/util/tools/DynamicTools.hx deleted file mode 100644 index 47501ea22..000000000 --- a/source/funkin/util/tools/DynamicTools.hx +++ /dev/null @@ -1,14 +0,0 @@ -package funkin.util.tools; - -class DynamicTools -{ - /** - * Creates a full clone of the input `Dynamic`. Only guaranteed to work on anonymous structures. - * @param input The `Dynamic` to clone. - * @return A clone of the input `Dynamic`. - */ - public static function clone(input:Dynamic):Dynamic - { - return Reflect.copy(input); - } -} From 2fa1d18dce85a6127148406d5f6822279e48bfc6 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 6 Mar 2024 12:29:54 -0500 Subject: [PATCH 13/31] Fix build --- source/funkin/ui/debug/charting/ChartEditorState.hx | 2 +- source/funkin/util/tools/DynamicAccessTools.hx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index 78e73bf27..191f3cb15 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -4532,7 +4532,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState else { // Create a note and place it in the chart. - var newNoteData:SongNoteData = new SongNoteData(cursorSnappedMs, cursorColumn, 0, noteKindToPlace.clone()); + var newNoteData:SongNoteData = new SongNoteData(cursorSnappedMs, cursorColumn, 0, noteKindToPlace); performCommand(new AddNotesCommand([newNoteData], FlxG.keys.pressed.CONTROL)); diff --git a/source/funkin/util/tools/DynamicAccessTools.hx b/source/funkin/util/tools/DynamicAccessTools.hx index 1c83ce039..14b9a6c68 100644 --- a/source/funkin/util/tools/DynamicAccessTools.hx +++ b/source/funkin/util/tools/DynamicAccessTools.hx @@ -9,7 +9,7 @@ class DynamicAccessTools * @param input The `Dynamic` to clone. * @return A clone of the input `Dynamic`. */ - public static function clone(input:DynamicAccess):DynamicAccess + public static function clone(input:DynamicAccess):DynamicAccess { return Reflect.copy(input); } From f671cc856902713618d270137592a4bb0a606348 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 6 Mar 2024 14:13:48 -0500 Subject: [PATCH 14/31] Remove DynamicAccessTools entirely. --- source/funkin/import.hx | 1 - .../funkin/ui/debug/charting/ChartEditorState.hx | 2 +- source/funkin/util/tools/DynamicAccessTools.hx | 16 ---------------- 3 files changed, 1 insertion(+), 18 deletions(-) delete mode 100644 source/funkin/util/tools/DynamicAccessTools.hx diff --git a/source/funkin/import.hx b/source/funkin/import.hx index 66c3470ff..250de99cb 100644 --- a/source/funkin/import.hx +++ b/source/funkin/import.hx @@ -13,7 +13,6 @@ using Lambda; using StringTools; using funkin.util.tools.ArraySortTools; using funkin.util.tools.ArrayTools; -using funkin.util.tools.DynamicAccessTools; using funkin.util.tools.FloatTools; using funkin.util.tools.Int64Tools; using funkin.util.tools.IntTools; diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index 191f3cb15..29d7ddf97 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -4525,7 +4525,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState { // Create an event and place it in the chart. // TODO: Figure out configuring event data. - var newEventData:SongEventData = new SongEventData(cursorSnappedMs, eventKindToPlace, eventDataToPlace.clone()); + var newEventData:SongEventData = new SongEventData(cursorSnappedMs, eventKindToPlace, eventDataToPlace.copy()); performCommand(new AddEventsCommand([newEventData], FlxG.keys.pressed.CONTROL)); } diff --git a/source/funkin/util/tools/DynamicAccessTools.hx b/source/funkin/util/tools/DynamicAccessTools.hx deleted file mode 100644 index 14b9a6c68..000000000 --- a/source/funkin/util/tools/DynamicAccessTools.hx +++ /dev/null @@ -1,16 +0,0 @@ -package funkin.util.tools; - -import haxe.DynamicAccess; - -class DynamicAccessTools -{ - /** - * Creates a full clone of the input `DynamicAccess`. - * @param input The `Dynamic` to clone. - * @return A clone of the input `Dynamic`. - */ - public static function clone(input:DynamicAccess):DynamicAccess - { - return Reflect.copy(input); - } -} From 332a81ec72a36af65757bf66a640d38e88f4ae77 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 6 Mar 2024 17:22:11 -0500 Subject: [PATCH 15/31] No longer miss notes during ending cutscene --- source/funkin/play/PlayState.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index ab693ef46..a6e4b4632 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -1032,7 +1032,7 @@ class PlayState extends MusicBeatSubState if (isInCutscene && !disableKeys) handleCutsceneKeys(elapsed); // Moving notes into position is now done by Strumline.update(). - processNotes(elapsed); + if (!isInCutscene) processNotes(elapsed); justUnpaused = false; } From aff7bbb4e9bd6d8307e1d0ef79d88cee035c7a40 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 6 Mar 2024 17:22:22 -0500 Subject: [PATCH 16/31] Update assets submodule --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 095e91fb3..bf61ea3c9 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 095e91fb33dad70a5b51e37542e335cedd025d09 +Subproject commit bf61ea3c9b56bc82c36976d5aa8052bc16ff1b4e From 69b28ca42c5f8f0be4052f09df05f3e782de2bc6 Mon Sep 17 00:00:00 2001 From: Mike Welsh Date: Wed, 6 Mar 2024 22:48:44 -0800 Subject: [PATCH 17/31] Add #if FLX_DEBUG in TrackerUtil `Tracker.addProfile` only exists when FLX_DEBUG is set, so add this conditional check to fix non-debug builds. --- source/funkin/util/TrackerUtil.hx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/funkin/util/TrackerUtil.hx b/source/funkin/util/TrackerUtil.hx index b8ed0c995..ffe374c5f 100644 --- a/source/funkin/util/TrackerUtil.hx +++ b/source/funkin/util/TrackerUtil.hx @@ -17,7 +17,9 @@ class TrackerUtil */ public static function initTrackers():Void { + #if FLX_DEBUG Tracker.addProfile(new TrackerProfile(Highscore, ["tallies"])); FlxG.console.registerClass(Highscore); + #end } } From b0abef0d527dcb90060a1b142fa4f4ed393a5ba2 Mon Sep 17 00:00:00 2001 From: Mike Welsh Date: Wed, 6 Mar 2024 23:37:50 -0800 Subject: [PATCH 18/31] Use correct resource URL when loading videos On HTML5, `VideoCutscene` was not stripping the library prefix from the video file path, causing the video to fail to load. Fixes FPIQ-281. --- source/funkin/play/cutscene/VideoCutscene.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/funkin/play/cutscene/VideoCutscene.hx b/source/funkin/play/cutscene/VideoCutscene.hx index 2d31b0a28..ff56e0919 100644 --- a/source/funkin/play/cutscene/VideoCutscene.hx +++ b/source/funkin/play/cutscene/VideoCutscene.hx @@ -61,7 +61,7 @@ class VideoCutscene VideoCutscene.cutsceneType = cutsceneType; #if html5 - playVideoHTML5(filePath); + playVideoHTML5(rawFilePath); #elseif hxCodec playVideoNative(rawFilePath); #else From 427e4810ad768879e5585915abb50f7ec5f43c7a Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Thu, 7 Mar 2024 03:57:16 -0500 Subject: [PATCH 19/31] faster bf processing... --- source/funkin/util/logging/AnsiTrace.hx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/source/funkin/util/logging/AnsiTrace.hx b/source/funkin/util/logging/AnsiTrace.hx index c8d27b86f..9fdc19e1b 100644 --- a/source/funkin/util/logging/AnsiTrace.hx +++ b/source/funkin/util/logging/AnsiTrace.hx @@ -52,7 +52,12 @@ class AnsiTrace public static function traceBF() { #if sys - if (colorSupported) Sys.println(ansiBF.join("\n")); + if (colorSupported) + { + for (line in ansiBF) + Sys.stdout().writeString(line + "\n"); + Sys.stdout().flush(); + } #end } From c385b7887f83d4bf5309c1aaa16ea32e4058b312 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Thu, 7 Mar 2024 15:41:00 -0500 Subject: [PATCH 20/31] Update assets submodule --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index bf61ea3c9..3510a2459 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit bf61ea3c9b56bc82c36976d5aa8052bc16ff1b4e +Subproject commit 3510a245986914ff72736f121347cf4a11db0b35 From a6c44412c50e2dc8ef663f9349bf3e08fde13e38 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Thu, 7 Mar 2024 23:20:02 -0500 Subject: [PATCH 21/31] Exclude WAV files from builds. --- Project.xml | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/Project.xml b/Project.xml index c368dacef..99c46ef9f 100644 --- a/Project.xml +++ b/Project.xml @@ -22,8 +22,8 @@ - - + +
@@ -53,28 +53,28 @@
- - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + +