mirror of
https://github.com/FunkinCrew/Funkin.git
synced 2024-11-14 11:15:24 -05:00
Compare commits
23 commits
c4d18d3fc9
...
1b5946f905
Author | SHA1 | Date | |
---|---|---|---|
|
1b5946f905 | ||
|
0d8e4a5330 | ||
|
6321983eba | ||
|
101b9f59b3 | ||
|
e570dfb8e7 | ||
|
e6b6b41766 | ||
|
090ddd1f1c | ||
|
76c8c8b520 | ||
|
20d9016984 | ||
|
144ba00377 | ||
|
7159cad2c1 | ||
|
62de2a0345 | ||
|
8cf346c3b0 | ||
|
a0cefba9fd | ||
|
f46f57e23d | ||
|
5819a43522 | ||
|
dff4135fc9 | ||
|
24ad7f4a39 | ||
|
4dbb0baa17 | ||
|
ce85ee20a7 | ||
|
4effd709eb | ||
|
1107c1e160 | ||
|
8ca227c219 |
16 changed files with 219 additions and 292 deletions
13
CHANGELOG.md
13
CHANGELOG.md
|
@ -4,6 +4,17 @@ All notable changes will be documented in this file.
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [0.5.3] - 2024-10-18
|
||||||
|
This patch resolves a critical issue which could cause user's save data to become corrupted. It is recommended that users switch to this version immediately and avoid using version 0.5.2.
|
||||||
|
### Fixed
|
||||||
|
- Fixed a critical issue in which the Stage Editor theme value could not be parsed by older versions of the game, resulting in all save data being destroyed.
|
||||||
|
- Added a check which prevents save data from being loaded if it is corrupted rather than overriding it.
|
||||||
|
- `optionsStageEditor.theme` in the save data converted from an Enum to a String to fix save data compatibility issues.
|
||||||
|
- In the future, Enum values should not be used in order to prevent incompatibilities caused by introducing new types to the save data that older versions cannot parse.
|
||||||
|
- `optionsChartEditor.theme` in the save data converted from an Enum to a String to fix save data compatibility issues.
|
||||||
|
- `optionsChartEditor.chartEditorLiveInputStyle` in the save data converted from an Enum to a String to fix save data compatibility issues.
|
||||||
|
- Fixed an issue where some publicly distributed builds of the game were debug builds instead of release builds.
|
||||||
|
|
||||||
## [0.5.2] - 2024-10-11
|
## [0.5.2] - 2024-10-11
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
@ -371,7 +382,7 @@ which would remove their rank if they had a lower one.
|
||||||
- Improvements to video cutscenes and dialogue, allowing them to be easily skipped or restarted.
|
- Improvements to video cutscenes and dialogue, allowing them to be easily skipped or restarted.
|
||||||
- Updated Polymod by several major versions, allowing for fully dynamic asset replacement and support for scripted classes.
|
- Updated Polymod by several major versions, allowing for fully dynamic asset replacement and support for scripted classes.
|
||||||
- Completely refactored almost every part of the game's code for performance, stability, and extensibility.
|
- Completely refactored almost every part of the game's code for performance, stability, and extensibility.
|
||||||
- This is not the Ludem Dare game held together with sticks and glue you played three years ago.
|
- This is not the Ludum Dare game held together with sticks and glue you played three years ago.
|
||||||
- Characters, stages, songs, story levels, and dialogue are now built from JSON data registries rather than being hardcoded.
|
- Characters, stages, songs, story levels, and dialogue are now built from JSON data registries rather than being hardcoded.
|
||||||
- All of these also support attaching scripts for custom behavior, more documentation on this soon.
|
- All of these also support attaching scripts for custom behavior, more documentation on this soon.
|
||||||
- You can forcibly reload the game's JSON data and scripts by pressing F5.
|
- You can forcibly reload the game's JSON data and scripts by pressing F5.
|
||||||
|
|
2
art
2
art
|
@ -1 +1 @@
|
||||||
Subproject commit fbd3e3df77734606d88516770b71b56e6fa04bce
|
Subproject commit 66572f85d826ce2ec1d45468c12733b161237ffa
|
2
assets
2
assets
|
@ -1 +1 @@
|
||||||
Subproject commit 4abd6cc06e56c6d56440fa858262932db118250c
|
Subproject commit c1899ffbefb9a7c98b030c75a33623431d7ea6ba
|
2
hmm.json
2
hmm.json
|
@ -11,7 +11,7 @@
|
||||||
"name": "flixel",
|
"name": "flixel",
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"dir": null,
|
"dir": null,
|
||||||
"ref": "f2b090d6c608471e730b051c8ee22b8b378964b1",
|
"ref": "ffa691cb2d2d81de35b900a4411e4062ac84ab58",
|
||||||
"url": "https://github.com/FunkinCrew/flixel"
|
"url": "https://github.com/FunkinCrew/flixel"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -25,7 +25,7 @@ class Project extends HXProject {
|
||||||
* REMEMBER TO CHANGE THIS WHEN THE GAME UPDATES!
|
* REMEMBER TO CHANGE THIS WHEN THE GAME UPDATES!
|
||||||
* You only have to change it here, the rest of the game will query this value.
|
* You only have to change it here, the rest of the game will query this value.
|
||||||
*/
|
*/
|
||||||
static final VERSION:String = "0.5.2";
|
static final VERSION:String = "0.5.3";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The game's name. Used as the default window title.
|
* The game's name. Used as the default window title.
|
||||||
|
@ -460,7 +460,6 @@ class Project extends HXProject {
|
||||||
|
|
||||||
// Should be false unless explicitly requested.
|
// Should be false unless explicitly requested.
|
||||||
GITHUB_BUILD.apply(this, false);
|
GITHUB_BUILD.apply(this, false);
|
||||||
FEATURE_STAGE_EDITOR.apply(this, false);
|
|
||||||
FEATURE_NEWGROUNDS.apply(this, false);
|
FEATURE_NEWGROUNDS.apply(this, false);
|
||||||
FEATURE_GHOST_TAPPING.apply(this, false);
|
FEATURE_GHOST_TAPPING.apply(this, false);
|
||||||
|
|
||||||
|
@ -471,10 +470,14 @@ class Project extends HXProject {
|
||||||
FEATURE_FUNKVIS.apply(this, true);
|
FEATURE_FUNKVIS.apply(this, true);
|
||||||
FEATURE_PARTIAL_SOUNDS.apply(this, true);
|
FEATURE_PARTIAL_SOUNDS.apply(this, true);
|
||||||
FEATURE_VIDEO_PLAYBACK.apply(this, true);
|
FEATURE_VIDEO_PLAYBACK.apply(this, true);
|
||||||
|
FEATURE_STAGE_EDITOR.apply(this, true);
|
||||||
|
|
||||||
// Should be true on debug builds or if GITHUB_BUILD is enabled.
|
// Should be true on debug builds or if GITHUB_BUILD is enabled.
|
||||||
FEATURE_DEBUG_FUNCTIONS.apply(this, isDebug() || GITHUB_BUILD.isEnabled(this));
|
FEATURE_DEBUG_FUNCTIONS.apply(this, isDebug() || GITHUB_BUILD.isEnabled(this));
|
||||||
FEATURE_LOG_TRACE.apply(this, isDebug());
|
|
||||||
|
// Got a lot of complains about this being turned off by default on some builds.
|
||||||
|
// TODO: Look into ways to optimize logging (maybe by using a thread pool?)
|
||||||
|
FEATURE_LOG_TRACE.apply(this, true);
|
||||||
|
|
||||||
// Should default to true on workspace builds and false on release builds.
|
// Should default to true on workspace builds and false on release builds.
|
||||||
REDIRECT_ASSETS_FOLDER.apply(this, isDebug() && isDesktop());
|
REDIRECT_ASSETS_FOLDER.apply(this, isDebug() && isDesktop());
|
||||||
|
|
|
@ -1,17 +1,13 @@
|
||||||
package funkin.play;
|
package funkin.play;
|
||||||
|
|
||||||
import flixel.addons.display.FlxPieDial;
|
|
||||||
import flixel.addons.transition.FlxTransitionableState;
|
import flixel.addons.transition.FlxTransitionableState;
|
||||||
import flixel.addons.transition.Transition;
|
import flixel.addons.transition.Transition;
|
||||||
import flixel.FlxCamera;
|
import flixel.FlxCamera;
|
||||||
import flixel.FlxObject;
|
import flixel.FlxObject;
|
||||||
import flixel.FlxState;
|
|
||||||
import flixel.FlxSubState;
|
import flixel.FlxSubState;
|
||||||
import flixel.math.FlxMath;
|
import flixel.math.FlxMath;
|
||||||
import flixel.math.FlxPoint;
|
import flixel.math.FlxPoint;
|
||||||
import flixel.math.FlxRect;
|
|
||||||
import flixel.text.FlxText;
|
import flixel.text.FlxText;
|
||||||
import flixel.tweens.FlxEase;
|
|
||||||
import flixel.tweens.FlxTween;
|
import flixel.tweens.FlxTween;
|
||||||
import flixel.ui.FlxBar;
|
import flixel.ui.FlxBar;
|
||||||
import flixel.util.FlxColor;
|
import flixel.util.FlxColor;
|
||||||
|
@ -22,7 +18,6 @@ import funkin.audio.FunkinSound;
|
||||||
import funkin.audio.VoicesGroup;
|
import funkin.audio.VoicesGroup;
|
||||||
import funkin.data.dialogue.conversation.ConversationRegistry;
|
import funkin.data.dialogue.conversation.ConversationRegistry;
|
||||||
import funkin.data.event.SongEventRegistry;
|
import funkin.data.event.SongEventRegistry;
|
||||||
import funkin.data.notestyle.NoteStyleData;
|
|
||||||
import funkin.data.notestyle.NoteStyleRegistry;
|
import funkin.data.notestyle.NoteStyleRegistry;
|
||||||
import funkin.data.song.SongData.SongCharacterData;
|
import funkin.data.song.SongData.SongCharacterData;
|
||||||
import funkin.data.song.SongData.SongEventData;
|
import funkin.data.song.SongData.SongEventData;
|
||||||
|
@ -45,7 +40,6 @@ import funkin.play.cutscene.VanillaCutscenes;
|
||||||
import funkin.play.cutscene.VideoCutscene;
|
import funkin.play.cutscene.VideoCutscene;
|
||||||
import funkin.play.notes.NoteDirection;
|
import funkin.play.notes.NoteDirection;
|
||||||
import funkin.play.notes.notekind.NoteKindManager;
|
import funkin.play.notes.notekind.NoteKindManager;
|
||||||
import funkin.play.notes.NoteSplash;
|
|
||||||
import funkin.play.notes.NoteSprite;
|
import funkin.play.notes.NoteSprite;
|
||||||
import funkin.play.notes.notestyle.NoteStyle;
|
import funkin.play.notes.notestyle.NoteStyle;
|
||||||
import funkin.play.notes.Strumline;
|
import funkin.play.notes.Strumline;
|
||||||
|
@ -58,15 +52,9 @@ import funkin.ui.debug.charting.ChartEditorState;
|
||||||
import funkin.ui.debug.stage.StageOffsetSubState;
|
import funkin.ui.debug.stage.StageOffsetSubState;
|
||||||
import funkin.ui.mainmenu.MainMenuState;
|
import funkin.ui.mainmenu.MainMenuState;
|
||||||
import funkin.ui.MusicBeatSubState;
|
import funkin.ui.MusicBeatSubState;
|
||||||
import funkin.ui.options.PreferencesMenu;
|
|
||||||
import funkin.ui.story.StoryMenuState;
|
|
||||||
import funkin.ui.transition.LoadingState;
|
import funkin.ui.transition.LoadingState;
|
||||||
import funkin.util.SerializerUtil;
|
import funkin.util.SerializerUtil;
|
||||||
import haxe.Int64;
|
import haxe.Int64;
|
||||||
import lime.ui.Haptic;
|
|
||||||
import openfl.display.BitmapData;
|
|
||||||
import openfl.geom.Rectangle;
|
|
||||||
import openfl.Lib;
|
|
||||||
#if FEATURE_DISCORD_RPC
|
#if FEATURE_DISCORD_RPC
|
||||||
import funkin.api.discord.DiscordClient;
|
import funkin.api.discord.DiscordClient;
|
||||||
#end
|
#end
|
||||||
|
@ -760,24 +748,6 @@ class PlayState extends MusicBeatSubState
|
||||||
refresh();
|
refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override function draw():Void
|
|
||||||
{
|
|
||||||
// if (FlxG.renderBlit)
|
|
||||||
// {
|
|
||||||
// camGame.fill(BACKGROUND_COLOR);
|
|
||||||
// }
|
|
||||||
// else if (FlxG.renderTile)
|
|
||||||
// {
|
|
||||||
// FlxG.log.warn("PlayState background not displayed properly on tile renderer!");
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// FlxG.log.warn("PlayState background not displayed properly, unknown renderer!");
|
|
||||||
// }
|
|
||||||
|
|
||||||
super.draw();
|
|
||||||
}
|
|
||||||
|
|
||||||
function assertChartExists():Bool
|
function assertChartExists():Bool
|
||||||
{
|
{
|
||||||
// Returns null if the song failed to load or doesn't have the selected difficulty.
|
// Returns null if the song failed to load or doesn't have the selected difficulty.
|
||||||
|
|
|
@ -1,25 +1,23 @@
|
||||||
package funkin.save;
|
package funkin.save;
|
||||||
|
|
||||||
import flixel.util.FlxSave;
|
import flixel.util.FlxSave;
|
||||||
import funkin.util.FileUtil;
|
|
||||||
import funkin.input.Controls.Device;
|
import funkin.input.Controls.Device;
|
||||||
import funkin.play.scoring.Scoring;
|
import funkin.play.scoring.Scoring;
|
||||||
import funkin.play.scoring.Scoring.ScoringRank;
|
import funkin.play.scoring.Scoring.ScoringRank;
|
||||||
import funkin.save.migrator.RawSaveData_v1_0_0;
|
import funkin.save.migrator.RawSaveData_v1_0_0;
|
||||||
import funkin.save.migrator.SaveDataMigrator;
|
import funkin.save.migrator.SaveDataMigrator;
|
||||||
import funkin.save.migrator.SaveDataMigrator;
|
|
||||||
import funkin.ui.debug.charting.ChartEditorState.ChartEditorLiveInputStyle;
|
import funkin.ui.debug.charting.ChartEditorState.ChartEditorLiveInputStyle;
|
||||||
import funkin.ui.debug.charting.ChartEditorState.ChartEditorTheme;
|
import funkin.ui.debug.charting.ChartEditorState.ChartEditorTheme;
|
||||||
import funkin.ui.debug.stageeditor.StageEditorState.StageEditorTheme;
|
import funkin.ui.debug.stageeditor.StageEditorState.StageEditorTheme;
|
||||||
|
import funkin.util.FileUtil;
|
||||||
import funkin.util.SerializerUtil;
|
import funkin.util.SerializerUtil;
|
||||||
import thx.semver.Version;
|
import thx.semver.Version;
|
||||||
import thx.semver.Version;
|
|
||||||
|
|
||||||
@:nullSafety
|
@:nullSafety
|
||||||
class Save
|
class Save
|
||||||
{
|
{
|
||||||
public static final SAVE_DATA_VERSION:thx.semver.Version = "2.0.4";
|
public static final SAVE_DATA_VERSION:thx.semver.Version = "2.1.0";
|
||||||
public static final SAVE_DATA_VERSION_RULE:thx.semver.VersionRule = "2.0.x";
|
public static final SAVE_DATA_VERSION_RULE:thx.semver.VersionRule = ">=2.1.0 <2.2.0";
|
||||||
|
|
||||||
// We load this version's saves from a new save path, to maintain SOME level of backwards compatibility.
|
// We load this version's saves from a new save path, to maintain SOME level of backwards compatibility.
|
||||||
static final SAVE_PATH:String = 'FunkinCrew';
|
static final SAVE_PATH:String = 'FunkinCrew';
|
||||||
|
@ -861,9 +859,9 @@ class Save
|
||||||
switch (inputType)
|
switch (inputType)
|
||||||
{
|
{
|
||||||
case Keys:
|
case Keys:
|
||||||
return (playerId == 0) ? data?.options?.controls?.p1.keyboard : data?.options?.controls?.p2.keyboard;
|
return (playerId == 1) ? data?.options?.controls?.p1.keyboard : data?.options?.controls?.p2.keyboard;
|
||||||
case Gamepad(_):
|
case Gamepad(_):
|
||||||
return (playerId == 0) ? data?.options?.controls?.p1.gamepad : data?.options?.controls?.p2.gamepad;
|
return (playerId == 1) ? data?.options?.controls?.p1.gamepad : data?.options?.controls?.p2.gamepad;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -878,27 +876,7 @@ class Save
|
||||||
|
|
||||||
public function setControls(playerId:Int, inputType:Device, controls:SaveControlsData):Void
|
public function setControls(playerId:Int, inputType:Device, controls:SaveControlsData):Void
|
||||||
{
|
{
|
||||||
switch (inputType)
|
getControls(playerId, inputType) = controls;
|
||||||
{
|
|
||||||
case Keys:
|
|
||||||
if (playerId == 0)
|
|
||||||
{
|
|
||||||
data.options.controls.p1.keyboard = controls;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
data.options.controls.p2.keyboard = controls;
|
|
||||||
}
|
|
||||||
case Gamepad(_):
|
|
||||||
if (playerId == 0)
|
|
||||||
{
|
|
||||||
data.options.controls.p1.gamepad = controls;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
data.options.controls.p2.gamepad = controls;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
flush();
|
flush();
|
||||||
}
|
}
|
||||||
|
@ -961,39 +939,61 @@ class Save
|
||||||
*/
|
*/
|
||||||
static function loadFromSlot(slot:Int):Save
|
static function loadFromSlot(slot:Int):Save
|
||||||
{
|
{
|
||||||
trace("[SAVE] Loading save from slot " + slot + "...");
|
trace('[SAVE] Loading save from slot $slot...');
|
||||||
|
|
||||||
// Prevent crashes if the save data is corrupted.
|
|
||||||
SerializerUtil.initSerializer();
|
|
||||||
|
|
||||||
FlxG.save.bind('$SAVE_NAME${slot}', SAVE_PATH);
|
FlxG.save.bind('$SAVE_NAME${slot}', SAVE_PATH);
|
||||||
|
|
||||||
if (FlxG.save.isEmpty())
|
switch (FlxG.save.status)
|
||||||
{
|
{
|
||||||
trace('[SAVE] Save data is empty, checking for legacy save data...');
|
case EMPTY:
|
||||||
var legacySaveData = fetchLegacySaveData();
|
trace('[SAVE] Save data in slot ${slot} is empty, checking for legacy save data...');
|
||||||
if (legacySaveData != null)
|
var legacySaveData = fetchLegacySaveData();
|
||||||
{
|
if (legacySaveData != null)
|
||||||
trace('[SAVE] Found legacy save data, converting...');
|
{
|
||||||
var gameSave = SaveDataMigrator.migrateFromLegacy(legacySaveData);
|
trace('[SAVE] Found legacy save data, converting...');
|
||||||
|
var gameSave = SaveDataMigrator.migrateFromLegacy(legacySaveData);
|
||||||
|
FlxG.save.mergeData(gameSave.data, true);
|
||||||
|
return gameSave;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
trace('[SAVE] No legacy save data found.');
|
||||||
|
var gameSave = new Save();
|
||||||
|
FlxG.save.mergeData(gameSave.data, true);
|
||||||
|
return gameSave;
|
||||||
|
}
|
||||||
|
case ERROR(_):
|
||||||
|
return handleSaveDataError(slot);
|
||||||
|
case BOUND(_, _):
|
||||||
|
trace('[SAVE] Loaded existing save data in slot ${slot}.');
|
||||||
|
var gameSave = SaveDataMigrator.migrate(FlxG.save.data);
|
||||||
FlxG.save.mergeData(gameSave.data, true);
|
FlxG.save.mergeData(gameSave.data, true);
|
||||||
|
|
||||||
return gameSave;
|
return gameSave;
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
{
|
|
||||||
trace('[SAVE] No legacy save data found.');
|
/**
|
||||||
var gameSave = new Save();
|
* Call this when there is an error loading the save data in slot X.
|
||||||
FlxG.save.mergeData(gameSave.data, true);
|
*/
|
||||||
return gameSave;
|
static function handleSaveDataError(slot:Int):Save
|
||||||
}
|
{
|
||||||
|
var msg = 'There was an error loading your save data in slot ${slot}.';
|
||||||
|
msg += '\nPlease report this issue to the developers.';
|
||||||
|
lime.app.Application.current.window.alert(msg, "Save Data Failure");
|
||||||
|
|
||||||
|
// Don't touch that slot anymore.
|
||||||
|
// Instead, load the next available slot.
|
||||||
|
|
||||||
|
var nextSlot = slot + 1;
|
||||||
|
|
||||||
|
if (nextSlot < 1000)
|
||||||
|
{
|
||||||
|
return loadFromSlot(nextSlot);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
trace('[SAVE] Found existing save data.');
|
throw "End of save data slots. Can't load any more.";
|
||||||
var gameSave = SaveDataMigrator.migrate(FlxG.save.data);
|
|
||||||
FlxG.save.mergeData(gameSave.data, true);
|
|
||||||
|
|
||||||
return gameSave;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1058,7 +1058,15 @@ class Save
|
||||||
{
|
{
|
||||||
var targetSaveData = new FlxSave();
|
var targetSaveData = new FlxSave();
|
||||||
targetSaveData.bind('$SAVE_NAME${slot}', SAVE_PATH);
|
targetSaveData.bind('$SAVE_NAME${slot}', SAVE_PATH);
|
||||||
return !targetSaveData.isEmpty();
|
switch (targetSaveData.status)
|
||||||
|
{
|
||||||
|
case EMPTY:
|
||||||
|
return false;
|
||||||
|
case ERROR(_):
|
||||||
|
return false;
|
||||||
|
case BOUND(_, _):
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -5,11 +5,17 @@ All notable changes to this project will be documented in this file.
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
## [2.0.4] - 2024-09-12
|
## [2.1.0] - 2024-10-18
|
||||||
Note to self: Only update to 2.1.0 when migration is needed.
|
This version introduces changes to save data loading in order to improve compatibility with older versions.
|
||||||
|
### Changed
|
||||||
|
- `optionsStageEditor.theme` converted from an Enum to a String to fix save data compatibility issues.
|
||||||
|
- In the future, Enum values should not be used in order to prevent incompatibilities caused by introducing new types to the save data that older versions cannot parse.
|
||||||
|
- `optionsChartEditor.theme` converted from an Enum to a String to fix save data compatibility issues.
|
||||||
|
- `optionsChartEditor.chartEditorLiveInputStyle` converted from an Enum to a String to fix save data compatibility issues.
|
||||||
|
|
||||||
|
## [2.0.6] - 2024-10-11
|
||||||
### Added
|
### Added
|
||||||
- `unlocks.charactersSeen:Array<String>` to `Save`
|
- `optionsStageEditor` to `Save` for storing user preferences for the stage editor.
|
||||||
- `unlocks.oldChar:Bool` to `Save`
|
|
||||||
|
|
||||||
## [2.0.5] - 2024-05-21
|
## [2.0.5] - 2024-05-21
|
||||||
### Fixed
|
### Fixed
|
||||||
|
@ -17,6 +23,8 @@ Note to self: Only update to 2.1.0 when migration is needed.
|
||||||
|
|
||||||
## [2.0.4] - 2024-05-21
|
## [2.0.4] - 2024-05-21
|
||||||
### Added
|
### Added
|
||||||
|
- `unlocks.charactersSeen:Array<String>` to `Save`
|
||||||
|
- `unlocks.oldChar:Bool` to `Save`
|
||||||
- `favoriteSongs:Array<String>` to `Save`
|
- `favoriteSongs:Array<String>` to `Save`
|
||||||
|
|
||||||
## [2.0.3] - 2024-01-09
|
## [2.0.3] - 2024-01-09
|
||||||
|
|
|
@ -32,6 +32,10 @@ class SaveDataMigrator
|
||||||
var save:Save = new Save(saveDataWithDefaults);
|
var save:Save = new Save(saveDataWithDefaults);
|
||||||
return save;
|
return save;
|
||||||
}
|
}
|
||||||
|
else if (VersionUtil.validateVersion(version, "2.0.x"))
|
||||||
|
{
|
||||||
|
return migrate_v2_0_0(inputData);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var message:String = 'Error migrating save data, expected ${Save.SAVE_DATA_VERSION}.';
|
var message:String = 'Error migrating save data, expected ${Save.SAVE_DATA_VERSION}.';
|
||||||
|
@ -43,6 +47,20 @@ class SaveDataMigrator
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static function migrate_v2_0_0(inputData:Dynamic):Save
|
||||||
|
{
|
||||||
|
// Import the structured data.
|
||||||
|
var saveDataWithDefaults:RawSaveData = cast thx.Objects.deepCombine(Save.getDefault(), inputData);
|
||||||
|
|
||||||
|
// Reset these values to valid ones.
|
||||||
|
saveDataWithDefaults.optionsChartEditor.chartEditorLiveInputStyle = funkin.ui.debug.charting.ChartEditorState.ChartEditorLiveInputStyle.None;
|
||||||
|
saveDataWithDefaults.optionsChartEditor.theme = funkin.ui.debug.charting.ChartEditorState.ChartEditorTheme.Light;
|
||||||
|
saveDataWithDefaults.optionsStageEditor.theme = funkin.ui.debug.stageeditor.StageEditorState.StageEditorTheme.Light;
|
||||||
|
|
||||||
|
var save:Save = new Save(saveDataWithDefaults);
|
||||||
|
return save;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Migrate from 1.x to the latest version.
|
* Migrate from 1.x to the latest version.
|
||||||
*/
|
*/
|
||||||
|
|
26
source/funkin/save/migrator/SaveData_v2_0_0.hx
Normal file
26
source/funkin/save/migrator/SaveData_v2_0_0.hx
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
package funkin.save.migrator;
|
||||||
|
|
||||||
|
// Internal enums used to ensure old save data can be parsed by the default Haxe unserializer.
|
||||||
|
// In the future, only primitive types and abstract enums should be used in save data!
|
||||||
|
|
||||||
|
@:native("funkin.ui.debug.stageeditor.StageEditorTheme")
|
||||||
|
enum StageEditorTheme
|
||||||
|
{
|
||||||
|
Light;
|
||||||
|
Dark;
|
||||||
|
}
|
||||||
|
|
||||||
|
@:native("funkin.ui.debug.charting.ChartEditorTheme")
|
||||||
|
enum ChartEditorTheme
|
||||||
|
{
|
||||||
|
Light;
|
||||||
|
Dark;
|
||||||
|
}
|
||||||
|
|
||||||
|
@:native("funkin.ui.debug.charting.ChartEditorLiveInputStyle")
|
||||||
|
enum ChartEditorLiveInputStyle
|
||||||
|
{
|
||||||
|
None;
|
||||||
|
NumberKeys;
|
||||||
|
WASDKeys;
|
||||||
|
}
|
|
@ -55,16 +55,19 @@ class DebugMenuSubState extends MusicBeatSubState
|
||||||
// Create each menu item.
|
// Create each menu item.
|
||||||
// Call onMenuChange when the first item is created to move the camera .
|
// Call onMenuChange when the first item is created to move the camera .
|
||||||
#if FEATURE_CHART_EDITOR
|
#if FEATURE_CHART_EDITOR
|
||||||
onMenuChange(createItem("CHART EDITOR", openChartEditor));
|
createItem("CHART EDITOR", openChartEditor);
|
||||||
|
#end
|
||||||
|
createItem("ANIMATION EDITOR", openAnimationEditor);
|
||||||
|
#if FEATURE_STAGE_EDITOR
|
||||||
|
createItem("STAGE EDITOR", openStageEditor);
|
||||||
#end
|
#end
|
||||||
// createItem("Input Offset Testing", openInputOffsetTesting);
|
// createItem("Input Offset Testing", openInputOffsetTesting);
|
||||||
createItem("CHARACTER SELECT", openCharSelect, true);
|
// createItem("CHARACTER SELECT", openCharSelect, true);
|
||||||
createItem("ANIMATION EDITOR", openAnimationEditor);
|
|
||||||
createItem("STAGE EDITOR", openStageEditor);
|
|
||||||
// createItem("TEST STICKERS", testStickers);
|
// createItem("TEST STICKERS", testStickers);
|
||||||
#if sys
|
#if sys
|
||||||
createItem("OPEN CRASH LOG FOLDER", openLogFolder);
|
createItem("OPEN CRASH LOG FOLDER", openLogFolder);
|
||||||
#end
|
#end
|
||||||
|
onMenuChange(items.members[0]);
|
||||||
FlxG.camera.focusOn(new FlxPoint(camFocusPoint.x, camFocusPoint.y + 500));
|
FlxG.camera.focusOn(new FlxPoint(camFocusPoint.x, camFocusPoint.y + 500));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -94,6 +94,7 @@ import funkin.ui.mainmenu.MainMenuState;
|
||||||
import funkin.ui.transition.LoadingState;
|
import funkin.ui.transition.LoadingState;
|
||||||
import funkin.util.Constants;
|
import funkin.util.Constants;
|
||||||
import funkin.util.FileUtil;
|
import funkin.util.FileUtil;
|
||||||
|
import funkin.util.MathUtil;
|
||||||
import funkin.util.logging.CrashHandler;
|
import funkin.util.logging.CrashHandler;
|
||||||
import funkin.util.SortUtil;
|
import funkin.util.SortUtil;
|
||||||
import funkin.util.WindowUtil;
|
import funkin.util.WindowUtil;
|
||||||
|
@ -244,7 +245,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
/**
|
/**
|
||||||
* Duration, in seconds, for the scroll easing animation.
|
* Duration, in seconds, for the scroll easing animation.
|
||||||
*/
|
*/
|
||||||
public static final SCROLL_EASE_DURATION:Float = 0.2;
|
public static final SCROLL_EASE_DURATION:Float = 0.4;
|
||||||
|
|
||||||
// Other
|
// Other
|
||||||
|
|
||||||
|
@ -773,9 +774,8 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The current process that is lerping the scroll position.
|
* The current process that is lerping the scroll position.
|
||||||
* Used to cancel the previous lerp if the user scrolls again.
|
|
||||||
*/
|
*/
|
||||||
var currentScrollEase:Null<VarTween>;
|
var currentScrollEase:Null<Float>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The position where the user middle clicked to place a scroll anchor.
|
* The position where the user middle clicked to place a scroll anchor.
|
||||||
|
@ -2707,6 +2707,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
playbarHeadLayout.playbarHead.width = FlxG.width;
|
playbarHeadLayout.playbarHead.width = FlxG.width;
|
||||||
playbarHeadLayout.playbarHead.height = 10;
|
playbarHeadLayout.playbarHead.height = 10;
|
||||||
playbarHeadLayout.playbarHead.styleString = 'padding-left: 0px; padding-right: 0px; border-left: 0px; border-right: 0px;';
|
playbarHeadLayout.playbarHead.styleString = 'padding-left: 0px; padding-right: 0px; border-left: 0px; border-right: 0px;';
|
||||||
|
playbarHeadLayout.playbarHead.min = 0;
|
||||||
|
|
||||||
playbarHeadLayout.playbarHead.onDragStart = function(_:DragEvent) {
|
playbarHeadLayout.playbarHead.onDragStart = function(_:DragEvent) {
|
||||||
playbarHeadDragging = true;
|
playbarHeadDragging = true;
|
||||||
|
@ -2723,13 +2724,12 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
playbarHeadLayout.playbarHead.onDrag = function(_:DragEvent) {
|
playbarHeadLayout.playbarHead.onDrag = function(d:DragEvent) {
|
||||||
if (playbarHeadDragging)
|
if (playbarHeadDragging)
|
||||||
{
|
{
|
||||||
// Set the song position to where the playhead was moved to.
|
|
||||||
scrollPositionInPixels = (songLengthInPixels) * playbarHeadLayout.playbarHead.value / 100;
|
|
||||||
// Update the conductor and audio tracks to match.
|
// Update the conductor and audio tracks to match.
|
||||||
moveSongToScrollPosition();
|
currentScrollEase = d.value;
|
||||||
|
easeSongToScrollPosition(currentScrollEase);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2740,8 +2740,9 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
if (playbarHeadDraggingWasPlaying)
|
if (playbarHeadDraggingWasPlaying)
|
||||||
{
|
{
|
||||||
playbarHeadDraggingWasPlaying = false;
|
playbarHeadDraggingWasPlaying = false;
|
||||||
|
|
||||||
// Disabled code to resume song playback on drag.
|
// Disabled code to resume song playback on drag.
|
||||||
// startAudioPlayback();
|
startAudioPlayback();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3417,10 +3418,14 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
audioInstTrack.time = -Conductor.instance.instrumentalOffset;
|
audioInstTrack.time = -Conductor.instance.instrumentalOffset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!audioInstTrack.isPlaying && currentScrollEase != scrollPositionInPixels) easeSongToScrollPosition(currentScrollEase);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (audioInstTrack != null && audioInstTrack.isPlaying)
|
if (audioInstTrack != null && audioInstTrack.isPlaying)
|
||||||
{
|
{
|
||||||
|
currentScrollEase = scrollPositionInPixels;
|
||||||
|
|
||||||
if (FlxG.keys.pressed.ALT)
|
if (FlxG.keys.pressed.ALT)
|
||||||
{
|
{
|
||||||
// If middle mouse panning during song playback, we move ONLY the playhead, without scrolling. Neat!
|
// If middle mouse panning during song playback, we move ONLY the playhead, without scrolling. Neat!
|
||||||
|
@ -3869,7 +3874,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mouse Wheel = Scroll
|
// Mouse Wheel = Scroll
|
||||||
if (FlxG.mouse.wheel != 0 && !FlxG.keys.pressed.CONTROL)
|
if (FlxG.mouse.wheel != 0)
|
||||||
{
|
{
|
||||||
scrollAmount = -50 * FlxG.mouse.wheel;
|
scrollAmount = -50 * FlxG.mouse.wheel;
|
||||||
shouldPause = true;
|
shouldPause = true;
|
||||||
|
@ -4057,27 +4062,13 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
shouldPause = true;
|
shouldPause = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Math.abs(scrollAmount) > GRID_SIZE * 8)
|
shouldEase = true;
|
||||||
{
|
if (shouldPause) stopAudioPlayback();
|
||||||
shouldEase = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resync the conductor and audio tracks.
|
// Resync the conductor and audio tracks.
|
||||||
if (scrollAmount != 0 || playheadAmount != 0)
|
if (playheadAmount != 0) this.playheadPositionInPixels += playheadAmount;
|
||||||
{
|
|
||||||
this.playheadPositionInPixels += playheadAmount;
|
if (scrollAmount != 0) currentScrollEase += scrollAmount;
|
||||||
if (shouldEase)
|
|
||||||
{
|
|
||||||
easeSongToScrollPosition(this.scrollPositionInPixels + scrollAmount);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Apply the scroll amount.
|
|
||||||
this.scrollPositionInPixels += scrollAmount;
|
|
||||||
moveSongToScrollPosition();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (shouldPause) stopAudioPlayback();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -4333,15 +4324,13 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
{
|
{
|
||||||
// Scroll up.
|
// Scroll up.
|
||||||
var diff:Float = MENU_BAR_HEIGHT - FlxG.mouse.viewY;
|
var diff:Float = MENU_BAR_HEIGHT - FlxG.mouse.viewY;
|
||||||
scrollPositionInPixels -= diff * 0.5; // Too fast!
|
currentScrollEase -= diff * 0.5; // Too fast!
|
||||||
moveSongToScrollPosition();
|
|
||||||
}
|
}
|
||||||
else if (FlxG.mouse.viewY > (playbarHeadLayout?.y ?? 0.0))
|
else if (FlxG.mouse.viewY > (playbarHeadLayout?.y ?? 0.0))
|
||||||
{
|
{
|
||||||
// Scroll down.
|
// Scroll down.
|
||||||
var diff:Float = FlxG.mouse.viewY - (playbarHeadLayout?.y ?? 0.0);
|
var diff:Float = FlxG.mouse.viewY - (playbarHeadLayout?.y ?? 0.0);
|
||||||
scrollPositionInPixels += diff * 0.5; // Too fast!
|
currentScrollEase += (diff * 0.5); // Too fast!
|
||||||
moveSongToScrollPosition();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render the selection box.
|
// Render the selection box.
|
||||||
|
@ -4480,8 +4469,8 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
var clickedPosInPixels:Float = FlxMath.remapToRange(FlxG.mouse.viewY, (notePreview?.y ?? 0.0), (notePreview?.y ?? 0.0) + (notePreview?.height ?? 0.0),
|
var clickedPosInPixels:Float = FlxMath.remapToRange(FlxG.mouse.viewY, (notePreview?.y ?? 0.0), (notePreview?.y ?? 0.0) + (notePreview?.height ?? 0.0),
|
||||||
0, songLengthInPixels);
|
0, songLengthInPixels);
|
||||||
|
|
||||||
scrollPositionInPixels = clickedPosInPixels;
|
currentScrollEase = clickedPosInPixels;
|
||||||
moveSongToScrollPosition();
|
easeSongToScrollPosition(currentScrollEase);
|
||||||
}
|
}
|
||||||
else if (scrollAnchorScreenPos != null)
|
else if (scrollAnchorScreenPos != null)
|
||||||
{
|
{
|
||||||
|
@ -4540,15 +4529,13 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
{
|
{
|
||||||
// Scroll up.
|
// Scroll up.
|
||||||
var diff:Float = MENU_BAR_HEIGHT - FlxG.mouse.viewY;
|
var diff:Float = MENU_BAR_HEIGHT - FlxG.mouse.viewY;
|
||||||
scrollPositionInPixels -= diff * 0.5; // Too fast!
|
currentScrollEase -= (diff * 0.5);
|
||||||
moveSongToScrollPosition();
|
|
||||||
}
|
}
|
||||||
else if (FlxG.mouse.viewY > (playbarHeadLayout?.y ?? 0.0))
|
else if (FlxG.mouse.viewY > (playbarHeadLayout?.y ?? 0.0))
|
||||||
{
|
{
|
||||||
// Scroll down.
|
// Scroll down.
|
||||||
var diff:Float = FlxG.mouse.viewY - (playbarHeadLayout?.y ?? 0.0);
|
var diff:Float = FlxG.mouse.viewY - (playbarHeadLayout?.y ?? 0.0);
|
||||||
scrollPositionInPixels += diff * 0.5; // Too fast!
|
currentScrollEase += (diff * 0.5);
|
||||||
moveSongToScrollPosition();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate distance between the position dragged to and the original position.
|
// Calculate distance between the position dragged to and the original position.
|
||||||
|
@ -5142,18 +5129,15 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
{
|
{
|
||||||
if (playbarHeadLayout == null) throw "ERROR: Tried to handle playbar, but playbarHeadLayout is null!";
|
if (playbarHeadLayout == null) throw "ERROR: Tried to handle playbar, but playbarHeadLayout is null!";
|
||||||
|
|
||||||
|
// Move the playhead to match the song position, if we aren't dragging it.
|
||||||
|
playbarHeadLayout.playbarHead.pos = currentScrollEase;
|
||||||
|
|
||||||
|
playbarHeadLayout.playbarHead.max = songLengthInPixels;
|
||||||
|
|
||||||
// Make sure the playbar is never nudged out of the correct spot.
|
// Make sure the playbar is never nudged out of the correct spot.
|
||||||
playbarHeadLayout.x = 4;
|
playbarHeadLayout.x = 4;
|
||||||
playbarHeadLayout.y = FlxG.height - 48 - 8;
|
playbarHeadLayout.y = FlxG.height - 48 - 8;
|
||||||
|
|
||||||
// Move the playhead to match the song position, if we aren't dragging it.
|
|
||||||
if (!playbarHeadDragging)
|
|
||||||
{
|
|
||||||
var songPosPercent = scrollPositionInPixels / (songLengthInPixels) * 100;
|
|
||||||
|
|
||||||
if (playbarHeadLayout.playbarHead.value != songPosPercent) playbarHeadLayout.playbarHead.value = songPosPercent;
|
|
||||||
}
|
|
||||||
|
|
||||||
var songPos:Float = Conductor.instance.songPosition + Conductor.instance.instrumentalOffset;
|
var songPos:Float = Conductor.instance.songPosition + Conductor.instance.instrumentalOffset;
|
||||||
var songPosMilliseconds:String = Std.string(Math.floor(Math.abs(songPos) % 1000)).lpad('0', 2).substr(0, 2);
|
var songPosMilliseconds:String = Std.string(Math.floor(Math.abs(songPos) % 1000)).lpad('0', 2).substr(0, 2);
|
||||||
var songPosSeconds:String = Std.string(Math.floor((Math.abs(songPos) / 1000) % 60)).lpad('0', 2);
|
var songPosSeconds:String = Std.string(Math.floor((Math.abs(songPos) / 1000) % 60)).lpad('0', 2);
|
||||||
|
@ -5654,7 +5638,8 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
function handleHelpKeybinds():Void
|
function handleHelpKeybinds():Void
|
||||||
{
|
{
|
||||||
// F1 = Open Help
|
// F1 = Open Help
|
||||||
if (FlxG.keys.justPressed.F1 && !isHaxeUIDialogOpen) {
|
if (FlxG.keys.justPressed.F1 && !isHaxeUIDialogOpen)
|
||||||
|
{
|
||||||
this.openUserGuideDialog();
|
this.openUserGuideDialog();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6133,43 +6118,12 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
*/
|
*/
|
||||||
function easeSongToScrollPosition(targetScrollPosition:Float):Void
|
function easeSongToScrollPosition(targetScrollPosition:Float):Void
|
||||||
{
|
{
|
||||||
if (currentScrollEase != null) cancelScrollEase(currentScrollEase);
|
currentScrollEase = Math.max(0, targetScrollPosition);
|
||||||
|
currentScrollEase = Math.min(currentScrollEase, songLengthInPixels);
|
||||||
currentScrollEase = FlxTween.tween(this, {scrollPositionInPixels: targetScrollPosition}, SCROLL_EASE_DURATION,
|
scrollPositionInPixels = MathUtil.smoothLerp(scrollPositionInPixels, currentScrollEase, FlxG.elapsed, SCROLL_EASE_DURATION, 1 / 1000);
|
||||||
{
|
|
||||||
ease: FlxEase.quintInOut,
|
|
||||||
onUpdate: this.onScrollEaseUpdate,
|
|
||||||
onComplete: this.cancelScrollEase,
|
|
||||||
type: ONESHOT
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback function executed every frame that the scroll position is being eased.
|
|
||||||
* @param _
|
|
||||||
*/
|
|
||||||
function onScrollEaseUpdate(_:FlxTween):Void
|
|
||||||
{
|
|
||||||
moveSongToScrollPosition();
|
moveSongToScrollPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback function executed when cancelling an existing scroll position ease.
|
|
||||||
* Ensures that the ease is immediately cancelled and the scroll position is set to the target value.
|
|
||||||
*/
|
|
||||||
function cancelScrollEase(_:FlxTween):Void
|
|
||||||
{
|
|
||||||
if (currentScrollEase != null)
|
|
||||||
{
|
|
||||||
@:privateAccess
|
|
||||||
var targetScrollPosition:Float = currentScrollEase._properties.scrollPositionInPixels;
|
|
||||||
|
|
||||||
currentScrollEase.cancel();
|
|
||||||
currentScrollEase = null;
|
|
||||||
this.scrollPositionInPixels = targetScrollPosition;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fix the current scroll position after exiting the PlayState used when testing.
|
* Fix the current scroll position after exiting the PlayState used when testing.
|
||||||
*/
|
*/
|
||||||
|
@ -6350,6 +6304,8 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
{
|
{
|
||||||
if (audioInstTrack == null) return;
|
if (audioInstTrack == null) return;
|
||||||
|
|
||||||
|
currentScrollEase = this.scrollPositionInPixels;
|
||||||
|
|
||||||
if (audioInstTrack.isPlaying)
|
if (audioInstTrack.isPlaying)
|
||||||
{
|
{
|
||||||
// Pause
|
// Pause
|
||||||
|
@ -6543,22 +6499,22 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
/**
|
/**
|
||||||
* Available input modes for the chart editor state. Numbers/arrows/WASD available for other keybinds.
|
* Available input modes for the chart editor state. Numbers/arrows/WASD available for other keybinds.
|
||||||
*/
|
*/
|
||||||
enum ChartEditorLiveInputStyle
|
enum abstract ChartEditorLiveInputStyle(String)
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* No hotkeys to place notes at the playbar.
|
* No hotkeys to place notes at the playbar.
|
||||||
*/
|
*/
|
||||||
None;
|
var None;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 1/2/3/4 to place notes on opponent's side, 5/6/7/8 to place notes on player's side.
|
* 1/2/3/4 to place notes on opponent's side, 5/6/7/8 to place notes on player's side.
|
||||||
*/
|
*/
|
||||||
NumberKeys;
|
var NumberKeys;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* WASD to place notes on opponent's side, Arrow keys to place notes on player's side.
|
* WASD to place notes on opponent's side, Arrow keys to place notes on player's side.
|
||||||
*/
|
*/
|
||||||
WASDKeys;
|
var WASDKeys;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef ChartEditorParams =
|
typedef ChartEditorParams =
|
||||||
|
@ -6577,15 +6533,15 @@ typedef ChartEditorParams =
|
||||||
/**
|
/**
|
||||||
* Available themes for the chart editor state.
|
* Available themes for the chart editor state.
|
||||||
*/
|
*/
|
||||||
enum ChartEditorTheme
|
enum abstract ChartEditorTheme(String)
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The default theme for the chart editor.
|
* The default theme for the chart editor.
|
||||||
*/
|
*/
|
||||||
Light;
|
var Light;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A theme which introduces darker colors.
|
* A theme which introduces darker colors.
|
||||||
*/
|
*/
|
||||||
Dark;
|
var Dark;
|
||||||
}
|
}
|
||||||
|
|
|
@ -253,42 +253,23 @@ class ChartEditorThemeHandler
|
||||||
var bottomTickY:Float = state.measureTickBitmap.height - (measureTickWidth / 2);
|
var bottomTickY:Float = state.measureTickBitmap.height - (measureTickWidth / 2);
|
||||||
state.measureTickBitmap.fillRect(new Rectangle(0, bottomTickY, state.measureTickBitmap.width, measureTickWidth / 2), GRID_MEASURE_DIVIDER_COLOR_LIGHT);
|
state.measureTickBitmap.fillRect(new Rectangle(0, bottomTickY, state.measureTickBitmap.width, measureTickWidth / 2), GRID_MEASURE_DIVIDER_COLOR_LIGHT);
|
||||||
|
|
||||||
// Draw the beat ticks.
|
// Draw the beat and step ticks. No need for two seperate loops thankfully.
|
||||||
var beatTick2Y:Float = state.measureTickBitmap.height * 1 / Conductor.instance.beatsPerMeasure - (beatTickWidth / 2);
|
// This'll be fun to update when beat tuplets become functional.
|
||||||
var beatTick3Y:Float = state.measureTickBitmap.height * 2 / Conductor.instance.beatsPerMeasure - (beatTickWidth / 2);
|
for (i in 1...(Conductor.instance.stepsPerMeasure))
|
||||||
var beatTick4Y:Float = state.measureTickBitmap.height * 3 / Conductor.instance.beatsPerMeasure - (beatTickWidth / 2);
|
{
|
||||||
var beatTickLength:Float = state.measureTickBitmap.width * 2 / 3;
|
if ((i % Constants.STEPS_PER_BEAT) == 0) // If we're on a beat, draw a beat tick.
|
||||||
state.measureTickBitmap.fillRect(new Rectangle(0, beatTick2Y, beatTickLength, beatTickWidth), GRID_MEASURE_DIVIDER_COLOR_LIGHT);
|
{
|
||||||
state.measureTickBitmap.fillRect(new Rectangle(0, beatTick3Y, beatTickLength, beatTickWidth), GRID_MEASURE_DIVIDER_COLOR_LIGHT);
|
var beatTickY:Float = state.measureTickBitmap.height * i / Conductor.instance.stepsPerMeasure - (beatTickWidth / 2);
|
||||||
state.measureTickBitmap.fillRect(new Rectangle(0, beatTick4Y, beatTickLength, beatTickWidth), GRID_MEASURE_DIVIDER_COLOR_LIGHT);
|
var beatTickLength:Float = state.measureTickBitmap.width * 2 / 3;
|
||||||
|
state.measureTickBitmap.fillRect(new Rectangle(0, beatTickY, beatTickLength, beatTickWidth), GRID_MEASURE_DIVIDER_COLOR_LIGHT);
|
||||||
// Draw the step ticks.
|
}
|
||||||
// TODO: Make this a loop or something.
|
else // Else, draw a step tick.
|
||||||
var stepTick2Y:Float = state.measureTickBitmap.height * 1 / Conductor.instance.stepsPerMeasure - (stepTickWidth / 2);
|
{
|
||||||
var stepTick3Y:Float = state.measureTickBitmap.height * 2 / Conductor.instance.stepsPerMeasure - (stepTickWidth / 2);
|
var stepTickY:Float = state.measureTickBitmap.height * i / Conductor.instance.stepsPerMeasure - (stepTickWidth / 2);
|
||||||
var stepTick4Y:Float = state.measureTickBitmap.height * 3 / Conductor.instance.stepsPerMeasure - (stepTickWidth / 2);
|
var stepTickLength:Float = state.measureTickBitmap.width * 1 / 3;
|
||||||
var stepTick6Y:Float = state.measureTickBitmap.height * 5 / Conductor.instance.stepsPerMeasure - (stepTickWidth / 2);
|
state.measureTickBitmap.fillRect(new Rectangle(0, stepTickY, stepTickLength, stepTickWidth), GRID_MEASURE_DIVIDER_COLOR_LIGHT);
|
||||||
var stepTick7Y:Float = state.measureTickBitmap.height * 6 / Conductor.instance.stepsPerMeasure - (stepTickWidth / 2);
|
}
|
||||||
var stepTick8Y:Float = state.measureTickBitmap.height * 7 / Conductor.instance.stepsPerMeasure - (stepTickWidth / 2);
|
}
|
||||||
var stepTick10Y:Float = state.measureTickBitmap.height * 9 / Conductor.instance.stepsPerMeasure - (stepTickWidth / 2);
|
|
||||||
var stepTick11Y:Float = state.measureTickBitmap.height * 10 / Conductor.instance.stepsPerMeasure - (stepTickWidth / 2);
|
|
||||||
var stepTick12Y:Float = state.measureTickBitmap.height * 11 / Conductor.instance.stepsPerMeasure - (stepTickWidth / 2);
|
|
||||||
var stepTick14Y:Float = state.measureTickBitmap.height * 13 / Conductor.instance.stepsPerMeasure - (stepTickWidth / 2);
|
|
||||||
var stepTick15Y:Float = state.measureTickBitmap.height * 14 / Conductor.instance.stepsPerMeasure - (stepTickWidth / 2);
|
|
||||||
var stepTick16Y:Float = state.measureTickBitmap.height * 15 / Conductor.instance.stepsPerMeasure - (stepTickWidth / 2);
|
|
||||||
var stepTickLength:Float = state.measureTickBitmap.width * 1 / 3;
|
|
||||||
state.measureTickBitmap.fillRect(new Rectangle(0, stepTick2Y, stepTickLength, stepTickWidth), GRID_MEASURE_DIVIDER_COLOR_LIGHT);
|
|
||||||
state.measureTickBitmap.fillRect(new Rectangle(0, stepTick3Y, stepTickLength, stepTickWidth), GRID_MEASURE_DIVIDER_COLOR_LIGHT);
|
|
||||||
state.measureTickBitmap.fillRect(new Rectangle(0, stepTick4Y, stepTickLength, stepTickWidth), GRID_MEASURE_DIVIDER_COLOR_LIGHT);
|
|
||||||
state.measureTickBitmap.fillRect(new Rectangle(0, stepTick6Y, stepTickLength, stepTickWidth), GRID_MEASURE_DIVIDER_COLOR_LIGHT);
|
|
||||||
state.measureTickBitmap.fillRect(new Rectangle(0, stepTick7Y, stepTickLength, stepTickWidth), GRID_MEASURE_DIVIDER_COLOR_LIGHT);
|
|
||||||
state.measureTickBitmap.fillRect(new Rectangle(0, stepTick8Y, stepTickLength, stepTickWidth), GRID_MEASURE_DIVIDER_COLOR_LIGHT);
|
|
||||||
state.measureTickBitmap.fillRect(new Rectangle(0, stepTick10Y, stepTickLength, stepTickWidth), GRID_MEASURE_DIVIDER_COLOR_LIGHT);
|
|
||||||
state.measureTickBitmap.fillRect(new Rectangle(0, stepTick11Y, stepTickLength, stepTickWidth), GRID_MEASURE_DIVIDER_COLOR_LIGHT);
|
|
||||||
state.measureTickBitmap.fillRect(new Rectangle(0, stepTick12Y, stepTickLength, stepTickWidth), GRID_MEASURE_DIVIDER_COLOR_LIGHT);
|
|
||||||
state.measureTickBitmap.fillRect(new Rectangle(0, stepTick14Y, stepTickLength, stepTickWidth), GRID_MEASURE_DIVIDER_COLOR_LIGHT);
|
|
||||||
state.measureTickBitmap.fillRect(new Rectangle(0, stepTick15Y, stepTickLength, stepTickWidth), GRID_MEASURE_DIVIDER_COLOR_LIGHT);
|
|
||||||
state.measureTickBitmap.fillRect(new Rectangle(0, stepTick16Y, stepTickLength, stepTickWidth), GRID_MEASURE_DIVIDER_COLOR_LIGHT);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -314,23 +295,16 @@ class ChartEditorThemeHandler
|
||||||
state.offsetTickBitmap.fillRect(new Rectangle(rightTickX, 0, majorTickWidth / 2, majorTickLength), GRID_MEASURE_DIVIDER_COLOR_LIGHT);
|
state.offsetTickBitmap.fillRect(new Rectangle(rightTickX, 0, majorTickWidth / 2, majorTickLength), GRID_MEASURE_DIVIDER_COLOR_LIGHT);
|
||||||
|
|
||||||
// Draw the minor ticks.
|
// Draw the minor ticks.
|
||||||
var minorTick2X:Float = state.offsetTickBitmap.width * 1 / 10 - (minorTickWidth / 2);
|
for (i in 1...11)
|
||||||
var minorTick3X:Float = state.offsetTickBitmap.width * 2 / 10 - (minorTickWidth / 2);
|
{
|
||||||
var minorTick4X:Float = state.offsetTickBitmap.width * 3 / 10 - (minorTickWidth / 2);
|
if (i % 5 == 0)
|
||||||
var minorTick5X:Float = state.offsetTickBitmap.width * 4 / 10 - (minorTickWidth / 2);
|
{
|
||||||
var minorTick7X:Float = state.offsetTickBitmap.width * 6 / 10 - (minorTickWidth / 2);
|
continue;
|
||||||
var minorTick8X:Float = state.offsetTickBitmap.width * 7 / 10 - (minorTickWidth / 2);
|
}
|
||||||
var minorTick9X:Float = state.offsetTickBitmap.width * 8 / 10 - (minorTickWidth / 2);
|
var minorTickX:Float = state.offsetTickBitmap.width * i / 10 - (minorTickWidth / 2);
|
||||||
var minorTick10X:Float = state.offsetTickBitmap.width * 9 / 10 - (minorTickWidth / 2);
|
var minorTickLength:Float = state.offsetTickBitmap.height * 1 / 3;
|
||||||
var minorTickLength:Float = state.offsetTickBitmap.height * 1 / 3;
|
state.offsetTickBitmap.fillRect(new Rectangle(minorTickX, 0, minorTickWidth, minorTickLength), GRID_MEASURE_DIVIDER_COLOR_LIGHT);
|
||||||
state.offsetTickBitmap.fillRect(new Rectangle(minorTick2X, 0, minorTickWidth, minorTickLength), GRID_MEASURE_DIVIDER_COLOR_LIGHT);
|
}
|
||||||
state.offsetTickBitmap.fillRect(new Rectangle(minorTick3X, 0, minorTickWidth, minorTickLength), GRID_MEASURE_DIVIDER_COLOR_LIGHT);
|
|
||||||
state.offsetTickBitmap.fillRect(new Rectangle(minorTick4X, 0, minorTickWidth, minorTickLength), GRID_MEASURE_DIVIDER_COLOR_LIGHT);
|
|
||||||
state.offsetTickBitmap.fillRect(new Rectangle(minorTick5X, 0, minorTickWidth, minorTickLength), GRID_MEASURE_DIVIDER_COLOR_LIGHT);
|
|
||||||
state.offsetTickBitmap.fillRect(new Rectangle(minorTick7X, 0, minorTickWidth, minorTickLength), GRID_MEASURE_DIVIDER_COLOR_LIGHT);
|
|
||||||
state.offsetTickBitmap.fillRect(new Rectangle(minorTick8X, 0, minorTickWidth, minorTickLength), GRID_MEASURE_DIVIDER_COLOR_LIGHT);
|
|
||||||
state.offsetTickBitmap.fillRect(new Rectangle(minorTick9X, 0, minorTickWidth, minorTickLength), GRID_MEASURE_DIVIDER_COLOR_LIGHT);
|
|
||||||
state.offsetTickBitmap.fillRect(new Rectangle(minorTick10X, 0, minorTickWidth, minorTickLength), GRID_MEASURE_DIVIDER_COLOR_LIGHT);
|
|
||||||
|
|
||||||
// Draw the offset ticks.
|
// Draw the offset ticks.
|
||||||
// var ticksWidth:Int = Std.int(ChartEditorState.GRID_SIZE * TOTAL_COLUMN_COUNT); // 1 grid squares wide.
|
// var ticksWidth:Int = Std.int(ChartEditorState.GRID_SIZE * TOTAL_COLUMN_COUNT); // 1 grid squares wide.
|
||||||
|
|
|
@ -1458,17 +1458,17 @@ class StageEditorState extends UIState
|
||||||
/**
|
/**
|
||||||
* Available themes for the stage editor state.
|
* Available themes for the stage editor state.
|
||||||
*/
|
*/
|
||||||
enum StageEditorTheme
|
enum abstract StageEditorTheme(String)
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The default theme for the stage editor.
|
* The default theme for the stage editor.
|
||||||
*/
|
*/
|
||||||
Light;
|
var Light;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A theme which introduces stage colors.
|
* A theme which introduces stage colors.
|
||||||
*/
|
*/
|
||||||
Dark;
|
var Dark;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum StageEditorDialogType
|
enum StageEditorDialogType
|
||||||
|
|
|
@ -136,8 +136,6 @@ class FunkinPreloader extends FlxBasePreloader
|
||||||
// We can't even call trace() yet, until Flixel loads.
|
// We can't even call trace() yet, until Flixel loads.
|
||||||
trace('Initializing custom preloader...');
|
trace('Initializing custom preloader...');
|
||||||
|
|
||||||
funkin.util.CLIUtil.resetWorkingDir();
|
|
||||||
|
|
||||||
this.siteLockTitleText = Constants.SITE_LOCK_TITLE;
|
this.siteLockTitleText = Constants.SITE_LOCK_TITLE;
|
||||||
this.siteLockBodyText = Constants.SITE_LOCK_DESC;
|
this.siteLockBodyText = Constants.SITE_LOCK_DESC;
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,31 +63,6 @@ class SerializerUtil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function initSerializer():Void
|
|
||||||
{
|
|
||||||
haxe.Unserializer.DEFAULT_RESOLVER = new FunkinTypeResolver();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Serialize a Haxe object using the built-in Serializer.
|
|
||||||
* @param input The object to serialize
|
|
||||||
* @return The serialized object as a string
|
|
||||||
*/
|
|
||||||
public static function fromHaxeObject(input:Dynamic):String
|
|
||||||
{
|
|
||||||
return haxe.Serializer.run(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert a serialized Haxe object back into a Haxe object.
|
|
||||||
* @param input The serialized object as a string
|
|
||||||
* @return The deserialized object
|
|
||||||
*/
|
|
||||||
public static function toHaxeObject(input:String):Dynamic
|
|
||||||
{
|
|
||||||
return haxe.Unserializer.run(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Customize how certain types are serialized when converting to JSON.
|
* Customize how certain types are serialized when converting to JSON.
|
||||||
*/
|
*/
|
||||||
|
@ -115,26 +90,3 @@ class SerializerUtil
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class FunkinTypeResolver
|
|
||||||
{
|
|
||||||
public function new()
|
|
||||||
{
|
|
||||||
// Blank constructor.
|
|
||||||
}
|
|
||||||
|
|
||||||
public function resolveClass(name:String):Class<Dynamic>
|
|
||||||
{
|
|
||||||
if (name == 'Dynamic')
|
|
||||||
{
|
|
||||||
FlxG.log.warn('Found invalid class type in save data, indicates partial save corruption.');
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return Type.resolveClass(name);
|
|
||||||
};
|
|
||||||
|
|
||||||
public function resolveEnum(name:String):Enum<Dynamic>
|
|
||||||
{
|
|
||||||
return Type.resolveEnum(name);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue