diff --git a/source/funkin/play/song/Song.hx b/source/funkin/play/song/Song.hx index a712de1cc..7de005cb0 100644 --- a/source/funkin/play/song/Song.hx +++ b/source/funkin/play/song/Song.hx @@ -324,7 +324,7 @@ class SongDifficulty /** * Build a list of vocal files for the given character. * Automatically resolves suffixed character IDs (so bf-car will resolve to bf if needed). - * + * * @param id The character we are about to play. */ public function buildVoiceList(?id:String = 'bf'):Array diff --git a/source/funkin/play/song/SongData.hx b/source/funkin/play/song/SongData.hx index 982ccb402..2ae38156a 100644 --- a/source/funkin/play/song/SongData.hx +++ b/source/funkin/play/song/SongData.hx @@ -1,5 +1,7 @@ package funkin.play.song; +import funkin.modding.events.ScriptEventDispatcher; +import funkin.modding.events.ScriptEvent; import flixel.util.typeLimit.OneOfTwo; import funkin.play.song.ScriptedSong; import funkin.util.assets.DataAssets; @@ -24,7 +26,7 @@ class SongDataParser /** * Parses and preloads the game's song metadata and scripts when the game starts. - * + * * If you want to force song metadata to be reloaded, you can just call this function again. */ public static function loadSongCache():Void @@ -95,6 +97,9 @@ class SongDataParser { var song:Song = songCache.get(songId); trace('Successfully fetch song: ${songId}'); + + var event:ScriptEvent = new ScriptEvent(ScriptEvent.CREATE, false); + ScriptEventDispatcher.callEvent(song, event); return song; } else @@ -112,12 +117,21 @@ class SongDataParser } } + /** + * A list of all the song IDs available to the game. + * @return The list of song IDs. + */ public static function listSongIds():Array { return songCache.keys().array(); } - public static function parseSongMetadata(songId:String):Array + /** + * Loads the song metadata for a particular song. + * @param songId The ID of the song to load. + * @return The song metadata for each variation, or an empty array if the song was not found. + */ + public static function loadSongMetadata(songId:String):Array { var result:Array = []; @@ -139,19 +153,13 @@ class SongDataParser result.push(songMetadata); - var variations = songMetadata.playData.songVariations; + var variations:Array = songMetadata.playData.songVariations; for (variation in variations) { - var variationJsonStr:String = loadSongMetadataFile(songId, variation); - var variationJsonData:Dynamic = null; - try - { - variationJsonData = Json.parse(variationJsonStr); - } - catch (e) {} - var variationSongMetadata:SongMetadata = SongMigrator.migrateSongMetadata(variationJsonData, '${songId}-${variation}'); - variationSongMetadata = SongValidator.validateSongMetadata(variationSongMetadata, '${songId}-${variation}'); + var variationRawJson:String = loadSongMetadataFile(songId, variation); + var variationSongMetadata:SongMetadata = SongMigrator.migrateSongMetadata(variationRawJson, '${songId}_${variation}'); + variationSongMetadata = SongValidator.validateSongMetadata(variationSongMetadata, '${songId}_${variation}'); if (variationSongMetadata != null) { variationSongMetadata.variation = variation; @@ -168,7 +176,7 @@ class SongDataParser var rawJson:String = Assets.getText(songMetadataFilePath).trim(); - while (!rawJson.endsWith("}")) + while (!rawJson.endsWith('}') && rawJson.length > 0) { rawJson = rawJson.substr(0, rawJson.length - 1); } @@ -176,7 +184,7 @@ class SongDataParser return rawJson; } - public static function parseSongChartData(songId:String, variation:String = ""):SongChartData + public static function parseSongChartData(songId:String, variation:String = ''):SongChartData { var rawJson:String = loadSongChartDataFile(songId, variation); var jsonData:Dynamic = null; @@ -184,7 +192,11 @@ class SongDataParser { jsonData = Json.parse(rawJson); } - catch (e) {} + catch (e) + { + trace('Failed to parse song chart data: ${songId} (${variation})'); + trace(e); + } var songChartData:SongChartData = SongMigrator.migrateSongChartData(jsonData, songId); songChartData = SongValidator.validateSongChartData(songChartData, songId); @@ -204,7 +216,7 @@ class SongDataParser var rawJson:String = Assets.getText(songChartDataFilePath).trim(); - while (!rawJson.endsWith("}")) + while (!rawJson.endsWith('}') && rawJson.length > 0) { rawJson = rawJson.substr(0, rawJson.length - 1); } @@ -217,7 +229,7 @@ typedef RawSongMetadata = { /** * A semantic versioning string for the song data format. - * + * */ var version:Version; @@ -272,7 +284,7 @@ abstract SongMetadata(RawSongMetadata) public function clone(?newVariation:String = null):SongMetadata { - var result = new SongMetadata(this.songName, this.artist, newVariation == null ? this.variation : newVariation); + var result:SongMetadata = new SongMetadata(this.songName, this.artist, newVariation == null ? this.variation : newVariation); result.version = this.version; result.timeFormat = this.timeFormat; result.divisions = this.divisions; @@ -350,22 +362,22 @@ abstract SongNoteData(RawSongNoteData) public var time(get, set):Float; - public function get_time():Float + function get_time():Float { return this.t; } - public function set_time(value:Float):Float + function set_time(value:Float):Float { return this.t = value; } public var stepTime(get, never):Float; - public function get_stepTime():Float + function get_stepTime():Float { // TODO: Account for changes in BPM. - return this.t / Conductor.stepCrochet; + return this.t / Conductor.stepLengthMs; } /** @@ -373,12 +385,12 @@ abstract SongNoteData(RawSongNoteData) */ public var data(get, set):Int; - public function get_data():Int + function get_data():Int { return this.d; } - public function set_data(value:Int):Int + function set_data(value:Int):Int { return this.d = value; } @@ -414,7 +426,7 @@ abstract SongNoteData(RawSongNoteData) /** * The strumline index of the note, if applicable. * Strips the direction from the data. - * + * * 0 = player, 1 = opponent, etc. */ public inline function getStrumlineIndex(strumlineSize:Int = 4):Int @@ -429,26 +441,26 @@ abstract SongNoteData(RawSongNoteData) public var length(get, set):Float; - public function get_length():Float + function get_length():Float { return this.l; } - public function set_length(value:Float):Float + function set_length(value:Float):Float { return this.l = value; } public var kind(get, set):String; - public function get_kind():String + function get_kind():String { if (this.k == null || this.k == '') return 'normal'; return this.k; } - public function set_kind(value:String):String + function set_kind(value:String):String { if (value == 'normal' || value == '') value = null; return this.k = value; @@ -536,56 +548,56 @@ abstract SongEventData(RawSongEventData) public var time(get, set):Float; - public function get_time():Float + function get_time():Float { return this.t; } - public function set_time(value:Float):Float + function set_time(value:Float):Float { return this.t = value; } public var stepTime(get, never):Float; - public function get_stepTime():Float + function get_stepTime():Float { // TODO: Account for changes in BPM. - return this.t / Conductor.stepCrochet; + return this.t / Conductor.stepLengthMs; } public var event(get, set):String; - public function get_event():String + function get_event():String { return this.e; } - public function set_event(value:String):String + function set_event(value:String):String { return this.e = value; } public var value(get, set):Dynamic; - public function get_value():Dynamic + function get_value():Dynamic { return this.v; } - public function set_value(value:Dynamic):Dynamic + function set_value(value:Dynamic):Dynamic { return this.v = value; } public var activated(get, set):Bool; - public function get_activated():Bool + function get_activated():Bool { return this.a; } - public function set_activated(value:Bool):Bool + function set_activated(value:Bool):Bool { return this.a = value; } @@ -664,7 +676,7 @@ abstract SongEventData(RawSongEventData) abstract SongPlayableChar(RawSongPlayableChar) { - public function new(girlfriend:String, opponent:String, inst:String = "") + public function new(girlfriend:String, opponent:String, inst:String = '') { this = { @@ -676,36 +688,36 @@ abstract SongPlayableChar(RawSongPlayableChar) public var girlfriend(get, set):String; - public function get_girlfriend():String + function get_girlfriend():String { return this.g; } - public function set_girlfriend(value:String):String + function set_girlfriend(value:String):String { return this.g = value; } public var opponent(get, set):String; - public function get_opponent():String + function get_opponent():String { return this.o; } - public function set_opponent(value:String):String + function set_opponent(value:String):String { return this.o = value; } public var inst(get, set):String; - public function get_inst():String + function get_inst():String { return this.i; } - public function set_inst(value:String):String + function set_inst(value:String):String { return this.i = value; } @@ -751,6 +763,35 @@ abstract SongChartData(RawSongChartData) return (result == 0.0) ? 1.0 : result; } + + public function setScrollSpeed(value:Float, diff:String = 'default'):Float + { + return this.scrollSpeed.set(diff, value); + } + + public function getNotes(diff:String):Array + { + var result:Array = this.notes.get(diff); + + if (result == null && diff != 'normal') return getNotes('normal'); + + return (result == null) ? [] : result; + } + + public function setNotes(value:Array, diff:String):Array + { + return this.notes.set(diff, value); + } + + public function getEvents():Array + { + return this.events; + } + + public function setEvents(value:Array):Array + { + return this.events = value; + } } typedef RawSongTimeChange = @@ -811,67 +852,67 @@ abstract SongTimeChange(RawSongTimeChange) public var timeStamp(get, set):Float; - public function get_timeStamp():Float + function get_timeStamp():Float { return this.t; } - public function set_timeStamp(value:Float):Float + function set_timeStamp(value:Float):Float { return this.t = value; } public var beatTime(get, set):Int; - public function get_beatTime():Int + function get_beatTime():Int { return this.b; } - public function set_beatTime(value:Int):Int + function set_beatTime(value:Int):Int { return this.b = value; } public var bpm(get, set):Float; - public function get_bpm():Float + function get_bpm():Float { return this.bpm; } - public function set_bpm(value:Float):Float + function set_bpm(value:Float):Float { return this.bpm = value; } public var timeSignatureNum(get, set):Int; - public function get_timeSignatureNum():Int + function get_timeSignatureNum():Int { return this.n; } - public function set_timeSignatureNum(value:Int):Int + function set_timeSignatureNum(value:Int):Int { return this.n = value; } public var timeSignatureDen(get, set):Int; - public function get_timeSignatureDen():Int + function get_timeSignatureDen():Int { return this.d; } - public function set_timeSignatureDen(value:Int):Int + function set_timeSignatureDen(value:Int):Int { return this.d = value; } public var beatTuplets(get, set):Array; - public function get_beatTuplets():Array + function get_beatTuplets():Array { if (Std.isOfType(this.bt, Int)) { @@ -883,7 +924,7 @@ abstract SongTimeChange(RawSongTimeChange) } } - public function set_beatTuplets(value:Array):Array + function set_beatTuplets(value:Array):Array { return this.bt = value; } @@ -891,7 +932,7 @@ abstract SongTimeChange(RawSongTimeChange) enum abstract SongTimeFormat(String) from String to String { - var TICKS = "ticks"; - var FLOAT = "float"; - var MILLISECONDS = "ms"; + var TICKS = 'ticks'; + var FLOAT = 'float'; + var MILLISECONDS = 'ms'; } diff --git a/source/funkin/play/song/SongDataUtils.hx b/source/funkin/play/song/SongDataUtils.hx index 27625e846..750d5f54b 100644 --- a/source/funkin/play/song/SongDataUtils.hx +++ b/source/funkin/play/song/SongDataUtils.hx @@ -14,14 +14,13 @@ class SongDataUtils * Given an array of SongNoteData objects, return a new array of SongNoteData objects * whose timestamps are shifted by the given amount. * Does not mutate the original array. - * + * * @param notes The notes to modify. * @param offset The time difference to apply in milliseconds. */ public static function offsetSongNoteData(notes:Array, offset:Int):Array { - return notes.map(function(note:SongNoteData):SongNoteData - { + return notes.map(function(note:SongNoteData):SongNoteData { return new SongNoteData(note.time + offset, note.data, note.length, note.kind); }); } @@ -30,14 +29,13 @@ class SongDataUtils * Given an array of SongEventData objects, return a new array of SongEventData objects * whose timestamps are shifted by the given amount. * Does not mutate the original array. - * + * * @param events The events to modify. * @param offset The time difference to apply in milliseconds. */ public static function offsetSongEventData(events:Array, offset:Int):Array { - return events.map(function(event:SongEventData):SongEventData - { + return events.map(function(event:SongEventData):SongEventData { return new SongEventData(event.time + offset, event.event, event.value); }); } @@ -45,7 +43,7 @@ class SongDataUtils /** * Return a new array without a certain subset of notes from an array of SongNoteData objects. * Does not mutate the original array. - * + * * @param notes The array of notes to be subtracted from. * @param subtrahend The notes to remove from the `notes` array. Yes, subtrahend is a real word. */ @@ -53,8 +51,7 @@ class SongDataUtils { if (notes.length == 0 || subtrahend.length == 0) return notes; - var result = notes.filter(function(note:SongNoteData):Bool - { + var result = notes.filter(function(note:SongNoteData):Bool { for (x in subtrahend) // SongNoteData's == operation has been overridden so that this will work. if (x == note) return false; @@ -68,7 +65,7 @@ class SongDataUtils /** * Return a new array without a certain subset of events from an array of SongEventData objects. * Does not mutate the original array. - * + * * @param events The array of events to be subtracted from. * @param subtrahend The events to remove from the `events` array. Yes, subtrahend is a real word. */ @@ -76,8 +73,7 @@ class SongDataUtils { if (events.length == 0 || subtrahend.length == 0) return events; - return events.filter(function(event:SongEventData):Bool - { + return events.filter(function(event:SongEventData):Bool { // SongEventData's == operation has been overridden so that this will work. return !subtrahend.has(event); }); @@ -89,8 +85,7 @@ class SongDataUtils */ public static function flipNotes(notes:Array, ?strumlineSize:Int = 4):Array { - return notes.map(function(note:SongNoteData):SongNoteData - { + return notes.map(function(note:SongNoteData):SongNoteData { var newData = note.data; if (newData < strumlineSize) newData += strumlineSize; @@ -103,22 +98,26 @@ class SongDataUtils /** * Prepare an array of notes to be used as the clipboard data. - * + * * Offset the provided array of notes such that the first note is at 0 milliseconds. */ - public static function buildNoteClipboard(notes:Array):Array + public static function buildNoteClipboard(notes:Array, ?timeOffset:Int = null):Array { - return offsetSongNoteData(sortNotes(notes), -Std.int(notes[0].time)); + if (notes.length == 0) return notes; + if (timeOffset == null) timeOffset = -Std.int(notes[0].time); + return offsetSongNoteData(sortNotes(notes), timeOffset); } /** * Prepare an array of events to be used as the clipboard data. - * + * * Offset the provided array of events such that the first event is at 0 milliseconds. */ - public static function buildEventClipboard(events:Array):Array + public static function buildEventClipboard(events:Array, ?timeOffset:Int = null):Array { - return offsetSongEventData(sortEvents(events), -Std.int(events[0].time)); + if (events.length == 0) return events; + if (timeOffset == null) timeOffset = -Std.int(events[0].time); + return offsetSongEventData(sortEvents(events), timeOffset); } /** @@ -127,8 +126,7 @@ class SongDataUtils public static function sortNotes(notes:Array, ?desc:Bool = false):Array { // TODO: Modifies the array in place. Is this okay? - notes.sort(function(a:SongNoteData, b:SongNoteData):Int - { + notes.sort(function(a:SongNoteData, b:SongNoteData):Int { return FlxSort.byValues(desc ? FlxSort.DESCENDING : FlxSort.ASCENDING, a.time, b.time); }); return notes; @@ -140,8 +138,7 @@ class SongDataUtils public static function sortEvents(events:Array, ?desc:Bool = false):Array { // TODO: Modifies the array in place. Is this okay? - events.sort(function(a:SongEventData, b:SongEventData):Int - { + events.sort(function(a:SongEventData, b:SongEventData):Int { return FlxSort.byValues(desc ? FlxSort.DESCENDING : FlxSort.ASCENDING, a.time, b.time); }); return events; @@ -192,8 +189,7 @@ class SongDataUtils */ public static function getNotesInTimeRange(notes:Array, start:Float, end:Float):Array { - return notes.filter(function(note:SongNoteData):Bool - { + return notes.filter(function(note:SongNoteData):Bool { return note.time >= start && note.time <= end; }); } @@ -203,8 +199,7 @@ class SongDataUtils */ public static function getEventsInTimeRange(events:Array, start:Float, end:Float):Array { - return events.filter(function(event:SongEventData):Bool - { + return events.filter(function(event:SongEventData):Bool { return event.time >= start && event.time <= end; }); } @@ -214,8 +209,7 @@ class SongDataUtils */ public static function getNotesInDataRange(notes:Array, start:Int, end:Int):Array { - return notes.filter(function(note:SongNoteData):Bool - { + return notes.filter(function(note:SongNoteData):Bool { return note.data >= start && note.data <= end; }); } @@ -225,8 +219,7 @@ class SongDataUtils */ public static function getNotesWithData(notes:Array, data:Array):Array { - return notes.filter(function(note:SongNoteData):Bool - { + return notes.filter(function(note:SongNoteData):Bool { return data.indexOf(note.data) != -1; }); } diff --git a/source/funkin/play/song/SongMigrator.hx b/source/funkin/play/song/SongMigrator.hx index 1872585d0..05adf2457 100644 --- a/source/funkin/play/song/SongMigrator.hx +++ b/source/funkin/play/song/SongMigrator.hx @@ -1,7 +1,11 @@ package funkin.play.song; +import funkin.play.song.formats.FNFLegacy; import funkin.play.song.SongData.SongChartData; +import funkin.play.song.SongData.SongEventData; import funkin.play.song.SongData.SongMetadata; +import funkin.play.song.SongData.SongNoteData; +import funkin.play.song.SongData.SongPlayableChar; import funkin.util.VersionUtil; class SongMigrator @@ -11,13 +15,22 @@ class SongMigrator * Handle breaking changes by incrementing this value * and adding migration to the SongMigrator class. */ - public static final CHART_VERSION:String = "2.0.0"; + public static final CHART_VERSION:String = '2.0.0'; - public static final CHART_VERSION_RULE:String = "2.0.x"; + /** + * Version rule for which chart versions are compatible with the current version. + */ + public static final CHART_VERSION_RULE:String = '2.0.x'; + /** + * Migrate song data from an older chart version to the current version. + * @param jsonData The song metadata to migrate. + * @param songId The ID of the song (only used for error reporting). + * @return The migrated song metadata, or null if the migration failed. + */ public static function migrateSongMetadata(jsonData:Dynamic, songId:String):SongMetadata { - if (jsonData.version) + if (jsonData.version != null) { if (VersionUtil.validateVersion(jsonData.version, CHART_VERSION_RULE)) { @@ -32,10 +45,11 @@ class SongMigrator trace('Song (${songId}) metadata version (${jsonData.version}) is outdated.'); switch (jsonData.version) { - // TODO: Add migration functions as cases here. + case '1.0.0': + return migrateSongMetadataFromLegacy(jsonData); default: - // Unknown version. - trace('Song (${songId}) unknown metadata version: ${jsonData.version}'); + trace('Song (${songId}) has unknown metadata version (${jsonData.version}), assuming FNF Legacy.'); + return migrateSongMetadataFromLegacy(jsonData); } } } @@ -46,6 +60,12 @@ class SongMigrator return null; } + /** + * Migrate song chart data from an older chart version to the current version. + * @param jsonData The song chart data to migrate. + * @param songId The ID of the song (only used for error reporting). + * @return The migrated song chart data, or null if the migration failed. + */ public static function migrateSongChartData(jsonData:Dynamic, songId:String):SongChartData { if (jsonData.version) @@ -76,4 +96,167 @@ class SongMigrator } return null; } + + /** + * Migrate song metadata from FNF Legacy chart version to the current version. + * @param jsonData The song metadata to migrate. + * @param songId The ID of the song (only used for error reporting). + * @return The migrated song metadata, or null if the migration failed. + */ + public static function migrateSongMetadataFromLegacy(jsonData:Dynamic):SongMetadata + { + trace('Migrating song metadata from FNF Legacy.'); + + var songData:FNFLegacy = cast jsonData; + + var songMetadata:SongMetadata = new SongMetadata('Import', 'Kawai Sprite', 'default'); + + var hadError:Bool = false; + + // Set generatedBy string for debugging. + songMetadata.generatedBy = 'Chart Editor Import (FNF Legacy)'; + + try + { + // Set the song's BPM. + songMetadata.timeChanges[0].bpm = songData.song.bpm; + } + catch (e) + { + trace("Couldn't parse BPM!"); + hadError = true; + } + + try + { + // Set the song's stage. + songMetadata.playData.stage = songData.song.stageDefault; + } + catch (e) + { + trace("Couldn't parse stage!"); + hadError = true; + } + + try + { + // Set's the song's name. + songMetadata.songName = songData.song.song; + } + catch (e) + { + trace("Couldn't parse song name!"); + hadError = true; + } + + songMetadata.playData.difficulties = []; + if (songData.song != null && songData.song.notes != null) + { + if (songData.song.notes.easy != null) songMetadata.playData.difficulties.push('easy'); + if (songData.song.notes.normal != null) songMetadata.playData.difficulties.push('normal'); + if (songData.song.notes.hard != null) songMetadata.playData.difficulties.push('hard'); + } + else + { + trace("Couldn't parse difficulties!"); + hadError = true; + } + + songMetadata.playData.songVariations = []; + + // Set the song's song variations. + songMetadata.playData.playableChars = {}; + try + { + Reflect.setField(songMetadata.playData.playableChars, songData.song.player1, new SongPlayableChar('', songData.song.player2)); + } + catch (e) + { + trace("Couldn't parse characters!"); + hadError = true; + } + + return songMetadata; + } + + /** + * Migrate song chart data from FNF Legacy chart version to the current version. + * @param jsonData The song data to migrate. + * @param songId The ID of the song (only used for error reporting). + * @param difficulty The difficulty to migrate. + * @return The migrated song chart data, or null if the migration failed. + */ + public static function migrateSongChartDataFromLegacy(jsonData:Dynamic):SongChartData + { + trace('Migrating song chart data from FNF Legacy.'); + + var songData:FNFLegacy = cast jsonData; + + var songChartData:SongChartData = new SongChartData(1.0, [], []); + + if (songData.song.notes.normal != null) + { + var songEventsEmpty:Bool = songChartData.getEvents() == null || songChartData.getEvents().length == 0; + if (songEventsEmpty) songChartData.setEvents(migrateSongEventDataFromLegacy(songData.song.notes.normal)); + songChartData.setNotes(migrateSongNoteDataFromLegacy(songData.song.notes.normal), 'normal'); + songChartData.setScrollSpeed(songData.song.speed.normal, 'normal'); + } + if (songData.song.notes.easy != null) + { + var songEventsEmpty:Bool = songChartData.getEvents() == null || songChartData.getEvents().length == 0; + if (songEventsEmpty) songChartData.setEvents(migrateSongEventDataFromLegacy(songData.song.notes.easy)); + songChartData.setNotes(migrateSongNoteDataFromLegacy(songData.song.notes.easy), 'easy'); + songChartData.setScrollSpeed(songData.song.speed.easy, 'easy'); + } + if (songData.song.notes.hard != null) + { + var songEventsEmpty:Bool = songChartData.getEvents() == null || songChartData.getEvents().length == 0; + if (songEventsEmpty) songChartData.setEvents(migrateSongEventDataFromLegacy(songData.song.notes.hard)); + songChartData.setNotes(migrateSongNoteDataFromLegacy(songData.song.notes.hard), 'hard'); + songChartData.setScrollSpeed(songData.song.speed.hard, 'hard'); + } + + return songChartData; + } + + static function migrateSongNoteDataFromLegacy(sections:Array):Array + { + var songNotes:Array = []; + + for (section in sections) + { + // Skip empty sections. + if (section.sectionNotes.length == 0) continue; + + for (note in section.sectionNotes) + { + songNotes.push(new SongNoteData(note.time, note.getData(section.mustHitSection), note.length, note.kind)); + } + } + + return songNotes; + } + + static function migrateSongEventDataFromLegacy(sections:Array):Array + { + var songEvents:Array = []; + + var lastSectionWasMustHit:Null = null; + for (section in sections) + { + // Skip empty sections. + if (section.sectionNotes.length == 0) continue; + + if (section.mustHitSection != lastSectionWasMustHit) + { + lastSectionWasMustHit = section.mustHitSection; + + var firstNote:LegacyNote = section.sectionNotes[0]; + + songEvents.push(new SongEventData(firstNote.time, 'FocusCamera', {char: section.mustHitSection ? 0 : 1})); + } + } + + return songEvents; + } } diff --git a/source/funkin/play/song/SongSerializer.hx b/source/funkin/play/song/SongSerializer.hx index c7933665a..968a7a1f5 100644 --- a/source/funkin/play/song/SongSerializer.hx +++ b/source/funkin/play/song/SongSerializer.hx @@ -50,8 +50,7 @@ class SongSerializer */ public static function importSongChartDataAsync(callback:SongChartData->Void):Void { - browseFileReference(function(fileReference:FileReference) - { + browseFileReference(function(fileReference:FileReference) { var data = fileReference.data.toString(); if (data == null) return; @@ -68,8 +67,7 @@ class SongSerializer */ public static function importSongMetadataAsync(callback:SongMetadata->Void):Void { - browseFileReference(function(fileReference:FileReference) - { + browseFileReference(function(fileReference:FileReference) { var data = fileReference.data.toString(); if (data == null) return; @@ -103,7 +101,7 @@ class SongSerializer /** * Save a SongChartData object as a JSON file to a specified path. * Works great on HTML5 and desktop. - * + * * @param path The file path to save to. */ public static function exportSongChartDataAs(path:String, data:SongChartData) @@ -116,7 +114,7 @@ class SongSerializer /** * Save a SongMetadata object as a JSON file to a specified path. * Works great on HTML5 and desktop. - * + * * @param path The file path to save to. */ public static function exportSongMetadataAs(path:String, data:SongMetadata) @@ -163,19 +161,17 @@ class SongSerializer /** * Browse for a file to read and execute a callback once we have a file reference. * Works great on HTML5 or desktop. - * + * * @param callback The function to call when the file is loaded. */ static function browseFileReference(callback:FileReference->Void) { var file = new FileReference(); - file.addEventListener(Event.SELECT, function(e) - { + file.addEventListener(Event.SELECT, function(e) { var selectedFileRef:FileReference = e.target; trace('Selected file: ' + selectedFileRef.name); - selectedFileRef.addEventListener(Event.COMPLETE, function(e) - { + selectedFileRef.addEventListener(Event.COMPLETE, function(e) { var loadedFileRef:FileReference = e.target; trace('Loaded file: ' + loadedFileRef.name); callback(loadedFileRef); @@ -192,16 +188,13 @@ class SongSerializer static function writeFileReference(path:String, data:String) { var file = new FileReference(); - file.addEventListener(Event.COMPLETE, function(e:Event) - { + file.addEventListener(Event.COMPLETE, function(e:Event) { trace('Successfully wrote file.'); }); - file.addEventListener(Event.CANCEL, function(e:Event) - { + file.addEventListener(Event.CANCEL, function(e:Event) { trace('Cancelled writing file.'); }); - file.addEventListener(IOErrorEvent.IO_ERROR, function(e:IOErrorEvent) - { + file.addEventListener(IOErrorEvent.IO_ERROR, function(e:IOErrorEvent) { trace('IO error writing file.'); }); file.save(data, path); diff --git a/source/funkin/play/song/SongValidator.hx b/source/funkin/play/song/SongValidator.hx index 950113bcf..d393c11eb 100644 --- a/source/funkin/play/song/SongValidator.hx +++ b/source/funkin/play/song/SongValidator.hx @@ -30,7 +30,7 @@ class SongValidator /** * Validates the fields of a SongMetadata object (excluding the version field). - * + * * @param input The SongMetadata object to validate. * @param songId The ID of the song being validated. Only used for error messages. * @return The validated SongMetadata object. @@ -73,7 +73,7 @@ class SongValidator /** * Validates the fields of a SongPlayData object. - * + * * @param input The SongPlayData object to validate. * @param songId The ID of the song being validated. Only used for error messages. * @return The validated SongPlayData object. @@ -85,7 +85,7 @@ class SongValidator /** * Validates the fields of a TimeChange object. - * + * * @param input The TimeChange object to validate. * @param songId The ID of the song being validated. Only used for error messages. * @return The validated TimeChange object. @@ -113,7 +113,7 @@ class SongValidator /** * Validates the fields of a SongChartData object (excluding the version field). - * + * * @param input The SongChartData object to validate. * @param songId The ID of the song being validated. Only used for error messages. * @return The validated SongChartData object. diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index c24c2db1b..43fa8b26a 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -20,7 +20,7 @@ import flixel.util.FlxColor; import flixel.util.FlxSort; import flixel.util.FlxTimer; import funkin.audio.visualize.PolygonSpectogram; -import funkin.audio.VocalGroup; +import funkin.audio.VoicesGroup; import funkin.input.Cursor; import funkin.input.TurboKeyHandler; import funkin.modding.events.ScriptEvent; @@ -197,12 +197,12 @@ class ChartEditorState extends HaxeUIState function get_scrollPositionInMs():Float { - return scrollPositionInSteps * Conductor.stepLengthMs; + return scrollPositionInSteps * Conductor.stepCrochet; } function set_scrollPositionInMs(value:Float):Float { - scrollPositionInPixels = value / Conductor.stepLengthMs; + scrollPositionInPixels = value / Conductor.stepCrochet; return value; } @@ -231,7 +231,7 @@ class ChartEditorState extends HaxeUIState function get_playheadPositionInMs():Float { - return playheadPositionInSteps * Conductor.stepLengthMs; + return playheadPositionInSteps * Conductor.stepCrochet; } /** @@ -271,7 +271,7 @@ class ChartEditorState extends HaxeUIState function get_songLengthInMs():Float { - return songLengthInSteps * Conductor.stepLengthMs; + return songLengthInSteps * Conductor.stepCrochet; } function set_songLengthInMs(value:Float):Float @@ -642,7 +642,7 @@ class ChartEditorState extends HaxeUIState /** * The audio track for the vocals. */ - var audioVocalTrackGroup:VocalGroup; + var audioVocalTrackGroup:VoicesGroup; /** * The raw byte data for the vocal audio tracks. @@ -1053,7 +1053,7 @@ class ChartEditorState extends HaxeUIState // Initialize the song chart data. songChartData = new Map(); - audioVocalTrackGroup = new VocalGroup(); + audioVocalTrackGroup = new VoicesGroup(); } /** @@ -1811,7 +1811,7 @@ class ChartEditorState extends HaxeUIState // The song position of the cursor, in steps. var cursorFractionalStep:Float = cursorY / GRID_SIZE / (16 / noteSnapQuant); var cursorStep:Int = Std.int(Math.floor(cursorFractionalStep)); - var cursorMs:Float = cursorStep * Conductor.stepLengthMs * (16 / noteSnapQuant); + var cursorMs:Float = cursorStep * Conductor.stepCrochet * (16 / noteSnapQuant); // The direction value for the column at the cursor. var cursorColumn:Int = Math.floor(cursorX / GRID_SIZE); if (cursorColumn < 0) cursorColumn = 0; @@ -1849,7 +1849,7 @@ class ChartEditorState extends HaxeUIState // We released the mouse. Select the notes in the box. var cursorFractionalStepStart:Float = cursorYStart / GRID_SIZE; var cursorStepStart:Int = Math.floor(cursorFractionalStepStart); - var cursorMsStart:Float = cursorStepStart * Conductor.stepLengthMs; + var cursorMsStart:Float = cursorStepStart * Conductor.stepCrochet; var cursorColumnBase:Int = Math.floor(cursorX / GRID_SIZE); var cursorColumnBaseStart:Int = Math.floor(cursorXStart / GRID_SIZE); @@ -2053,12 +2053,12 @@ class ChartEditorState extends HaxeUIState { // Handle extending the note as you drag. - // Since use Math.floor and stepLengthMs here, the hold notes will be beat snapped. - var dragLengthSteps:Float = Math.floor((cursorMs - currentPlaceNoteData.time) / Conductor.stepLengthMs); + // Since use Math.floor and stepCrochet here, the hold notes will be beat snapped. + var dragLengthSteps:Float = Math.floor((cursorMs - currentPlaceNoteData.time) / Conductor.stepCrochet); // Without this, the newly placed note feels too short compared to the user's input. var INCREMENT:Float = 1.0; - var dragLengthMs:Float = (dragLengthSteps + INCREMENT) * Conductor.stepLengthMs; + var dragLengthMs:Float = (dragLengthSteps + INCREMENT) * Conductor.stepCrochet; // TODO: Add and update some sort of preview? @@ -2363,7 +2363,7 @@ class ChartEditorState extends HaxeUIState } // Get the position the note should be at. - var noteTimePixels:Float = noteData.time / Conductor.stepLengthMs * GRID_SIZE; + var noteTimePixels:Float = noteData.time / Conductor.stepCrochet * GRID_SIZE; // Make sure the note appears when scrolling up. var modifiedViewAreaTop:Float = viewAreaTop - GRID_SIZE; @@ -2389,7 +2389,7 @@ class ChartEditorState extends HaxeUIState { // If the note is a hold, we need to make sure it's long enough. var noteLengthMs:Float = noteSprite.noteData.length; - var noteLengthSteps:Float = (noteLengthMs / Conductor.stepLengthMs); + var noteLengthSteps:Float = (noteLengthMs / Conductor.stepCrochet); var lastNoteSprite:ChartEditorNoteSprite = noteSprite; while (noteLengthSteps > 0) @@ -2413,7 +2413,7 @@ class ChartEditorState extends HaxeUIState // Make sure the last note sprite shows the end cap properly. lastNoteSprite.childNoteSprite = null; - // var noteLengthPixels:Float = (noteLengthMs / Conductor.stepLengthMs + 1) * GRID_SIZE; + // var noteLengthPixels:Float = (noteLengthMs / Conductor.stepCrochet + 1) * GRID_SIZE; // add(new FlxSprite(noteSprite.x, noteSprite.y - renderedNotes.y + noteLengthPixels).makeGraphic(40, 2, 0xFFFF0000)); } } @@ -2428,7 +2428,7 @@ class ChartEditorState extends HaxeUIState } // Get the position the event should be at. - var eventTimePixels:Float = eventData.time / Conductor.stepLengthMs * GRID_SIZE; + var eventTimePixels:Float = eventData.time / Conductor.stepCrochet * GRID_SIZE; // Make sure the event appears when scrolling up. var modifiedViewAreaTop:Float = viewAreaTop - GRID_SIZE; @@ -3115,7 +3115,7 @@ class ChartEditorState extends HaxeUIState var playheadPos:Float = scrollPositionInPixels + playheadPositionInPixels; var playheadPosFractionalStep:Float = playheadPos / GRID_SIZE / (16 / noteSnapQuant); var playheadPosStep:Int = Std.int(Math.floor(playheadPosFractionalStep)); - var playheadPosMs:Float = playheadPosStep * Conductor.stepLengthMs * (16 / noteSnapQuant); + var playheadPosMs:Float = playheadPosStep * Conductor.stepCrochet * (16 / noteSnapQuant); var newNoteData:SongNoteData = new SongNoteData(playheadPosMs, column, 0, selectedNoteKind); performCommand(new AddNotesCommand([newNoteData], FlxG.keys.pressed.CONTROL)); @@ -3363,10 +3363,10 @@ class ChartEditorState extends HaxeUIState audioVocalTrackGroup.clear(); } // Add player vocals. - if (currentSongCharacterPlayer != null) audioVocalTrackGroup.setPlayerVocals(new FlxSound().loadEmbedded(Assets.getSound(Paths.voices(songId, + if (currentSongCharacterPlayer != null) audioVocalTrackGroup.addPlayerVocals(new FlxSound().loadEmbedded(Assets.getSound(Paths.voices(songId, '-$currentSongCharacterPlayer')))); // Add opponent vocals. - if (currentSongCharacterOpponent != null) audioVocalTrackGroup.setOpponentVocals(new FlxSound().loadEmbedded(Assets.getSound(Paths.voices(songId, + if (currentSongCharacterOpponent != null) audioVocalTrackGroup.addOpponentVocals(new FlxSound().loadEmbedded(Assets.getSound(Paths.voices(songId, '-$currentSongCharacterOpponent')))); postLoadInstrumental();