Replace meta difficulties with variations.
This commit is contained in:
@@ -45,12 +45,15 @@
|
||||
<group name="CharterSelection" prefix="charterSelection.">
|
||||
<str id="desc">Select a song to modify the charts from.</str>
|
||||
<str id="acceptSong">Press ACCEPT to choose a difficulty to edit.</str>
|
||||
<str id="acceptVariation">Press ACCEPT to choose a song variation to edit.</str>
|
||||
<str id="acceptDifficulty">Press ACCEPT to edit the chart for the selected difficulty.</str>
|
||||
<str id="selectDifficulty">Select a difficulty to continue.</str>
|
||||
<str id="newDifficulty">New Difficulty</str>
|
||||
<str id="newDifficultyDesc">Press ACCEPT to create a new chart difficulty.</str>
|
||||
<str id="newSong">New Song</str>
|
||||
<str id="newSongDesc">Press ACCEPT to create a new song.</str>
|
||||
<str id="newVariation">New Variation</str>
|
||||
<str id="newVariationDesc">Press ACCEPT to create a new song variation.</str>
|
||||
</group>
|
||||
|
||||
<group name="ChartCreationScreen" prefix="chartCreation.">
|
||||
@@ -104,6 +107,7 @@
|
||||
|
||||
<str id="songData">Song Data</str>
|
||||
<str id="songName">Song Name</str>
|
||||
<str id="variation">Song Variation</str>
|
||||
<str id="bpm">BPM</str>
|
||||
<str id="timeSignature">Time Signature</str>
|
||||
<str id="menusData">Menus Data (Freeplay/Story)</str>
|
||||
|
||||
@@ -128,11 +128,11 @@ class Paths
|
||||
return scriptPath;
|
||||
}
|
||||
|
||||
static public function chart(song:String, ?difficulty:String):String
|
||||
static public function chart(song:String, ?difficulty:String, ?variant:String):String
|
||||
{
|
||||
difficulty = (difficulty != null ? difficulty : Flags.DEFAULT_DIFFICULTY);
|
||||
|
||||
return getPath('songs/$song/charts/$difficulty.json', null);
|
||||
return getPath('songs/$song/charts/${variant != null ? variant + "/" : ""}$difficulty.json', null);
|
||||
}
|
||||
|
||||
public static function character(character:String):String {
|
||||
|
||||
@@ -83,77 +83,75 @@ class Chart {
|
||||
return data;
|
||||
}
|
||||
|
||||
public static function loadChartMeta(songName:String, difficulty:String = '', fromMods:Bool = true, includeMetaDifficulties:Bool = true):ChartMetaData {
|
||||
var metaPath = Paths.file('songs/${songName}/meta.json');
|
||||
var metaDiffPath = Paths.file('songs/${songName}/meta-${difficulty}.json');
|
||||
|
||||
var data:ChartMetaData = null, fromDifficulty = false;
|
||||
var fromMods:Bool = fromMods;
|
||||
for (path in [metaDiffPath, metaPath]) if (Assets.exists(path)) {
|
||||
public static function loadChartMeta(songName:String, ?variant:String, fromMods:Bool = true, includeMetaVariations = true):ChartMetaData {
|
||||
var defaultPath = Paths.file('songs/$songName/meta.json'), isVariant = false;
|
||||
var data:ChartMetaData = null, paths = (variant == null || variant == '') ? [defaultPath] : [Paths.file('songs/$songName/meta-$variant.json'), defaultPath];
|
||||
for (path in paths) if (Assets.exists(path)) {
|
||||
fromMods = Paths.assetsTree.existsSpecific(path, "TEXT", MODS);
|
||||
try {
|
||||
var tempData = Json.parse(Assets.getText(path));
|
||||
tempData.color = CoolUtil.getColorFromDynamic(tempData.color).getDefault(Flags.DEFAULT_COLOR);
|
||||
data = tempData;
|
||||
} catch(e) Logs.trace('Failed to load song metadata for ${songName} ($path): ${Std.string(e)}', ERROR);
|
||||
} catch(e) Logs.trace('Failed to load song metadata for $songName ($path): ${Std.string(e)}', ERROR);
|
||||
|
||||
if (data != null) {
|
||||
fromDifficulty = path == metaDiffPath;
|
||||
isVariant = path != defaultPath;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (data == null) {
|
||||
data = {
|
||||
name: songName,
|
||||
bpm: Flags.DEFAULT_BPM
|
||||
};
|
||||
}
|
||||
else {
|
||||
data.name = songName;
|
||||
data.setFieldDefault("bpm", Flags.DEFAULT_BPM);
|
||||
}
|
||||
if (data != null) data.name = songName;
|
||||
else data = {
|
||||
name: songName,
|
||||
color: Flags.DEFAULT_COLOR
|
||||
};
|
||||
|
||||
if (isVariant) data.variant = variant;
|
||||
else data.variant = null;
|
||||
|
||||
data.setFieldDefault("displayName", data.name);
|
||||
|
||||
data.setFieldDefault("bpm", Flags.DEFAULT_BPM);
|
||||
data.setFieldDefault("beatsPerMeasure", Flags.DEFAULT_BEATS_PER_MEASURE);
|
||||
data.setFieldDefault("stepsPerBeat", Flags.DEFAULT_STEPS_PER_BEAT);
|
||||
data.setFieldDefault("icon", Flags.DEFAULT_HEALTH_ICON);
|
||||
data.setFieldDefault("difficulties", []);
|
||||
data.setFieldDefault("coopAllowed", Flags.DEFAULT_COOP_ALLOWED);
|
||||
data.setFieldDefault("opponentModeAllowed", Flags.DEFAULT_OPPONENT_MODE_ALLOWED);
|
||||
data.setFieldDefault("instSuffix", "");
|
||||
data.setFieldDefault("vocalsSuffix", "");
|
||||
data.setFieldDefault("needsVoices", true);
|
||||
data.setFieldDefault("difficulties", []);
|
||||
data.setFieldDefault("variants", []);
|
||||
|
||||
if (data.difficulties.length <= 0) {
|
||||
data.difficulties = [for(f in Paths.getFolderContent('songs/${songName}/charts/', false, fromMods ? MODS : SOURCE)) if (Path.extension(f.toUpperCase()) == "JSON") Path.withoutExtension(f)];
|
||||
var tempDiffs = [];
|
||||
var path = 'songs/$songName/charts/';
|
||||
if (isVariant) path += '$variant/';
|
||||
|
||||
data.difficulties = [for(f in Paths.getFolderContent(path, false, fromMods ? MODS : SOURCE)) if (Path.extension(f.toUpperCase()) == "JSON") Path.withoutExtension(f)];
|
||||
if (data.difficulties.length == 3) {
|
||||
for(d in data.difficulties) {
|
||||
switch(d.toLowerCase()) {
|
||||
case "easy": tempDiffs.insert(0, d);
|
||||
case "normal": tempDiffs.insert(1, d);
|
||||
case "hard": tempDiffs.insert(2, d);
|
||||
}
|
||||
var tempDiffs = [];
|
||||
for (d in data.difficulties) switch(d.toLowerCase()) {
|
||||
case "easy": tempDiffs.insert(0, d);
|
||||
case "normal": tempDiffs.insert(1, d);
|
||||
case "hard": tempDiffs.insert(2, d);
|
||||
}
|
||||
data.difficulties = tempDiffs;
|
||||
if (tempDiffs.length == 3) data.difficulties = tempDiffs;
|
||||
}
|
||||
}
|
||||
|
||||
if (includeMetaDifficulties && !fromDifficulty) {
|
||||
data.metas = [];
|
||||
for (difficulty in data.difficulties) {
|
||||
if (!data.metas.exists(difficulty) && Assets.exists(Paths.file('songs/${songName}/meta-${difficulty}.json')))
|
||||
data.metas.set(difficulty, loadChartMeta(songName, difficulty, fromMods, false));
|
||||
}
|
||||
data.metas = [];
|
||||
if (includeMetaVariations && data.variants.length > 0) for (variant in data.variants) {
|
||||
if (!data.metas.exists(variant) && Assets.exists(Paths.file('songs/$songName/meta-$variant.json')))
|
||||
data.metas.set(variant, loadChartMeta(songName, variant, fromMods));
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
public static function parse(songName:String, ?difficulty:String):ChartData {
|
||||
public static function parse(songName:String, ?difficulty:String, ?variant:String):ChartData {
|
||||
if (difficulty == null) difficulty = Flags.DEFAULT_DIFFICULTY;
|
||||
|
||||
var chartPath = Paths.chart(songName, difficulty);
|
||||
var chartPath = Paths.chart(songName, difficulty, variant);
|
||||
var base:ChartData = {
|
||||
strumLines: [],
|
||||
noteTypes: [],
|
||||
@@ -167,15 +165,15 @@ class Chart {
|
||||
fromMods: Paths.assetsTree.existsSpecific(chartPath, "TEXT", MODS)
|
||||
};
|
||||
|
||||
var valid:Bool = true;
|
||||
var valid:Bool = true, namePrint = '$songName $difficulty' + ((variant != null && variant != '') ? ' ($variant)' : '');
|
||||
if (!Assets.exists(chartPath)) {
|
||||
Logs.error('Chart for song ${songName} ($difficulty) at "$chartPath" was not found.');
|
||||
Logs.error('Chart for song $namePrint at "$chartPath" was not found.');
|
||||
valid = false;
|
||||
}
|
||||
var data:Dynamic = null;
|
||||
if (valid) {
|
||||
try data = Json.parse(Assets.getText(chartPath))
|
||||
catch(e) Logs.trace('Could not parse chart for song ${songName} ($difficulty): ${Std.string(e)}', ERROR, RED);
|
||||
catch(e) Logs.trace('Could not parse chart for song $namePrint: ${Std.string(e)}', ERROR, RED);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -207,13 +205,12 @@ class Chart {
|
||||
}
|
||||
#end
|
||||
|
||||
var loadedMeta = loadChartMeta(songName, difficulty, base.fromMods);
|
||||
var loadedMeta = loadChartMeta(songName, variant, base.fromMods, false);
|
||||
if (base.meta == null) base.meta = loadedMeta;
|
||||
else {
|
||||
for (field in Reflect.fields(base.meta)) {
|
||||
var f = Reflect.field(base.meta, field);
|
||||
if (f != null)
|
||||
Reflect.setField(loadedMeta, field, f);
|
||||
if (f != null) Reflect.setField(loadedMeta, field, f);
|
||||
}
|
||||
base.meta = loadedMeta;
|
||||
}
|
||||
@@ -223,8 +220,7 @@ class Chart {
|
||||
*/
|
||||
#if REGION
|
||||
var extraEvents:Array<ChartEvent> = loadEventsJson(songName);
|
||||
if (extraEvents != null)
|
||||
base.events = base.events.concat(extraEvents);
|
||||
if (extraEvents != null) base.events = base.events.concat(extraEvents);
|
||||
#end
|
||||
|
||||
/**
|
||||
@@ -252,61 +248,60 @@ class Chart {
|
||||
|
||||
/**
|
||||
* Saves the chart to the specific song folder path.
|
||||
* @param songFolderPath Path to the song folder (ex: `mods/your mod/songs/song/`)
|
||||
* @param chart Chart to save
|
||||
* @param difficulty Name of the difficulty
|
||||
* @param saveSettings
|
||||
* @param chart Chart to save.
|
||||
* @param difficulty Name of the difficulty (Optional).
|
||||
* @param variant Name of the Variant (Optional).
|
||||
* @param saveSettings (Optional).
|
||||
* @return Filtered chart used for saving.
|
||||
*/
|
||||
public static function save(songFolderPath:String, chart:ChartData, ?difficulty:String, ?saveSettings:ChartSaveSettings):ChartData {
|
||||
public static function save(chart:ChartData, ?difficulty:String, ?variant:String, ?saveSettings:ChartSaveSettings):ChartData {
|
||||
if (difficulty == null) difficulty = Flags.DEFAULT_DIFFICULTY;
|
||||
if (saveSettings == null) saveSettings = {};
|
||||
|
||||
if (saveSettings.saveMetaInChart == null) saveSettings.saveMetaInChart = true;
|
||||
if (saveSettings.saveLocalEvents == null) saveSettings.saveLocalEvents = true;
|
||||
if (saveSettings.saveGlobalEvents == null) saveSettings.saveGlobalEvents = false;
|
||||
|
||||
var filteredChart = filterChartForSaving(chart, saveSettings.saveMetaInChart, saveSettings.saveLocalEvents, saveSettings.saveGlobalEvents);
|
||||
var meta = filteredChart.meta;
|
||||
var filteredChart = filterChartForSaving(chart, saveSettings.saveMetaInChart, saveSettings.saveLocalEvents, saveSettings.saveGlobalEvents && saveSettings.seperateGlobalEvents != true);
|
||||
|
||||
#if sys
|
||||
var saveFolder:String = saveSettings.folder == null ? "charts" : saveSettings.folder;
|
||||
var songPath = saveSettings.songFolder == null ? 'assets/songs/${chart.meta.name}' : saveSettings.songFolder;
|
||||
var metaPath = 'meta.json', prettyPrint = saveSettings.prettyPrint == true ? Flags.JSON_PRETTY_PRINT : null, temp:String;
|
||||
if ((temp = Paths.assetsTree.getPath('$songPath/$metaPath')) != null) {
|
||||
songPath = temp.substr(0, temp.length - metaPath.length - 1);
|
||||
metaPath = temp;
|
||||
}
|
||||
else if (saveSettings.songFolder == null)
|
||||
metaPath = (songPath = '${Paths.getAssetsRoot()}/$songPath') + '/$metaPath';
|
||||
|
||||
if (!FileSystem.exists('${songFolderPath}/$saveFolder/'))
|
||||
FileSystem.createDirectory('${songFolderPath}/$saveFolder/');
|
||||
var chartFolder = saveSettings.folder == null ? ((variant == null || variant == '') ? 'charts' : 'charts/$variant') : saveSettings.folder;
|
||||
var chartPath = '$songPath/$chartFolder/${difficulty.trim()}.json';
|
||||
|
||||
var chartPath = '${songFolderPath}/$saveFolder/${difficulty.trim()}.json';
|
||||
var metaPath = '${songFolderPath}/meta.json';
|
||||
if (saveSettings.saveChart == null || saveSettings.saveChart == true)
|
||||
CoolUtil.safeSaveFile(chartPath, Json.stringify(filteredChart, null, prettyPrint));
|
||||
|
||||
CoolUtil.safeSaveFile(chartPath, Json.stringify(filteredChart, null, saveSettings.prettyPrint == true ? Flags.JSON_PRETTY_PRINT : null));
|
||||
if (saveSettings.overrideExistingMeta || !FileSystem.exists(metaPath))
|
||||
CoolUtil.safeSaveFile(metaPath, Json.stringify(filterMetaForSaving(chart.meta), null, prettyPrint));
|
||||
|
||||
if (meta != null && (saveSettings.overrideExistingMeta || !FileSystem.exists(metaPath)))
|
||||
CoolUtil.safeSaveFile(metaPath, makeMetaSaveable(meta));
|
||||
if (saveSettings.seperateGlobalEvents == true) {
|
||||
var eventsPath = '$songPath/events.json', events = filterEventsForSaving(chart.events, false, true);
|
||||
|
||||
if (events.length != 0) CoolUtil.safeSaveFile(eventsPath, Json.stringify({events: events}, null, prettyPrint));
|
||||
else if (FileSystem.exists(eventsPath)) FileSystem.deleteFile(eventsPath);
|
||||
}
|
||||
#end
|
||||
|
||||
return filteredChart;
|
||||
}
|
||||
|
||||
public static function filterChartForSaving(chart:ChartData, ?saveMetaInChart:Bool, ?saveLocalEvents:Bool, ?saveGlobalEvents:Bool):ChartData {
|
||||
public static function filterChartForSaving(chart:ChartData, saveMetaInChart = true, ?saveLocalEvents:Bool, ?saveGlobalEvents:Bool):ChartData {
|
||||
var meta = chart.meta, events = chart.events;
|
||||
chart.meta = null;
|
||||
chart.events = null;
|
||||
|
||||
var data = Reflect.copy(chart); // make a copy of the chart to leave the OG intact
|
||||
if (saveMetaInChart != true) {
|
||||
data.meta = null;
|
||||
} else {
|
||||
data.meta = Reflect.copy(chart.meta); // also make a copy of the metadata to leave the OG intact.
|
||||
if (data.meta != null && Reflect.hasField(data.meta, "parsedColor")) Reflect.deleteField(data.meta, "parsedColor");
|
||||
}
|
||||
|
||||
// in this part abt the events, i gotta account that these booleans can be null - Nex
|
||||
if (saveLocalEvents != true && saveGlobalEvents != true) data.events = null;
|
||||
else {
|
||||
data.events = [];
|
||||
for (event in chart.events) if ((saveLocalEvents == true && event.global != true) || (saveGlobalEvents == true && event.global == true)) {
|
||||
var copy = Reflect.copy(event);
|
||||
if (saveLocalEvents == true ? event.global != true : event.global == true) Reflect.deleteField(copy, "global"); // should NOT delete the field when saving with the local events and the event should have been global - Nex
|
||||
data.events.push(copy);
|
||||
}
|
||||
if (data.events.length == 0) data.events = null;
|
||||
}
|
||||
chart.meta = meta;
|
||||
chart.events = events;
|
||||
|
||||
data.meta = saveMetaInChart ? filterMetaForSaving(meta) : null;
|
||||
data.events = filterEventsForSaving(events, saveLocalEvents, saveGlobalEvents);
|
||||
data.fromMods = null;
|
||||
|
||||
var sortedData:Dynamic = {};
|
||||
@@ -318,19 +313,46 @@ class Chart {
|
||||
return sortedData;
|
||||
}
|
||||
|
||||
public static inline function makeMetaSaveable(meta:ChartMetaData, prettyPrint:Bool = true):String {
|
||||
public static function filterEventsForSaving(events:Array<ChartEvent>, saveLocalEvents = true, saveGlobalEvents = false):Array<ChartEvent> {
|
||||
var data = [];
|
||||
if (!saveLocalEvents && !saveGlobalEvents) return data;
|
||||
|
||||
for (event in events) if ((saveLocalEvents && event.global != true) || (saveGlobalEvents && event.global == true)) {
|
||||
var copy = Reflect.copy(event);
|
||||
if (saveLocalEvents ? event.global != true : event.global == true) Reflect.deleteField(copy, "global"); // should NOT delete the field when saving with the local events and the event should have been global - Nex
|
||||
data.push(copy);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
public static inline function makeMetaSaveable(meta:ChartMetaData, prettyPrint:Bool = true):String
|
||||
return Json.stringify(filterMetaForSaving(meta), null, prettyPrint ? Flags.JSON_PRETTY_PRINT : null);
|
||||
|
||||
public static inline function filterMetaForSaving(meta:ChartMetaData):ChartMetaData {
|
||||
var data:Dynamic = Reflect.copy(meta);
|
||||
if (data.color != null) data.color = FlxColor.fromInt(data.color).toWebString(); // dont even ask me - Nex
|
||||
if (data.color != null) data.color = FlxColor.fromInt(data.color).toWebString(); // dont even ask me - Nex
|
||||
Reflect.deleteField(data, 'parsedColor');
|
||||
Reflect.deleteField(data, 'metas');
|
||||
return Json.stringify(data, null, prettyPrint ? Flags.JSON_PRETTY_PRINT : null);
|
||||
Reflect.deleteField(data, "variant");
|
||||
if (data.instSuffix != null && data.instSuffix == "") Reflect.deleteField(data, "instSuffix");
|
||||
if (data.vocalsSuffix != null && data.vocalsSuffix == "") Reflect.deleteField(data, "vocalsSuffix");
|
||||
if (data.variants != null && data.variants.length == 0) Reflect.deleteField(data, "variants");
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
typedef ChartSaveSettings = {
|
||||
var ?overrideExistingMeta:Bool;
|
||||
var ?prettyPrint:Bool;
|
||||
|
||||
var ?saveMetaInChart:Bool;
|
||||
var ?saveLocalEvents:Bool;
|
||||
var ?saveGlobalEvents:Bool;
|
||||
var ?prettyPrint:Bool;
|
||||
|
||||
var ?saveChart:Bool;
|
||||
var ?overrideExistingMeta:Bool;
|
||||
var ?seperateGlobalEvents:Bool;
|
||||
|
||||
var ?folder:String;
|
||||
var ?songFolder:String;
|
||||
}
|
||||
|
||||
@@ -18,16 +18,23 @@ typedef ChartData = {
|
||||
|
||||
typedef ChartMetaData = {
|
||||
public var name:String;
|
||||
public var ?bpm:Float;
|
||||
public var ?variant:String;
|
||||
public var ?displayName:String;
|
||||
|
||||
public var ?bpm:Float;
|
||||
public var ?beatsPerMeasure:Float;
|
||||
public var ?stepsPerBeat:Int;
|
||||
|
||||
public var ?difficulties:Array<String>;
|
||||
public var ?variants:Array<String>;
|
||||
public var ?customValues:Dynamic;
|
||||
|
||||
public var ?icon:String;
|
||||
public var ?color:FlxColor;
|
||||
public var ?difficulties:Array<String>;
|
||||
|
||||
public var ?coopAllowed:Bool;
|
||||
public var ?opponentModeAllowed:Bool;
|
||||
public var ?customValues:Dynamic;
|
||||
|
||||
public var ?metas:Map<String, ChartMetaData>;
|
||||
public var ?instSuffix:String;
|
||||
public var ?vocalsSuffix:String;
|
||||
|
||||
@@ -9,6 +9,10 @@ final class FreeplaySongSelectEvent extends CancellableEvent {
|
||||
* Difficulty name
|
||||
*/
|
||||
public var difficulty:String;
|
||||
/**
|
||||
* Variation Name
|
||||
*/
|
||||
public var variant:String;
|
||||
/**
|
||||
* Whenever opponent mode is enabled or not.
|
||||
*/
|
||||
|
||||
@@ -43,7 +43,6 @@ class FunkinSaveMacro {
|
||||
* SAVE FUNCTION
|
||||
*/
|
||||
var saveFuncBlocks:Array<Expr> = [for(f in fieldNames) macro $i{saveFieldName}.data.$f = $i{f}];
|
||||
|
||||
saveFuncBlocks.push(macro $i{saveFieldName}.flush());
|
||||
|
||||
fields.push({
|
||||
@@ -59,6 +58,8 @@ class FunkinSaveMacro {
|
||||
access: [APublic, AStatic]
|
||||
});
|
||||
|
||||
var loadFuncBlocks:Array<Expr> = [for(f in fieldNames) macro if ($i{saveFieldName}.data.$f != null) $i{f} = $i{saveFieldName}.data.$f];
|
||||
|
||||
/**
|
||||
* LOAD FUNCTION
|
||||
*/
|
||||
@@ -69,10 +70,7 @@ class FunkinSaveMacro {
|
||||
args: [],
|
||||
expr: {
|
||||
pos: Context.currentPos(),
|
||||
expr: EBlock([for(f in fieldNames)
|
||||
macro if ($i{saveFieldName}.data.$f != null)
|
||||
$i{f} = $i{saveFieldName}.data.$f
|
||||
])
|
||||
expr: EBlock(loadFuncBlocks)
|
||||
}
|
||||
}),
|
||||
access: [APublic, AStatic]
|
||||
|
||||
@@ -26,6 +26,7 @@ typedef WeekCharacter = {
|
||||
|
||||
typedef WeekSong = {
|
||||
var name:String;
|
||||
var ?variation:String;
|
||||
var hide:Bool;
|
||||
var ?displayName:String;
|
||||
}
|
||||
@@ -28,6 +28,7 @@ import flixel.util.FlxColor;
|
||||
class Charter extends UIState {
|
||||
public static var __song:String;
|
||||
static var __diff:String;
|
||||
static var __variant:String;
|
||||
static var __reload:Bool;
|
||||
|
||||
var chart(get, never):ChartData;
|
||||
@@ -114,11 +115,12 @@ class Charter extends UIState {
|
||||
private var SONGPOSINFO_BPM = TU.getRaw("songPosInfo.bpm");
|
||||
private var SONGPOSINFO_TIMESIGNATURE = TU.getRaw("songPosInfo.timeSignature");
|
||||
|
||||
public function new(song:String, diff:String, reload:Bool = true) {
|
||||
public function new(song:String, diff:String, variant:String, reload:Bool = true) {
|
||||
super();
|
||||
if (song != null) {
|
||||
__song = song;
|
||||
__diff = diff;
|
||||
__variant = variant;
|
||||
__reload = reload;
|
||||
}
|
||||
}
|
||||
@@ -578,7 +580,7 @@ class Charter extends UIState {
|
||||
dataDisplay.screenCenter(Y);
|
||||
dataDisplay.cameras = [charterCamera]; dataDisplay.x = -dataDisplay.width; add(dataDisplay);*/
|
||||
|
||||
DiscordUtil.call("onEditorLoaded", ["Chart Editor", __song + " (" + __diff + ")"]);
|
||||
DiscordUtil.call("onEditorLoaded", ["Chart Editor", __song + " (" + __diff + ")" + (__variant != null && __variant != "" ? " (" + __variant + ")" : "")]);
|
||||
}
|
||||
|
||||
override function destroy() {
|
||||
@@ -595,9 +597,10 @@ class Charter extends UIState {
|
||||
public function loadSong() {
|
||||
if (__reload) {
|
||||
EventsData.reloadEvents();
|
||||
PlayState.loadSong(__song, __diff, false, false);
|
||||
PlayState.loadSong(__song, __diff, __variant, false, false);
|
||||
__resetStatics();
|
||||
}
|
||||
|
||||
Conductor.setupSong(PlayState.SONG);
|
||||
noteTypes = PlayState.SONG.noteTypes;
|
||||
|
||||
@@ -612,14 +615,14 @@ class Charter extends UIState {
|
||||
|
||||
gridBackdrops.createGrids(PlayState.SONG.strumLines.length);
|
||||
|
||||
for(strL in PlayState.SONG.strumLines)
|
||||
var noteCount:Int = 0;
|
||||
for (strL in PlayState.SONG.strumLines) {
|
||||
createStrumline(strumLines.members.length, strL, false, false);
|
||||
noteCount += strL.notes.length;
|
||||
}
|
||||
|
||||
// create notes
|
||||
notesGroup.autoSort = false;
|
||||
var noteCount:Int = 0;
|
||||
for (strL in PlayState.SONG.strumLines)
|
||||
noteCount += strL.notes.length;
|
||||
notesGroup.preallocate(noteCount);
|
||||
|
||||
var notesCreated:Int = 0;
|
||||
@@ -683,10 +686,10 @@ class Charter extends UIState {
|
||||
var wavesToGenerate:Array<{name:String, sound:FlxSound}> = [];
|
||||
|
||||
if (FlxG.sound.music.loaded)
|
||||
wavesToGenerate.push({name: "Inst.ogg", sound: FlxG.sound.music});
|
||||
wavesToGenerate.push({name: 'Inst${PlayState.SONG.meta.instSuffix}.ogg', sound: FlxG.sound.music});
|
||||
|
||||
if (vocals.loaded)
|
||||
wavesToGenerate.push({name: "Voices.ogg", sound: vocals});
|
||||
wavesToGenerate.push({name: 'Voices${PlayState.SONG.meta.vocalsSuffix}.ogg', sound: vocals});
|
||||
|
||||
for (strumLine in strumLines)
|
||||
if (strumLine.vocals != null && strumLine.strumLine.vocalsSuffix != null && strumLine.strumLine.vocalsSuffix != "" && strumLine.vocals.loaded)
|
||||
@@ -788,12 +791,13 @@ class Charter extends UIState {
|
||||
autoSaveTimer = Options.charterAutoSaveTime;
|
||||
if (!autoSaveNotif.cancelled) {
|
||||
buildChart();
|
||||
var songPath:String = '${Paths.getAssetsRoot()}/songs/${__song.toLowerCase()}';
|
||||
|
||||
if (Options.charterAutoSavesSeparateFolder)
|
||||
Chart.save(songPath, PlayState.SONG, __autoSaveLocation, {saveMetaInChart: true, saveLocalEvents: true, saveGlobalEvents: true, folder: "autosaves", prettyPrint: Options.editorCharterPrettyPrint});
|
||||
else // These two chart saves are particular, to avoid any kind of loss, stuff like meta, global and local events will be save all together - Nex
|
||||
Chart.save(songPath, PlayState.SONG, __diff.toLowerCase(), {saveMetaInChart: true, saveLocalEvents: true, saveGlobalEvents: true, prettyPrint: Options.editorCharterPrettyPrint});
|
||||
Chart.save(PlayState.SONG, __diff.toLowerCase(), __autoSaveLocation, {saveMetaInChart: true, saveLocalEvents: true, seperateGlobalEvents: true, folder: 'autosaves', prettyPrint: Options.editorCharterPrettyPrint});
|
||||
else
|
||||
Chart.save(PlayState.SONG, __diff.toLowerCase(), __variant, {saveMetaInChart: true, saveLocalEvents: true, seperateGlobalEvents: true, prettyPrint: Options.editorCharterPrettyPrint});
|
||||
|
||||
FlxG.sound.play(Paths.sound('editors/save'));
|
||||
undos.save();
|
||||
}
|
||||
autoSaveNotif.cancelled = false;
|
||||
@@ -1476,8 +1480,8 @@ class Charter extends UIState {
|
||||
|
||||
public static function saveChart(shouldBuild:Bool = true, withEvents:Bool = true) {
|
||||
#if sys
|
||||
FlxG.sound.play(Paths.sound('editors/save'));
|
||||
saveTo('${Paths.getAssetsRoot()}/songs/${__song.toLowerCase()}', !withEvents, shouldBuild);
|
||||
if (shouldBuild && instance != null) instance.buildChart();
|
||||
Chart.save(PlayState.SONG, __diff.toLowerCase(), __variant, {saveMetaInChart: false, saveLocalEvents: withEvents, prettyPrint: Options.editorCharterPrettyPrint});
|
||||
if (undos != null) undos.save();
|
||||
#else
|
||||
saveChartAs(shouldBuild, withEvents);
|
||||
@@ -1493,13 +1497,9 @@ class Charter extends UIState {
|
||||
|
||||
public static function saveEvents(shouldBuild:Bool = true) {
|
||||
#if sys
|
||||
FlxG.sound.play(Paths.sound('editors/save'));
|
||||
if (shouldBuild && instance != null) instance.buildChart();
|
||||
var data = {events: Chart.filterChartForSaving(PlayState.SONG, false, false, true).events};
|
||||
|
||||
var path = '${Paths.getAssetsRoot()}/songs/${__song.toLowerCase()}/events.json';
|
||||
if (data.events != null && data.events.length > 0) CoolUtil.safeSaveFile(path, Json.stringify(data, null, Options.editorCharterPrettyPrint ? Flags.JSON_PRETTY_PRINT : null));
|
||||
else if (FileSystem.exists(path)) FileSystem.deleteFile(path); // Instead of replacing with a useless empty file, deletes the file directly - Nex
|
||||
Chart.save(PlayState.SONG, __diff.toLowerCase(), __variant, {saveChart: false, seperateGlobalEvents: true, prettyPrint: Options.editorCharterPrettyPrint});
|
||||
if (undos != null) undos.save();
|
||||
#else
|
||||
saveEventsAs(shouldBuild);
|
||||
#end
|
||||
@@ -1507,7 +1507,7 @@ class Charter extends UIState {
|
||||
|
||||
public static function saveEventsAs(shouldBuild:Bool = true) {
|
||||
if (shouldBuild && instance != null) instance.buildChart();
|
||||
var data = {events: Chart.filterChartForSaving(PlayState.SONG, false, false, true).events};
|
||||
var data = {events: Chart.filterEventsForSaving(PlayState.SONG.events, false, true)};
|
||||
|
||||
saveAs(data, null, Options.editorCharterPrettyPrint ? Flags.JSON_PRETTY_PRINT : null, {
|
||||
defaultSaveFile: 'events.json'
|
||||
@@ -1516,12 +1516,8 @@ class Charter extends UIState {
|
||||
|
||||
public static function saveMeta(shouldBuild:Bool = true) {
|
||||
#if sys
|
||||
FlxG.sound.play(Paths.sound('editors/save'));
|
||||
if (shouldBuild && instance != null) instance.buildChart();
|
||||
CoolUtil.safeSaveFile(
|
||||
'${Paths.getAssetsRoot()}/songs/${__song.toLowerCase()}/meta.json',
|
||||
Json.stringify(PlayState.SONG.meta == null ? {} : Chart.filterChartForSaving(PlayState.SONG, true, false, false).meta, null, Flags.JSON_PRETTY_PRINT)
|
||||
);
|
||||
Chart.save(PlayState.SONG, __diff.toLowerCase(), __variant, {saveChart: false, overrideExistingMeta: true, prettyPrint: true});
|
||||
#else
|
||||
saveMetaAs(shouldBuild);
|
||||
#end
|
||||
@@ -1545,9 +1541,7 @@ class Charter extends UIState {
|
||||
}, null, shouldBuild);
|
||||
}
|
||||
|
||||
public static function saveAs(data:Dynamic, ?replacer:(key:Dynamic, value:Dynamic) -> Dynamic, ?space:String, ?options:SaveSubstate.SaveSubstateData, ?saveOptions:Map<String, Bool>, shouldBuild:Bool = true) {
|
||||
FlxG.sound.play(Paths.sound('editors/save'));
|
||||
if (shouldBuild && instance != null) instance.buildChart();
|
||||
public static function saveAs(data:Dynamic, ?replacer:(key:Dynamic, value:Dynamic) -> Dynamic, ?space:String, ?options:SaveSubstate.SaveSubstateData, ?saveOptions:Map<String, Bool>, shouldBuild:Bool = true) { if (shouldBuild && instance != null) instance.buildChart();
|
||||
var cur = FlxG.state;
|
||||
while(true) {
|
||||
if (instance != null || cur.subState == null) return cur.openSubState(new SaveSubstate(Json.stringify(data, replacer, space), options, saveOptions));
|
||||
@@ -1558,7 +1552,7 @@ class Charter extends UIState {
|
||||
#if sys
|
||||
public static function saveTo(path:String, separateEvents:Bool = false, shouldBuild:Bool = true) {
|
||||
if (shouldBuild && instance != null) instance.buildChart();
|
||||
Chart.save(path, PlayState.SONG, __diff.toLowerCase(), {saveMetaInChart: false, saveLocalEvents: !separateEvents, prettyPrint: Options.editorCharterPrettyPrint});
|
||||
Chart.save(PlayState.SONG, __diff.toLowerCase(), __variant, {saveMetaInChart: false, saveLocalEvents: !separateEvents, songFolder: path, prettyPrint: Options.editorCharterPrettyPrint});
|
||||
}
|
||||
#end
|
||||
#end
|
||||
@@ -1570,17 +1564,17 @@ class Charter extends UIState {
|
||||
else {undos = null; FlxG.switchState(new CharterSelection()); Charter.instance.__clearStatics();}
|
||||
}
|
||||
|
||||
function _file_save_all(_) saveEverything();
|
||||
function _file_save(_) saveChart();
|
||||
function _file_saveas(_) saveChartAs();
|
||||
function _file_events_save(_) saveEvents();
|
||||
function _file_events_saveas(_) saveEventsAs();
|
||||
function _file_save_no_events(_) saveChart(true, false);
|
||||
function _file_saveas_no_events(_) saveChartAs(true, false);
|
||||
function _file_meta_save(_) saveMeta();
|
||||
function _file_meta_saveas(_) saveMetaAs();
|
||||
function _file_saveas_fnflegacy(_) saveLegacyChartAs();
|
||||
function _file_saveas_psych(_) savePsychChartAs();
|
||||
function _file_save_all(_) {saveEverything(); FlxG.sound.play(Paths.sound('editors/save'));}
|
||||
function _file_save(_) {saveChart(); FlxG.sound.play(Paths.sound('editors/save'));}
|
||||
function _file_saveas(_) {saveChartAs(); FlxG.sound.play(Paths.sound('editors/save'));}
|
||||
function _file_events_save(_) {saveEvents(); FlxG.sound.play(Paths.sound('editors/save'));}
|
||||
function _file_events_saveas(_) {saveEventsAs(); FlxG.sound.play(Paths.sound('editors/save'));}
|
||||
function _file_save_no_events(_) {saveChart(true, false); FlxG.sound.play(Paths.sound('editors/save'));}
|
||||
function _file_saveas_no_events(_) {saveChartAs(true, false); FlxG.sound.play(Paths.sound('editors/save'));}
|
||||
function _file_meta_save(_) {saveMeta(); FlxG.sound.play(Paths.sound('editors/save'));}
|
||||
function _file_meta_saveas(_) {saveMetaAs(); FlxG.sound.play(Paths.sound('editors/save'));}
|
||||
function _file_saveas_fnflegacy(_) {saveLegacyChartAs(); FlxG.sound.play(Paths.sound('editors/save'));}
|
||||
function _file_saveas_psych(_) {savePsychChartAs(); FlxG.sound.play(Paths.sound('editors/save'));}
|
||||
|
||||
function _edit_copy(_, playSFX=true) {
|
||||
if (playSFX) FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_COPY_SOUND));
|
||||
|
||||
@@ -150,20 +150,21 @@ class CharterMetaDataScreen extends UISubstateWindow {
|
||||
Reflect.setProperty(customVals, vals.propertyText.label.text, vals.valueText.label.text);
|
||||
}
|
||||
|
||||
PlayState.SONG.meta = {
|
||||
name: songNameTextBox.label.text,
|
||||
bpm: bpmStepper.value,
|
||||
needsVoices: needsVoicesCheckbox.checked,
|
||||
beatsPerMeasure: Std.int(beatsPerMeasureStepper.value),
|
||||
stepsPerBeat: Std.int(16 / denominatorStepper.value),
|
||||
displayName: displayNameTextBox.label.text,
|
||||
icon: iconTextBox.label.text,
|
||||
color: colorWheel.curColor,
|
||||
opponentModeAllowed: opponentModeCheckbox.checked,
|
||||
coopAllowed: coopAllowedCheckbox.checked,
|
||||
difficulties: [for (diff in difficultiesTextBox.label.text.split(",")) diff.trim()],
|
||||
customValues: customVals,
|
||||
};
|
||||
var meta = PlayState.SONG.meta;
|
||||
if (meta == null) meta = {name: songNameTextBox.label.text};
|
||||
else meta.name = songNameTextBox.label.text;
|
||||
|
||||
meta.bpm = bpmStepper.value;
|
||||
meta.needsVoices = needsVoicesCheckbox.checked;
|
||||
meta.beatsPerMeasure = Std.int(beatsPerMeasureStepper.value);
|
||||
meta.stepsPerBeat = Std.int(16 / denominatorStepper.value);
|
||||
meta.displayName = displayNameTextBox.label.text;
|
||||
meta.icon = iconTextBox.label.text;
|
||||
meta.color = colorWheel.curColor;
|
||||
meta.opponentModeAllowed = opponentModeCheckbox.checked;
|
||||
meta.coopAllowed = coopAllowedCheckbox.checked;
|
||||
meta.difficulties = [for (diff in difficultiesTextBox.label.text.split(",")) diff.trim()];
|
||||
meta.customValues = customVals;
|
||||
|
||||
Charter.instance.updateBPMEvents();
|
||||
Charter.instance.vocals.muted = !needsVoicesCheckbox.checked;
|
||||
|
||||
@@ -25,48 +25,65 @@ class CharterSelectionScreen extends EditorTreeMenuScreen {
|
||||
public var songList:Array<String> = [];
|
||||
public var curSong:ChartMetaData;
|
||||
|
||||
inline public function makeChartOption(d:String, name:String):TextOption {
|
||||
return new TextOption(d, getID('acceptDifficulty'), () -> FlxG.switchState(new Charter(name, d)));
|
||||
inline public function makeChartOption(d:String, v:String, name:String):TextOption {
|
||||
return new TextOption(d, getID('acceptDifficulty'), () -> FlxG.switchState(new Charter(name, d, v)));
|
||||
}
|
||||
|
||||
inline public function makeVariationOption(s:ChartMetaData):TextOption {
|
||||
return new TextOption(s.variant, getID('acceptVariation'), " >", () -> openSongOption(s, false));
|
||||
}
|
||||
|
||||
public function openSongOption(s:ChartMetaData, first = true) {
|
||||
curSong = s;
|
||||
|
||||
var isVariant = s.variant != null && s.variant != '';
|
||||
var screen = new EditorTreeMenuScreen((first || !isVariant) ? (s.name + (isVariant ? ' (${s.variant})' : '')) : s.variant, getID('selectDifficulty'));
|
||||
|
||||
for (d in s.difficulties) if (d != '') screen.add(makeChartOption(d, isVariant ? s.variant : null, s.name));
|
||||
screen.add(new Separator());
|
||||
for (v in s.variants) if (s.metas.get(v) != null) screen.add(makeVariationOption(s.metas.get(v)));
|
||||
|
||||
#if sys
|
||||
screen.insert(0, new NewOption(getID('newDifficulty'), getID('newDifficultyDesc'), () -> {
|
||||
parent.openSubState(new ChartCreationScreen(saveChart));
|
||||
}));
|
||||
|
||||
if (!first) screen.curSelected = 1;
|
||||
else {
|
||||
cast(screen.members[0], NewOption).itemHeight = 120;
|
||||
screen.insert(1, new NewOption(getID('newVariation'), getID('newVariationDesc'), () -> {
|
||||
parent.openSubState(new VariationCreationScreen(s, saveSong));
|
||||
}));
|
||||
screen.curSelected = 2;
|
||||
}
|
||||
#end
|
||||
|
||||
parent.addMenu(screen);
|
||||
}
|
||||
|
||||
public function makeSongOption(s:ChartMetaData):IconOption {
|
||||
songList.push(s.name.toLowerCase());
|
||||
|
||||
var opt = new IconOption(s.name, getID('acceptSong'), s.icon, () -> {
|
||||
curSong = s;
|
||||
|
||||
var screen = new EditorTreeMenuScreen(s.name, getID('selectDifficulty'), [
|
||||
for (d in s.difficulties) if (d != '') makeChartOption(d, s.name)
|
||||
]);
|
||||
|
||||
#if sys
|
||||
screen.insert(0, new NewOption(getID('newDifficulty'), getID('newDifficultyDesc'), () -> {
|
||||
parent.openSubState(new ChartCreationScreen(saveChart));
|
||||
}));
|
||||
screen.curSelected = 1;
|
||||
#end
|
||||
|
||||
parent.addMenu(screen);
|
||||
});
|
||||
opt.suffix = ' >';
|
||||
var opt = new IconOption(s.name, getID('acceptSong'), s.icon, () -> openSongOption(s, true));
|
||||
opt.suffix = " >";
|
||||
opt.editorFlashColor = s.color.getDefault(FlxColor.WHITE);
|
||||
|
||||
return opt;
|
||||
}
|
||||
|
||||
public function new() {
|
||||
super('editor.chart.name', 'charterSelection.desc', 'charterSelection.', 'newSong', 'newSongDesc', () -> {
|
||||
super('editor.chart.name', 'charterSelection.desc', 'charterSelection.', 'newSong', 'newSongDesc', #if sys () -> {
|
||||
parent.openSubState(new SongCreationScreen(saveSong));
|
||||
});
|
||||
} #end);
|
||||
freeplayList = FreeplaySonglist.get(false);
|
||||
|
||||
for (i => s in freeplayList.songs) add(makeSongOption(s));
|
||||
}
|
||||
|
||||
#if sys
|
||||
public function saveSong(creation:SongCreationData, ?callback:String -> SongCreationData -> Void) {
|
||||
var songAlreadyExists:Bool = songList.contains(creation.meta.name.toLowerCase());
|
||||
if (songAlreadyExists) {
|
||||
public function saveSong(creation:SongCreationData, ?callback:String -> Void) {
|
||||
var variant = creation.meta.variant != null && creation.meta.variant != "" ? creation.meta.variant : null;
|
||||
if (songList.contains(creation.meta.name.toLowerCase()) || (variant != null && curSong != null && curSong.variants.contains(variant))) {
|
||||
parent.openSubState(new UIWarningSubstate(TU.translate("chartCreation.warnings.song-exists-title"), TU.translate("chartCreation.warnings.song-exists-body"), [
|
||||
{label: TU.translate("editor.ok"), color: 0xFFFF0000, onClick: (t) -> {}},
|
||||
]));
|
||||
@@ -74,6 +91,7 @@ class CharterSelectionScreen extends EditorTreeMenuScreen {
|
||||
}
|
||||
|
||||
var songFolder:String = '${Paths.getAssetsRoot()}/songs/${creation.meta.name}';
|
||||
creation.meta = Chart.filterMetaForSaving(creation.meta);
|
||||
|
||||
#if sys
|
||||
// Make Directories
|
||||
@@ -82,24 +100,32 @@ class CharterSelectionScreen extends EditorTreeMenuScreen {
|
||||
sys.FileSystem.createDirectory('$songFolder/charts');
|
||||
|
||||
// Save Files
|
||||
CoolUtil.safeSaveFile('$songFolder/meta.json', Chart.makeMetaSaveable(creation.meta));
|
||||
if (creation.instBytes != null) sys.io.File.saveBytes('$songFolder/song/Inst.${Flags.SOUND_EXT}', creation.instBytes);
|
||||
if (creation.voicesBytes != null) sys.io.File.saveBytes('$songFolder/song/Voices.${Flags.SOUND_EXT}', creation.voicesBytes);
|
||||
var instSuffix = creation.meta.instSuffix != null ? creation.meta.instSuffix : '', vocalsSuffix = creation.meta.vocalsSuffix != null ? creation.meta.vocalsSuffix : '';
|
||||
CoolUtil.safeSaveFile('$songFolder/meta${variant != null ? "-" + variant : ""}.json', Json.stringify(creation.meta, null, Flags.JSON_PRETTY_PRINT));
|
||||
if (creation.instBytes != null) sys.io.File.saveBytes('$songFolder/song/Inst$instSuffix.${Flags.SOUND_EXT}', creation.instBytes);
|
||||
if (creation.voicesBytes != null) sys.io.File.saveBytes('$songFolder/song/Voices$vocalsSuffix.${Flags.SOUND_EXT}', creation.voicesBytes);
|
||||
|
||||
if (creation.playerVocals != null) sys.io.File.saveBytes('$songFolder/song/Voices-Player.${Flags.SOUND_EXT}', creation.playerVocals);
|
||||
if (creation.oppVocals != null) sys.io.File.saveBytes('$songFolder/song/Voices-Opponent.${Flags.SOUND_EXT}', creation.oppVocals);
|
||||
if (creation.playerVocals != null) sys.io.File.saveBytes('$songFolder/song/Voices-Player$vocalsSuffix.${Flags.SOUND_EXT}', creation.playerVocals);
|
||||
if (creation.oppVocals != null) sys.io.File.saveBytes('$songFolder/song/Voices-Opponent$vocalsSuffix.${Flags.SOUND_EXT}', creation.oppVocals);
|
||||
#end
|
||||
|
||||
if (callback != null) callback(songFolder, creation);
|
||||
if (callback != null) callback(songFolder);
|
||||
|
||||
// Add to List
|
||||
freeplayList.songs.insert(0, creation.meta);
|
||||
insert(1, makeSongOption(creation.meta));
|
||||
if (variant != null && curSong != null) {
|
||||
curSong.variants.push(variant);
|
||||
curSong.metas.set(variant, creation.meta);
|
||||
|
||||
parent.tree.last().add(makeVariationOption(creation.meta));
|
||||
}
|
||||
else {
|
||||
freeplayList.songs.insert(0, creation.meta);
|
||||
insert(1, makeSongOption(creation.meta));
|
||||
}
|
||||
}
|
||||
|
||||
public function saveChart(name:String, data:ChartData) {
|
||||
var difficultyAlreadyExists:Bool = curSong.difficulties.contains(name);
|
||||
if (difficultyAlreadyExists) {
|
||||
if (curSong.difficulties.contains(name)) {
|
||||
parent.openSubState(new UIWarningSubstate(TU.translate("chartCreation.warnings.chart-exists-title"), TU.translate("chartCreation.warnings.chart-exists-body"), [
|
||||
{label: TU.translate("editor.ok"), color: 0xFFFF0000, onClick: (t) -> {}},
|
||||
]));
|
||||
@@ -113,11 +139,16 @@ class CharterSelectionScreen extends EditorTreeMenuScreen {
|
||||
|
||||
// Add to List
|
||||
curSong.difficulties.push(name);
|
||||
parent.tree.last().insert(parent.tree.last().length - 1, makeChartOption(name, curSong.name));
|
||||
|
||||
var screen = parent.tree.last();
|
||||
var idx = 0;
|
||||
while (!(screen.members[idx] is Separator)) idx++;
|
||||
screen.insert(idx, makeChartOption(name, curSong.variant != null && curSong.variant != "" ? curSong.variant : null, curSong.name));
|
||||
|
||||
// Add to Meta
|
||||
var meta = Json.parse(sys.io.File.getContent('$songFolder/meta.json'));
|
||||
if (meta.difficulties != null && !meta.difficulties.contains(name)) {
|
||||
if (meta.difficulties == null) meta.difficulties = [];
|
||||
if (!meta.difficulties.contains(name)) {
|
||||
meta.difficulties.push(name);
|
||||
CoolUtil.safeSaveFile('$songFolder/meta.json', Chart.makeMetaSaveable(meta));
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ typedef SongCreationData = {
|
||||
}
|
||||
|
||||
class SongCreationScreen extends UISubstateWindow {
|
||||
private var onSave:Null<SongCreationData> -> Null<String -> SongCreationData -> Void> -> Void = null;
|
||||
private var onSave:Null<SongCreationData> -> Null<String -> Void> -> Void = null;
|
||||
|
||||
public var songNameTextBox:UITextBox;
|
||||
public var bpmStepper:UINumericStepper;
|
||||
@@ -72,7 +72,7 @@ class SongCreationScreen extends UISubstateWindow {
|
||||
|
||||
public var curPage:Int = 0;
|
||||
|
||||
public function new(?onSave:SongCreationData -> Null<String -> SongCreationData -> Void> -> Void) {
|
||||
public function new(?onSave:SongCreationData -> Null<String -> Void> -> Void) {
|
||||
super();
|
||||
if (onSave != null) this.onSave = onSave;
|
||||
}
|
||||
@@ -356,6 +356,12 @@ class SongCreationScreen extends UISubstateWindow {
|
||||
iconSprite.scrollFactor.set(1, 1);
|
||||
}
|
||||
|
||||
// for variations
|
||||
function formatMeta(meta:ChartMetaData):ChartMetaData return meta;
|
||||
|
||||
function getChartSavePath(meta:ChartMetaData, diff:String):String
|
||||
return 'charts/${meta.variant != null && meta.variant != "" ? meta.variant + "/" : ""}$diff.json';
|
||||
|
||||
function saveSongInfo() {
|
||||
if (isImporting)
|
||||
{
|
||||
@@ -390,20 +396,21 @@ class SongCreationScreen extends UISubstateWindow {
|
||||
codenameChart: true
|
||||
};
|
||||
PsychParser.parse(oldChart, base);
|
||||
var meta = formatMeta({
|
||||
name: songId,
|
||||
difficulties: ['normal'],
|
||||
bpm: oldChart.bpm,
|
||||
beatsPerMeasure: oldChart.beatsPerMeasure,
|
||||
stepsPerBeat: oldChart.stepsPerBeat,
|
||||
displayName: oldChart.song
|
||||
});
|
||||
if (onSave != null) onSave({
|
||||
meta: {
|
||||
name: songId,
|
||||
difficulties: [songId],
|
||||
bpm: oldChart.bpm,
|
||||
beatsPerMeasure: oldChart.beatsPerMeasure,
|
||||
stepsPerBeat: oldChart.stepsPerBeat,
|
||||
displayName: oldChart.song
|
||||
},
|
||||
meta: meta,
|
||||
instBytes: importInstExplorer.file,
|
||||
voicesBytes: importVoicesExplorer.file,
|
||||
}, (songFolder:String, creation:SongCreationData) -> {
|
||||
}, (songFolder:String) -> {
|
||||
#if sys
|
||||
CoolUtil.safeSaveFile('$songFolder/charts/${songId}.json', Json.stringify(base, Flags.JSON_PRETTY_PRINT));
|
||||
CoolUtil.safeSaveFile('$songFolder/${getChartSavePath(meta, "normal")}', Json.stringify(base, Flags.JSON_PRETTY_PRINT));
|
||||
#end
|
||||
});
|
||||
} catch (e:haxe.Exception) {
|
||||
@@ -415,7 +422,7 @@ class SongCreationScreen extends UISubstateWindow {
|
||||
for (stepper in [bpmStepper, beatsPerMeasureStepper, denominatorStepper])
|
||||
@:privateAccess stepper.__onChange(stepper.label.text);
|
||||
|
||||
var meta:ChartMetaData = {
|
||||
var meta:ChartMetaData = formatMeta({
|
||||
name: songNameTextBox.label.text,
|
||||
bpm: bpmStepper.value,
|
||||
beatsPerMeasure: Std.int(beatsPerMeasureStepper.value),
|
||||
@@ -426,7 +433,7 @@ class SongCreationScreen extends UISubstateWindow {
|
||||
opponentModeAllowed: opponentModeCheckbox.checked,
|
||||
coopAllowed: coopAllowedCheckbox.checked,
|
||||
difficulties: [for (diff in difficultiesTextBox.label.text.split(",")) diff.trim()],
|
||||
};
|
||||
});
|
||||
|
||||
if (onSave != null) onSave({
|
||||
meta: meta,
|
||||
@@ -445,6 +452,7 @@ class SongCreationScreen extends UISubstateWindow {
|
||||
var meta:ChartMetaData = {name: songId};
|
||||
var diffCharts:Array<ChartDataWithInfo> = [];
|
||||
VSliceParser.parse(vslicemeta, vslicechart, meta, diffCharts, songId);
|
||||
formatMeta(meta);
|
||||
|
||||
if (onSave != null) onSave({
|
||||
meta: meta,
|
||||
@@ -452,9 +460,9 @@ class SongCreationScreen extends UISubstateWindow {
|
||||
voicesBytes: files.get('Voices.${Flags.SOUND_EXT}'), //it may exist
|
||||
playerVocals: files.get('Voices-${playData.characters.playerVocals != null ? playData.characters.playerVocals[0] : playData.characters.player}.${Flags.SOUND_EXT}'),
|
||||
oppVocals: files.get('Voices-${playData.characters.opponentVocals != null ? playData.characters.opponentVocals[0] : playData.characters.opponent}.${Flags.SOUND_EXT}'),
|
||||
}, (songFolder:String, creation:SongCreationData) -> {
|
||||
}, (songFolder:String) -> {
|
||||
#if sys
|
||||
for (diff in diffCharts) CoolUtil.safeSaveFile('$songFolder/charts/${diff.diffName}.json', Json.stringify(diff.chart, Flags.JSON_PRETTY_PRINT));
|
||||
for (diff in diffCharts) CoolUtil.safeSaveFile('$songFolder/${getChartSavePath(meta, diff.diffName)}', Json.stringify(diff.chart, Flags.JSON_PRETTY_PRINT));
|
||||
#end
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package funkin.editors.charter;
|
||||
|
||||
import flixel.text.FlxText.FlxTextFormat;
|
||||
import flixel.text.FlxText.FlxTextFormatMarkerPair;
|
||||
import funkin.backend.chart.ChartData;
|
||||
import funkin.editors.charter.SongCreationScreen;
|
||||
|
||||
class VariationCreationScreen extends SongCreationScreen {
|
||||
public var parentMeta:ChartMetaData;
|
||||
|
||||
public function new(parent:ChartMetaData, ?onSave:SongCreationData -> Null<String -> Void> -> Void) {
|
||||
super(onSave);
|
||||
this.parentMeta = parent;
|
||||
}
|
||||
|
||||
override function create() {
|
||||
super.create();
|
||||
|
||||
songNameTextBox.label.text = translateMeta("variation");
|
||||
cast(songNameTextBox.members[songNameTextBox.members.length - 1], UIText).text = translateMeta("variation");
|
||||
|
||||
cast(importIdTextBox.members[importIdTextBox.members.length - 1], UIText).applyMarkup(
|
||||
translate("variation"),
|
||||
[new FlxTextFormatMarkerPair(new FlxTextFormat(0xFFAD1212), "$")]
|
||||
);
|
||||
|
||||
bpmStepper.value = parentMeta.bpm;
|
||||
beatsPerMeasureStepper.value = parentMeta.beatsPerMeasure;
|
||||
denominatorStepper.value = Std.int(16 / parentMeta.stepsPerBeat);
|
||||
displayNameTextBox.label.text = parentMeta.displayName;
|
||||
iconTextBox.label.text = parentMeta.icon;
|
||||
opponentModeCheckbox.checked = parentMeta.opponentModeAllowed;
|
||||
coopAllowedCheckbox.checked = parentMeta.coopAllowed;
|
||||
colorWheel.setColor(parentMeta.color);
|
||||
difficultiesTextBox.label.text = parentMeta.difficulties.join(', ');
|
||||
}
|
||||
|
||||
override function formatMeta(meta:ChartMetaData):ChartMetaData {
|
||||
meta.variant = meta.name;
|
||||
meta.vocalsSuffix = meta.instSuffix = '-${meta.variant}';
|
||||
meta.name = parentMeta.name;
|
||||
return meta;
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,6 @@ class UIColorwheel extends UISliceSprite {
|
||||
hue = curColor.hue; saturation = curColor.saturation; brightness = curColor.brightness;
|
||||
|
||||
colorPickerShader = new CustomShader("engine/colorPicker");
|
||||
colorPickerShader.hset("hue", hue);
|
||||
|
||||
colorPicker = new FlxSprite(x + 12.5, (y + 125/2) - (100/2)).makeGraphic(100, 100, FlxColor.TRANSPARENT);
|
||||
colorPicker.drawRoundRect(0, 0, 100, 100, 10, 10, FlxColor.WHITE);
|
||||
@@ -91,6 +90,11 @@ class UIColorwheel extends UISliceSprite {
|
||||
updateWheel(false);
|
||||
}
|
||||
|
||||
public function setColor(color:FlxColor) {
|
||||
hue = curColor.hue; saturation = curColor.saturation; brightness = curColor.brightness;
|
||||
updateWheel(false);
|
||||
}
|
||||
|
||||
inline function updateColorPickerSelector()
|
||||
colorPickerSelector.selector.setPosition(colorPicker.x + (colorPicker.width *saturation) - 8, colorPicker.y + (colorPicker.height + (colorPicker.height * -brightness))- 8);
|
||||
|
||||
|
||||
@@ -60,11 +60,15 @@ class PlayState extends MusicBeatState
|
||||
/**
|
||||
* The remaining songs in the Story Mode playlist.
|
||||
*/
|
||||
public static var storyPlaylist:Array<String> = [];
|
||||
public static var storyPlaylist:Array<WeekSong> = [];
|
||||
/**
|
||||
* The selected difficulty name.
|
||||
*/
|
||||
public static var difficulty:String = Flags.DEFAULT_DIFFICULTY;
|
||||
/**
|
||||
* The selected variation name. (It can be null)
|
||||
*/
|
||||
public static var variation:Null<String>;
|
||||
/**
|
||||
* Whenever the week is coming from the mods folder or not.
|
||||
*/
|
||||
@@ -657,7 +661,7 @@ class PlayState extends MusicBeatState
|
||||
persistentDraw = true;
|
||||
|
||||
if (SONG == null)
|
||||
SONG = Chart.parse('tutorial', 'normal');
|
||||
SONG = Chart.parse('tutorial', difficulty = 'normal', variation = null);
|
||||
|
||||
scrollSpeed = SONG.scrollSpeed;
|
||||
|
||||
@@ -1352,7 +1356,7 @@ class PlayState extends MusicBeatState
|
||||
updateRatingStuff();
|
||||
|
||||
if (canAccessDebugMenus && chartingMode && controls.DEV_ACCESS)
|
||||
FlxG.switchState(new funkin.editors.charter.Charter(SONG.meta.name, difficulty, false));
|
||||
FlxG.switchState(new funkin.editors.charter.Charter(SONG.meta.name, difficulty, variation, false));
|
||||
|
||||
if (Options.camZoomOnBeat && camZooming) {
|
||||
var beat = Conductor.getBeats(camZoomingEvery, camZoomingInterval, camZoomingOffset);
|
||||
@@ -1697,7 +1701,7 @@ class PlayState extends MusicBeatState
|
||||
|
||||
if (validScore) {
|
||||
#if !switch
|
||||
FunkinSave.setSongHighscore(SONG.meta.name, difficulty, {
|
||||
FunkinSave.setSongHighscore(SONG.meta.name, difficulty, variation, {
|
||||
score: songScore,
|
||||
misses: misses,
|
||||
accuracy: accuracy,
|
||||
@@ -1748,16 +1752,16 @@ class PlayState extends MusicBeatState
|
||||
FlxG.save.flush();
|
||||
}
|
||||
else {
|
||||
Logs.infos('Loading next song (${storyPlaylist[0].toLowerCase()}/$difficulty)', "PlayState");
|
||||
Logs.infos('Loading next song (${storyPlaylist[0].name.toLowerCase()}/$difficulty/${storyPlaylist[0].variation})', "PlayState");
|
||||
|
||||
registerSmoothTransition();
|
||||
|
||||
__loadSong(storyPlaylist[0], difficulty);
|
||||
__loadSong(storyPlaylist[0].name, difficulty, variation);
|
||||
FlxG.switchState(new PlayState());
|
||||
}
|
||||
}
|
||||
else if (chartingMode)
|
||||
FlxG.switchState(new funkin.editors.charter.Charter(SONG.meta.name, difficulty, false));
|
||||
FlxG.switchState(new funkin.editors.charter.Charter(SONG.meta.name, difficulty, variation, false));
|
||||
else
|
||||
FlxG.switchState(new FreeplayState());
|
||||
}
|
||||
@@ -2131,44 +2135,51 @@ class PlayState extends MusicBeatState
|
||||
public static function loadWeek(weekData:WeekData, ?difficulty:String) {
|
||||
if (difficulty == null) difficulty = Flags.DEFAULT_DIFFICULTY;
|
||||
storyWeek = weekData;
|
||||
storyPlaylist = [for (e in weekData.songs) e.name];
|
||||
storyPlaylist = weekData.songs.copy();
|
||||
isStoryMode = true;
|
||||
campaignScore = 0;
|
||||
campaignMisses = 0;
|
||||
campaignAccuracyTotal = 0;
|
||||
campaignAccuracyCount = 0;
|
||||
chartingMode = false;
|
||||
opponentMode = coopMode = false;
|
||||
__loadSong(storyPlaylist[0], difficulty);
|
||||
chartingMode = coopMode = opponentMode = false;
|
||||
__loadSong(storyPlaylist[0].name, difficulty, storyPlaylist[0].variation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a song into PlayState
|
||||
* @param name Song name
|
||||
* @param difficulty Chart difficulty (if invalid, will load an empty chart)
|
||||
* @param variation Song Variation
|
||||
* @param opponentMode Whenever opponent mode is on
|
||||
* @param coopMode Whenever co-op mode is on.
|
||||
*/
|
||||
public static function loadSong(_name:String, ?_difficulty:String, _opponentMode:Bool = false, _coopMode:Bool = false) {
|
||||
public static function loadSong(_name:String, ?_difficulty:String, ?_variation:Dynamic, _opponentMode:Bool = false, _coopMode:Bool = false) {
|
||||
if (_difficulty == null) difficulty = Flags.DEFAULT_DIFFICULTY;
|
||||
isStoryMode = false;
|
||||
if (_variation is Bool) { // vackward cumpatibshit
|
||||
_coopMode = _opponentMode;
|
||||
_opponentMode = cast _variation;
|
||||
_variation = null;
|
||||
}
|
||||
else if (!(_variation is String)) _variation = null;
|
||||
opponentMode = _opponentMode;
|
||||
chartingMode = false;
|
||||
coopMode = _coopMode;
|
||||
__loadSong(_name, _difficulty);
|
||||
isStoryMode = chartingMode = false;
|
||||
__loadSong(_name, _difficulty, cast _variation);
|
||||
}
|
||||
|
||||
/**
|
||||
* (INTERNAL) Loads a song without resetting story mode/opponent mode/coop mode values.
|
||||
* @param name Song name
|
||||
* @param difficulty Song difficulty
|
||||
* @param difficulty Chart difficulty
|
||||
* @param variation Song Variation
|
||||
*/
|
||||
public static function __loadSong(_name:String, _difficulty:String) {
|
||||
public static function __loadSong(_name:String, _difficulty:String, _variation:String) {
|
||||
difficulty = _difficulty;
|
||||
variation = _variation;
|
||||
seenCutscene = false;
|
||||
deathCounter = 0;
|
||||
|
||||
SONG = Chart.parse(_name, _difficulty);
|
||||
SONG = Chart.parse(_name, _difficulty, _variation);
|
||||
fromMods = SONG.fromMods;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,9 +25,14 @@ class FreeplayState extends MusicBeatState
|
||||
public var curSong:Null<ChartMetaData>;
|
||||
|
||||
/**
|
||||
* Current song metadata difficulty
|
||||
* Current song difficulties
|
||||
*/
|
||||
public var curSongDifficulty:Null<String>;
|
||||
public var curDifficulties:Array<String>;
|
||||
|
||||
/**
|
||||
* songs[curSelected].metas.get(curDiffMetaIndices[curDifficulty])
|
||||
*/
|
||||
public var curDiffMetaKeys:Array<String> = [];
|
||||
|
||||
/**
|
||||
* Currently selected song
|
||||
@@ -117,10 +122,12 @@ class FreeplayState extends MusicBeatState
|
||||
curSelected = k;
|
||||
}
|
||||
}
|
||||
updateCurSongMeta();
|
||||
|
||||
updateCurDifficulties();
|
||||
updateCurSong();
|
||||
|
||||
if (curSong != null) {
|
||||
for(k=>diff in songs[curSelected].difficulties) {
|
||||
for(k=>diff in curDifficulties) {
|
||||
if (diff == Options.freeplayLastDifficulty) {
|
||||
curDifficulty = k;
|
||||
}
|
||||
@@ -257,7 +264,7 @@ class FreeplayState extends MusicBeatState
|
||||
var dontPlaySongThisFrame = false;
|
||||
autoplayElapsed += elapsed;
|
||||
if (!disableAutoPlay && !songInstPlaying && (autoplayElapsed > timeUntilAutoplay)) {
|
||||
if (curPlayingInst != (curPlayingInst = Paths.inst(curSong.name, curSongDifficulty, curSong.instSuffix))) {
|
||||
if (curPlayingInst != (curPlayingInst = Paths.inst(curSong.name, curDifficulties[curDifficulty], curSong.instSuffix))) {
|
||||
var streamed = false;
|
||||
if (Options.streamedMusic) {
|
||||
var sound = Assets.getMusic(curPlayingInst, true, false);
|
||||
@@ -330,9 +337,9 @@ class FreeplayState extends MusicBeatState
|
||||
public function select() {
|
||||
updateCoopModes();
|
||||
|
||||
if (songs[curSelected].difficulties.length <= 0) return;
|
||||
if (curDifficulties.length == 0) return;
|
||||
|
||||
var event = event("onSelect", EventManager.get(FreeplaySongSelectEvent).recycle(curSong.name, curSongDifficulty, __opponentMode, __coopMode));
|
||||
var event = event("onSelect", EventManager.get(FreeplaySongSelectEvent).recycle(curSong.name, curDifficulties[curDifficulty], curSong.variant, __opponentMode, __coopMode));
|
||||
|
||||
if (event.cancelled) return;
|
||||
|
||||
@@ -341,16 +348,17 @@ class FreeplayState extends MusicBeatState
|
||||
#end
|
||||
|
||||
Options.freeplayLastSong = curSong.name;
|
||||
Options.freeplayLastDifficulty = curSongDifficulty;
|
||||
Options.freeplayLastDifficulty = curDifficulties[curDifficulty];
|
||||
Options.freeplayLastVariation = curSong.variant;
|
||||
|
||||
PlayState.loadSong(event.song, event.difficulty, event.opponentMode, event.coopMode);
|
||||
PlayState.loadSong(event.song, event.difficulty, event.variant, event.opponentMode, event.coopMode);
|
||||
FlxG.switchState(new PlayState());
|
||||
}
|
||||
|
||||
public function convertChart() {
|
||||
trace('Converting ${curSong.name} (${curSongDifficulty}) to Codename format...');
|
||||
var chart = Chart.parse(curSong.name, curSongDifficulty);
|
||||
Chart.save('${Main.pathBack}assets/songs/${curSong.name}', chart, curSongDifficulty);
|
||||
trace('Converting ${curSong.name} ${curDifficulties[curDifficulty]} ${curSong.variant} to Codename format...');
|
||||
var chart = Chart.parse(curSong.name, curDifficulties[curDifficulty], curSong.variant);
|
||||
Chart.save(chart, curDifficulties[curDifficulty], curSong.variant);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -358,39 +366,35 @@ class FreeplayState extends MusicBeatState
|
||||
* @param change How much to change.
|
||||
* @param force Force the change if `change` is equal to 0
|
||||
*/
|
||||
public function changeDiff(change:Int = 0, force:Bool = false)
|
||||
{
|
||||
public function changeDiff(change:Int = 0, force:Bool = false) {
|
||||
if (change == 0 && !force) return;
|
||||
|
||||
var song = songs[curSelected];
|
||||
var validDifficulties = song.difficulties.length > 0;
|
||||
var event = event("onChangeDiff", EventManager.get(MenuChangeEvent).recycle(curDifficulty, validDifficulties ? FlxMath.wrap(curDifficulty + change, 0, song.difficulties.length-1) : 0, change));
|
||||
var validDifficulties = curDifficulties.length > 0;
|
||||
var event = event("onChangeDiff", EventManager.get(MenuChangeEvent).recycle(curDifficulty, validDifficulties ? FlxMath.wrap(curDifficulty + change, 0, curDifficulties.length-1) : 0, change));
|
||||
|
||||
if (event.cancelled) {
|
||||
if (force) updateCurSongMeta();
|
||||
if (force) updateCurSong();
|
||||
return;
|
||||
}
|
||||
|
||||
var prevInstSuffix = curSong.instSuffix;
|
||||
var prevSong = curSong;
|
||||
curDifficulty = event.value;
|
||||
updateCurSongMeta();
|
||||
updateCurSong();
|
||||
updateScore();
|
||||
|
||||
#if PRELOAD_ALL
|
||||
if (curSong.instSuffix != prevInstSuffix) {
|
||||
if (curSong != prevSong) {
|
||||
autoplayElapsed = 0;
|
||||
songInstPlaying = false;
|
||||
}
|
||||
#end
|
||||
|
||||
if (song.difficulties.length > 1)
|
||||
diffText.text = '< ${song.difficulties[curDifficulty].toUpperCase()} >';
|
||||
else
|
||||
diffText.text = validDifficulties ? song.difficulties[curDifficulty].toUpperCase() : "-";
|
||||
var text = validDifficulties ? curDifficulties[curDifficulty].toUpperCase() + (curSong != songs[curSelected] ? ' (${curSong.variant.toUpperCase()})' : '') : '-';
|
||||
diffText.text = curDifficulties.length > 1 ? '< $text >' : text;
|
||||
}
|
||||
|
||||
function updateScore() {
|
||||
if (songs[curSelected].difficulties.length <= 0) {
|
||||
if (curDifficulties.length == 0) {
|
||||
intendedScore = 0;
|
||||
return;
|
||||
}
|
||||
@@ -398,7 +402,7 @@ class FreeplayState extends MusicBeatState
|
||||
var changes:Array<HighscoreChange> = [];
|
||||
if (__coopMode) changes.push(CCoopMode);
|
||||
if (__opponentMode) changes.push(COpponentMode);
|
||||
var saveData = FunkinSave.getSongHighscore(curSong.name, curSongDifficulty, changes);
|
||||
var saveData = FunkinSave.getSongHighscore(curSong.name, curDifficulties[curDifficulty], curSong.variant, changes);
|
||||
intendedScore = saveData.score;
|
||||
}
|
||||
|
||||
@@ -448,8 +452,7 @@ class FreeplayState extends MusicBeatState
|
||||
* @param change How much to change
|
||||
* @param force Force the change, even if `change` is equal to 0.
|
||||
*/
|
||||
public function changeSelection(change:Int = 0, force:Bool = false)
|
||||
{
|
||||
public function changeSelection(change:Int = 0, force:Bool = false) {
|
||||
if (change == 0 && !force) return;
|
||||
|
||||
var event = event("onChangeSelection", EventManager.get(MenuChangeEvent).recycle(curSelected, FlxMath.wrap(curSelected + change, 0, songs.length-1), change));
|
||||
@@ -458,6 +461,14 @@ class FreeplayState extends MusicBeatState
|
||||
curSelected = event.value;
|
||||
if (event.playMenuSFX) CoolUtil.playMenuSFX(SCROLL, 0.7);
|
||||
|
||||
var prevDiff = curDifficulties[curDifficulty], prevVariant = curDiffMetaKeys[curDifficulty];
|
||||
updateCurDifficulties();
|
||||
|
||||
for (i => diff in curDifficulties) if (diff == prevDiff && curDiffMetaKeys[i] == prevVariant) {
|
||||
curDifficulty = i;
|
||||
break;
|
||||
}
|
||||
|
||||
changeDiff(0, true);
|
||||
|
||||
#if PRELOAD_ALL
|
||||
@@ -491,13 +502,22 @@ class FreeplayState extends MusicBeatState
|
||||
}
|
||||
}
|
||||
|
||||
function updateCurSongMeta() {
|
||||
var song = songs[curSelected];
|
||||
if (song == null) {
|
||||
curSong = null;
|
||||
curSongDifficulty = null;
|
||||
function updateCurDifficulties() {
|
||||
curDiffMetaKeys.resize(0);
|
||||
curDifficulties = songs[curSelected].difficulties.copy();
|
||||
for (i in 0...curDifficulties.length) curDiffMetaKeys.push(null);
|
||||
|
||||
var meta:ChartMetaData;
|
||||
for (variant in songs[curSelected].variants) if ((meta = songs[curSelected].metas.get(variant)) != null) {
|
||||
curDifficulties = curDifficulties.concat(meta.difficulties);
|
||||
for (i in 0...meta.difficulties.length) curDiffMetaKeys.push(variant);
|
||||
}
|
||||
else if ((curSong = song.metas.get(curSongDifficulty = song.difficulties[curDifficulty])) == null)
|
||||
}
|
||||
|
||||
function updateCurSong() {
|
||||
var song = songs[curSelected];
|
||||
if (song == null) curSong = null;
|
||||
else if ((curSong = song.metas.get(curDiffMetaKeys[curDifficulty])) == null)
|
||||
curSong = song;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,7 +167,7 @@ class PauseSubState extends MusicBeatSubstate
|
||||
case "Change Options":
|
||||
FlxG.switchState(new OptionsMenu((_) -> FlxG.switchState(new PlayState())));
|
||||
case "Exit to charter":
|
||||
FlxG.switchState(new Charter(PlayState.SONG.meta.name, PlayState.difficulty, false));
|
||||
FlxG.switchState(new Charter(PlayState.SONG.meta.name, PlayState.difficulty, PlayState.variation, false));
|
||||
case "Exit to menu":
|
||||
if (PlayState.chartingMode && Charter.undos.unsaved)
|
||||
game.saveWarn(false);
|
||||
|
||||
@@ -73,6 +73,7 @@ class Options
|
||||
*/
|
||||
public static var freeplayLastSong:String = null;
|
||||
public static var freeplayLastDifficulty:String = "normal";
|
||||
public static var freeplayLastVariation:String = null;
|
||||
public static var contributors:Array<funkin.backend.system.github.GitHubContributor.CreditsGitHubContributor> = [];
|
||||
public static var mainDevs:Array<Int> = []; // IDs
|
||||
public static var lastUpdated:Null<Float>;
|
||||
|
||||
@@ -128,11 +128,12 @@ class TreeMenuScreen extends FlxSpriteGroup {
|
||||
var i = curSelected, y = initY, object:FlxSprite = null, itemHeight:Float = 0;
|
||||
|
||||
inline function updateItem() {
|
||||
object.y = CoolUtil.fpsLerp(object.y, y - (itemHeight = object.height) * 0.5, r);
|
||||
object.y = CoolUtil.fpsLerp(object.y, y - itemHeight * 0.5, r);
|
||||
object.x = x + 100 - Math.pow(Math.abs((object.y - (FlxG.height - itemHeight) * 0.5) / itemHeight / FlxG.height * FlxG.initialHeight), 1.6) * 15;
|
||||
}
|
||||
|
||||
while (i < length) if ((object = members[i++]) != null) {
|
||||
itemHeight = object.height;
|
||||
updateItem();
|
||||
y += itemHeight;
|
||||
}
|
||||
@@ -140,7 +141,7 @@ class TreeMenuScreen extends FlxSpriteGroup {
|
||||
y = initY;
|
||||
i = curSelected;
|
||||
while (i-- > 0) if ((object = members[i]) != null) {
|
||||
y -= itemHeight;
|
||||
y -= (itemHeight = object.height);
|
||||
updateItem();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ class NewOption extends TextOption {
|
||||
|
||||
public function new(name:String, desc:String, callback:Void->Void) {
|
||||
super(name, desc, callback);
|
||||
itemHeight = 160;
|
||||
itemHeight = 150;
|
||||
|
||||
__text.color = FlxColor.LIME;
|
||||
__text.x = 100;
|
||||
|
||||
@@ -3,25 +3,24 @@ package funkin.savedata;
|
||||
import flixel.util.FlxSave;
|
||||
import lime.app.Application;
|
||||
import openfl.Lib;
|
||||
import haxe.Serializer;
|
||||
import haxe.Unserializer;
|
||||
|
||||
/**
|
||||
* Class used for saves WITHOUT going through the struggle of type checks
|
||||
* Just add your save variables the way you would do in the Options.hx file.
|
||||
* The macro will automatically generate the `flush` and `load` functions.
|
||||
*/
|
||||
@:build(funkin.backend.system.macros.FunkinSaveMacro.build("save", "flush", "load"))
|
||||
@:build(funkin.backend.system.macros.FunkinSaveMacro.build("save", "__flush", "__load"))
|
||||
class FunkinSave {
|
||||
public static var highscores:Map<HighscoreEntry, SongScore> = [];
|
||||
|
||||
@:doNotSave public static var highscores:Map<HighscoreEntry, SongScore> = [];
|
||||
|
||||
/**
|
||||
* ONLY OPEN IF YOU WANT TO EDIT FUNCTIONS RELATED TO SAVING, LOADING OR HIGHSCORES.
|
||||
*/
|
||||
#if REGION
|
||||
@:dox(hide) @:doNotSave
|
||||
private static var __eventAdded = false;
|
||||
@:doNotSave
|
||||
public static var save:FlxSave;
|
||||
@:dox(hide) @:doNotSave private static var __eventAdded = false;
|
||||
@:doNotSave public static var save:FlxSave;
|
||||
|
||||
public static function init() {
|
||||
var path = Flags.SAVE_PATH, name = Flags.SAVE_NAME;
|
||||
@@ -46,20 +45,64 @@ class FunkinSave {
|
||||
}
|
||||
}
|
||||
|
||||
public static function load() {
|
||||
__load();
|
||||
if (save.data.highscores != null) {
|
||||
var temp;
|
||||
for (entryData in Reflect.fields(save.data.highscores))
|
||||
if ((temp = __getHighscoreEntry(entryData)) != null && Reflect.field(save.data.highscores, entryData) != null)
|
||||
highscores.set(temp, Reflect.field(save.data.highscores, entryData));
|
||||
}
|
||||
}
|
||||
|
||||
public static function flush() {
|
||||
__flush();
|
||||
if (save.data.highscores == null) save.data.highscores = {};
|
||||
for (entry => score in highscores) Reflect.setField(save.data.highscores, __formatHighscoreEntry(entry), score);
|
||||
}
|
||||
|
||||
static function __getHighscoreEntry(data:String):HighscoreEntry {
|
||||
try {
|
||||
var d = Unserializer.run(data);
|
||||
if (d.song is String)
|
||||
return HSongEntry(d.song, d.diff, d.variation, d.changes);
|
||||
else if (d.week is String)
|
||||
return HWeekEntry(d.week, d.diff);
|
||||
}
|
||||
catch (e) {}
|
||||
return null;
|
||||
}
|
||||
|
||||
static function __formatHighscoreEntry(entry:HighscoreEntry):String {
|
||||
switch (entry) {
|
||||
case HWeekEntry(weekName, difficulty):
|
||||
return Serializer.run({week: weekName, diff: difficulty});
|
||||
case HSongEntry(songName, difficulty, variation, changes):
|
||||
var d:Dynamic = {
|
||||
song: songName,
|
||||
diff: difficulty,
|
||||
changes: changes
|
||||
};
|
||||
if (variation != null && variation != '') d.variation = variation;
|
||||
return Serializer.run(d);
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the high-score for a song.
|
||||
* @param name Song name
|
||||
* @param diff Song difficulty
|
||||
* @param changes Changes made to that song in freeplay.
|
||||
*/
|
||||
public static inline function getSongHighscore(name:String, diff:String, ?changes:Array<HighscoreChange>) {
|
||||
public static inline function getSongHighscore(name:String, diff:String, ?variation:String, ?changes:Array<HighscoreChange>) {
|
||||
if (changes == null) changes = [];
|
||||
return safeGetHighscore(HSongEntry(name.toLowerCase(), diff.toLowerCase(), changes));
|
||||
return safeGetHighscore(HSongEntry(name.toLowerCase(), diff.toLowerCase(), variation, changes));
|
||||
}
|
||||
|
||||
public static inline function setSongHighscore(name:String, diff:String, highscore:SongScore, ?changes:Array<HighscoreChange>) {
|
||||
public static inline function setSongHighscore(name:String, diff:String, ?variation:String, highscore:SongScore, ?changes:Array<HighscoreChange>) {
|
||||
if (changes == null) changes = [];
|
||||
if (safeRegisterHighscore(HSongEntry(name.toLowerCase(), diff.toLowerCase(), changes), highscore)) {
|
||||
if (safeRegisterHighscore(HSongEntry(name.toLowerCase(), diff.toLowerCase(), variation, changes), highscore)) {
|
||||
flush();
|
||||
return true;
|
||||
}
|
||||
@@ -103,7 +146,7 @@ class FunkinSave {
|
||||
|
||||
enum HighscoreEntry {
|
||||
HWeekEntry(weekName:String, difficulty:String);
|
||||
HSongEntry(songName:String, difficulty:String, changes:Array<HighscoreChange>);
|
||||
HSongEntry(songName:String, difficulty:String, variation:Null<String>, changes:Array<HighscoreChange>);
|
||||
}
|
||||
|
||||
enum HighscoreChange {
|
||||
|
||||
Reference in New Issue
Block a user