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
+}