mirror of
https://github.com/ninjamuffin99/Funkin.git
synced 2025-08-31 02:45:13 +00:00
Compare commits
20 commits
4ef0acb138
...
e6b5551a6a
Author | SHA1 | Date | |
---|---|---|---|
|
e6b5551a6a | ||
|
d0f0974d1e | ||
|
369b936056 | ||
|
c40e1e3958 | ||
|
0ade24bac4 | ||
|
7370ef3249 | ||
|
b2f162a8ae | ||
|
11fa9b4050 | ||
|
89bd7a3f43 | ||
|
942fb5efc9 | ||
|
c2eff142bd | ||
|
2784fa18c0 | ||
|
ad07fddf89 | ||
|
969f36a1cb | ||
|
4768eedd5b | ||
|
f1c3e99a11 | ||
|
eefe8927c4 | ||
|
3ff4f14510 | ||
|
955b0db542 | ||
|
d2df4f0832 |
2
.github/ISSUE_TEMPLATE/bug.yml
vendored
2
.github/ISSUE_TEMPLATE/bug.yml
vendored
|
@ -59,7 +59,7 @@ body:
|
|||
attributes:
|
||||
label: Version
|
||||
description: Which version are you playing on? The game version is in the bottom left corner of the main menu.
|
||||
placeholder: ex. 0.7.3
|
||||
placeholder: ex. 0.7.4
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
|
2
.github/ISSUE_TEMPLATE/charting.yml
vendored
2
.github/ISSUE_TEMPLATE/charting.yml
vendored
|
@ -36,7 +36,7 @@ body:
|
|||
attributes:
|
||||
label: Version
|
||||
description: Which version are you playing on? The game version is in the bottom left corner of the main menu.
|
||||
placeholder: ex. 0.7.3
|
||||
placeholder: ex. 0.7.4
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
|
2
.github/ISSUE_TEMPLATE/compiling.yml
vendored
2
.github/ISSUE_TEMPLATE/compiling.yml
vendored
|
@ -36,7 +36,7 @@ body:
|
|||
attributes:
|
||||
label: Version
|
||||
description: Which version are you compiling? The game version is in the bottom left corner of the main menu or in the project.hxp file.
|
||||
placeholder: ex. 0.7.3
|
||||
placeholder: ex. 0.7.4
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
|
2
.github/ISSUE_TEMPLATE/crash.yml
vendored
2
.github/ISSUE_TEMPLATE/crash.yml
vendored
|
@ -60,7 +60,7 @@ body:
|
|||
attributes:
|
||||
label: Version
|
||||
description: Which version are you playing on? The game version is in the bottom left corner of the main menu.
|
||||
placeholder: ex. 0.7.3
|
||||
placeholder: ex. 0.7.4
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
|
|
@ -60,7 +60,7 @@ This section provides guidelines to follow when [opening an issue](https://githu
|
|||
|
||||
## Requirements
|
||||
Make sure you're playing:
|
||||
- the latest version of the game (currently v0.7.3)
|
||||
- the latest version of the game (currently v0.7.4)
|
||||
- without any mods
|
||||
- on [Newgrounds](https://www.newgrounds.com/portal/view/770371) or downloaded from [itch.io](https://ninja-muffin24.itch.io/funkin)
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
"name": "EliteMasterEric"
|
||||
}
|
||||
],
|
||||
"api_version": "0.5.0",
|
||||
"api_version": "0.7.0",
|
||||
"mod_version": "1.0.0",
|
||||
"license": "Apache-2.0"
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
"name": "EliteMasterEric"
|
||||
}
|
||||
],
|
||||
"api_version": "0.5.0",
|
||||
"api_version": "0.7.0",
|
||||
"mod_version": "1.0.0",
|
||||
"license": "Apache-2.0"
|
||||
}
|
||||
|
|
156
source/funkin/api/newgrounds/NGSaveSlot.hx
Normal file
156
source/funkin/api/newgrounds/NGSaveSlot.hx
Normal file
|
@ -0,0 +1,156 @@
|
|||
package funkin.api.newgrounds;
|
||||
|
||||
#if FEATURE_NEWGROUNDS
|
||||
import io.newgrounds.utils.SaveSlotList;
|
||||
import io.newgrounds.objects.SaveSlot;
|
||||
import io.newgrounds.Call.CallError;
|
||||
import io.newgrounds.objects.events.Outcome;
|
||||
import funkin.save.Save;
|
||||
|
||||
@:nullSafety
|
||||
@:access(funkin.save.Save)
|
||||
class NGSaveSlot
|
||||
{
|
||||
public static var instance(get, never):NGSaveSlot;
|
||||
static var _instance:Null<NGSaveSlot> = null;
|
||||
|
||||
static function get_instance():NGSaveSlot
|
||||
{
|
||||
if (_instance == null)
|
||||
{
|
||||
return loadInstance();
|
||||
}
|
||||
return _instance;
|
||||
}
|
||||
|
||||
public static function loadInstance():NGSaveSlot
|
||||
{
|
||||
var loadedSave:NGSaveSlot = loadSlot(Save.BASE_SAVE_SLOT);
|
||||
if (_instance == null) _instance = loadedSave;
|
||||
|
||||
return loadedSave;
|
||||
}
|
||||
|
||||
static function loadSlot(slot:Int):NGSaveSlot
|
||||
{
|
||||
trace('[NEWGROUNDS] Getting save slot from ID $slot');
|
||||
|
||||
var saveSlot:Null<SaveSlot> = NewgroundsClient.instance.saveSlots?.getById(slot);
|
||||
|
||||
var saveSlotObj:NGSaveSlot = new NGSaveSlot(saveSlot);
|
||||
return saveSlotObj;
|
||||
}
|
||||
|
||||
public var ngSaveSlot:Null<SaveSlot> = null;
|
||||
|
||||
public function new(?ngSaveSlot:Null<SaveSlot>)
|
||||
{
|
||||
this.ngSaveSlot = ngSaveSlot;
|
||||
|
||||
#if FLX_DEBUG
|
||||
FlxG.console.registerClass(NGSaveSlot);
|
||||
FlxG.console.registerClass(Save);
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves `data` to the newgrounds save slot.
|
||||
* @param data The raw save data.
|
||||
*/
|
||||
public function save(data:RawSaveData):Void
|
||||
{
|
||||
var encodedData:String = haxe.Serializer.run(data);
|
||||
|
||||
try
|
||||
{
|
||||
ngSaveSlot?.save(encodedData, function(outcome:Outcome<CallError>) {
|
||||
switch (outcome)
|
||||
{
|
||||
case SUCCESS:
|
||||
trace('[NEWGROUNDS] Successfully saved save data to save slot!');
|
||||
case FAIL(error):
|
||||
trace('[NEWGROUNDS] Failed to save data to save slot!');
|
||||
trace(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (error:String)
|
||||
{
|
||||
trace('[NEWGROUNDS] Failed to save data to save slot!');
|
||||
trace(error);
|
||||
}
|
||||
}
|
||||
|
||||
public function load(?onComplete:Null<Dynamic->Void>, ?onError:Null<CallError->Void>):Void
|
||||
{
|
||||
try
|
||||
{
|
||||
ngSaveSlot?.load(function(outcome:SaveSlotOutcome):Void {
|
||||
switch (outcome)
|
||||
{
|
||||
case SUCCESS(value):
|
||||
trace('[NEWGROUNDS] Loaded save slot with the ID of ${ngSaveSlot?.id}!');
|
||||
#if FEATURE_DEBUG_FUNCTIONS
|
||||
trace('Save Slot Data:');
|
||||
trace(value);
|
||||
#end
|
||||
|
||||
if (onComplete != null && value != null)
|
||||
{
|
||||
var decodedData:Dynamic = haxe.Unserializer.run(value);
|
||||
onComplete(decodedData);
|
||||
}
|
||||
case FAIL(error):
|
||||
trace('[NEWGROUNDS] Failed to load save slot with the ID of ${ngSaveSlot?.id}!');
|
||||
trace(error);
|
||||
|
||||
if (onError != null)
|
||||
{
|
||||
onError(error);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (error:String)
|
||||
{
|
||||
trace('[NEWGROUNDS] Failed to load save slot with the ID of ${ngSaveSlot?.id}!');
|
||||
trace(error);
|
||||
|
||||
if (onError != null)
|
||||
{
|
||||
onError(RESPONSE({message: error, code: 500}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function clear():Void
|
||||
{
|
||||
try
|
||||
{
|
||||
ngSaveSlot?.clear(function(outcome:Outcome<CallError>) {
|
||||
switch (outcome)
|
||||
{
|
||||
case SUCCESS:
|
||||
trace('[NEWGROUNDS] Successfully cleared save slot!');
|
||||
case FAIL(error):
|
||||
trace('[NEWGROUNDS] Failed to clear save slot!');
|
||||
trace(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (error:String)
|
||||
{
|
||||
trace('[NEWGROUNDS] Failed to clear save slot!');
|
||||
trace(error);
|
||||
}
|
||||
}
|
||||
|
||||
public function checkSlot():Void
|
||||
{
|
||||
trace('[NEWGROUNDS] Checking save slot with the ID of ${ngSaveSlot?.id}...');
|
||||
|
||||
trace(' Is null? ${ngSaveSlot == null}');
|
||||
trace(' Is empty? ${ngSaveSlot?.isEmpty() ?? false}');
|
||||
}
|
||||
}
|
||||
#end
|
|
@ -10,6 +10,7 @@ import io.newgrounds.NGLite.LoginOutcome;
|
|||
import io.newgrounds.NGLite.LoginFail;
|
||||
import io.newgrounds.objects.events.Outcome;
|
||||
import io.newgrounds.utils.MedalList;
|
||||
import io.newgrounds.utils.SaveSlotList;
|
||||
import io.newgrounds.utils.ScoreBoardList;
|
||||
import io.newgrounds.objects.User;
|
||||
|
||||
|
@ -29,6 +30,7 @@ class NewgroundsClient
|
|||
public var user(get, never):Null<User>;
|
||||
public var medals(get, never):Null<MedalList>;
|
||||
public var leaderboards(get, never):Null<ScoreBoardList>;
|
||||
public var saveSlots(get, never):Null<SaveSlotList>;
|
||||
|
||||
private function new()
|
||||
{
|
||||
|
@ -236,6 +238,8 @@ class NewgroundsClient
|
|||
|
||||
trace('[NEWGROUNDS] Submitting leaderboard request...');
|
||||
NG.core.scoreBoards.loadList(onFetchedLeaderboards);
|
||||
trace('[NEWGROUNDS] Submitting save slot request...');
|
||||
NG.core.saveSlots.loadList(onFetchedSaveSlots);
|
||||
}
|
||||
|
||||
function onLoginFailed(result:LoginFail):Void
|
||||
|
@ -301,6 +305,13 @@ class NewgroundsClient
|
|||
// trace(funkin.api.newgrounds.Leaderboards.listLeaderboardData());
|
||||
}
|
||||
|
||||
function onFetchedSaveSlots(outcome:Outcome<CallError>):Void
|
||||
{
|
||||
trace('[NEWGROUNDS] Fetched save slots!');
|
||||
|
||||
NGSaveSlot.instance.checkSlot();
|
||||
}
|
||||
|
||||
function get_user():Null<User>
|
||||
{
|
||||
if (NG.core == null || !this.isLoggedIn()) return null;
|
||||
|
@ -319,6 +330,12 @@ class NewgroundsClient
|
|||
return NG.core.scoreBoards;
|
||||
}
|
||||
|
||||
function get_saveSlots():Null<SaveSlotList>
|
||||
{
|
||||
if (NG.core == null || !this.isLoggedIn()) return null;
|
||||
return NG.core.saveSlots;
|
||||
}
|
||||
|
||||
static function getSessionId():Null<String>
|
||||
{
|
||||
#if js
|
||||
|
|
|
@ -52,7 +52,12 @@ import funkin.play.scoring.Scoring;
|
|||
import funkin.play.song.Song;
|
||||
import funkin.play.stage.Stage;
|
||||
import funkin.save.Save;
|
||||
#if FEATURE_CHART_EDITOR
|
||||
import funkin.ui.debug.charting.ChartEditorState;
|
||||
#end
|
||||
#if FEATURE_STAGE_EDITOR
|
||||
import funkin.ui.debug.stageeditor.StageEditorState;
|
||||
#end
|
||||
import funkin.ui.debug.stage.StageOffsetSubState;
|
||||
import funkin.ui.mainmenu.MainMenuState;
|
||||
import funkin.ui.MusicBeatSubState;
|
||||
|
@ -2923,7 +2928,18 @@ class PlayState extends MusicBeatSubState
|
|||
// hack for HaxeUI generation, doesn't work unless persistentUpdate is false at state creation!!
|
||||
disableKeys = true;
|
||||
persistentUpdate = false;
|
||||
openSubState(new StageOffsetSubState());
|
||||
// The strings have to be get like this otherwise it just NORs when setting the params?
|
||||
// Or, none of the characters show up, in the case of the pico songs?
|
||||
var bf:String = currentStage?.getBoyfriend()?.characterId ?? '';
|
||||
var gf:String = currentStage?.getGirlfriend()?.characterId ?? '';
|
||||
var dad:String = currentStage?.getDad()?.characterId ?? '';
|
||||
FlxG.switchState(() -> new StageEditorState(
|
||||
{
|
||||
targetStageId: currentStageId,
|
||||
targetBfChar: bf,
|
||||
targetGfChar: gf,
|
||||
targetDadChar: dad
|
||||
}));
|
||||
}
|
||||
#end
|
||||
|
||||
|
|
|
@ -1356,6 +1356,45 @@ class Save
|
|||
{
|
||||
FileUtil.saveFile(haxe.io.Bytes.ofString(this.serialize()), [FileUtil.FILE_FILTER_JSON], null, null, './save.json', 'Write save data as JSON...');
|
||||
}
|
||||
|
||||
#if FEATURE_NEWGROUNDS
|
||||
public static function saveToNewgrounds():Void
|
||||
{
|
||||
if (_instance == null) return;
|
||||
trace('[SAVE] Saving Save Data to Newgrounds...');
|
||||
funkin.api.newgrounds.NGSaveSlot.instance.save(_instance.data);
|
||||
}
|
||||
|
||||
public static function loadFromNewgrounds(onFinish:Void->Void):Void
|
||||
{
|
||||
trace('[SAVE] Loading Save Data from Newgrounds...');
|
||||
funkin.api.newgrounds.NGSaveSlot.instance.load(function(data:Dynamic) {
|
||||
FlxG.save.bind('$SAVE_NAME${BASE_SAVE_SLOT}', SAVE_PATH);
|
||||
|
||||
if (FlxG.save.status != EMPTY)
|
||||
{
|
||||
// best i can do in case the NG file is corrupted or something along those lines
|
||||
var backupSlot:Int = Save.archiveBadSaveData(FlxG.save.data);
|
||||
trace('[SAVE] Backed up current save data in case of emergency to $backupSlot!');
|
||||
}
|
||||
|
||||
FlxG.save.erase();
|
||||
FlxG.save.bind('$SAVE_NAME${BASE_SAVE_SLOT}', SAVE_PATH); // forces regeneration of the file as erase deletes it
|
||||
|
||||
var gameSave = SaveDataMigrator.migrate(data);
|
||||
FlxG.save.mergeData(gameSave.data, true);
|
||||
_instance = gameSave;
|
||||
onFinish();
|
||||
}, function(error:io.newgrounds.Call.CallError) {
|
||||
var errorMsg:String = io.newgrounds.Call.CallErrorTools.toString(error);
|
||||
|
||||
var msg = 'There was an error loading your save data from Newgrounds.';
|
||||
msg += '\n${errorMsg}';
|
||||
msg += '\nAre you sure you are connected to the internet?';
|
||||
lime.app.Application.current.window.alert(msg, "Newgrounds Save Slot Failure");
|
||||
});
|
||||
}
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -290,7 +290,10 @@ class StageEditorState extends UIState
|
|||
this.showChars = value;
|
||||
|
||||
for (cooldude in getCharacters())
|
||||
{
|
||||
if (cooldude == null) continue;
|
||||
cooldude.visible = showChars;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
@ -342,20 +345,20 @@ class StageEditorState extends UIState
|
|||
Screen.instance.addComponent(root);
|
||||
|
||||
// Characters setup.
|
||||
var gf = CharacterDataParser.fetchCharacter(Save.instance.stageGirlfriendChar, true);
|
||||
gf.characterType = CharacterType.GF;
|
||||
var dad = CharacterDataParser.fetchCharacter(Save.instance.stageDadChar, true);
|
||||
dad.characterType = CharacterType.DAD;
|
||||
var bf = CharacterDataParser.fetchCharacter(Save.instance.stageBoyfriendChar, true);
|
||||
bf.characterType = CharacterType.BF;
|
||||
var gf = CharacterDataParser.fetchCharacter(params?.targetGfChar ?? Save.instance.stageGirlfriendChar, true);
|
||||
if (gf != null) gf.characterType = CharacterType.GF;
|
||||
var dad = CharacterDataParser.fetchCharacter(params?.targetDadChar ?? Save.instance.stageDadChar, true);
|
||||
if (dad != null) dad.characterType = CharacterType.DAD;
|
||||
var bf = CharacterDataParser.fetchCharacter(params?.targetBfChar ?? Save.instance.stageBoyfriendChar, true);
|
||||
if (bf != null) bf.characterType = CharacterType.BF;
|
||||
|
||||
bf.flipX = !bf.getDataFlipX();
|
||||
gf.flipX = gf.getDataFlipX();
|
||||
dad.flipX = dad.getDataFlipX();
|
||||
if (bf != null) bf.flipX = !bf.getDataFlipX();
|
||||
if (gf != null) gf.flipX = gf.getDataFlipX();
|
||||
if (dad != null) dad.flipX = dad.getDataFlipX();
|
||||
|
||||
gf.updateHitbox();
|
||||
dad.updateHitbox();
|
||||
bf.updateHitbox();
|
||||
gf?.updateHitbox();
|
||||
dad?.updateHitbox();
|
||||
bf?.updateHitbox();
|
||||
|
||||
// Only one character per group allowed.
|
||||
charGroups = [
|
||||
|
@ -364,12 +367,21 @@ class StageEditorState extends UIState
|
|||
CharacterType.DAD => new FlxTypedGroup<BaseCharacter>(1)
|
||||
];
|
||||
|
||||
gf.x = charPos[CharacterType.GF][0] - gf.characterOrigin.x + gf.globalOffsets[0];
|
||||
gf.y = charPos[CharacterType.GF][1] - gf.characterOrigin.y + gf.globalOffsets[1];
|
||||
dad.x = charPos[CharacterType.DAD][0] - dad.characterOrigin.x + dad.globalOffsets[0];
|
||||
dad.y = charPos[CharacterType.DAD][1] - dad.characterOrigin.y + dad.globalOffsets[1];
|
||||
bf.x = charPos[CharacterType.BF][0] - bf.characterOrigin.x + bf.globalOffsets[0];
|
||||
bf.y = charPos[CharacterType.BF][1] - bf.characterOrigin.y + bf.globalOffsets[1];
|
||||
if (gf != null)
|
||||
{
|
||||
gf.x = charPos[CharacterType.GF][0] - gf.characterOrigin.x + gf.globalOffsets[0];
|
||||
gf.y = charPos[CharacterType.GF][1] - gf.characterOrigin.y + gf.globalOffsets[1];
|
||||
}
|
||||
if (dad != null)
|
||||
{
|
||||
dad.x = charPos[CharacterType.DAD][0] - dad.characterOrigin.x + dad.globalOffsets[0];
|
||||
dad.y = charPos[CharacterType.DAD][1] - dad.characterOrigin.y + dad.globalOffsets[1];
|
||||
}
|
||||
if (bf != null)
|
||||
{
|
||||
bf.x = charPos[CharacterType.BF][0] - bf.characterOrigin.x + bf.globalOffsets[0];
|
||||
bf.y = charPos[CharacterType.BF][1] - bf.characterOrigin.y + bf.globalOffsets[1];
|
||||
}
|
||||
|
||||
selectedChar = bf;
|
||||
|
||||
|
@ -537,7 +549,7 @@ class StageEditorState extends UIState
|
|||
if (conductorInUse.currentBeat % 2 == 0)
|
||||
{
|
||||
for (char in getCharacters())
|
||||
char.dance(true);
|
||||
char?.dance(true);
|
||||
}
|
||||
|
||||
for (asset in spriteArray)
|
||||
|
@ -584,7 +596,10 @@ class StageEditorState extends UIState
|
|||
if (testingMode)
|
||||
{
|
||||
for (char in getCharacters())
|
||||
{
|
||||
if (char == null) continue;
|
||||
char.shader = null;
|
||||
}
|
||||
|
||||
// spriteMarker.visible = camMarker.visible = false;
|
||||
findObjDialog.hideDialog(DialogButton.CANCEL);
|
||||
|
@ -597,11 +612,15 @@ class StageEditorState extends UIState
|
|||
|
||||
if (curTestChar >= getCharacters().length) curTestChar = 0;
|
||||
|
||||
bottomBarSelectText.text = Std.string(getCharacters()[curTestChar].characterType);
|
||||
var text = Std.string(getCharacters()[curTestChar]?.characterType);
|
||||
bottomBarSelectText.text = (text == 'null') ? 'None' : text;
|
||||
|
||||
var char = getCharacters()[curTestChar];
|
||||
camFollow.x = char.cameraFocusPoint.x + charCamOffsets.get(char.characterType)[0];
|
||||
camFollow.y = char.cameraFocusPoint.y + charCamOffsets.get(char.characterType)[1];
|
||||
if (char != null)
|
||||
{
|
||||
camFollow.x = char.cameraFocusPoint.x + charCamOffsets.get(char.characterType)[0];
|
||||
camFollow.y = char.cameraFocusPoint.y + charCamOffsets.get(char.characterType)[1];
|
||||
}
|
||||
|
||||
// EXIT
|
||||
if (FlxG.keys.justPressed.ENTER) // so we dont accidentally get stuck (happened to me once, terrible experience)
|
||||
|
@ -740,14 +759,18 @@ class StageEditorState extends UIState
|
|||
arrowMovement(selectedSprite);
|
||||
|
||||
for (char in getCharacters())
|
||||
{
|
||||
if (char == null) continue;
|
||||
char.shader = null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
selectedChar.shader = null;
|
||||
if (selectedChar != null) selectedChar.shader = null;
|
||||
|
||||
for (char in getCharacters())
|
||||
{
|
||||
if (char == null) continue;
|
||||
if (char != selectedChar) char.shader = charDeselectShader;
|
||||
|
||||
if (char != null && checkCharOverlaps(char)) // flxg.mouse.overlaps crashes the game
|
||||
|
@ -860,6 +883,7 @@ class StageEditorState extends UIState
|
|||
for (i in 0...getCharacters().length)
|
||||
{
|
||||
var char = getCharacters()[i];
|
||||
if (char == null) continue;
|
||||
var type = char.characterType;
|
||||
|
||||
charPos.set(type, [
|
||||
|
@ -890,6 +914,7 @@ class StageEditorState extends UIState
|
|||
// it comes from some flxobject/polymod error apparently and I have no idea why
|
||||
function checkCharOverlaps(char:BaseCharacter)
|
||||
{
|
||||
if (char == null) return false;
|
||||
var mouseX = FlxG.mouse.x >= char.x && FlxG.mouse.x <= char.x + char.width;
|
||||
var mouseY = FlxG.mouse.y >= char.y && FlxG.mouse.y <= char.y + char.height;
|
||||
|
||||
|
@ -1303,7 +1328,6 @@ class StageEditorState extends UIState
|
|||
index++;
|
||||
|
||||
if (index >= chars.length) index = 0;
|
||||
|
||||
selectedChar = chars[index];
|
||||
}
|
||||
else
|
||||
|
@ -1599,4 +1623,18 @@ typedef StageEditorParams =
|
|||
* If non-null, load this stage immediately instead of the welcome screen.
|
||||
*/
|
||||
var ?targetStageId:String;
|
||||
/**
|
||||
* If non-null, load this character as Boyfriend.
|
||||
*/
|
||||
var ?targetBfChar:String;
|
||||
|
||||
/**
|
||||
* If non-null, load this character as Girlfriend.
|
||||
*/
|
||||
var ?targetGfChar:String;
|
||||
|
||||
/**
|
||||
* If non-null, load this character as Dad.
|
||||
*/
|
||||
var ?targetDadChar:String;
|
||||
};
|
||||
|
|
|
@ -240,6 +240,7 @@ class StageDataHandler
|
|||
for (char in chars)
|
||||
{
|
||||
var charData:StageDataCharacter = null;
|
||||
if (char == null) continue;
|
||||
|
||||
switch (char.characterType)
|
||||
{
|
||||
|
|
|
@ -21,6 +21,7 @@ class UndoRedoHandler
|
|||
|
||||
for (char in state.getCharacters())
|
||||
{
|
||||
if (char == null) continue;
|
||||
if (char.characterType == type) state.selectedChar = char;
|
||||
}
|
||||
|
||||
|
|
|
@ -46,29 +46,35 @@ class StageEditorCharacterToolbox extends StageEditorDefaultToolbox
|
|||
|
||||
charZIdx.max = StageEditorState.MAX_Z_INDEX;
|
||||
charZIdx.onChange = function(_) {
|
||||
if (state.selectedChar == null) return;
|
||||
state.charGroups[state.selectedChar.characterType].zIndex = Std.int(charZIdx.pos);
|
||||
state.sortAssets();
|
||||
}
|
||||
|
||||
charCamX.onChange = charCamY.onChange = function(_) {
|
||||
if (state.selectedChar == null) return;
|
||||
state.charCamOffsets[state.selectedChar.characterType] = [charCamX.pos, charCamY.pos];
|
||||
state.updateMarkerPos();
|
||||
}
|
||||
|
||||
charScale.onChange = function(_) {
|
||||
if (state.selectedChar == null) return;
|
||||
state.selectedChar.setScale(state.selectedChar.getBaseScale() * charScale.pos);
|
||||
repositionCharacter();
|
||||
}
|
||||
|
||||
charAlpha.onChange = function(_) {
|
||||
if (state.selectedChar == null) return;
|
||||
state.selectedChar.alpha = charAlpha.pos;
|
||||
}
|
||||
|
||||
charAngle.onChange = function(_) {
|
||||
if (state.selectedChar == null) return;
|
||||
state.selectedChar.angle = charAngle.pos;
|
||||
}
|
||||
|
||||
charScrollX.onChange = charScrollY.onChange = function(_) {
|
||||
if (state.selectedChar == null) return;
|
||||
state.selectedChar.scrollFactor.set(charScrollX.pos, charScrollY.pos);
|
||||
}
|
||||
|
||||
|
@ -90,7 +96,7 @@ class StageEditorCharacterToolbox extends StageEditorDefaultToolbox
|
|||
|
||||
override public function refresh()
|
||||
{
|
||||
var name = stageEditorState.selectedChar.characterType;
|
||||
var name = stageEditorState.selectedChar?.characterType;
|
||||
var curChar = stageEditorState.selectedChar;
|
||||
|
||||
charPosX.step = charPosY.step = stageEditorState.moveStep;
|
||||
|
@ -99,20 +105,23 @@ class StageEditorCharacterToolbox extends StageEditorDefaultToolbox
|
|||
|
||||
// Always update the displays, since selectedChar is never null.
|
||||
|
||||
if (charPosX.pos != stageEditorState.charPos[name][0]) charPosX.pos = stageEditorState.charPos[name][0];
|
||||
if (charPosY.pos != stageEditorState.charPos[name][1]) charPosY.pos = stageEditorState.charPos[name][1];
|
||||
if (charZIdx.pos != stageEditorState.charGroups[name].zIndex) charZIdx.pos = stageEditorState.charGroups[name].zIndex;
|
||||
if (charCamX.pos != stageEditorState.charCamOffsets[name][0]) charCamX.pos = stageEditorState.charCamOffsets[name][0];
|
||||
if (charCamY.pos != stageEditorState.charCamOffsets[name][1]) charCamY.pos = stageEditorState.charCamOffsets[name][1];
|
||||
if (charScale.pos != curChar.scale.x / curChar.getBaseScale()) charScale.pos = curChar.scale.x / curChar.getBaseScale();
|
||||
if (charAlpha.pos != curChar.alpha) charAlpha.pos = curChar.alpha;
|
||||
if (charAngle.pos != curChar.angle) charAngle.pos = curChar.angle;
|
||||
if (charScrollX.pos != curChar.scrollFactor.x) charScrollX.pos = curChar.scrollFactor.x;
|
||||
if (charScrollY.pos != curChar.scrollFactor.y) charScrollY.pos = curChar.scrollFactor.y;
|
||||
if (curChar != null)
|
||||
{
|
||||
if (charPosX.pos != stageEditorState.charPos[name][0]) charPosX.pos = stageEditorState.charPos[name][0];
|
||||
if (charPosY.pos != stageEditorState.charPos[name][1]) charPosY.pos = stageEditorState.charPos[name][1];
|
||||
if (charZIdx.pos != stageEditorState.charGroups[name].zIndex) charZIdx.pos = stageEditorState.charGroups[name].zIndex;
|
||||
if (charCamX.pos != stageEditorState.charCamOffsets[name][0]) charCamX.pos = stageEditorState.charCamOffsets[name][0];
|
||||
if (charCamY.pos != stageEditorState.charCamOffsets[name][1]) charCamY.pos = stageEditorState.charCamOffsets[name][1];
|
||||
if (charScale.pos != curChar.scale.x / curChar.getBaseScale()) charScale.pos = curChar.scale.x / curChar.getBaseScale();
|
||||
if (charAlpha.pos != curChar.alpha) charAlpha.pos = curChar.alpha;
|
||||
if (charAngle.pos != curChar.angle) charAngle.pos = curChar.angle;
|
||||
if (charScrollX.pos != curChar.scrollFactor.x) charScrollX.pos = curChar.scrollFactor.x;
|
||||
if (charScrollY.pos != curChar.scrollFactor.y) charScrollY.pos = curChar.scrollFactor.y;
|
||||
}
|
||||
|
||||
var prevText = charType.text;
|
||||
var charData = CharacterDataParser.fetchCharacterData(curChar.characterId);
|
||||
charType.icon = (charData == null ? null : CharacterDataParser.getCharPixelIconAsset(curChar.characterId));
|
||||
var charData = CharacterDataParser.fetchCharacterData(curChar?.characterId);
|
||||
charType.icon = (charData == null ? null : CharacterDataParser.getCharPixelIconAsset(curChar?.characterId));
|
||||
charType.text = (charData == null ? "None" : charData.name.length > 6 ? '${charData.name.substr(0, 6)}.' : '${charData.name}');
|
||||
|
||||
if (prevText != charType.text) Screen.instance.removeComponent(charMenu);
|
||||
|
@ -120,6 +129,7 @@ class StageEditorCharacterToolbox extends StageEditorDefaultToolbox
|
|||
|
||||
public function repositionCharacter()
|
||||
{
|
||||
if (stageEditorState.selectedChar == null) return;
|
||||
stageEditorState.selectedChar.x = charPosX.pos - stageEditorState.selectedChar.characterOrigin.x + stageEditorState.selectedChar.globalOffsets[0];
|
||||
stageEditorState.selectedChar.y = charPosY.pos - stageEditorState.selectedChar.characterOrigin.y + stageEditorState.selectedChar.globalOffsets[1];
|
||||
|
||||
|
@ -165,7 +175,7 @@ class StageEditorCharacterMenu extends Menu // copied from chart editor
|
|||
charButton.padding = 8;
|
||||
charButton.iconPosition = "top";
|
||||
|
||||
if (charId == state.selectedChar.characterId)
|
||||
if (charId == state.selectedChar?.characterId)
|
||||
{
|
||||
// Scroll to the character if it is already selected.
|
||||
charSelectScroll.hscrollPos = Math.floor(charIndex / 5) * 80;
|
||||
|
@ -179,8 +189,25 @@ class StageEditorCharacterMenu extends Menu // copied from chart editor
|
|||
charButton.text = charData.name.length > LIMIT ? '${charData.name.substr(0, LIMIT)}.' : '${charData.name}';
|
||||
|
||||
charButton.onClick = _ -> {
|
||||
var type = state.selectedChar.characterType;
|
||||
if (state.selectedChar.characterId == charId) return; // saves on memory
|
||||
var type = state.selectedChar?.characterType;
|
||||
if (type == null)
|
||||
{
|
||||
// Hack - choose the type based of the first null character in getCharacters,
|
||||
// since this is how the click on the char type gets the selected char anyway, we can be sure this is the right type
|
||||
var chars = state.getCharacters();
|
||||
var index = chars.indexOf(state.selectedChar);
|
||||
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
type = CharacterType.GF;
|
||||
case 1:
|
||||
type = CharacterType.DAD;
|
||||
case 2:
|
||||
type = CharacterType.BF;
|
||||
}
|
||||
}
|
||||
if (state.selectedChar?.characterId == charId) return; // saves on memory
|
||||
|
||||
var group = state.charGroups[type];
|
||||
group.killMembers();
|
||||
|
|
|
@ -49,8 +49,13 @@ import funkin.util.HapticUtil;
|
|||
import funkin.util.MathUtil;
|
||||
import funkin.util.SortUtil;
|
||||
import openfl.display.BlendMode;
|
||||
import funkin.ui.freeplay.DifficultyDot;
|
||||
import funkin.data.freeplay.style.FreeplayStyleRegistry;
|
||||
#if FEATURE_CHART_EDITOR
|
||||
import funkin.ui.debug.charting.ChartEditorState;
|
||||
#end
|
||||
#if FEATURE_STAGE_EDITOR
|
||||
import funkin.ui.debug.stageeditor.StageEditorState;
|
||||
#end
|
||||
#if FEATURE_DISCORD_RPC
|
||||
import funkin.api.discord.DiscordClient;
|
||||
#end
|
||||
|
@ -2116,26 +2121,153 @@ class FreeplayState extends MusicBeatSubState
|
|||
_parentState.persistentDraw = true;
|
||||
}
|
||||
|
||||
new FlxTimer().start(longestTimer, (_) -> {
|
||||
FlxTransitionableState.skipNextTransIn = true;
|
||||
FlxTransitionableState.skipNextTransOut = true;
|
||||
if (Type.getClass(_parentState) == MainMenuState)
|
||||
new FlxTimer().start(longestTimer, (_) -> {
|
||||
FlxTransitionableState.skipNextTransIn = true;
|
||||
FlxTransitionableState.skipNextTransOut = true;
|
||||
if (Type.getClass(_parentState) == MainMenuState)
|
||||
{
|
||||
FunkinSound.playMusic('freakyMenu',
|
||||
{
|
||||
overrideExisting: true,
|
||||
restartTrack: false,
|
||||
// Continue playing this music between states, until a different music track gets played.
|
||||
persist: true
|
||||
});
|
||||
FlxG.sound.music.fadeIn(4.0, 0.0, 1.0);
|
||||
close();
|
||||
}
|
||||
else
|
||||
{
|
||||
FlxG.switchState(() -> new MainMenuState());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (accepted && !busy)
|
||||
{
|
||||
grpCapsules.members[curSelected].onConfirm();
|
||||
}
|
||||
#if FEATURE_CHART_EDITOR
|
||||
if (controls.DEBUG_CHART && !busy)
|
||||
{
|
||||
busy = true;
|
||||
var targetSongID = grpCapsules.members[curSelected]?.freeplayData?.data.id ?? 'unknown';
|
||||
if (targetSongID == 'unknown')
|
||||
{
|
||||
FunkinSound.playMusic('freakyMenu',
|
||||
{
|
||||
overrideExisting: true,
|
||||
restartTrack: false,
|
||||
// Continue playing this music between states, until a different music track gets played.
|
||||
persist: true
|
||||
});
|
||||
FlxG.sound.music.fadeIn(4.0, 0.0, 1.0);
|
||||
close();
|
||||
trace('CHART RANDOM SONG');
|
||||
letterSort.inputEnabled = false;
|
||||
|
||||
var availableSongCapsules:Array<SongMenuItem> = grpCapsules.members.filter(function(cap:SongMenuItem) {
|
||||
// Dead capsules are ones which were removed from the list when changing filters.
|
||||
return cap.alive && cap.freeplayData != null;
|
||||
});
|
||||
|
||||
trace('Available songs: ${availableSongCapsules.map(function(cap) {
|
||||
return cap?.freeplayData?.data.songName;
|
||||
})}');
|
||||
|
||||
if (availableSongCapsules.length == 0)
|
||||
{
|
||||
trace('No songs available!');
|
||||
busy = false;
|
||||
letterSort.inputEnabled = true;
|
||||
FunkinSound.playOnce(Paths.sound('cancelMenu'));
|
||||
return;
|
||||
}
|
||||
|
||||
var targetSong:SongMenuItem = FlxG.random.getObject(availableSongCapsules);
|
||||
|
||||
// Seeing if I can do an animation...
|
||||
curSelected = grpCapsules.members.indexOf(targetSong);
|
||||
changeSelection(0);
|
||||
targetSongID = grpCapsules.members[curSelected]?.freeplayData?.data.id ?? 'unknown';
|
||||
}
|
||||
else
|
||||
FlxG.switchState(() -> new ChartEditorState(
|
||||
{
|
||||
targetSongId: targetSongID,
|
||||
}));
|
||||
}
|
||||
#end
|
||||
|
||||
#if FEATURE_STAGE_EDITOR
|
||||
if (controls.DEBUG_STAGE && !busy)
|
||||
{
|
||||
busy = true;
|
||||
|
||||
var targetSongID = grpCapsules.members[curSelected]?.freeplayData?.data.id ?? 'unknown';
|
||||
if (targetSongID == 'unknown')
|
||||
{
|
||||
FlxG.switchState(() -> new MainMenuState());
|
||||
trace('CHART RANDOM SONG');
|
||||
letterSort.inputEnabled = false;
|
||||
|
||||
var availableSongCapsules:Array<SongMenuItem> = grpCapsules.members.filter(function(cap:SongMenuItem) {
|
||||
// Dead capsules are ones which were removed from the list when changing filters.
|
||||
return cap.alive && cap.freeplayData != null;
|
||||
});
|
||||
|
||||
trace('Available songs: ${availableSongCapsules.map(function(cap) {
|
||||
return cap?.freeplayData?.data.songName;
|
||||
})}');
|
||||
|
||||
if (availableSongCapsules.length == 0)
|
||||
{
|
||||
trace('No songs available!');
|
||||
busy = false;
|
||||
letterSort.inputEnabled = true;
|
||||
FunkinSound.playOnce(Paths.sound('cancelMenu'));
|
||||
return;
|
||||
}
|
||||
|
||||
var targetSong:SongMenuItem = FlxG.random.getObject(availableSongCapsules);
|
||||
|
||||
// Seeing if I can do an animation...
|
||||
curSelected = grpCapsules.members.indexOf(targetSong);
|
||||
changeSelection(0);
|
||||
targetSongID = grpCapsules.members[curSelected]?.freeplayData?.data.id ?? 'unknown';
|
||||
}
|
||||
});
|
||||
|
||||
var targetSongNullable:Null<Song> = SongRegistry.instance.fetchEntry(targetSongID);
|
||||
if (targetSongNullable == null)
|
||||
{
|
||||
FlxG.log.warn('WARN: could not find song with id (${targetSongID})');
|
||||
busy = false;
|
||||
letterSort.inputEnabled = true;
|
||||
return;
|
||||
}
|
||||
var targetSong:Song = targetSongNullable;
|
||||
var targetVariation:Null<String> = currentVariation;
|
||||
|
||||
var targetDifficulty:Null<SongDifficulty> = targetSong.getDifficulty(currentDifficulty, currentVariation);
|
||||
if (targetDifficulty == null)
|
||||
{
|
||||
FlxG.log.warn('WARN: could not find difficulty with id (${currentDifficulty})');
|
||||
busy = false;
|
||||
letterSort.inputEnabled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
FlxG.switchState(() -> new StageEditorState(
|
||||
{
|
||||
targetStageId: targetDifficulty.stage,
|
||||
targetBfChar: targetDifficulty.characters.player,
|
||||
targetGfChar: targetDifficulty.characters.girlfriend,
|
||||
targetDadChar: targetDifficulty.characters.opponent
|
||||
}));
|
||||
}
|
||||
#end
|
||||
}
|
||||
|
||||
override function beatHit():Bool
|
||||
{
|
||||
backingCard?.beatHit();
|
||||
|
||||
return super.beatHit();
|
||||
}
|
||||
|
||||
public override function destroy():Void
|
||||
{
|
||||
super.destroy();
|
||||
FlxG.cameras.remove(funnyCam);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -69,7 +69,8 @@ class OptionsState extends MusicBeatState
|
|||
optionsCodex = new Codex<OptionsMenuPageName>(Options);
|
||||
add(optionsCodex);
|
||||
|
||||
var options:OptionsMenu = optionsCodex.addPage(Options, new OptionsMenu());
|
||||
var saveData:SaveDataMenu = optionsCodex.addPage(SaveData, new SaveDataMenu());
|
||||
var options:OptionsMenu = optionsCodex.addPage(Options, new OptionsMenu(saveData));
|
||||
var preferences:PreferencesMenu = optionsCodex.addPage(Preferences, new PreferencesMenu());
|
||||
var controls:ControlsMenu = optionsCodex.addPage(Controls, new ControlsMenu());
|
||||
#if FEATURE_INPUT_OFFSETS
|
||||
|
@ -84,6 +85,7 @@ class OptionsState extends MusicBeatState
|
|||
#if FEATURE_INPUT_OFFSETS
|
||||
offsets.onExit.add(exitOffsets);
|
||||
#end
|
||||
saveData.onExit.add(optionsCodex.switchPage.bind(Options));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -159,7 +161,7 @@ class OptionsMenu extends Page<OptionsMenuPageName>
|
|||
|
||||
final CAMERA_MARGIN:Int = 150;
|
||||
|
||||
public function new()
|
||||
public function new(saveDataMenu:SaveDataMenu)
|
||||
{
|
||||
super();
|
||||
add(items = new TextMenuList());
|
||||
|
@ -227,9 +229,19 @@ class OptionsMenu extends Page<OptionsMenuPageName>
|
|||
});
|
||||
}
|
||||
#end
|
||||
createItem("CLEAR SAVE DATA", function() {
|
||||
promptClearSaveData();
|
||||
});
|
||||
|
||||
// no need to show an entire new menu for just one option
|
||||
if (saveDataMenu.hasMultipleOptions())
|
||||
{
|
||||
createItem("SAVE DATA OPTIONS", function() {
|
||||
codex.switchPage(SaveData);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
createItem("CLEAR SAVE DATA", saveDataMenu.openSaveDataPrompt);
|
||||
}
|
||||
|
||||
#if NO_FEATURE_TOUCH_CONTROLS
|
||||
createItem("EXIT", exit);
|
||||
#else
|
||||
|
@ -277,7 +289,6 @@ class OptionsMenu extends Page<OptionsMenuPageName>
|
|||
|
||||
override function update(elapsed:Float):Void
|
||||
{
|
||||
enabled = (prompt == null);
|
||||
#if FEATURE_TOUCH_CONTROLS
|
||||
backButton.active = (!goingBack) ? !items.busy : true;
|
||||
#end
|
||||
|
@ -298,31 +309,6 @@ class OptionsMenu extends Page<OptionsMenuPageName>
|
|||
{
|
||||
return items.length > 2;
|
||||
}
|
||||
|
||||
var prompt:Prompt;
|
||||
|
||||
function promptClearSaveData():Void
|
||||
{
|
||||
if (prompt != null) return;
|
||||
prompt = new Prompt("This will delete
|
||||
\nALL your save data.
|
||||
\nAre you sure?
|
||||
", Custom("Delete", "Cancel"));
|
||||
prompt.create();
|
||||
prompt.createBgFromMargin(100, 0xFFFAFD6D);
|
||||
prompt.back.scrollFactor.set(0, 0);
|
||||
add(prompt);
|
||||
prompt.onYes = function() {
|
||||
// Clear the save data.
|
||||
funkin.save.Save.clearData();
|
||||
FlxG.switchState(() -> new funkin.InitState());
|
||||
};
|
||||
prompt.onNo = function() {
|
||||
prompt.close();
|
||||
prompt.destroy();
|
||||
prompt = null;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
enum abstract OptionsMenuPageName(String) to PageName
|
||||
|
@ -333,4 +319,5 @@ enum abstract OptionsMenuPageName(String) to PageName
|
|||
var Mods = "mods";
|
||||
var Preferences = "preferences";
|
||||
var Offsets = "offsets";
|
||||
var SaveData = "saveData";
|
||||
}
|
||||
|
|
125
source/funkin/ui/options/SaveDataMenu.hx
Normal file
125
source/funkin/ui/options/SaveDataMenu.hx
Normal file
|
@ -0,0 +1,125 @@
|
|||
package funkin.ui.options;
|
||||
|
||||
#if FEATURE_NEWGROUNDS
|
||||
import funkin.api.newgrounds.NewgroundsClient;
|
||||
#end
|
||||
import funkin.save.Save;
|
||||
|
||||
class SaveDataMenu extends Page<OptionsState.OptionsMenuPageName>
|
||||
{
|
||||
var items:TextMenuList;
|
||||
|
||||
public function new()
|
||||
{
|
||||
super();
|
||||
|
||||
add(items = new TextMenuList());
|
||||
|
||||
createItem("CLEAR SAVE DATA", openSaveDataPrompt);
|
||||
|
||||
#if FEATURE_NEWGROUNDS
|
||||
if (NewgroundsClient.instance.isLoggedIn())
|
||||
{
|
||||
createItem("LOAD FROM NG", function() {
|
||||
openConfirmPrompt("This will overwrite
|
||||
\nALL your save data.
|
||||
\nAre you sure?
|
||||
", "Overwrite", function() {
|
||||
Save.loadFromNewgrounds(function() {
|
||||
FlxG.switchState(() -> new funkin.InitState());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
createItem("SAVE TO NG", function() {
|
||||
openConfirmPrompt("This will overwrite
|
||||
\nALL save data saved
|
||||
\non NG. Are you sure?", "Overwrite", function() {
|
||||
Save.saveToNewgrounds();
|
||||
});
|
||||
});
|
||||
|
||||
createItem("CLEAR NG SAVE DATA", function() {
|
||||
openConfirmPrompt("This will delete
|
||||
\nALL save data saved
|
||||
\non NG. Are you sure?", "Delete", function() {
|
||||
funkin.api.newgrounds.NGSaveSlot.instance.clear();
|
||||
});
|
||||
});
|
||||
}
|
||||
#end
|
||||
|
||||
createItem("EXIT", exit);
|
||||
}
|
||||
|
||||
function createItem(name:String, callback:Void->Void, fireInstantly = false)
|
||||
{
|
||||
var item = items.createItem(0, 100 + items.length * 100, name, BOLD, callback);
|
||||
item.fireInstantly = fireInstantly;
|
||||
item.screenCenter(X);
|
||||
return item;
|
||||
}
|
||||
|
||||
override function update(elapsed:Float)
|
||||
{
|
||||
enabled = (prompt == null);
|
||||
super.update(elapsed);
|
||||
}
|
||||
|
||||
override function set_enabled(value:Bool)
|
||||
{
|
||||
items.enabled = value;
|
||||
return super.set_enabled(value);
|
||||
}
|
||||
|
||||
var prompt:Prompt;
|
||||
|
||||
function openConfirmPrompt(text:String, yesText:String, onYes:Void->Void, ?groupToOpenOn:Null<flixel.group.FlxGroup>):Void
|
||||
{
|
||||
if (prompt != null) return;
|
||||
|
||||
prompt = new Prompt(text, Custom(yesText, "Cancel"));
|
||||
prompt.create();
|
||||
prompt.createBgFromMargin(100, 0xFFFAFD6D);
|
||||
prompt.back.scrollFactor.set(0, 0);
|
||||
FlxG.state.add(prompt);
|
||||
|
||||
prompt.onYes = function() {
|
||||
onYes();
|
||||
|
||||
if (prompt != null)
|
||||
{
|
||||
prompt.close();
|
||||
prompt.destroy();
|
||||
prompt = null;
|
||||
}
|
||||
};
|
||||
|
||||
prompt.onNo = function() {
|
||||
prompt.close();
|
||||
prompt.destroy();
|
||||
prompt = null;
|
||||
}
|
||||
}
|
||||
public function openSaveDataPrompt()
|
||||
{
|
||||
openConfirmPrompt("This will delete
|
||||
\nALL your save data.
|
||||
\nAre you sure?
|
||||
", "Delete", function() {
|
||||
// Clear the save data.
|
||||
Save.clearData();
|
||||
|
||||
FlxG.switchState(() -> new funkin.InitState());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* True if this page has multiple options, excluding the exit option.
|
||||
* If false, there's no reason to ever show this page.
|
||||
*/
|
||||
public function hasMultipleOptions():Bool
|
||||
{
|
||||
return items.length > 2;
|
||||
}
|
||||
}
|
|
@ -102,12 +102,19 @@ class WindowUtil
|
|||
*/
|
||||
public static final windowExit:FlxTypedSignal<Int->Void> = new FlxTypedSignal<Int->Void>();
|
||||
|
||||
/**
|
||||
* Has `initWindowEvents()` been called already?
|
||||
* This is to prevent multiple instances of the same function.
|
||||
*/
|
||||
private static var _initializedWindowEvents:Bool = false;
|
||||
|
||||
/**
|
||||
* Wires up FlxSignals that happen based on window activity.
|
||||
* For example, we can run a callback when the window is closed.
|
||||
*/
|
||||
public static function initWindowEvents():Void
|
||||
{
|
||||
if (_initializedWindowEvents) return; // Fix that annoying
|
||||
// onUpdate is called every frame just before rendering.
|
||||
|
||||
// onExit is called when the game window is closed.
|
||||
|
@ -137,6 +144,7 @@ class WindowUtil
|
|||
}
|
||||
});
|
||||
#end
|
||||
_initializedWindowEvents = true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in a new issue