diff --git a/assets/data/characters/bf-car.xml b/assets/data/characters/bf-car.xml index b4c9b24c..bd276ca8 100644 --- a/assets/data/characters/bf-car.xml +++ b/assets/data/characters/bf-car.xml @@ -1,12 +1,18 @@ - - - - - - - - - + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/data/characters/da.xml b/assets/data/characters/da.xml index 2ef057ba..f176be02 100644 --- a/assets/data/characters/da.xml +++ b/assets/data/characters/da.xml @@ -1,8 +1,13 @@ - - - - - + + + + + + + + + + \ No newline at end of file diff --git a/assets/data/characters/dad.xml b/assets/data/characters/dad.xml index 1b1e0f04..39319727 100644 --- a/assets/data/characters/dad.xml +++ b/assets/data/characters/dad.xml @@ -1,8 +1,13 @@ - - - - - + + + + + + + + + + \ No newline at end of file diff --git a/assets/data/characters/mom-car.xml b/assets/data/characters/mom-car.xml index fa7c18dd..e511498f 100644 --- a/assets/data/characters/mom-car.xml +++ b/assets/data/characters/mom-car.xml @@ -1,10 +1,16 @@ - - - - + + + + + + + + + + \ No newline at end of file diff --git a/assets/data/characters/mom.xml b/assets/data/characters/mom.xml index 4781ede4..43d52076 100644 --- a/assets/data/characters/mom.xml +++ b/assets/data/characters/mom.xml @@ -1,10 +1,16 @@ - - - - + + + + - + + + + + + + \ No newline at end of file diff --git a/assets/data/config/credits.xml b/assets/data/config/credits.xml index 56a5cafd..4f300fcb 100644 --- a/assets/data/config/credits.xml +++ b/assets/data/config/credits.xml @@ -3,5 +3,5 @@ - + diff --git a/assets/data/config/menuItems.txt b/assets/data/config/menuItems.txt index 6fc72f13..a8ec55b1 100644 --- a/assets/data/config/menuItems.txt +++ b/assets/data/config/menuItems.txt @@ -1,4 +1,4 @@ story mode freeplay options -donate \ No newline at end of file +credits \ No newline at end of file diff --git a/assets/images/characters/mom.xml b/assets/images/characters/mom.xml index 67eeb6b7..51132b97 100644 --- a/assets/images/characters/mom.xml +++ b/assets/images/characters/mom.xml @@ -15,8 +15,9 @@ - - + + diff --git a/assets/images/menus/mainmenu/credits.png b/assets/images/menus/mainmenu/credits.png new file mode 100644 index 00000000..cb1d9863 Binary files /dev/null and b/assets/images/menus/mainmenu/credits.png differ diff --git a/assets/images/menus/mainmenu/credits.xml b/assets/images/menus/mainmenu/credits.xml new file mode 100644 index 00000000..c3c2385e --- /dev/null +++ b/assets/images/menus/mainmenu/credits.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/images/menus/mainmenu/donate.png b/assets/images/menus/mainmenu/donate.png index e4a42749..cf6a6d11 100644 Binary files a/assets/images/menus/mainmenu/donate.png and b/assets/images/menus/mainmenu/donate.png differ diff --git a/assets/images/menus/mainmenu/freeplay.png b/assets/images/menus/mainmenu/freeplay.png index a34eed2e..361ddfa7 100644 Binary files a/assets/images/menus/mainmenu/freeplay.png and b/assets/images/menus/mainmenu/freeplay.png differ diff --git a/assets/images/menus/mainmenu/freeplay.xml b/assets/images/menus/mainmenu/freeplay.xml index 4c288a07..f66f748f 100644 --- a/assets/images/menus/mainmenu/freeplay.xml +++ b/assets/images/menus/mainmenu/freeplay.xml @@ -1,17 +1,18 @@ - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/images/menus/mainmenu/options.png b/assets/images/menus/mainmenu/options.png index ebe1f86f..20688ecc 100644 Binary files a/assets/images/menus/mainmenu/options.png and b/assets/images/menus/mainmenu/options.png differ diff --git a/assets/images/menus/mainmenu/story mode.png b/assets/images/menus/mainmenu/story mode.png index 162f403d..466fa338 100644 Binary files a/assets/images/menus/mainmenu/story mode.png and b/assets/images/menus/mainmenu/story mode.png differ diff --git a/assets/videos/guns-cutscene.mp4 b/assets/videos/guns-cutscene.mp4 deleted file mode 100644 index cfc7500a..00000000 Binary files a/assets/videos/guns-cutscene.mp4 and /dev/null differ diff --git a/assets/videos/stress-cutscene.mp4 b/assets/videos/stress-cutscene.mp4 deleted file mode 100644 index 036a54d1..00000000 Binary files a/assets/videos/stress-cutscene.mp4 and /dev/null differ diff --git a/assets/videos/ugh-cutscene.mp4 b/assets/videos/ugh-cutscene.mp4 deleted file mode 100644 index 8f30a8c7..00000000 Binary files a/assets/videos/ugh-cutscene.mp4 and /dev/null differ diff --git a/source/funkin/backend/FunkinSprite.hx b/source/funkin/backend/FunkinSprite.hx index 97500198..94835653 100644 --- a/source/funkin/backend/FunkinSprite.hx +++ b/source/funkin/backend/FunkinSprite.hx @@ -105,6 +105,14 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I public override function update(elapsed:Float) { super.update(elapsed); + + // hate how it looks like but hey at least its optimized and fast - Nex + if (!debugMode && isAnimFinished()) { + var name = getAnimName() + '-loop'; + if (animation.exists(name)) + playAnim(name, false, lastAnimContext); + } + if (animateAtlas != null) animateAtlas.update(elapsed); } @@ -324,6 +332,10 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I return (animateAtlas != null) ? atlasPlayingAnim : animation.name; } + public inline function isAnimReversed():Bool { + return animateAtlas != null ? animateAtlas.anim.reversed : animation.curAnim != null ? animation.curAnim.reversed : false; + } + public inline function removeAnimation(name:String) { if (animateAtlas != null) @:privateAccess animateAtlas.anim.animsMap.remove(name); diff --git a/source/funkin/backend/utils/CoolUtil.hx b/source/funkin/backend/utils/CoolUtil.hx index ca71693b..365e0a82 100644 --- a/source/funkin/backend/utils/CoolUtil.hx +++ b/source/funkin/backend/utils/CoolUtil.hx @@ -925,6 +925,91 @@ final class CoolUtil return b ? 1 : 0; #end } + + /** + * Converts a string of "1..3,5,7..9,8..5" into an array of numbers like [1,2,3,5,7,8,9,8,7,6,5] + * @param input String to parse + * @return Array of numbers + */ + public static function parseNumberRange(input:String):Array { + var result:Array = []; + var parts:Array = input.split(","); + + for (part in parts) { + part = part.trim(); + var idx = part.indexOf(".."); + if (idx != -1) { + var start = Std.parseInt(part.substring(0, idx).trim()); + var end = Std.parseInt(part.substring(idx + 2).trim()); + + if(start == null || end == null) { + continue; + } + + if (start < end) { + for (j in start...end + 1) { + result.push(j); + } + } else { + for (j in end...start + 1) { + result.push(start + end - j); + } + } + } else { + var num = Std.parseInt(part); + if (num != null) { + result.push(num); + } + } + } + return result; + } + + /** + * Converts an array of numbers into a string of ranges. + * Example: [1,2,3,5,7,8,9,8,7,6,5] -> "1..3,5,7..9,8..5" + * @param numbers Array of numbers + * @return String representing the ranges + */ + public static function formatNumberRange(numbers:Array, seperator:String = ","):String { + if (numbers.length == 0) return ""; + + var result:Array = []; + var i = 0; + + while (i < numbers.length) { + var start = numbers[i]; + var end = start; + var direction = 0; // 0: no sequence, 1: increasing, -1: decreasing + + if (i + 1 < numbers.length) { // detect direction of sequence + if (numbers[i + 1] == end + 1) { + direction = 1; + } else if (numbers[i + 1] == end - 1) { + direction = -1; + } + } + + if(direction != 0) { + while (i + 1 < numbers.length && (numbers[i + 1] == end + direction)) { + end = numbers[i + 1]; + i++; + } + } + + if (start == end) { // no direction + result.push('${start}'); + } else if (start + direction == end) { // 1 step increment + result.push('${start},${end}'); + } else { // store as range + result.push('${start}..${end}'); + } + + i++; + } + + return result.join(seperator); + } } /** diff --git a/source/funkin/backend/utils/XMLUtil.hx b/source/funkin/backend/utils/XMLUtil.hx index b09d2b4a..7ee23c23 100644 --- a/source/funkin/backend/utils/XMLUtil.hx +++ b/source/funkin/backend/utils/XMLUtil.hx @@ -214,14 +214,7 @@ final class XMLUtil { if (anim.has.y) animData.y = Std.parseFloat(anim.att.y).getDefault(animData.y); if (anim.has.loop) animData.loop = anim.att.loop == "true"; if (anim.has.forced) animData.forced = anim.att.forced == "true"; - if (anim.has.indices) { - var indicesSplit = anim.att.indices.split(","); - for(indice in indicesSplit) { - var i = Std.parseInt(indice.trim()); - if (i != null) - animData.indices.push(i); - } - } + if (anim.has.indices) animData.indices = CoolUtil.parseNumberRange(anim.att.indices); return animData; } diff --git a/source/funkin/editors/character/CharacterAnimButton.hx b/source/funkin/editors/character/CharacterAnimButton.hx index c5425f93..5c52b1b9 100644 --- a/source/funkin/editors/character/CharacterAnimButton.hx +++ b/source/funkin/editors/character/CharacterAnimButton.hx @@ -117,15 +117,9 @@ class CharacterAnimButton extends UIButton { loopedCheckbox.x += 8; loopedCheckbox.y += 6; - indicesTextBox = new UITextBox(nameTextBox.x, nameTextBox.y, animData.indices.getDefault([]).join(","), 278, 22, false, true); - indicesTextBox.onChange = (text:String) -> { - var indices:Array = []; - for(indice in text.split(",")) { - var i = Std.parseInt(indice.trim()); - if (i != null) indices.push(i); - } - - this.changeIndicies(indices); + indicesTextBox = new UITextBox(nameTextBox.x, nameTextBox.y, CoolUtil.formatNumberRange(animData.indices.getDefault([]), ", "), 278, 22, false, true); + indicesTextBox.onChange = (text:String) -> { + this.changeIndicies(CoolUtil.parseNumberRange(text)); } members.push(indicesTextBox); foldableButtons.push(indicesTextBox); diff --git a/source/funkin/editors/charter/Charter.hx b/source/funkin/editors/charter/Charter.hx index a6bbe70a..6e85eff9 100644 --- a/source/funkin/editors/charter/Charter.hx +++ b/source/funkin/editors/charter/Charter.hx @@ -1,7 +1,7 @@ package funkin.editors.charter; // ! FUCK YOU CHUF (your biggest fan -lunar) <3 -import flixel.FlxLayer; +//import flixel.FlxLayer; import funkin.editors.extra.CameraHoverDummy; import flixel.input.keyboard.FlxKey; import flixel.math.FlxPoint; @@ -89,7 +89,7 @@ class Charter extends UIState { public static var clipboard:Array = []; public static var waveformHandler:CharterWaveformHandler; - public var textLayer:FlxLayer; + //public var textLayer:FlxLayer; public function new(song:String, diff:String, reload:Bool = true) { super(); @@ -487,7 +487,7 @@ class Charter extends UIState { // adds grid and notes so that they're ALWAYS behind the UI add(gridBackdrops); - add(textLayer = new FlxLayer()); + //add(textLayer = new FlxLayer()); add(eventsBackdrop); add(addEventSpr); add(eventsGroup); diff --git a/source/funkin/editors/charter/CharterNote.hx b/source/funkin/editors/charter/CharterNote.hx index bc1bb4e0..6416cca9 100644 --- a/source/funkin/editors/charter/CharterNote.hx +++ b/source/funkin/editors/charter/CharterNote.hx @@ -68,7 +68,7 @@ class CharterNote extends UISprite implements ICharterSelectable { if(noteTypeTexts[v] == null || noteTypeTexts[v].graphic.isDestroyed) { noteTypeTexts[v] = new UIText(x, y, 0, Std.string(v)); noteTypeTexts[v].exists = v != 0; - noteTypeTexts[v].layer = (Charter.instance != null) ? Charter.instance.textLayer : null; + //noteTypeTexts[v].layer = (Charter.instance != null) ? Charter.instance.textLayer : null; } typeText = noteTypeTexts[v]; diff --git a/source/funkin/game/Character.hx b/source/funkin/game/Character.hx index 87d51a48..a8cbb285 100644 --- a/source/funkin/game/Character.hx +++ b/source/funkin/game/Character.hx @@ -470,7 +470,7 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset offset.putWeak(); if (anim.indices.length > 0) - animXml.set("indices", anim.indices.join(",")); + animXml.set("indices", CoolUtil.formatNumberRange(anim.indices)); xml.addChild(animXml); } diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx index c1f2537a..14fbed61 100644 --- a/source/funkin/game/PlayState.hx +++ b/source/funkin/game/PlayState.hx @@ -1266,7 +1266,7 @@ class PlayState extends MusicBeatState if (Conductor.songPosition >= 0) startSong(); } - } else { + } else if (FlxG.sound.music != null) { var instTime = FlxG.sound.music.time; var isOffsync = vocals.time != instTime || [for(strumLine in strumLines.members) strumLine.vocals.time != instTime].contains(true); __vocalOffsetViolation = Math.max(0, __vocalOffsetViolation + (isOffsync ? elapsed : -elapsed / 2)); diff --git a/source/funkin/menus/MainMenuState.hx b/source/funkin/menus/MainMenuState.hx index 27391e0a..cd40bc90 100644 --- a/source/funkin/menus/MainMenuState.hx +++ b/source/funkin/menus/MainMenuState.hx @@ -162,7 +162,7 @@ class MainMenuState extends MusicBeatState { case 'story mode': FlxG.switchState(new StoryMenuState()); case 'freeplay': FlxG.switchState(new FreeplayState()); - case 'donate': FlxG.switchState(new CreditsMain()); + case 'donate', 'credits': FlxG.switchState(new CreditsMain()); // kept donate for not breaking scripts, if you dont want donate to bring you to the credits menu, thats easy softcodable - Nex case 'options': FlxG.switchState(new OptionsMenu()); } }); diff --git a/source/funkin/menus/credits/CreditsMain.hx b/source/funkin/menus/credits/CreditsMain.hx index 5295b6ed..916066eb 100644 --- a/source/funkin/menus/credits/CreditsMain.hx +++ b/source/funkin/menus/credits/CreditsMain.hx @@ -4,9 +4,11 @@ import funkin.options.OptionsScreen; import funkin.options.TreeMenu; import funkin.options.type.*; import haxe.xml.Access; +import flixel.util.FlxColor; class CreditsMain extends TreeMenu { var bg:FlxSprite; + var items:Array = []; public override function create() { bg = new FlxSprite(-80).loadGraphic(Paths.image('menus/menuBGBlue')); @@ -18,7 +20,6 @@ class CreditsMain extends TreeMenu { bg.antialiasing = true; add(bg); - var selectables:Array = []; var xmlPath = Paths.xml('config/credits'); for(source in [funkin.backend.assets.AssetsLibraryList.AssetSource.SOURCE, funkin.backend.assets.AssetsLibraryList.AssetSource.MODS]) { if (Paths.assetsTree.existsSpecific(xmlPath, "TEXT", source)) { @@ -31,17 +32,17 @@ class CreditsMain extends TreeMenu { if (access != null) for(c in parseCreditsFromXML(access, source)) - selectables.push(c); + items.push(c); } } - selectables.push(new TextOption("Codename Engine >", "Select this to see all the contributors of the engine!", function() { + items.push(new TextOption("Codename Engine >", "Select this to see all the contributors of the engine!", function() { optionsTree.add(Type.createInstance(CreditsCodename, [])); })); - selectables.push(new TextOption("Friday Night Funkin'", "Select this to open the itch.io page of the original game to donate!", function() { + items.push(new TextOption("Friday Night Funkin'", "Select this to open the itch.io page of the original game to donate!", function() { CoolUtil.openURL("https://ninja-muffin24.itch.io/funkin"); })); - main = new OptionsScreen('Credits', 'The people who made this possible!', selectables); + main = new OptionsScreen('Credits', 'The people who made this possible!', items); super.create(); DiscordUtil.call("onMenuLoaded", ["Credits Menu"]); @@ -68,11 +69,13 @@ class CreditsMain extends TreeMenu { html_url: 'https://github.com/$username', avatar_url: 'https://github.com/$username.png' }; - - credsMenus.push(new GithubIconOption(user, desc, null, + var opt:GithubIconOption = new GithubIconOption(user, desc, null, node.has.customName ? node.att.customName : null, node.has.size ? Std.parseInt(node.att.size) : 96, node.has.portrait ? node.att.portrait.toLowerCase() == "false" ? false : true : true - )); + ); + if (node.has.color) + @:privateAccess opt.__text.color = FlxColor.fromString(node.att.color); + credsMenus.push(opt); } else { if (!node.has.name) { Logs.trace("[CreditsMain] A credit node requires a name attribute.", WARNING); @@ -82,11 +85,14 @@ class CreditsMain extends TreeMenu { switch(node.name) { case "credit": - credsMenus.push(new PortraitOption(name, desc, function() if(node.has.url) CoolUtil.openURL(node.att.url), + var opt:PortraitOption = new PortraitOption(name, desc, function() if(node.has.url) CoolUtil.openURL(node.att.url), node.has.icon && Paths.assetsTree.existsSpecific(Paths.image('credits/${node.att.icon}'), "IMAGE", source) ? FlxG.bitmap.add(Paths.image('credits/${node.att.icon}')) : null, node.has.size ? Std.parseInt(node.att.size) : 96, node.has.portrait ? node.att.portrait.toLowerCase() == "false" ? false : true : true - )); + ); + if (node.has.color) + @:privateAccess opt.__text.color = FlxColor.fromString(node.att.color); + credsMenus.push(opt); case "menu": credsMenus.push(new TextOption(name + " >", desc, function() { @@ -98,4 +104,4 @@ class CreditsMain extends TreeMenu { return credsMenus; } -} \ No newline at end of file +}