mirror of
https://github.com/FunkinCrew/Funkin.git
synced 2024-11-14 19:25:16 -05:00
Lots of reported bugs fixed.
This commit is contained in:
parent
d47c5f5765
commit
cdab8753ea
14 changed files with 147 additions and 26 deletions
6
.vscode/launch.json
vendored
6
.vscode/launch.json
vendored
|
@ -23,6 +23,12 @@
|
|||
"name": "Haxe Eval",
|
||||
"type": "haxe-eval",
|
||||
"request": "launch"
|
||||
},
|
||||
{
|
||||
// Attaches the debugger to an already running game
|
||||
"name": "HXCPP - Attach",
|
||||
"type": "hxcpp",
|
||||
"request": "attach"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -240,7 +240,7 @@ class PauseSubState extends MusicBeatSubState
|
|||
|
||||
case 'Exit to Chart Editor':
|
||||
this.close();
|
||||
if (FlxG.sound.music != null) FlxG.sound.music.stop();
|
||||
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!
|
||||
|
||||
case 'BACK':
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
package funkin.data;
|
||||
|
||||
import funkin.data.song.importer.FNFLegacyData.LegacyNote;
|
||||
import hxjsonast.Json;
|
||||
import hxjsonast.Tools;
|
||||
import hxjsonast.Json.JObjectField;
|
||||
import haxe.ds.Either;
|
||||
import funkin.data.song.importer.FNFLegacyData.LegacyNoteSection;
|
||||
import funkin.data.song.importer.FNFLegacyData.LegacyNoteData;
|
||||
import funkin.data.song.importer.FNFLegacyData.LegacyNoteSection;
|
||||
import funkin.data.song.importer.FNFLegacyData.LegacyScrollSpeeds;
|
||||
import haxe.ds.Either;
|
||||
import hxjsonast.Json;
|
||||
import hxjsonast.Json.JObjectField;
|
||||
import hxjsonast.Tools;
|
||||
import thx.semver.Version;
|
||||
import thx.semver.VersionRule;
|
||||
|
||||
/**
|
||||
* `json2object` has an annotation `@:jcustomparse` which allows for mutation of parsed values.
|
||||
|
@ -23,7 +25,8 @@ class DataParse
|
|||
* `@:jcustomparse(funkin.data.DataParse.stringNotEmpty)`
|
||||
* @param json Contains the `pos` and `value` of the property.
|
||||
* @param name The name of the property.
|
||||
* @throws If the property is not a string or is empty.
|
||||
* @throws Error If the property is not a string or is empty.
|
||||
* @return The string value.
|
||||
*/
|
||||
public static function stringNotEmpty(json:Json, name:String):String
|
||||
{
|
||||
|
@ -37,6 +40,42 @@ class DataParse
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* `@:jcustomparse(funkin.data.DataParse.semverVersion)`
|
||||
* @param json Contains the `pos` and `value` of the property.
|
||||
* @param name The name of the property.
|
||||
* @return The value of the property as a `thx.semver.Version`.
|
||||
*/
|
||||
public static function semverVersion(json:Json, name:String):Version
|
||||
{
|
||||
switch (json.value)
|
||||
{
|
||||
case JString(s):
|
||||
if (s == "") throw 'Expected version property $name to be non-empty.';
|
||||
return s;
|
||||
default:
|
||||
throw 'Expected version property $name to be a string, but it was ${json.value}.';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* `@:jcustomparse(funkin.data.DataParse.semverVersionRule)`
|
||||
* @param json Contains the `pos` and `value` of the property.
|
||||
* @param name The name of the property.
|
||||
* @return The value of the property as a `thx.semver.VersionRule`.
|
||||
*/
|
||||
public static function semverVersionRule(json:Json, name:String):VersionRule
|
||||
{
|
||||
switch (json.value)
|
||||
{
|
||||
case JString(s):
|
||||
if (s == "") throw 'Expected version rule property $name to be non-empty.';
|
||||
return s;
|
||||
default:
|
||||
throw 'Expected version rule property $name to be a string, but it was ${json.value}.';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parser which outputs a Dynamic value, either a object or something else.
|
||||
* @param json
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package funkin.data;
|
||||
|
||||
import funkin.util.SerializerUtil;
|
||||
import thx.semver.Version;
|
||||
import thx.semver.VersionRule;
|
||||
|
||||
/**
|
||||
* `json2object` has an annotation `@:jcustomwrite` which allows for custom serialization of values to be written to JSON.
|
||||
|
@ -9,9 +11,30 @@ import funkin.util.SerializerUtil;
|
|||
*/
|
||||
class DataWrite
|
||||
{
|
||||
/**
|
||||
* `@:jcustomwrite(funkin.data.DataWrite.dynamicValue)`
|
||||
* @param value
|
||||
* @return String
|
||||
*/
|
||||
public static function dynamicValue(value:Dynamic):String
|
||||
{
|
||||
// Is this cheating? Yes. Do I care? No.
|
||||
return SerializerUtil.toJSON(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* `@:jcustomwrite(funkin.data.DataWrite.semverVersion)`
|
||||
*/
|
||||
public static function semverVersion(value:Version):String
|
||||
{
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* `@:jcustomwrite(funkin.data.DataWrite.semverVersionRule)`
|
||||
*/
|
||||
public static function semverVersionRule(value:VersionRule):String
|
||||
{
|
||||
return value.toString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,8 @@ class SongMetadata
|
|||
*
|
||||
*/
|
||||
// @:default(funkin.data.song.SongRegistry.SONG_METADATA_VERSION)
|
||||
@:jcustomparse(funkin.data.DataParse.semverVersion)
|
||||
@:jcustomwrite(funkin.data.DataWrite.semverVersion)
|
||||
public var version:Version;
|
||||
|
||||
@:default("Unknown")
|
||||
|
@ -203,6 +205,8 @@ class SongMusicData
|
|||
*
|
||||
*/
|
||||
// @:default(funkin.data.song.SongRegistry.SONG_METADATA_VERSION)
|
||||
@:jcustomparse(funkin.data.DataParse.semverVersion)
|
||||
@:jcustomwrite(funkin.data.DataWrite.semverVersion)
|
||||
public var version:Version;
|
||||
|
||||
@:default("Unknown")
|
||||
|
@ -367,6 +371,8 @@ class SongCharacterData
|
|||
class SongChartData
|
||||
{
|
||||
@:default(funkin.data.song.SongRegistry.SONG_CHART_DATA_VERSION)
|
||||
@:jcustomparse(funkin.data.DataParse.semverVersion)
|
||||
@:jcustomwrite(funkin.data.DataWrite.semverVersion)
|
||||
public var version:Version;
|
||||
|
||||
public var scrollSpeed:Map<String, Float>;
|
||||
|
|
|
@ -246,7 +246,8 @@ class SongDataUtils
|
|||
|
||||
typedef SongClipboardItems =
|
||||
{
|
||||
?valid:Bool,
|
||||
notes:Array<SongNoteData>,
|
||||
events:Array<SongEventData>
|
||||
@:optional
|
||||
var valid:Bool;
|
||||
var notes:Array<SongNoteData>;
|
||||
var events:Array<SongEventData>;
|
||||
}
|
||||
|
|
|
@ -24,6 +24,8 @@ class SongMetadata_v2_0_0
|
|||
// ==========
|
||||
// UNMODIFIED VALUES
|
||||
// ==========
|
||||
@:jcustomparse(funkin.data.DataParse.semverVersion)
|
||||
@:jcustomwrite(funkin.data.DataWrite.semverVersion)
|
||||
public var version:Version;
|
||||
|
||||
@:default("Unknown")
|
||||
|
|
|
@ -129,7 +129,6 @@ class HealthIcon extends FlxSprite
|
|||
if (value == characterId) return value;
|
||||
|
||||
characterId = value ?? Constants.DEFAULT_HEALTH_ICON;
|
||||
loadCharacter(characterId);
|
||||
return characterId;
|
||||
}
|
||||
|
||||
|
@ -138,7 +137,6 @@ class HealthIcon extends FlxSprite
|
|||
if (value == isPixel) return value;
|
||||
|
||||
isPixel = value;
|
||||
loadCharacter(characterId);
|
||||
return isPixel;
|
||||
}
|
||||
|
||||
|
@ -165,8 +163,11 @@ class HealthIcon extends FlxSprite
|
|||
{
|
||||
if (data == null)
|
||||
{
|
||||
this.isPixel = false;
|
||||
this.characterId = Constants.DEFAULT_HEALTH_ICON;
|
||||
this.isPixel = false;
|
||||
|
||||
loadCharacter(characterId);
|
||||
|
||||
this.size.set(1.0, 1.0);
|
||||
this.offset.x = 0.0;
|
||||
this.offset.y = 0.0;
|
||||
|
@ -174,8 +175,11 @@ class HealthIcon extends FlxSprite
|
|||
}
|
||||
else
|
||||
{
|
||||
this.isPixel = data.isPixel ?? false;
|
||||
this.characterId = data.id;
|
||||
this.isPixel = data.isPixel ?? false;
|
||||
|
||||
loadCharacter(characterId);
|
||||
|
||||
this.size.set(data.scale ?? 1.0, data.scale ?? 1.0);
|
||||
this.offset.x = (data.offsets != null) ? data.offsets[0] : 0.0;
|
||||
this.offset.y = (data.offsets != null) ? data.offsets[1] : 0.0;
|
||||
|
|
|
@ -698,7 +698,15 @@ class PlayState extends MusicBeatSubState
|
|||
FlxG.sound.music.pause();
|
||||
FlxG.sound.music.time = (startTimestamp);
|
||||
|
||||
vocals = currentChart.buildVocals();
|
||||
if (!overrideMusic)
|
||||
{
|
||||
vocals = currentChart.buildVocals();
|
||||
|
||||
if (vocals.members.length == 0)
|
||||
{
|
||||
trace('WARNING: No vocals found for this song.');
|
||||
}
|
||||
}
|
||||
vocals.pause();
|
||||
vocals.time = 0;
|
||||
|
||||
|
|
|
@ -154,6 +154,12 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
|
|||
{
|
||||
if (metadata == null || metadata.playData == null) continue;
|
||||
|
||||
// If there are no difficulties in the metadata, there's a problem.
|
||||
if (metadata.playData.difficulties.length == 0)
|
||||
{
|
||||
throw 'Song $id has no difficulties listed in metadata!';
|
||||
}
|
||||
|
||||
// There may be more difficulties in the chart file than in the metadata,
|
||||
// (i.e. non-playable charts like the one used for Pico on the speaker in Stress)
|
||||
// but all the difficulties in the metadata must be in the chart file.
|
||||
|
|
|
@ -421,6 +421,8 @@ typedef RawSaveData =
|
|||
/**
|
||||
* A semantic versioning string for the save data format.
|
||||
*/
|
||||
@:jcustomparse(funkin.data.DataParse.semverVersion)
|
||||
@:jcustomwrite(funkin.data.DataWrite.semverVersion)
|
||||
var version:Version;
|
||||
|
||||
var api:SaveApiData;
|
||||
|
|
|
@ -404,7 +404,6 @@ class ChartEditorDialogHandler
|
|||
{
|
||||
if (ChartEditorAudioHandler.loadInstFromBytes(state, selectedFile.bytes, instId))
|
||||
{
|
||||
trace('Selected file: ' + selectedFile.fullPath);
|
||||
#if !mac
|
||||
NotificationManager.instance.addNotification(
|
||||
{
|
||||
|
@ -415,13 +414,12 @@ class ChartEditorDialogHandler
|
|||
});
|
||||
#end
|
||||
|
||||
state.switchToCurrentInstrumental();
|
||||
dialog.hideDialog(DialogButton.APPLY);
|
||||
removeDropHandler(onDropFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
trace('Failed to load instrumental (${selectedFile.fullPath})');
|
||||
|
||||
#if !mac
|
||||
NotificationManager.instance.addNotification(
|
||||
{
|
||||
|
@ -452,6 +450,7 @@ class ChartEditorDialogHandler
|
|||
});
|
||||
#end
|
||||
|
||||
state.switchToCurrentInstrumental();
|
||||
dialog.hideDialog(DialogButton.APPLY);
|
||||
removeDropHandler(onDropFile);
|
||||
}
|
||||
|
@ -570,6 +569,12 @@ class ChartEditorDialogHandler
|
|||
|
||||
var newSongMetadata:SongMetadata = new SongMetadata('', '', 'default');
|
||||
|
||||
newSongMetadata.playData.difficulties = switch (targetVariation)
|
||||
{
|
||||
case 'erect': ['erect', 'nightmare'];
|
||||
default: ['easy', 'normal', 'hard'];
|
||||
};
|
||||
|
||||
var inputSongName:Null<TextField> = dialog.findComponent('inputSongName', TextField);
|
||||
if (inputSongName == null) throw 'Could not locate inputSongName TextField in Song Metadata dialog';
|
||||
inputSongName.onChange = function(event:UIEvent) {
|
||||
|
|
|
@ -554,6 +554,9 @@ class ChartEditorState extends HaxeUIState
|
|||
notePreviewDirty = true;
|
||||
notePreviewViewportBoundsDirty = true;
|
||||
|
||||
// Make sure the difficulty we selected is in the list of difficulties.
|
||||
currentSongMetadata.playData.difficulties.pushUnique(selectedDifficulty);
|
||||
|
||||
return selectedDifficulty;
|
||||
}
|
||||
|
||||
|
@ -971,6 +974,7 @@ class ChartEditorState extends HaxeUIState
|
|||
result = [];
|
||||
trace('Initializing blank note data for difficulty ' + selectedDifficulty);
|
||||
currentSongChartData.notes.set(selectedDifficulty, result);
|
||||
currentSongMetadata.playData.difficulties.pushUnique(selectedDifficulty);
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
|
@ -979,6 +983,7 @@ class ChartEditorState extends HaxeUIState
|
|||
function set_currentSongChartNoteData(value:Array<SongNoteData>):Array<SongNoteData>
|
||||
{
|
||||
currentSongChartData.notes.set(selectedDifficulty, value);
|
||||
currentSongMetadata.playData.difficulties.pushUnique(selectedDifficulty);
|
||||
return value;
|
||||
}
|
||||
|
||||
|
@ -4089,7 +4094,7 @@ class ChartEditorState extends HaxeUIState
|
|||
}
|
||||
|
||||
subStateClosed.add(fixCamera);
|
||||
subStateClosed.add(updateConductor);
|
||||
subStateClosed.add(resetConductorAfterTest);
|
||||
|
||||
FlxTransitionableState.skipNextTransIn = false;
|
||||
FlxTransitionableState.skipNextTransOut = false;
|
||||
|
@ -4122,10 +4127,9 @@ class ChartEditorState extends HaxeUIState
|
|||
add(this.component);
|
||||
}
|
||||
|
||||
function updateConductor(_:FlxSubState = null):Void
|
||||
function resetConductorAfterTest(_:FlxSubState = null):Void
|
||||
{
|
||||
var targetPos = scrollPositionInMs;
|
||||
Conductor.update(targetPos);
|
||||
moveSongToScrollPosition();
|
||||
}
|
||||
|
||||
public function postLoadInstrumental():Void
|
||||
|
@ -4179,12 +4183,14 @@ class ChartEditorState extends HaxeUIState
|
|||
function moveSongToScrollPosition():Void
|
||||
{
|
||||
// Update the songPosition in the audio tracks.
|
||||
if (audioInstTrack != null) audioInstTrack.time = scrollPositionInMs + playheadPositionInMs;
|
||||
if (audioInstTrack != null)
|
||||
{
|
||||
audioInstTrack.time = scrollPositionInMs + playheadPositionInMs;
|
||||
// Update the songPosition in the Conductor.
|
||||
Conductor.update(audioInstTrack.time);
|
||||
}
|
||||
if (audioVocalTrackGroup != null) audioVocalTrackGroup.time = scrollPositionInMs + playheadPositionInMs;
|
||||
|
||||
// Update the songPosition in the Conductor.
|
||||
Conductor.update(audioInstTrack.time);
|
||||
|
||||
// We need to update the note sprites because we changed the scroll position.
|
||||
noteDisplayDirty = true;
|
||||
}
|
||||
|
|
|
@ -38,6 +38,19 @@ class ArrayTools
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Push an element to the array if it is not already present.
|
||||
* @param input The array to push to
|
||||
* @param element The element to push
|
||||
* @return Whether the element was pushed
|
||||
*/
|
||||
public static function pushUnique<T>(input:Array<T>, element:T):Bool
|
||||
{
|
||||
if (input.contains(element)) return false;
|
||||
input.push(element);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all elements from the array, without creating a new array.
|
||||
* @param array The array to clear.
|
||||
|
|
Loading…
Reference in a new issue