diff --git a/.vscode/settings.json b/.vscode/settings.json
index 92d49c3d4..cefbadcf6 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -4,8 +4,7 @@
"editor.formatOnSave": true,
"editor.formatOnPaste": true,
"editor.codeActionsOnSave": {
- // Compilation server issues can cause auto-cleanup to remove valid imports.
- "source.organizeImports": false
+ "source.organizeImports": "never"
},
"editor.defaultFormatter": "nadako.vshaxe",
"editor.tabSize": 2
diff --git a/Project.xml b/Project.xml
index 4c0ffdce7..e0677b026 100644
--- a/Project.xml
+++ b/Project.xml
@@ -99,18 +99,20 @@
-
+
+
-
+
+
+
+
-
-
diff --git a/assets b/assets
index 54065ef5b..309e60cee 160000
--- a/assets
+++ b/assets
@@ -1 +1 @@
-Subproject commit 54065ef5b7dec959aea478fd79b44e692e7c248a
+Subproject commit 309e60ceea2c298e8bf34575359c74f64833479b
diff --git a/hmm.json b/hmm.json
index 28c2eeb3e..d461edd24 100644
--- a/hmm.json
+++ b/hmm.json
@@ -11,15 +11,20 @@
"name": "flixel",
"type": "git",
"dir": null,
- "ref": "da04cbda49a4c5eebe93fb61296dbaf4f0f1b556",
- "url": "https://github.com/EliteMasterEric/flixel"
+ "ref": "a83738673e7edbf8acba3a1426af284dfe6719fe",
+ "url": "https://github.com/FunkinCrew/flixel"
},
{
"name": "flixel-addons",
"type": "git",
"dir": null,
- "ref": "c8c41e26d463aaf2edc0582fb23b6e228235bd16",
- "url": "https://github.com/EliteMasterEric/flixel-addons"
+ "ref": "fd3aecdeb5635fa0428dffee204fc78fc26b5885",
+ "url": "https://github.com/FunkinCrew/flixel-addons"
+ },
+ {
+ "name": "flixel-text-input",
+ "type": "haxelib",
+ "version": "1.1.0"
},
{
"name": "flixel-ui",
@@ -32,8 +37,8 @@
"name": "flxanimate",
"type": "git",
"dir": null,
- "ref": "dd2903f7dc7024335b981edf2a770760cec912e1",
- "url": "https://github.com/ninjamuffin99/flxanimate"
+ "ref": "d7c5621be742e2c98d523dfe5af7528835eaff1e",
+ "url": "https://github.com/FunkinCrew/flxanimate"
},
{
"name": "format",
@@ -49,14 +54,14 @@
"name": "haxeui-core",
"type": "git",
"dir": null,
- "ref": "1e40adb6bf58b0eb6a5600f75face565b647ab36",
+ "ref": "5086e59e7551d775ed4d1fb0188e31de22d1312b",
"url": "https://github.com/haxeui/haxeui-core"
},
{
"name": "haxeui-flixel",
"type": "git",
"dir": null,
- "ref": "26b6bb132c92dfa9b77b4a61eaeda8f9a9efda98",
+ "ref": "2b9cff727999b53ed292b1675ac1c9089ac77600",
"url": "https://github.com/haxeui/haxeui-flixel"
},
{
@@ -95,15 +100,15 @@
"name": "json2object",
"type": "git",
"dir": null,
- "ref": "f4df19cfa196f85eece55c3367021fc965f1fa9a",
- "url": "https://github.com/EliteMasterEric/json2object"
+ "ref": "a8c26f18463c98da32f744c214fe02273e1823fa",
+ "url": "https://github.com/FunkinCrew/json2object"
},
{
"name": "lime",
"type": "git",
"dir": null,
"ref": "737b86f121cdc90358d59e2e527934f267c94a2c",
- "url": "https://github.com/EliteMasterEric/lime"
+ "url": "https://github.com/FunkinCrew/lime"
},
{
"name": "mconsole",
@@ -124,21 +129,21 @@
"type": "git",
"dir": "src",
"ref": "master",
- "url": "https://github.com/EliteMasterEric/mockatoo"
+ "url": "https://github.com/FunkinCrew/mockatoo"
},
{
"name": "munit",
"type": "git",
"dir": "src",
"ref": "master",
- "url": "https://github.com/EliteMasterEric/MassiveUnit"
+ "url": "https://github.com/FunkinCrew/MassiveUnit"
},
{
"name": "openfl",
"type": "git",
"dir": null,
"ref": "f229d76361c7e31025a048fe7909847f75bb5d5e",
- "url": "https://github.com/EliteMasterEric/openfl"
+ "url": "https://github.com/FunkinCrew/openfl"
},
{
"name": "polymod",
diff --git a/source/funkin/Conductor.hx b/source/funkin/Conductor.hx
index 72706bbc1..09cdf7df4 100644
--- a/source/funkin/Conductor.hx
+++ b/source/funkin/Conductor.hx
@@ -11,6 +11,7 @@ import funkin.data.song.SongDataUtils;
* A core class which handles musical timing throughout the game,
* both in gameplay and in menus.
*/
+@:nullSafety
class Conductor
{
// onBeatHit is called every quarter note
@@ -61,7 +62,7 @@ class Conductor
/**
* The most recent time change for the current song position.
*/
- public var currentTimeChange(default, null):SongTimeChange;
+ public var currentTimeChange(default, null):Null;
/**
* The current position in the song in milliseconds.
@@ -156,32 +157,32 @@ class Conductor
/**
* Current position in the song, in measures.
*/
- public var currentMeasure(default, null):Int;
+ public var currentMeasure(default, null):Int = 0;
/**
* Current position in the song, in beats.
*/
- public var currentBeat(default, null):Int;
+ public var currentBeat(default, null):Int = 0;
/**
* Current position in the song, in steps.
*/
- public var currentStep(default, null):Int;
+ public var currentStep(default, null):Int = 0;
/**
* Current position in the song, in measures and fractions of a measure.
*/
- public var currentMeasureTime(default, null):Float;
+ public var currentMeasureTime(default, null):Float = 0;
/**
* Current position in the song, in beats and fractions of a measure.
*/
- public var currentBeatTime(default, null):Float;
+ public var currentBeatTime(default, null):Float = 0;
/**
* Current position in the song, in steps and fractions of a step.
*/
- public var currentStepTime(default, null):Float;
+ public var currentStepTime(default, null):Float = 0;
/**
* An offset tied to the current chart file to compensate for a delay in the instrumental.
diff --git a/source/funkin/data/DataParse.hx b/source/funkin/data/DataParse.hx
index cbd168a61..49dde0198 100644
--- a/source/funkin/data/DataParse.hx
+++ b/source/funkin/data/DataParse.hx
@@ -178,7 +178,31 @@ class DataParse
switch (json.value)
{
case JObject(fields):
- return cast Tools.getValue(json);
+ var result:LegacyNoteSection =
+ {
+ mustHitSection: false,
+ sectionNotes: [],
+ };
+ for (field in fields)
+ {
+ switch (field.name)
+ {
+ case 'sectionNotes':
+ result.sectionNotes = legacyNotes(field.value, field.name);
+
+ case 'mustHitSection':
+ result.mustHitSection = Tools.getValue(field.value);
+ case 'typeOfSection':
+ result.typeOfSection = Tools.getValue(field.value);
+ case 'lengthInSteps':
+ result.lengthInSteps = Tools.getValue(field.value);
+ case 'changeBPM':
+ result.changeBPM = Tools.getValue(field.value);
+ case 'bpm':
+ result.bpm = Tools.getValue(field.value);
+ }
+ }
+ return result;
default:
throw 'Expected property $name to be an object, but it was ${json.value}.';
}
@@ -189,7 +213,12 @@ class DataParse
switch (json.value)
{
case JObject(fields):
- return cast Tools.getValue(json);
+ var result = {};
+ for (field in fields)
+ {
+ Reflect.setField(result, field.name, legacyNoteSectionArray(field.value, field.name));
+ }
+ return result;
default:
throw 'Expected property $name to be an object, but it was ${json.value}.';
}
@@ -211,13 +240,13 @@ class DataParse
switch (json.value)
{
case JArray(values):
- // var time:Null = values[0] == null ? null : Tools.getValue(values[0]);
- // var data:Null = values[1] == null ? null : Tools.getValue(values[1]);
- // var length:Null = values[2] == null ? null : Tools.getValue(values[2]);
- // var alt:Null = values[3] == null ? null : Tools.getValue(values[3]);
+ var time:Null = values[0] == null ? null : Tools.getValue(values[0]);
+ var data:Null = values[1] == null ? null : Tools.getValue(values[1]);
+ var length:Null = values[2] == null ? null : Tools.getValue(values[2]);
+ var alt:Null = values[3] == null ? null : Tools.getValue(values[3]);
- // return new LegacyNote(time, data, length, alt);
- return null;
+ return new LegacyNote(time, data, length, alt);
+ // return null;
default:
throw 'Expected property $name to be a note, but it was ${json.value}.';
}
diff --git a/source/funkin/data/level/LevelRegistry.hx b/source/funkin/data/level/LevelRegistry.hx
index 75b0b11f6..b5c15de0f 100644
--- a/source/funkin/data/level/LevelRegistry.hx
+++ b/source/funkin/data/level/LevelRegistry.hx
@@ -30,6 +30,7 @@ class LevelRegistry extends BaseRegistry
// JsonParser does not take type parameters,
// otherwise this function would be in BaseRegistry.
var parser = new json2object.JsonParser();
+ parser.ignoreUnknownVariables = false;
switch (loadEntryFile(id))
{
@@ -57,6 +58,7 @@ class LevelRegistry extends BaseRegistry
public function parseEntryDataRaw(contents:String, ?fileName:String):Null
{
var parser = new json2object.JsonParser();
+ parser.ignoreUnknownVariables = false;
parser.fromJson(contents, fileName);
if (parser.errors.length > 0)
diff --git a/source/funkin/data/notestyle/NoteStyleRegistry.hx b/source/funkin/data/notestyle/NoteStyleRegistry.hx
index 4255a644b..ffb9bf490 100644
--- a/source/funkin/data/notestyle/NoteStyleRegistry.hx
+++ b/source/funkin/data/notestyle/NoteStyleRegistry.hx
@@ -35,6 +35,7 @@ class NoteStyleRegistry extends BaseRegistry
// JsonParser does not take type parameters,
// otherwise this function would be in BaseRegistry.
var parser = new json2object.JsonParser();
+ parser.ignoreUnknownVariables = false;
switch (loadEntryFile(id))
{
@@ -62,6 +63,7 @@ class NoteStyleRegistry extends BaseRegistry
public function parseEntryDataRaw(contents:String, ?fileName:String):Null
{
var parser = new json2object.JsonParser();
+ parser.ignoreUnknownVariables = false;
parser.fromJson(contents, fileName);
if (parser.errors.length > 0)
diff --git a/source/funkin/data/song/SongData.hx b/source/funkin/data/song/SongData.hx
index 7677ad637..98cee3f17 100644
--- a/source/funkin/data/song/SongData.hx
+++ b/source/funkin/data/song/SongData.hx
@@ -747,7 +747,7 @@ class SongNoteDataRaw
/**
* The kind of the note.
* This can allow the note to include information used for custom behavior.
- * Defaults to blank or `"normal"`.
+ * Defaults to blank or `Constants.DEFAULT_DIFFICULTY`.
*/
@:alias("k")
@:default("normal")
diff --git a/source/funkin/data/song/SongDataUtils.hx b/source/funkin/data/song/SongDataUtils.hx
index 4ae4b1426..309676884 100644
--- a/source/funkin/data/song/SongDataUtils.hx
+++ b/source/funkin/data/song/SongDataUtils.hx
@@ -230,6 +230,7 @@ class SongDataUtils
trace('Read ${notesString.length} characters from clipboard.');
var parser = new json2object.JsonParser();
+ parser.ignoreUnknownVariables = false;
parser.fromJson(notesString, 'clipboard');
if (parser.errors.length > 0)
{
diff --git a/source/funkin/data/song/SongRegistry.hx b/source/funkin/data/song/SongRegistry.hx
index 850654eb7..5a0835f57 100644
--- a/source/funkin/data/song/SongRegistry.hx
+++ b/source/funkin/data/song/SongRegistry.hx
@@ -126,6 +126,8 @@ class SongRegistry extends BaseRegistry
variation = variation == null ? Constants.DEFAULT_VARIATION : variation;
var parser = new json2object.JsonParser();
+ parser.ignoreUnknownVariables = false;
+
switch (loadEntryMetadataFile(id, variation))
{
case {fileName: fileName, contents: contents}:
@@ -147,6 +149,7 @@ class SongRegistry extends BaseRegistry
variation = variation == null ? Constants.DEFAULT_VARIATION : variation;
var parser = new json2object.JsonParser();
+ parser.ignoreUnknownVariables = false;
parser.fromJson(contents, fileName);
if (parser.errors.length > 0)
@@ -206,6 +209,8 @@ class SongRegistry extends BaseRegistry
variation = variation == null ? Constants.DEFAULT_VARIATION : variation;
var parser = new json2object.JsonParser();
+ parser.ignoreUnknownVariables = false;
+
switch (loadEntryMetadataFile(id, variation))
{
case {fileName: fileName, contents: contents}:
@@ -226,6 +231,8 @@ class SongRegistry extends BaseRegistry
variation = variation == null ? Constants.DEFAULT_VARIATION : variation;
var parser = new json2object.JsonParser();
+ parser.ignoreUnknownVariables = false;
+
switch (loadEntryMetadataFile(id, variation))
{
case {fileName: fileName, contents: contents}:
@@ -244,6 +251,7 @@ class SongRegistry extends BaseRegistry
function parseEntryMetadataRaw_v2_1_0(contents:String, ?fileName:String = 'raw'):Null
{
var parser = new json2object.JsonParser();
+ parser.ignoreUnknownVariables = false;
parser.fromJson(contents, fileName);
if (parser.errors.length > 0)
@@ -257,6 +265,7 @@ class SongRegistry extends BaseRegistry
function parseEntryMetadataRaw_v2_0_0(contents:String, ?fileName:String = 'raw'):Null
{
var parser = new json2object.JsonParser();
+ parser.ignoreUnknownVariables = false;
parser.fromJson(contents, fileName);
if (parser.errors.length > 0)
@@ -272,6 +281,8 @@ class SongRegistry extends BaseRegistry
variation = variation == null ? Constants.DEFAULT_VARIATION : variation;
var parser = new json2object.JsonParser();
+ parser.ignoreUnknownVariables = false;
+
switch (loadMusicDataFile(id, variation))
{
case {fileName: fileName, contents: contents}:
@@ -291,6 +302,7 @@ class SongRegistry extends BaseRegistry
public function parseMusicDataRaw(contents:String, ?fileName:String = 'raw'):Null
{
var parser = new json2object.JsonParser();
+ parser.ignoreUnknownVariables = false;
parser.fromJson(contents, fileName);
if (parser.errors.length > 0)
@@ -334,6 +346,7 @@ class SongRegistry extends BaseRegistry
variation = variation == null ? Constants.DEFAULT_VARIATION : variation;
var parser = new json2object.JsonParser();
+ parser.ignoreUnknownVariables = false;
switch (loadEntryChartFile(id, variation))
{
@@ -356,6 +369,7 @@ class SongRegistry extends BaseRegistry
variation = variation == null ? Constants.DEFAULT_VARIATION : variation;
var parser = new json2object.JsonParser();
+ parser.ignoreUnknownVariables = false;
parser.fromJson(contents, fileName);
if (parser.errors.length > 0)
diff --git a/source/funkin/data/song/importer/ChartManifestData.hx b/source/funkin/data/song/importer/ChartManifestData.hx
index 0c7d2f0b0..dd0d28479 100644
--- a/source/funkin/data/song/importer/ChartManifestData.hx
+++ b/source/funkin/data/song/importer/ChartManifestData.hx
@@ -68,6 +68,7 @@ class ChartManifestData
public static function deserialize(contents:String):Null
{
var parser = new json2object.JsonParser();
+ parser.ignoreUnknownVariables = false;
parser.fromJson(contents, 'manifest.json');
if (parser.errors.length > 0)
diff --git a/source/funkin/data/song/importer/FNFLegacyData.hx b/source/funkin/data/song/importer/FNFLegacyData.hx
index 5b75368c9..52380d344 100644
--- a/source/funkin/data/song/importer/FNFLegacyData.hx
+++ b/source/funkin/data/song/importer/FNFLegacyData.hx
@@ -19,7 +19,8 @@ class LegacySongData
@:jcustomparse(funkin.data.DataParse.eitherLegacyScrollSpeeds)
public var speed:Either;
- public var stageDefault:String;
+ @:optional
+ public var stageDefault:Null;
public var bpm:Float;
@:jcustomparse(funkin.data.DataParse.eitherLegacyNoteData)
diff --git a/source/funkin/data/song/importer/FNFLegacyImporter.hx b/source/funkin/data/song/importer/FNFLegacyImporter.hx
index ee68513dc..ab2abda8e 100644
--- a/source/funkin/data/song/importer/FNFLegacyImporter.hx
+++ b/source/funkin/data/song/importer/FNFLegacyImporter.hx
@@ -14,6 +14,7 @@ class FNFLegacyImporter
public static function parseLegacyDataRaw(input:String, fileName:String = 'raw'):FNFLegacyData
{
var parser = new json2object.JsonParser();
+ parser.ignoreUnknownVariables = true; // Set to true to ignore extra variables that might be included in the JSON.
parser.fromJson(input, fileName);
if (parser.errors.length > 0)
@@ -185,15 +186,34 @@ class FNFLegacyImporter
return result;
}
+ static final STRUMLINE_SIZE = 4;
+
static function migrateNoteSections(input:Array):Array
{
var result:Array = [];
for (section in input)
{
+ var mustHitSection = section.mustHitSection ?? false;
for (note in section.sectionNotes)
{
- result.push(new SongNoteData(note.time, note.data, note.length, note.getKind()));
+ // Handle the dumb logic for mustHitSection.
+ var noteData = note.data;
+
+ // Flip notes if mustHitSection is FALSE (not true lol).
+ if (!mustHitSection)
+ {
+ if (noteData >= STRUMLINE_SIZE)
+ {
+ noteData -= STRUMLINE_SIZE;
+ }
+ else
+ {
+ noteData += STRUMLINE_SIZE;
+ }
+ }
+
+ result.push(new SongNoteData(note.time, noteData, note.length, note.getKind()));
}
}
diff --git a/source/funkin/input/Cursor.hx b/source/funkin/input/Cursor.hx
index b4bf43808..39f399465 100644
--- a/source/funkin/input/Cursor.hx
+++ b/source/funkin/input/Cursor.hx
@@ -34,6 +34,18 @@ class Cursor
Cursor.cursorMode = null;
}
+ public static inline function toggle():Void
+ {
+ if (FlxG.mouse.visible)
+ {
+ hide();
+ }
+ else
+ {
+ show();
+ }
+ }
+
public static final CURSOR_DEFAULT_PARAMS:CursorParams =
{
graphic: "assets/images/cursor/cursor-default.png",
diff --git a/source/funkin/play/GameOverSubState.hx b/source/funkin/play/GameOverSubState.hx
index ff79688f1..20233ad88 100644
--- a/source/funkin/play/GameOverSubState.hx
+++ b/source/funkin/play/GameOverSubState.hx
@@ -64,9 +64,16 @@ class GameOverSubState extends MusicBeatSubState
*/
var isEnding:Bool = false;
- public function new()
+ var isChartingMode:Bool = false;
+
+ var transparent:Bool;
+
+ public function new(params:GameOverParams)
{
super();
+
+ this.isChartingMode = params?.isChartingMode ?? false;
+ transparent = params.transparent;
}
/**
@@ -87,9 +94,10 @@ class GameOverSubState extends MusicBeatSubState
//
// Add a black background to the screen.
- // We make this transparent so that we can see the stage underneath during debugging.
var bg = new FlxSprite().makeGraphic(FlxG.width, FlxG.height, FlxColor.BLACK);
- bg.alpha = 0.25;
+ // We make this transparent so that we can see the stage underneath during debugging,
+ // but it's normally opaque.
+ bg.alpha = transparent ? 0.25 : 1.0;
bg.scrollFactor.set();
add(bg);
@@ -176,9 +184,20 @@ class GameOverSubState extends MusicBeatSubState
// PlayState.seenCutscene = false; // old thing...
gameOverMusic.stop();
- if (PlayStatePlaylist.isStoryMode) FlxG.switchState(new StoryMenuState());
+ if (isChartingMode)
+ {
+ this.close();
+ if (FlxG.sound.music != null) FlxG.sound.music.pause(); // Don't reset song position!
+ PlayState.instance.close(); // This only works because PlayState is a substate!
+ }
+ else if (PlayStatePlaylist.isStoryMode)
+ {
+ FlxG.switchState(new StoryMenuState());
+ }
else
+ {
FlxG.switchState(new FreeplayState());
+ }
}
if (gameOverMusic.playing)
@@ -307,3 +326,9 @@ class GameOverSubState extends MusicBeatSubState
});
}
}
+
+typedef GameOverParams =
+{
+ var isChartingMode:Bool;
+ var transparent:Bool;
+}
diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx
index bbe63199f..f15529a04 100644
--- a/source/funkin/play/PlayState.hx
+++ b/source/funkin/play/PlayState.hx
@@ -922,7 +922,11 @@ class PlayState extends MusicBeatSubState
}
#end
- var gameOverSubState = new GameOverSubState();
+ var gameOverSubState = new GameOverSubState(
+ {
+ isChartingMode: isChartingMode,
+ transparent: persistentDraw
+ });
FlxTransitionableSubState.skipNextTransIn = true;
FlxTransitionableSubState.skipNextTransOut = true;
openSubState(gameOverSubState);
@@ -1097,23 +1101,6 @@ class PlayState extends MusicBeatSubState
}
#end
- /**
- * This function is called whenever Flixel switches switching to a new FlxState.
- * @return Whether to actually switch to the new state.
- */
- @:haxe.warning("-WDeprecated")
- override function switchTo(nextState:FlxState):Bool
- {
- var result:Bool = super.switchTo(nextState);
-
- if (result)
- {
- performCleanup();
- }
-
- return result;
- }
-
/**
* Removes any references to the current stage, then clears the stage cache,
* then reloads all the stages.
@@ -1251,7 +1238,7 @@ class PlayState extends MusicBeatSubState
return true;
}
- override function destroy():Void
+ public override function destroy():Void
{
if (currentConversation != null)
{
@@ -1259,6 +1246,8 @@ class PlayState extends MusicBeatSubState
currentConversation.kill();
}
+ performCleanup();
+
super.destroy();
}
diff --git a/source/funkin/play/character/MultiSparrowCharacter.hx b/source/funkin/play/character/MultiSparrowCharacter.hx
index 968f613ff..0fc07399c 100644
--- a/source/funkin/play/character/MultiSparrowCharacter.hx
+++ b/source/funkin/play/character/MultiSparrowCharacter.hx
@@ -205,7 +205,7 @@ class MultiSparrowCharacter extends BaseCharacter
graphic = value.parent;
this.frames = value;
this.frame = value.getByIndex(0);
- this.numFrames = value.numFrames;
+ // this.numFrames = value.numFrames;
resetHelpers();
this.bakedRotationAngle = 0;
this.animation.frameIndex = 0;
diff --git a/source/funkin/play/stage/StageData.hx b/source/funkin/play/stage/StageData.hx
index d89995ef3..2d87dec31 100644
--- a/source/funkin/play/stage/StageData.hx
+++ b/source/funkin/play/stage/StageData.hx
@@ -164,6 +164,7 @@ class StageDataParser
try
{
var parser = new json2object.JsonParser();
+ parser.ignoreUnknownVariables = false;
parser.fromJson(rawJson, '$stageId.json');
if (parser.errors.length > 0)
diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx
index 660d1a71a..0924fc2dc 100644
--- a/source/funkin/ui/debug/charting/ChartEditorState.hx
+++ b/source/funkin/ui/debug/charting/ChartEditorState.hx
@@ -754,15 +754,23 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
function set_currentNoteSelection(value:Array):Array
{
+ // This value is true if all elements of the current selection are also in the new selection.
+ var isSuperset:Bool = currentNoteSelection.isSubset(value);
+ var isEqual:Bool = currentNoteSelection.isEqualUnordered(value);
+
currentNoteSelection = value;
- if (currentNoteSelection.length > 0)
+ if (!isEqual)
{
- notePreview.addNotes(currentNoteSelection, Std.int(songLengthInMs), true);
- }
- else
- {
- notePreviewDirty = true;
+ if (currentNoteSelection.length > 0 && isSuperset)
+ {
+ notePreview.addSelectedNotes(currentNoteSelection, Std.int(songLengthInMs));
+ }
+ else
+ {
+ // The new selection removes elements from the old selection, so we have to redraw the note preview.
+ notePreviewDirty = true;
+ }
}
return currentNoteSelection;
@@ -1011,7 +1019,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
function get_availableDifficulties():Array
{
var m:Null = songMetadata.get(selectedVariation);
- return m?.playData?.difficulties ?? [];
+ return m?.playData?.difficulties ?? [Constants.DEFAULT_DIFFICULTY];
}
/**
@@ -1070,7 +1078,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
var result:Null = songChartData.get(selectedVariation);
if (result == null)
{
- result = new SongChartData(["normal" => 1.0], [], ["normal" => []]);
+ result = new SongChartData([Constants.DEFAULT_DIFFICULTY => 1.0], [], [Constants.DEFAULT_DIFFICULTY => []]);
songChartData.set(selectedVariation, result);
}
return result;
@@ -1294,6 +1302,8 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
function set_selectedDifficulty(value:String):String
{
+ if (value == null) value = availableDifficulties[0] ?? Constants.DEFAULT_DIFFICULTY;
+
selectedDifficulty = value;
// Make sure view is updated when the difficulty changes.
@@ -2547,8 +2557,25 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
menubarItemPlayPause.onClick = _ -> toggleAudioPlayback();
- menubarItemLoadInstrumental.onClick = _ -> this.openUploadInstDialog(true);
- menubarItemLoadVocals.onClick = _ -> this.openUploadVocalsDialog(true);
+ menubarItemLoadInstrumental.onClick = _ -> {
+ var dialog = this.openUploadInstDialog(true);
+ // Ensure instrumental and vocals are reloaded properly.
+ dialog.onDialogClosed = function(_) {
+ this.isHaxeUIDialogOpen = false;
+ this.switchToCurrentInstrumental();
+ this.postLoadInstrumental();
+ }
+ };
+
+ menubarItemLoadVocals.onClick = _ -> {
+ var dialog = this.openUploadVocalsDialog(true);
+ // Ensure instrumental and vocals are reloaded properly.
+ dialog.onDialogClosed = function(_) {
+ this.isHaxeUIDialogOpen = false;
+ this.switchToCurrentInstrumental();
+ this.postLoadInstrumental();
+ }
+ };
menubarItemVolumeMetronome.onChange = event -> {
var volume:Float = event.value.toFloat() / 100.0;
@@ -2686,14 +2713,16 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
* Open the backups folder in the file explorer.
* Don't call this on HTML5.
*/
- function openBackupsFolder(?_):Void
+ function openBackupsFolder(?_):Bool
{
#if sys
// TODO: Is there a way to open a folder and highlight a file in it?
var absoluteBackupsPath:String = Path.join([Sys.getCwd(), ChartEditorImportExportHandler.BACKUPS_PATH]);
WindowUtil.openFolder(absoluteBackupsPath);
+ return true;
#else
trace('No file system access, cannot open backups folder.');
+ return false;
#end
}
@@ -4054,7 +4083,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
}
else
{
- // If we clicked and released outside the grid, do nothing.
+ // If we clicked and released outside the grid (or on HaxeUI), do nothing.
}
}
@@ -4377,7 +4406,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
playbarNoteSnap.text = '1/${noteSnapQuant}';
playbarDifficulty.text = "Difficulty: " + selectedDifficulty.toTitleCase();
- playbarBPM.text = "BPM: " + Conductor.instance.currentTimeChange.bpm;
+ playbarBPM.text = 'BPM: ${Conductor.instance.currentTimeChange.bpm}';
}
function handlePlayhead():Void
@@ -5320,6 +5349,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
// TODO: Only update the notes that have changed.
notePreview.erase();
notePreview.addNotes(currentSongChartNoteData, Std.int(songLengthInMs));
+ notePreview.addSelectedNotes(currentNoteSelection, Std.int(songLengthInMs));
notePreview.addEvents(currentSongChartEventData, Std.int(songLengthInMs));
}
diff --git a/source/funkin/ui/debug/charting/components/ChartEditorNotePreview.hx b/source/funkin/ui/debug/charting/components/ChartEditorNotePreview.hx
index 09c99531d..598cbb544 100644
--- a/source/funkin/ui/debug/charting/components/ChartEditorNotePreview.hx
+++ b/source/funkin/ui/debug/charting/components/ChartEditorNotePreview.hx
@@ -80,11 +80,24 @@ class ChartEditorNotePreview extends FlxSprite
* @param notes The data for the notes.
* @param songLengthInMs The total length of the song in milliseconds.
*/
- public function addNotes(notes:Array, songLengthInMs:Int, ?isSelection:Bool = false):Void
+ public function addNotes(notes:Array, songLengthInMs:Int):Void
{
for (note in notes)
{
- addNote(note, songLengthInMs, isSelection);
+ addNote(note, songLengthInMs, false);
+ }
+ }
+
+ /**
+ * Add an array of selected notes to the preview.
+ * @param notes The data for the notes.
+ * @param songLengthInMs The total length of the song in milliseconds.
+ */
+ public function addSelectedNotes(notes:Array, songLengthInMs:Int):Void
+ {
+ for (note in notes)
+ {
+ addNote(note, songLengthInMs, true);
}
}
diff --git a/source/funkin/ui/debug/charting/dialogs/ChartEditorAboutDialog.hx b/source/funkin/ui/debug/charting/dialogs/ChartEditorAboutDialog.hx
index e6f57c49f..8b55be540 100644
--- a/source/funkin/ui/debug/charting/dialogs/ChartEditorAboutDialog.hx
+++ b/source/funkin/ui/debug/charting/dialogs/ChartEditorAboutDialog.hx
@@ -5,14 +5,14 @@ import funkin.ui.debug.charting.dialogs.ChartEditorBaseDialog.DialogParams;
@:build(haxe.ui.ComponentBuilder.build("assets/exclude/data/ui/chart-editor/dialogs/about.xml"))
class ChartEditorAboutDialog extends ChartEditorBaseDialog
{
- public function new(state2:ChartEditorState, params2:DialogParams)
+ public function new(chartEditorState2:ChartEditorState, params2:DialogParams)
{
- super(state2, params2);
+ super(chartEditorState2, params2);
}
- public static function build(state:ChartEditorState, ?closable:Bool, ?modal:Bool):ChartEditorAboutDialog
+ public static function build(chartEditorState:ChartEditorState, ?closable:Bool, ?modal:Bool):ChartEditorAboutDialog
{
- var dialog = new ChartEditorAboutDialog(state,
+ var dialog = new ChartEditorAboutDialog(chartEditorState,
{
closable: closable ?? true,
modal: modal ?? true
diff --git a/source/funkin/ui/debug/charting/dialogs/ChartEditorBaseDialog.hx b/source/funkin/ui/debug/charting/dialogs/ChartEditorBaseDialog.hx
index 6f76e543e..5b7cb15e1 100644
--- a/source/funkin/ui/debug/charting/dialogs/ChartEditorBaseDialog.hx
+++ b/source/funkin/ui/debug/charting/dialogs/ChartEditorBaseDialog.hx
@@ -11,16 +11,16 @@ import haxe.ui.core.Component;
@:access(funkin.ui.debug.charting.ChartEditorState)
class ChartEditorBaseDialog extends Dialog
{
- var state:ChartEditorState;
+ var chartEditorState:ChartEditorState;
var params:DialogParams;
var locked:Bool = false;
- public function new(state:ChartEditorState, params:DialogParams)
+ public function new(chartEditorState:ChartEditorState, params:DialogParams)
{
super();
- this.state = state;
+ this.chartEditorState = chartEditorState;
this.params = params;
this.destroyOnClose = true;
@@ -47,7 +47,7 @@ class ChartEditorBaseDialog extends Dialog
*/
public function onClose(event:DialogEvent):Void
{
- state.isHaxeUIDialogOpen = false;
+ chartEditorState.isHaxeUIDialogOpen = false;
}
/**
diff --git a/source/funkin/ui/debug/charting/dialogs/ChartEditorBaseMenu.hx b/source/funkin/ui/debug/charting/dialogs/ChartEditorBaseMenu.hx
index cb4cb447b..19ca2535c 100644
--- a/source/funkin/ui/debug/charting/dialogs/ChartEditorBaseMenu.hx
+++ b/source/funkin/ui/debug/charting/dialogs/ChartEditorBaseMenu.hx
@@ -11,13 +11,13 @@ import haxe.ui.containers.menus.Menu;
@:access(funkin.ui.debug.charting.ChartEditorState)
class ChartEditorBaseMenu extends Menu
{
- var state:ChartEditorState;
+ var chartEditorState:ChartEditorState;
- public function new(state:ChartEditorState)
+ public function new(chartEditorState:ChartEditorState)
{
super();
- this.state = state;
+ this.chartEditorState = chartEditorState;
// this.destroyOnClose = true;
}
diff --git a/source/funkin/ui/debug/charting/dialogs/ChartEditorCharacterIconSelectorMenu.hx b/source/funkin/ui/debug/charting/dialogs/ChartEditorCharacterIconSelectorMenu.hx
index d7e9c259b..eb60cb6db 100644
--- a/source/funkin/ui/debug/charting/dialogs/ChartEditorCharacterIconSelectorMenu.hx
+++ b/source/funkin/ui/debug/charting/dialogs/ChartEditorCharacterIconSelectorMenu.hx
@@ -24,9 +24,9 @@ class ChartEditorCharacterIconSelectorMenu extends ChartEditorBaseMenu
public var charSelectScroll:ScrollView;
public var charIconName:Label;
- public function new(state2:ChartEditorState, charType:CharacterType, lockPosition:Bool = false)
+ public function new(chartEditorState2:ChartEditorState, charType:CharacterType, lockPosition:Bool = false)
{
- super(state2);
+ super(chartEditorState2);
initialize(charType, lockPosition);
this.alpha = 0;
@@ -38,17 +38,17 @@ class ChartEditorCharacterIconSelectorMenu extends ChartEditorBaseMenu
{
var currentCharId:String = switch (charType)
{
- case BF: state.currentSongMetadata.playData.characters.player;
- case GF: state.currentSongMetadata.playData.characters.girlfriend;
- case DAD: state.currentSongMetadata.playData.characters.opponent;
+ case BF: chartEditorState.currentSongMetadata.playData.characters.player;
+ case GF: chartEditorState.currentSongMetadata.playData.characters.girlfriend;
+ case DAD: chartEditorState.currentSongMetadata.playData.characters.opponent;
default: throw 'Invalid charType: ' + charType;
};
// Position this menu.
var targetHealthIcon:Null = switch (charType)
{
- case BF: state.healthIconBF;
- case DAD: state.healthIconDad;
+ case BF: chartEditorState.healthIconBF;
+ case DAD: chartEditorState.healthIconDad;
default: null;
};
@@ -101,14 +101,14 @@ class ChartEditorCharacterIconSelectorMenu extends ChartEditorBaseMenu
charButton.onClick = _ -> {
switch (charType)
{
- case BF: state.currentSongMetadata.playData.characters.player = charId;
- case GF: state.currentSongMetadata.playData.characters.girlfriend = charId;
- case DAD: state.currentSongMetadata.playData.characters.opponent = charId;
+ case BF: chartEditorState.currentSongMetadata.playData.characters.player = charId;
+ case GF: chartEditorState.currentSongMetadata.playData.characters.girlfriend = charId;
+ case DAD: chartEditorState.currentSongMetadata.playData.characters.opponent = charId;
default: throw 'Invalid charType: ' + charType;
};
- state.healthIconsDirty = true;
- state.refreshToolbox(ChartEditorState.CHART_EDITOR_TOOLBOX_METADATA_LAYOUT);
+ chartEditorState.healthIconsDirty = true;
+ chartEditorState.refreshToolbox(ChartEditorState.CHART_EDITOR_TOOLBOX_METADATA_LAYOUT);
};
charButton.onMouseOver = _ -> {
@@ -123,9 +123,9 @@ class ChartEditorCharacterIconSelectorMenu extends ChartEditorBaseMenu
charIconName.text = defaultText;
}
- public static function build(state2:ChartEditorState, charType:CharacterType, lockPosition:Bool = false):ChartEditorCharacterIconSelectorMenu
+ public static function build(chartEditorState:ChartEditorState, charType:CharacterType, lockPosition:Bool = false):ChartEditorCharacterIconSelectorMenu
{
- var menu = new ChartEditorCharacterIconSelectorMenu(state2, charType, lockPosition);
+ var menu = new ChartEditorCharacterIconSelectorMenu(chartEditorState, charType, lockPosition);
Screen.instance.addComponent(menu);
diff --git a/source/funkin/ui/debug/charting/dialogs/ChartEditorUploadChartDialog.hx b/source/funkin/ui/debug/charting/dialogs/ChartEditorUploadChartDialog.hx
index 4b7a950be..5b84148c6 100644
--- a/source/funkin/ui/debug/charting/dialogs/ChartEditorUploadChartDialog.hx
+++ b/source/funkin/ui/debug/charting/dialogs/ChartEditorUploadChartDialog.hx
@@ -64,12 +64,12 @@ class ChartEditorUploadChartDialog extends ChartEditorBaseDialog
if (event.button != DialogButton.APPLY && !this.closable)
{
// User cancelled the wizard! Back to the welcome dialog.
- state.openWelcomeDialog(this.closable);
+ chartEditorState.openWelcomeDialog(this.closable);
}
for (dropTarget in dropHandlers)
{
- state.removeDropHandler(dropTarget);
+ chartEditorState.removeDropHandler(dropTarget);
}
}
@@ -111,20 +111,21 @@ class ChartEditorUploadChartDialog extends ChartEditorBaseDialog
try
{
- var result:Null> = ChartEditorImportExportHandler.loadFromFNFCPath(state, path.toString());
+ var result:Null> = ChartEditorImportExportHandler.loadFromFNFCPath(chartEditorState, path.toString());
if (result != null)
{
- state.success('Loaded Chart', result.length == 0 ? 'Loaded chart (${path.toString()})' : 'Loaded chart (${path.toString()})\n${result.join("\n")}');
+ chartEditorState.success('Loaded Chart',
+ result.length == 0 ? 'Loaded chart (${path.toString()})' : 'Loaded chart (${path.toString()})\n${result.join("\n")}');
this.hideDialog(DialogButton.APPLY);
}
else
{
- state.failure('Failed to Load Chart', 'Failed to load chart (${path.toString()})');
+ chartEditorState.failure('Failed to Load Chart', 'Failed to load chart (${path.toString()})');
}
}
catch (err)
{
- state.failure('Failed to Load Chart', 'Failed to load chart (${path.toString()}): ${err}');
+ chartEditorState.failure('Failed to Load Chart', 'Failed to load chart (${path.toString()}): ${err}');
}
}
@@ -139,19 +140,19 @@ class ChartEditorUploadChartDialog extends ChartEditorBaseDialog
{
try
{
- var result:Null> = ChartEditorImportExportHandler.loadFromFNFC(state, selectedFile.bytes);
+ var result:Null> = ChartEditorImportExportHandler.loadFromFNFC(chartEditorState, selectedFile.bytes);
if (result != null)
{
- state.success('Loaded Chart',
+ chartEditorState.success('Loaded Chart',
result.length == 0 ? 'Loaded chart (${selectedFile.name})' : 'Loaded chart (${selectedFile.name})\n${result.join("\n")}');
- if (selectedFile.fullPath != null) state.currentWorkingFilePath = selectedFile.fullPath;
+ if (selectedFile.fullPath != null) chartEditorState.currentWorkingFilePath = selectedFile.fullPath;
this.hideDialog(DialogButton.APPLY);
}
}
catch (err)
{
- state.failure('Failed to Load Chart', 'Failed to load chart (${selectedFile.name}): ${err}');
+ chartEditorState.failure('Failed to Load Chart', 'Failed to load chart (${selectedFile.name}): ${err}');
}
}
}
diff --git a/source/funkin/ui/debug/charting/dialogs/ChartEditorWelcomeDialog.hx b/source/funkin/ui/debug/charting/dialogs/ChartEditorWelcomeDialog.hx
index 2a30c7d9e..7539b9725 100644
--- a/source/funkin/ui/debug/charting/dialogs/ChartEditorWelcomeDialog.hx
+++ b/source/funkin/ui/debug/charting/dialogs/ChartEditorWelcomeDialog.hx
@@ -41,26 +41,26 @@ class ChartEditorWelcomeDialog extends ChartEditorBaseDialog
// Add items to the Recent Charts list
#if sys
- for (chartPath in state.previousWorkingFilePaths)
+ for (chartPath in chartEditorState.previousWorkingFilePaths)
{
if (chartPath == null) continue;
- this.addRecentFilePath(state, chartPath);
+ this.addRecentFilePath(chartEditorState, chartPath);
}
#else
this.addHTML5RecentFileMessage();
#end
// Add items to the Load From Template list
- this.buildTemplateSongList(state);
+ this.buildTemplateSongList(chartEditorState);
}
/**
* @param state The current state of the chart editor.
* @return A newly created `ChartEditorWelcomeDialog`.
*/
- public static function build(state:ChartEditorState, ?closable:Bool, ?modal:Bool):ChartEditorWelcomeDialog
+ public static function build(chartEditorState:ChartEditorState, ?closable:Bool, ?modal:Bool):ChartEditorWelcomeDialog
{
- var dialog = new ChartEditorWelcomeDialog(state,
+ var dialog = new ChartEditorWelcomeDialog(chartEditorState,
{
closable: closable ?? false,
modal: modal ?? true
@@ -102,12 +102,12 @@ class ChartEditorWelcomeDialog extends ChartEditorBaseDialog
var result:Null> = ChartEditorImportExportHandler.loadFromFNFCPath(state, chartPath);
if (result != null)
{
- state.success('Loaded Chart',
+ chartEditorState.success('Loaded Chart',
result.length == 0 ? 'Loaded chart (${chartPath.toString()})' : 'Loaded chart (${chartPath.toString()})\n${result.join("\n")}');
}
else
{
- state.error('Failed to Load Chart', 'Failed to load chart (${chartPath.toString()})');
+ chartEditorState.error('Failed to Load Chart', 'Failed to load chart (${chartPath.toString()})');
}
}
@@ -157,7 +157,7 @@ class ChartEditorWelcomeDialog extends ChartEditorBaseDialog
this.hideDialog(DialogButton.CANCEL);
// Load song from template
- state.loadSongAsTemplate(targetSongId);
+ chartEditorState.loadSongAsTemplate(targetSongId);
});
}
}
@@ -184,7 +184,7 @@ class ChartEditorWelcomeDialog extends ChartEditorBaseDialog
this.hideDialog(DialogButton.CANCEL);
// Open the "Open Chart" dialog
- state.openBrowseFNFC(false);
+ chartEditorState.openBrowseFNFC(false);
}
/**
@@ -199,7 +199,7 @@ class ChartEditorWelcomeDialog extends ChartEditorBaseDialog
//
// Create Song Wizard
//
- state.openCreateSongWizardBasicOnly(false);
+ chartEditorState.openCreateSongWizardBasicOnly(false);
}
/**
@@ -214,7 +214,7 @@ class ChartEditorWelcomeDialog extends ChartEditorBaseDialog
//
// Create Song Wizard
//
- state.openCreateSongWizardErectOnly(false);
+ chartEditorState.openCreateSongWizardErectOnly(false);
}
/**
@@ -229,7 +229,7 @@ class ChartEditorWelcomeDialog extends ChartEditorBaseDialog
//
// Create Song Wizard
//
- state.openCreateSongWizardBasicErect(false);
+ chartEditorState.openCreateSongWizardBasicErect(false);
}
/**
@@ -242,6 +242,6 @@ class ChartEditorWelcomeDialog extends ChartEditorBaseDialog
this.hideDialog(DialogButton.CANCEL);
// Open the "Import Chart" dialog
- state.openImportChartWizard('legacy', false);
+ chartEditorState.openImportChartWizard('legacy', false);
}
}
diff --git a/source/funkin/ui/debug/charting/handlers/ChartEditorAudioHandler.hx b/source/funkin/ui/debug/charting/handlers/ChartEditorAudioHandler.hx
index 54347160a..2ef3fd683 100644
--- a/source/funkin/ui/debug/charting/handlers/ChartEditorAudioHandler.hx
+++ b/source/funkin/ui/debug/charting/handlers/ChartEditorAudioHandler.hx
@@ -28,11 +28,11 @@ class ChartEditorAudioHandler
* @param instId The instrumental this vocal track will be for.
* @return Success or failure.
*/
- public static function loadVocalsFromPath(state:ChartEditorState, path:Path, charId:String, instId:String = ''):Bool
+ public static function loadVocalsFromPath(state:ChartEditorState, path:Path, charId:String, instId:String = '', wipeFirst:Bool = false):Bool
{
#if sys
var fileBytes:Bytes = sys.io.File.getBytes(path.toString());
- return loadVocalsFromBytes(state, fileBytes, charId, instId);
+ return loadVocalsFromBytes(state, fileBytes, charId, instId, wipeFirst);
#else
trace("[WARN] This platform can't load audio from a file path, you'll need to fetch the bytes some other way.");
return false;
@@ -47,12 +47,12 @@ class ChartEditorAudioHandler
* @param instId The instrumental this vocal track will be for.
* @return Success or failure.
*/
- public static function loadVocalsFromAsset(state:ChartEditorState, path:String, charId:String, instId:String = ''):Bool
+ public static function loadVocalsFromAsset(state:ChartEditorState, path:String, charId:String, instId:String = '', wipeFirst:Bool = false):Bool
{
var trackData:Null = Assets.getBytes(path);
if (trackData != null)
{
- return loadVocalsFromBytes(state, trackData, charId, instId);
+ return loadVocalsFromBytes(state, trackData, charId, instId, wipeFirst);
}
return false;
}
@@ -63,10 +63,12 @@ class ChartEditorAudioHandler
* @param bytes The audio byte data.
* @param charId The character this vocal track will be for.
* @param instId The instrumental this vocal track will be for.
+ * @param wipeFirst Whether to wipe the existing vocal data before loading.
*/
- public static function loadVocalsFromBytes(state:ChartEditorState, bytes:Bytes, charId:String, instId:String = ''):Bool
+ public static function loadVocalsFromBytes(state:ChartEditorState, bytes:Bytes, charId:String, instId:String = '', wipeFirst:Bool = false):Bool
{
var trackId:String = '${charId}${instId == '' ? '' : '-${instId}'}';
+ if (wipeFirst) wipeVocalData(state);
state.audioVocalTrackData.set(trackId, bytes);
return true;
}
@@ -78,11 +80,11 @@ class ChartEditorAudioHandler
* @param instId The instrumental this vocal track will be for.
* @return Success or failure.
*/
- public static function loadInstFromPath(state:ChartEditorState, path:Path, instId:String = ''):Bool
+ public static function loadInstFromPath(state:ChartEditorState, path:Path, instId:String = '', wipeFirst:Bool = false):Bool
{
#if sys
var fileBytes:Bytes = sys.io.File.getBytes(path.toString());
- return loadInstFromBytes(state, fileBytes, instId);
+ return loadInstFromBytes(state, fileBytes, instId, wipeFirst);
#else
trace("[WARN] This platform can't load audio from a file path, you'll need to fetch the bytes some other way.");
return false;
@@ -96,12 +98,12 @@ class ChartEditorAudioHandler
* @param instId The instrumental this vocal track will be for.
* @return Success or failure.
*/
- public static function loadInstFromAsset(state:ChartEditorState, path:String, instId:String = ''):Bool
+ public static function loadInstFromAsset(state:ChartEditorState, path:String, instId:String = '', wipeFirst:Bool = false):Bool
{
var trackData:Null = Assets.getBytes(path);
if (trackData != null)
{
- return loadInstFromBytes(state, trackData, instId);
+ return loadInstFromBytes(state, trackData, instId, wipeFirst);
}
return false;
}
@@ -113,9 +115,10 @@ class ChartEditorAudioHandler
* @param charId The character this vocal track will be for.
* @param instId The instrumental this vocal track will be for.
*/
- public static function loadInstFromBytes(state:ChartEditorState, bytes:Bytes, instId:String = ''):Bool
+ public static function loadInstFromBytes(state:ChartEditorState, bytes:Bytes, instId:String = '', wipeFirst:Bool = false):Bool
{
if (instId == '') instId = 'default';
+ if (wipeFirst) wipeInstrumentalData(state);
state.audioInstTrackData.set(instId, bytes);
return true;
}
@@ -127,9 +130,9 @@ class ChartEditorAudioHandler
stopExistingVocals(state);
result = playVocals(state, BF, playerId, instId);
- if (!result) return false;
+ // if (!result) return false;
result = playVocals(state, DAD, opponentId, instId);
- if (!result) return false;
+ // if (!result) return false;
return true;
}
diff --git a/source/funkin/ui/debug/charting/handlers/ChartEditorDialogHandler.hx b/source/funkin/ui/debug/charting/handlers/ChartEditorDialogHandler.hx
index 7e9ae1fd4..1d53dfebd 100644
--- a/source/funkin/ui/debug/charting/handlers/ChartEditorDialogHandler.hx
+++ b/source/funkin/ui/debug/charting/handlers/ChartEditorDialogHandler.hx
@@ -687,6 +687,9 @@ class ChartEditorDialogHandler
Conductor.instance.instrumentalOffset = state.currentInstrumentalOffset; // Loads from the metadata.
Conductor.instance.mapTimeChanges(state.currentSongMetadata.timeChanges);
+ state.selectedVariation = Constants.DEFAULT_VARIATION;
+ state.selectedDifficulty = state.availableDifficulties[0];
+
state.difficultySelectDirty = true;
dialog.hideDialog(DialogButton.APPLY);
@@ -755,14 +758,9 @@ class ChartEditorDialogHandler
trace('Selected file: $pathStr');
var path:Path = new Path(pathStr);
- if (!hasClearedVocals)
+ if (state.loadVocalsFromPath(path, charKey, instId, !hasClearedVocals))
{
hasClearedVocals = true;
- state.stopExistingVocals();
- }
-
- if (state.loadVocalsFromPath(path, charKey, instId))
- {
// Tell the user the load was successful.
state.success('Loaded Vocals', 'Loaded vocals for $charName (${path.file}.${path.ext}), variation ${state.selectedVariation}');
#if FILE_DROP_SUPPORTED
@@ -796,13 +794,10 @@ class ChartEditorDialogHandler
if (selectedFile != null && selectedFile.bytes != null)
{
trace('Selected file: ' + selectedFile.name);
- if (!hasClearedVocals)
+
+ if (state.loadVocalsFromBytes(selectedFile.bytes, charKey, instId, !hasClearedVocals))
{
hasClearedVocals = true;
- state.stopExistingVocals();
- }
- if (state.loadVocalsFromBytes(selectedFile.bytes, charKey, instId))
- {
// Tell the user the load was successful.
state.success('Loaded Vocals', 'Loaded vocals for $charName (${selectedFile.name}), variation ${state.selectedVariation}');
diff --git a/source/funkin/ui/debug/charting/handlers/ChartEditorImportExportHandler.hx b/source/funkin/ui/debug/charting/handlers/ChartEditorImportExportHandler.hx
index 4ae55d191..472492abc 100644
--- a/source/funkin/ui/debug/charting/handlers/ChartEditorImportExportHandler.hx
+++ b/source/funkin/ui/debug/charting/handlers/ChartEditorImportExportHandler.hx
@@ -315,6 +315,8 @@ class ChartEditorImportExportHandler
public static function getLatestBackupPath():Null
{
#if sys
+ if (!sys.FileSystem.exists(BACKUPS_PATH)) sys.FileSystem.createDirectory(BACKUPS_PATH);
+
var entries:Array = sys.FileSystem.readDirectory(BACKUPS_PATH);
entries.sort(SortUtil.alphabetically);
diff --git a/source/funkin/ui/debug/charting/toolboxes/ChartEditorBaseToolbox.hx b/source/funkin/ui/debug/charting/toolboxes/ChartEditorBaseToolbox.hx
index c4c532205..7df06c249 100644
--- a/source/funkin/ui/debug/charting/toolboxes/ChartEditorBaseToolbox.hx
+++ b/source/funkin/ui/debug/charting/toolboxes/ChartEditorBaseToolbox.hx
@@ -12,13 +12,13 @@ import haxe.ui.core.Component;
@:access(funkin.ui.debug.charting.ChartEditorState)
class ChartEditorBaseToolbox extends CollapsibleDialog
{
- var state:ChartEditorState;
+ var chartEditorState:ChartEditorState;
- private function new(state:ChartEditorState)
+ private function new(chartEditorState:ChartEditorState)
{
super();
- this.state = state;
+ this.chartEditorState = chartEditorState;
}
/**
diff --git a/source/funkin/ui/debug/charting/toolboxes/ChartEditorMetadataToolbox.hx b/source/funkin/ui/debug/charting/toolboxes/ChartEditorMetadataToolbox.hx
index 58aa69a3e..700e5ec6a 100644
--- a/source/funkin/ui/debug/charting/toolboxes/ChartEditorMetadataToolbox.hx
+++ b/source/funkin/ui/debug/charting/toolboxes/ChartEditorMetadataToolbox.hx
@@ -40,9 +40,9 @@ class ChartEditorMetadataToolbox extends ChartEditorBaseToolbox
var frameVariation:Frame;
var frameDifficulty:Frame;
- public function new(state2:ChartEditorState)
+ public function new(chartEditorState2:ChartEditorState)
{
- super(state2);
+ super(chartEditorState2);
initialize();
@@ -51,7 +51,7 @@ class ChartEditorMetadataToolbox extends ChartEditorBaseToolbox
function onClose(event:UIEvent)
{
- state.menubarItemToggleToolboxMetadata.selected = false;
+ chartEditorState.menubarItemToggleToolboxMetadata.selected = false;
}
function initialize():Void
@@ -67,11 +67,11 @@ class ChartEditorMetadataToolbox extends ChartEditorBaseToolbox
if (valid)
{
inputSongName.removeClass('invalid-value');
- state.currentSongMetadata.songName = event.target.text;
+ chartEditorState.currentSongMetadata.songName = event.target.text;
}
else
{
- state.currentSongMetadata.songName = '';
+ chartEditorState.currentSongMetadata.songName = '';
}
};
@@ -81,11 +81,11 @@ class ChartEditorMetadataToolbox extends ChartEditorBaseToolbox
if (valid)
{
inputSongArtist.removeClass('invalid-value');
- state.currentSongMetadata.artist = event.target.text;
+ chartEditorState.currentSongMetadata.artist = event.target.text;
}
else
{
- state.currentSongMetadata.artist = '';
+ chartEditorState.currentSongMetadata.artist = '';
}
};
@@ -94,41 +94,41 @@ class ChartEditorMetadataToolbox extends ChartEditorBaseToolbox
if (valid)
{
- state.currentSongMetadata.playData.stage = event.data.id;
+ chartEditorState.currentSongMetadata.playData.stage = event.data.id;
}
};
- var startingValueStage = ChartEditorDropdowns.populateDropdownWithStages(inputStage, state.currentSongMetadata.playData.stage);
+ var startingValueStage = ChartEditorDropdowns.populateDropdownWithStages(inputStage, chartEditorState.currentSongMetadata.playData.stage);
inputStage.value = startingValueStage;
inputNoteStyle.onChange = function(event:UIEvent) {
if (event.data?.id == null) return;
- state.currentSongNoteStyle = event.data.id;
+ chartEditorState.currentSongNoteStyle = event.data.id;
};
inputBPM.onChange = function(event:UIEvent) {
if (event.value == null || event.value <= 0) return;
// Use a command so we can undo/redo this action.
- var startingBPM = state.currentSongMetadata.timeChanges[0].bpm;
+ var startingBPM = chartEditorState.currentSongMetadata.timeChanges[0].bpm;
if (event.value != startingBPM)
{
- state.performCommand(new ChangeStartingBPMCommand(event.value));
+ chartEditorState.performCommand(new ChangeStartingBPMCommand(event.value));
}
};
inputOffsetInst.onChange = function(event:UIEvent) {
if (event.value == null) return;
- state.currentInstrumentalOffset = event.value;
+ chartEditorState.currentInstrumentalOffset = event.value;
Conductor.instance.instrumentalOffset = event.value;
// Update song length.
- state.songLengthInMs = (state.audioInstTrack?.length ?? 1000.0) + Conductor.instance.instrumentalOffset;
+ chartEditorState.songLengthInMs = (chartEditorState.audioInstTrack?.length ?? 1000.0) + Conductor.instance.instrumentalOffset;
};
inputOffsetVocal.onChange = function(event:UIEvent) {
if (event.value == null) return;
- state.currentSongMetadata.offsets.setVocalOffset(state.currentSongMetadata.playData.characters.player, event.value);
+ chartEditorState.currentSongMetadata.offsets.setVocalOffset(chartEditorState.currentSongMetadata.playData.characters.player, event.value);
};
inputScrollSpeed.onChange = function(event:UIEvent) {
var valid:Bool = event.target.value != null && event.target.value > 0;
@@ -136,25 +136,25 @@ class ChartEditorMetadataToolbox extends ChartEditorBaseToolbox
if (valid)
{
inputScrollSpeed.removeClass('invalid-value');
- state.currentSongChartScrollSpeed = event.target.value;
+ chartEditorState.currentSongChartScrollSpeed = event.target.value;
}
else
{
- state.currentSongChartScrollSpeed = 1.0;
+ chartEditorState.currentSongChartScrollSpeed = 1.0;
}
- labelScrollSpeed.text = 'Scroll Speed: ${state.currentSongChartScrollSpeed}x';
+ labelScrollSpeed.text = 'Scroll Speed: ${chartEditorState.currentSongChartScrollSpeed}x';
};
buttonCharacterOpponent.onClick = function(_) {
- state.openCharacterDropdown(CharacterType.DAD, false);
+ chartEditorState.openCharacterDropdown(CharacterType.DAD, false);
};
buttonCharacterGirlfriend.onClick = function(_) {
- state.openCharacterDropdown(CharacterType.GF, false);
+ chartEditorState.openCharacterDropdown(CharacterType.GF, false);
};
buttonCharacterPlayer.onClick = function(_) {
- state.openCharacterDropdown(CharacterType.BF, false);
+ chartEditorState.openCharacterDropdown(CharacterType.BF, false);
};
refresh();
@@ -162,17 +162,17 @@ class ChartEditorMetadataToolbox extends ChartEditorBaseToolbox
public override function refresh():Void
{
- inputSongName.value = state.currentSongMetadata.songName;
- inputSongArtist.value = state.currentSongMetadata.artist;
- inputStage.value = state.currentSongMetadata.playData.stage;
- inputNoteStyle.value = state.currentSongMetadata.playData.noteStyle;
- inputBPM.value = state.currentSongMetadata.timeChanges[0].bpm;
- inputScrollSpeed.value = state.currentSongChartScrollSpeed;
- labelScrollSpeed.text = 'Scroll Speed: ${state.currentSongChartScrollSpeed}x';
- frameVariation.text = 'Variation: ${state.selectedVariation.toTitleCase()}';
- frameDifficulty.text = 'Difficulty: ${state.selectedDifficulty.toTitleCase()}';
+ inputSongName.value = chartEditorState.currentSongMetadata.songName;
+ inputSongArtist.value = chartEditorState.currentSongMetadata.artist;
+ inputStage.value = chartEditorState.currentSongMetadata.playData.stage;
+ inputNoteStyle.value = chartEditorState.currentSongMetadata.playData.noteStyle;
+ inputBPM.value = chartEditorState.currentSongMetadata.timeChanges[0].bpm;
+ inputScrollSpeed.value = chartEditorState.currentSongChartScrollSpeed;
+ labelScrollSpeed.text = 'Scroll Speed: ${chartEditorState.currentSongChartScrollSpeed}x';
+ frameVariation.text = 'Variation: ${chartEditorState.selectedVariation.toTitleCase()}';
+ frameDifficulty.text = 'Difficulty: ${chartEditorState.selectedDifficulty.toTitleCase()}';
- var stageId:String = state.currentSongMetadata.playData.stage;
+ var stageId:String = chartEditorState.currentSongMetadata.playData.stage;
var stageData:Null = StageDataParser.parseStageData(stageId);
if (inputStage != null)
{
@@ -183,21 +183,45 @@ class ChartEditorMetadataToolbox extends ChartEditorBaseToolbox
var LIMIT = 6;
- var charDataOpponent:CharacterData = CharacterDataParser.fetchCharacterData(state.currentSongMetadata.playData.characters.opponent);
- buttonCharacterOpponent.icon = CharacterDataParser.getCharPixelIconAsset(state.currentSongMetadata.playData.characters.opponent);
- buttonCharacterOpponent.text = charDataOpponent.name.length > LIMIT ? '${charDataOpponent.name.substr(0, LIMIT)}.' : '${charDataOpponent.name}';
+ var charDataOpponent:Null = CharacterDataParser.fetchCharacterData(chartEditorState.currentSongMetadata.playData.characters.opponent);
+ if (charDataOpponent != null)
+ {
+ buttonCharacterOpponent.icon = CharacterDataParser.getCharPixelIconAsset(chartEditorState.currentSongMetadata.playData.characters.opponent);
+ buttonCharacterOpponent.text = charDataOpponent.name.length > LIMIT ? '${charDataOpponent.name.substr(0, LIMIT)}.' : '${charDataOpponent.name}';
+ }
+ else
+ {
+ buttonCharacterOpponent.icon = null;
+ buttonCharacterOpponent.text = "None";
+ }
- var charDataGirlfriend:CharacterData = CharacterDataParser.fetchCharacterData(state.currentSongMetadata.playData.characters.girlfriend);
- buttonCharacterGirlfriend.icon = CharacterDataParser.getCharPixelIconAsset(state.currentSongMetadata.playData.characters.girlfriend);
- buttonCharacterGirlfriend.text = charDataGirlfriend.name.length > LIMIT ? '${charDataGirlfriend.name.substr(0, LIMIT)}.' : '${charDataGirlfriend.name}';
+ var charDataGirlfriend:Null = CharacterDataParser.fetchCharacterData(chartEditorState.currentSongMetadata.playData.characters.girlfriend);
+ if (charDataGirlfriend != null)
+ {
+ buttonCharacterGirlfriend.icon = CharacterDataParser.getCharPixelIconAsset(chartEditorState.currentSongMetadata.playData.characters.girlfriend);
+ buttonCharacterGirlfriend.text = charDataGirlfriend.name.length > LIMIT ? '${charDataGirlfriend.name.substr(0, LIMIT)}.' : '${charDataGirlfriend.name}';
+ }
+ else
+ {
+ buttonCharacterGirlfriend.icon = null;
+ buttonCharacterGirlfriend.text = "None";
+ }
- var charDataPlayer:CharacterData = CharacterDataParser.fetchCharacterData(state.currentSongMetadata.playData.characters.player);
- buttonCharacterPlayer.icon = CharacterDataParser.getCharPixelIconAsset(state.currentSongMetadata.playData.characters.player);
- buttonCharacterPlayer.text = charDataPlayer.name.length > LIMIT ? '${charDataPlayer.name.substr(0, LIMIT)}.' : '${charDataPlayer.name}';
+ var charDataPlayer:Null = CharacterDataParser.fetchCharacterData(chartEditorState.currentSongMetadata.playData.characters.player);
+ if (charDataPlayer != null)
+ {
+ buttonCharacterPlayer.icon = CharacterDataParser.getCharPixelIconAsset(chartEditorState.currentSongMetadata.playData.characters.player);
+ buttonCharacterPlayer.text = charDataPlayer.name.length > LIMIT ? '${charDataPlayer.name.substr(0, LIMIT)}.' : '${charDataPlayer.name}';
+ }
+ else
+ {
+ buttonCharacterPlayer.icon = null;
+ buttonCharacterPlayer.text = "None";
+ }
}
- public static function build(state:ChartEditorState):ChartEditorMetadataToolbox
+ public static function build(chartEditorState:ChartEditorState):ChartEditorMetadataToolbox
{
- return new ChartEditorMetadataToolbox(state);
+ return new ChartEditorMetadataToolbox(chartEditorState);
}
}
diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx
index 371021114..f17c3d91e 100644
--- a/source/funkin/ui/freeplay/FreeplayState.hx
+++ b/source/funkin/ui/freeplay/FreeplayState.hx
@@ -97,7 +97,7 @@ class FreeplayState extends MusicBeatSubState
var stickerSubState:StickerSubState;
//
- static var rememberedDifficulty:Null = "normal";
+ static var rememberedDifficulty:Null = Constants.DEFAULT_DIFFICULTY;
static var rememberedSongId:Null = null;
public function new(?stickers:StickerSubState = null)
@@ -874,15 +874,14 @@ class FreeplayState extends MusicBeatSubState
}
}
- @:haxe.warning("-WDeprecated")
- override function switchTo(nextState:FlxState):Bool
+ public override function destroy():Void
{
+ super.destroy();
var daSong = songs[curSelected];
if (daSong != null)
{
clearDaCache(daSong.songName);
}
- return super.switchTo(nextState);
}
function changeDiff(change:Int = 0)
diff --git a/source/funkin/ui/haxeui/components/CharacterPlayer.hx b/source/funkin/ui/haxeui/components/CharacterPlayer.hx
index 66b94bfa2..c7171fac7 100644
--- a/source/funkin/ui/haxeui/components/CharacterPlayer.hx
+++ b/source/funkin/ui/haxeui/components/CharacterPlayer.hx
@@ -35,7 +35,7 @@ class CharacterPlayer extends Box
public function new(defaultToBf:Bool = true)
{
super();
- _overrideSkipTransformChildren = false;
+ // _overrideSkipTransformChildren = false;
if (defaultToBf)
{
diff --git a/source/funkin/ui/story/StoryMenuState.hx b/source/funkin/ui/story/StoryMenuState.hx
index a608d833a..d6dd536f7 100644
--- a/source/funkin/ui/story/StoryMenuState.hx
+++ b/source/funkin/ui/story/StoryMenuState.hx
@@ -106,7 +106,7 @@ class StoryMenuState extends MusicBeatState
var stickerSubState:StickerSubState;
static var rememberedLevelId:Null = null;
- static var rememberedDifficulty:Null = "normal";
+ static var rememberedDifficulty:Null = Constants.DEFAULT_DIFFICULTY;
public function new(?stickers:StickerSubState = null)
{
diff --git a/source/funkin/util/Constants.hx b/source/funkin/util/Constants.hx
index f8749567b..123267a49 100644
--- a/source/funkin/util/Constants.hx
+++ b/source/funkin/util/Constants.hx
@@ -123,6 +123,7 @@ class Constants
/**
* Default list of difficulties for charts.
+ * Assumes no Erect mode, etc.
*/
public static final DEFAULT_DIFFICULTY_LIST:Array = ['easy', 'normal', 'hard'];
diff --git a/source/funkin/util/tools/ArrayTools.hx b/source/funkin/util/tools/ArrayTools.hx
index a88f8a861..925bb67a5 100644
--- a/source/funkin/util/tools/ArrayTools.hx
+++ b/source/funkin/util/tools/ArrayTools.hx
@@ -76,4 +76,56 @@ class ArrayTools
while (array.length > 0)
array.pop();
}
+
+ /**
+ * Return true only if both arrays contain the same elements (possibly in a different order).
+ * @param a The first array to compare.
+ * @param b The second array to compare.
+ * @return Weather both arrays contain the same elements.
+ */
+ public static function isEqualUnordered(a:Array, b:Array):Bool
+ {
+ if (a.length != b.length) return false;
+ for (element in a)
+ {
+ if (!b.contains(element)) return false;
+ }
+ for (element in b)
+ {
+ if (!a.contains(element)) return false;
+ }
+ return true;
+ }
+
+ /**
+ * Returns true if `superset` contains all elements of `subset`.
+ * @param superset The array to query for each element.
+ * @param subset The array containing the elements to query for.
+ * @return Weather `superset` contains all elements of `subset`.
+ */
+ public static function isSuperset(superset:Array, subset:Array):Bool
+ {
+ // Shortcuts.
+ if (subset.length == 0) return true;
+ if (subset.length > superset.length) return false;
+
+ // Check each element.
+ for (element in subset)
+ {
+ if (!superset.contains(element)) return false;
+ }
+ return true;
+ }
+
+ /**
+ * Returns true if `superset` contains all elements of `subset`.
+ * @param subset The array containing the elements to query for.
+ * @param superset The array to query for each element.
+ * @return Weather `superset` contains all elements of `subset`.
+ */
+ public static function isSubset(subset:Array, superset:Array):Bool
+ {
+ // Switch it around.
+ return isSuperset(superset, subset);
+ }
}
diff --git a/tests/unit/source/funkin/data/BaseRegistryTest.hx b/tests/unit/source/funkin/data/BaseRegistryTest.hx
index 0be932d35..5f837ba97 100644
--- a/tests/unit/source/funkin/data/BaseRegistryTest.hx
+++ b/tests/unit/source/funkin/data/BaseRegistryTest.hx
@@ -156,6 +156,7 @@ class MyTypeRegistry extends BaseRegistry
// JsonParser does not take type parameters,
// otherwise this function would be in BaseRegistry.
var parser = new json2object.JsonParser();
+ parser.ignoreUnknownVariables = false;
switch (loadEntryFile(id))
{
@@ -181,6 +182,7 @@ class MyTypeRegistry extends BaseRegistry
// JsonParser does not take type parameters,
// otherwise this function would be in BaseRegistry.
var parser = new json2object.JsonParser();
+ parser.ignoreUnknownVariables = false;
switch (loadEntryFile(id))
{