1
0
Fork 0
mirror of https://github.com/ninjamuffin99/Funkin.git synced 2025-09-03 20:28:04 +00:00

Compare commits

...

20 commits

Author SHA1 Message Date
Lasercar 6fb7252101
Merge 47815cff2e into d0f0974d1e 2025-07-27 15:47:02 -05:00
Abnormal d0f0974d1e erm...soggy!!! compile error fixie 2025-07-27 15:46:26 -05:00
Abnormal 369b936056 i love mergy conflicties 2025-07-27 01:09:57 -05:00
TechnikTil c40e1e3958 fix fullscreen bug and show clear save data if logged out of ng 2025-07-27 01:09:57 -05:00
TechnikTil 0ade24bac4 HOPEFULLY FIX EVERYTHING WRONG (this time it was tested between two computers) 2025-07-27 01:09:57 -05:00
TechnikTil 7370ef3249 hopefully fix everything with load from ng 2025-07-27 01:09:57 -05:00
TechnikTil b2f162a8ae move clear save data to the top, add clear ng save data 2025-07-27 01:09:57 -05:00
TechnikTil 11fa9b4050 rewrite NGSaveSlot.load to be async, dont precache assets, error handling 2025-07-27 01:09:57 -05:00
TechnikTil 89bd7a3f43 make newgrounds functions in Save static and only available if newgrounds feature flag is enabled, make sure base save slot is loaded and is not empty, and recover save data in case newgrounds doesnt wanna play nice 2025-07-27 01:09:57 -05:00
TechnikTil 942fb5efc9 Add Save Data Options, also allow user to load and save with Newgrounds Save Slots. 2025-07-27 01:09:57 -05:00
Lasercar c2eff142bd Bunker infiltrated 2025-07-22 14:45:47 -07:00
Lasercar 47815cff2e Animation editor fixes + near complete null safety 2025-07-23 04:40:09 +10:00
Eric 2784fa18c0
Merge pull request #5476 from Trofem/api-version-example-mods-for-0.7.0
[CHORE] Updates example_mods's API version up to 0.7.0
2025-07-22 12:03:19 -04:00
Eric ad07fddf89
Merge pull request #5489 from FunkinCrew/main
Update develop to latest main
2025-07-22 11:59:29 -04:00
trofim.al 4768eedd5b api version 0.7.0
Update example mod's API version so it actually loads. part 2.
2025-07-22 15:04:20 +11:00
Hyper_ f1c3e99a11 lasercar................................................................................................................................................................................................. 2025-07-22 07:56:29 +08:00
Hyper_ eefe8927c4 The most deranged line loss of all time 2025-07-21 17:43:55 -05:00
cherry 3ff4f14510 Clear key from correct map 2025-07-21 17:13:42 -05:00
Abnormal 955b0db542 THE REALLY COOL STUTTERING FIX!!!!!!!!!!!! 2025-07-21 15:06:19 -05:00
Eric d2df4f0832
Merge pull request #5440 from FunkinCrew/main
Update develop branch
2025-07-21 13:08:44 -04:00
18 changed files with 552 additions and 169 deletions

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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"
}

View file

@ -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"
}

View file

@ -341,7 +341,7 @@ class InitState extends FlxState
}));
#elseif ANIMDEBUG
// -DANIMDEBUG
FlxG.switchState(() -> new funkin.ui.debug.anim.DebugBoundingState());
FlxG.switchState(() -> new funkin.ui.debug.anim.AnimationEditorState());
#elseif LATENCY
// -DLATENCY
FlxG.switchState(() -> new funkin.LatencyState());

View 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

View file

@ -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

View file

@ -58,7 +58,7 @@ class BaseCharacter extends Bopper
*/
public var dropNoteCounts(default, null):Array<Int>;
@:allow(funkin.ui.debug.anim.DebugBoundingState)
@:allow(funkin.ui.debug.anim.AnimationEditorState)
final _data:CharacterData;
final singTimeSteps:Float;

View file

@ -81,7 +81,7 @@ class Bopper extends StageProp implements IPlayStateScriptedClass
return globalOffsets = value;
}
@:allow(funkin.ui.debug.anim.DebugBoundingState)
@:allow(funkin.ui.debug.anim.AnimationEditorState)
var animOffsets(default, set):Array<Float> = [0, 0];
public var originalPosition:FlxPoint = new FlxPoint(0, 0);

View file

@ -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
}
/**

View file

@ -113,7 +113,7 @@ class DebugMenuSubState extends MusicBeatSubState
function openAnimationEditor():Void
{
FlxG.switchState(() -> new funkin.ui.debug.anim.DebugBoundingState());
FlxG.switchState(() -> new funkin.ui.debug.anim.AnimationEditorState());
trace('Animation Editor');
}

View file

@ -31,30 +31,27 @@ import openfl.net.FileReference;
using flixel.util.FlxSpriteUtil;
class DebugBoundingState extends FlxState
@:nullSafety
class AnimationEditorState extends MusicBeatState
{
/*
TODAY'S TO-DO
- Cleaner UI
*/
var bg:FlxBackdrop;
var fileInfo:FlxText;
var bg:Null<FlxBackdrop>;
var fileInfo:Null<FlxText>;
var txtGrp:FlxTypedGroup<FlxText>;
var txtGrp:Null<FlxTypedGroup<FlxText>>;
var hudCam:FlxCamera;
var hudCam:Null<FlxCamera>;
var curView:ANIMDEBUGVIEW = SPRITESHEET;
var spriteSheetView:FlxGroup;
var offsetView:FlxGroup;
var spriteSheetView:Null<FlxGroup>;
var offsetView:Null<FlxGroup>;
var dropDownSetup:Bool = false;
var onionSkinChar:FlxSprite;
var txtOffsetShit:FlxText;
var onionSkinChar:Null<FlxSprite>;
var txtOffsetShit:Null<FlxText>;
var offsetEditorDialog:CollapsibleDialog;
var offsetAnimationDropdown:DropDown;
var offsetEditorDialog:Null<CollapsibleDialog>;
var offsetAnimationDropdown:Null<DropDown>;
var haxeUIFocused(get, default):Bool = false;
@ -89,8 +86,10 @@ class DebugBoundingState extends FlxState
offsetEditorDialog = cast RuntimeComponentBuilder.fromAsset(str);
// offsetEditorDialog.findComponent("btnViewSpriteSheet").onClick = _ -> curView = SPRITESHEET;
var viewDropdown:DropDown = offsetEditorDialog.findComponent("swapper", DropDown);
viewDropdown.onChange = function(e:UIEvent) {
var viewDropdown:Null<DropDown> = offsetEditorDialog.findComponent("swapper", DropDown);
if (viewDropdown != null) viewDropdown.onChange = function(e:UIEvent) {
trace(e.type);
curView = cast e.data.curView;
trace(e.data);
@ -120,8 +119,8 @@ class DebugBoundingState extends FlxState
super.create();
}
var bf:FlxSprite;
var swagOutlines:FlxSprite;
var bf:Null<FlxSprite>;
var swagOutlines:Null<FlxSprite>;
function initSpritesheetView():Void
{
@ -140,7 +139,7 @@ class DebugBoundingState extends FlxState
generateOutlines(tex.frames);
txtGrp = new FlxTypedGroup<FlxText>();
txtGrp.cameras = [hudCam];
if (hudCam != null) txtGrp.cameras = [hudCam];
spriteSheetView.add(txtGrp);
addInfo('boyfriend.xml', "");
@ -154,7 +153,7 @@ class DebugBoundingState extends FlxState
{
// swagOutlines.width = frameShit[0].parent.width;
// swagOutlines.height = frameShit[0].parent.height;
swagOutlines.pixels.fillRect(new Rectangle(0, 0, swagOutlines.width, swagOutlines.height), 0x00000000);
if (swagOutlines != null) swagOutlines.pixels.fillRect(new Rectangle(0, 0, swagOutlines.width, swagOutlines.height), 0x00000000);
for (i in frameShit)
{
@ -164,7 +163,7 @@ class DebugBoundingState extends FlxState
var uvH:Float = (i.uv.height * i.parent.height) - (i.uv.y * i.parent.height);
// trace(Std.int(i.uv.width * i.parent.width));
swagOutlines.drawRect(i.uv.x * i.parent.width, i.uv.y * i.parent.height, uvW, uvH, FlxColor.TRANSPARENT, lineStyle);
if (swagOutlines != null) swagOutlines.drawRect(i.uv.x * i.parent.width, i.uv.y * i.parent.height, uvW, uvH, FlxColor.TRANSPARENT, lineStyle);
// swagGraphic.setPosition(, );
// trace(uvH);
}
@ -181,26 +180,31 @@ class DebugBoundingState extends FlxState
txtOffsetShit = new FlxText(20, 20, 0, "", 20);
txtOffsetShit.setFormat(Paths.font("vcr.ttf"), 26, FlxColor.WHITE, LEFT, FlxTextBorderStyle.OUTLINE, FlxColor.BLACK);
txtOffsetShit.cameras = [hudCam];
if (hudCam != null) txtOffsetShit.cameras = [hudCam];
txtOffsetShit.y = FlxG.height - 20 - txtOffsetShit.height;
offsetView.add(txtOffsetShit);
var characters:Array<String> = CharacterDataParser.listCharacterIds();
characters = characters.filter(function(charId:String) {
var char = CharacterDataParser.fetchCharacterData(charId);
@:nullSafety(Off)
return char.renderType != AnimateAtlas;
});
characters.sort(SortUtil.alphabetically);
var charDropdown:DropDown = offsetEditorDialog.findComponent('characterDropdown', DropDown);
for (char in characters)
{
charDropdown.dataSource.add({text: char});
}
var charDropdown:Null<DropDown> = null;
if (offsetEditorDialog != null) charDropdown = offsetEditorDialog.findComponent('characterDropdown', DropDown);
charDropdown.onChange = function(e:UIEvent) {
loadAnimShit(e.data.text);
};
if (charDropdown != null){
for (char in characters)
{
charDropdown.dataSource.add({text: char});
}
charDropdown.onChange = function(e:UIEvent) {
loadAnimShit(e.data.text);
};
}
}
public var mouseOffset:FlxPoint = FlxPoint.get(0, 0);
@ -223,10 +227,12 @@ class DebugBoundingState extends FlxState
{
swagChar.animOffsets = [(FlxG.mouse.x - mouseOffset.x) * -1, (FlxG.mouse.y - mouseOffset.y) * -1];
swagChar.animationOffsets.set(offsetAnimationDropdown.value.id, swagChar.animOffsets);
if (offsetAnimationDropdown != null) swagChar.animationOffsets.set(offsetAnimationDropdown.value.id, swagChar.animOffsets);
txtOffsetShit.text = 'Offset: ' + swagChar.animOffsets;
txtOffsetShit.y = FlxG.height - 20 - txtOffsetShit.height;
if (txtOffsetShit != null) {
txtOffsetShit.text = 'Offset: ' + swagChar?.animOffsets;
txtOffsetShit.y = FlxG.height - 20 - txtOffsetShit.height;
}
}
if (FlxG.mouse.justReleased)
@ -247,18 +253,22 @@ class DebugBoundingState extends FlxState
swagText.setFormat(Paths.font("vcr.ttf"), 26, FlxColor.WHITE, LEFT, FlxTextBorderStyle.OUTLINE, FlxColor.BLACK);
swagText.scrollFactor.set();
for (text in txtGrp.members)
{
text.y -= swagText.height;
if (txtGrp != null){
var texts = txtGrp.members;
if (texts != null)
for (text in texts)
{
text.y -= swagText.height;
}
txtGrp.add(swagText);
}
txtGrp.add(swagText);
swagText.text = str + ": " + Std.string(value);
}
function clearInfo()
{
txtGrp.clear();
if (txtGrp != null) txtGrp.clear();
}
function checkLibrary(library:String)
@ -281,15 +291,17 @@ class DebugBoundingState extends FlxState
{
if (FlxG.keys.justPressed.ONE)
{
var lv:DropDown = offsetEditorDialog.findComponent("swapper", DropDown);
lv.selectedIndex = 0;
var lv:Null<DropDown> = null;
if (offsetEditorDialog != null) lv = offsetEditorDialog.findComponent("swapper", DropDown);
if (lv != null) lv.selectedIndex = 0;
curView = SPRITESHEET;
}
if (FlxG.keys.justReleased.TWO)
{
var lv:DropDown = offsetEditorDialog.findComponent("swapper", DropDown);
lv.selectedIndex = 1;
var lv:Null<DropDown> = null;
if (offsetEditorDialog != null) lv = offsetEditorDialog.findComponent("swapper", DropDown);
if (lv != null) lv.selectedIndex = 1;
curView = ANIMATIONS;
if (swagChar != null)
{
@ -298,33 +310,36 @@ class DebugBoundingState extends FlxState
}
}
switch (curView)
if (spriteSheetView != null && offsetView != null && offsetAnimationDropdown != null)
{
case SPRITESHEET:
spriteSheetView.visible = true;
offsetView.visible = false;
offsetView.active = false;
offsetAnimationDropdown.visible = false;
case ANIMATIONS:
spriteSheetView.visible = false;
offsetView.visible = true;
offsetView.active = true;
offsetAnimationDropdown.visible = true;
offsetControls();
mouseOffsetMovement();
switch (curView)
{
case SPRITESHEET:
spriteSheetView.visible = true;
offsetView.visible = false;
offsetView.active = false;
offsetAnimationDropdown.visible = false;
case ANIMATIONS:
spriteSheetView.visible = false;
offsetView.visible = true;
offsetView.active = true;
offsetAnimationDropdown.visible = true;
offsetControls();
mouseOffsetMovement();
}
}
if (FlxG.keys.justPressed.H) hudCam.visible = !hudCam.visible;
if (FlxG.keys.justPressed.H && hudCam != null) hudCam.visible = !hudCam.visible;
if (FlxG.keys.justPressed.F4) FlxG.switchState(() -> new MainMenuState());
MouseUtil.mouseCamDrag();
MouseUtil.mouseCamDrag(FlxG.camera.scroll);
if (!haxeUIFocused) MouseUtil.mouseWheelZoom();
// bg.scale.x = FlxG.camera.zoom;
// bg.scale.y = FlxG.camera.zoom;
bg.setGraphicSize(Std.int(bg.width / FlxG.camera.zoom));
if (bg != null) bg.setGraphicSize(Std.int(bg.width / FlxG.camera.zoom));
super.update(elapsed);
}
@ -333,29 +348,31 @@ class DebugBoundingState extends FlxState
{
if (FlxG.keys.justPressed.RBRACKET || FlxG.keys.justPressed.E)
{
if (offsetAnimationDropdown.selectedIndex + 1 <= offsetAnimationDropdown.dataSource.size)
if (offsetAnimationDropdown != null && offsetAnimationDropdown.selectedIndex + 1 <= offsetAnimationDropdown.dataSource.size)
{
offsetAnimationDropdown.selectedIndex += 1;
}
else
{
offsetAnimationDropdown.selectedIndex = 0;
if (offsetAnimationDropdown != null) offsetAnimationDropdown.selectedIndex = 0;
}
trace(offsetAnimationDropdown.selectedIndex);
trace(offsetAnimationDropdown.dataSource.size);
trace(offsetAnimationDropdown.value);
if (offsetAnimationDropdown != null){
trace(offsetAnimationDropdown.selectedIndex);
trace(offsetAnimationDropdown.dataSource.size);
trace(offsetAnimationDropdown.value);
}
trace(currentAnimationName);
playCharacterAnimation(currentAnimationName, true);
}
if (FlxG.keys.justPressed.LBRACKET || FlxG.keys.justPressed.Q)
{
if (offsetAnimationDropdown.selectedIndex - 1 >= 0)
if (offsetAnimationDropdown != null && offsetAnimationDropdown.selectedIndex - 1 >= 0)
{
offsetAnimationDropdown.selectedIndex -= 1;
}
else
{
offsetAnimationDropdown.selectedIndex = offsetAnimationDropdown.dataSource.size - 1;
if (offsetAnimationDropdown != null) offsetAnimationDropdown.selectedIndex = offsetAnimationDropdown.dataSource.size - 1;
}
playCharacterAnimation(currentAnimationName, true);
}
@ -377,7 +394,7 @@ class DebugBoundingState extends FlxState
if (targetLabel != currentAnimationName)
{
offsetAnimationDropdown.value = {id: targetLabel, text: targetLabel};
if (offsetAnimationDropdown != null) offsetAnimationDropdown.value = {id: targetLabel, text: targetLabel};
// Play the new animation if the IDs are the different.
// Override the onion skin.
@ -393,18 +410,18 @@ class DebugBoundingState extends FlxState
if (FlxG.keys.justPressed.F)
{
onionSkinChar.visible = !onionSkinChar.visible;
if (onionSkinChar != null) onionSkinChar.visible = !onionSkinChar.visible;
}
if (FlxG.keys.justPressed.G)
{
swagChar.flipX = !swagChar.flipX;
if (swagChar != null) swagChar.flipX = !swagChar.flipX;
}
// Plays the idle animation
if (FlxG.keys.justPressed.SPACE)
{
offsetAnimationDropdown.value = {id: 'idle', text: 'idle'};
if (offsetAnimationDropdown != null) offsetAnimationDropdown.value = {id: 'idle', text: 'idle'};
playCharacterAnimation(currentAnimationName, true);
}
@ -417,33 +434,39 @@ class DebugBoundingState extends FlxState
if (FlxG.keys.justPressed.RIGHT || FlxG.keys.justPressed.LEFT || FlxG.keys.justPressed.UP || FlxG.keys.justPressed.DOWN)
{
var animName = currentAnimationName;
var coolValues:Array<Float> = swagChar.animationOffsets.get(animName).copy();
if (swagChar != null)
{
var animName = currentAnimationName;
@:nullSafety(Off)
var coolValues:Array<Float> = swagChar.animationOffsets.get(animName).copy();
var multiplier:Int = 5;
var multiplier:Int = 5;
if (FlxG.keys.pressed.CONTROL) multiplier = 1;
if (FlxG.keys.pressed.CONTROL) multiplier = 1;
if (FlxG.keys.pressed.SHIFT) multiplier = 10;
if (FlxG.keys.pressed.SHIFT) multiplier = 10;
if (FlxG.keys.justPressed.RIGHT) coolValues[0] -= 1 * multiplier;
else if (FlxG.keys.justPressed.LEFT) coolValues[0] += 1 * multiplier;
else if (FlxG.keys.justPressed.UP) coolValues[1] += 1 * multiplier;
else if (FlxG.keys.justPressed.DOWN) coolValues[1] -= 1 * multiplier;
if (FlxG.keys.justPressed.RIGHT) coolValues[0] -= 1 * multiplier;
else if (FlxG.keys.justPressed.LEFT) coolValues[0] += 1 * multiplier;
else if (FlxG.keys.justPressed.UP) coolValues[1] += 1 * multiplier;
else if (FlxG.keys.justPressed.DOWN) coolValues[1] -= 1 * multiplier;
swagChar.animationOffsets.set(currentAnimationName, coolValues);
swagChar.playAnimation(animName);
swagChar.animationOffsets.set(currentAnimationName, coolValues);
swagChar.playAnimation(animName);
txtOffsetShit.text = 'Offset: ' + coolValues;
txtOffsetShit.y = FlxG.height - 20 - txtOffsetShit.height;
if (txtOffsetShit != null){
txtOffsetShit.text = 'Offset: ' + coolValues;
txtOffsetShit.y = FlxG.height - 20 - txtOffsetShit.height;
}
trace(animName);
trace(animName);
}
}
if (FlxG.keys.justPressed.ESCAPE)
{
var outputString = FlxG.keys.pressed.CONTROL ? buildOutputStringOld() : buildOutputStringNew();
saveOffsets(outputString, FlxG.keys.pressed.CONTROL ? swagChar.characterId + "Offsets.txt" : swagChar.characterId + ".json");
if (swagChar != null) saveOffsets(outputString, FlxG.keys.pressed.CONTROL ? swagChar.characterId + "Offsets.txt" : swagChar.characterId + ".json");
}
}
@ -451,9 +474,15 @@ class DebugBoundingState extends FlxState
{
var outputString:String = "";
for (i in swagChar.animationOffsets.keys())
if (swagChar != null)
{
outputString += i + " " + swagChar.animationOffsets.get(i)[0] + " " + swagChar.animationOffsets.get(i)[1] + "\n";
var keys = swagChar.animationOffsets.keys();
if (keys != null)
for (i in keys)
{
@:nullSafety(Off) // This bug is fixed in haxe 5.0 or something
outputString += '${i} ${swagChar.animationOffsets.get(i)[0]} ${swagChar.animationOffsets.get(i)[1]}\n';
}
}
outputString.trim();
@ -463,18 +492,21 @@ class DebugBoundingState extends FlxState
function buildOutputStringNew():String
{
var charData:CharacterData = Reflect.copy(swagChar._data);
var charData:Null<CharacterData> = Reflect.copy(swagChar?._data);
for (charDataAnim in charData.animations)
var animations = charData?.animations;
if (animations != null)
{
var animName:String = charDataAnim.name;
charDataAnim.offsets = swagChar.animationOffsets.get(animName);
for (charDataAnim in animations)
{
var animName:String = charDataAnim.name;
charDataAnim.offsets = swagChar?.animationOffsets.get(animName);
}
}
return SerializerUtil.toJSON(charData, true);
}
var swagChar:BaseCharacter;
var swagChar:Null<BaseCharacter>;
/*
Called when animation dropdown is changed!
@ -483,86 +515,97 @@ class DebugBoundingState extends FlxState
{
if (swagChar != null)
{
offsetView.remove(swagChar);
if (offsetView != null) offsetView.remove(swagChar);
swagChar.destroy();
}
swagChar = CharacterDataParser.fetchCharacter(char);
swagChar.x = 100;
swagChar.y = 100;
swagChar.debug = true;
offsetView.add(swagChar);
if (swagChar == null || swagChar.frames == null)
{
trace('ERROR: Failed to load character ${char}!');
}
else if (swagChar != null)
{
swagChar.x = 100;
swagChar.y = 100;
swagChar.debug = true;
if (offsetView != null) offsetView.add(swagChar);
generateOutlines(swagChar.frames.frames);
bf.pixels = swagChar.pixels;
if (bf != null) bf.pixels = swagChar.pixels;
clearInfo();
addInfo(swagChar._data.assetPath, "");
addInfo('Width', bf.width);
addInfo('Height', bf.height);
if (bf != null) addInfo('Width', bf.width);
if (bf != null) addInfo('Height', bf.height);
characterAnimNames = [];
for (i in swagChar.animationOffsets.keys())
var keys = swagChar.animationOffsets?.keys();
if (keys != null)
for (i in keys)
{
characterAnimNames.push(i);
trace(i);
trace(swagChar.animationOffsets[i]);
trace(swagChar?.animationOffsets[i]);
}
offsetAnimationDropdown.dataSource.clear();
if (offsetAnimationDropdown != null){
offsetAnimationDropdown.dataSource.clear();
for (charAnim in characterAnimNames)
{
trace('Adding ${charAnim} to HaxeUI dropdown');
offsetAnimationDropdown.dataSource.add({id: charAnim, text: charAnim});
}
offsetAnimationDropdown.selectedIndex = 0;
trace('Added ${offsetAnimationDropdown.dataSource.size} to HaxeUI dropdown');
offsetAnimationDropdown.onChange = function(event:UIEvent) {
if (event.data != null)
for (charAnim in characterAnimNames)
{
trace('Selected animation ${event.data.id}');
trace('Adding ${charAnim} to HaxeUI dropdown');
offsetAnimationDropdown.dataSource.add({id: charAnim, text: charAnim});
}
offsetAnimationDropdown.selectedIndex = 0;
trace('Added ${offsetAnimationDropdown.dataSource.size} to HaxeUI dropdown');
offsetAnimationDropdown.onChange = function(event:UIEvent) {
if (event?.data?.id == null) return;
trace('Selected animation ${event?.data?.id}');
playCharacterAnimation(event.data.id, true);
}
}
txtOffsetShit.text = 'Offset: ' + swagChar.animOffsets;
txtOffsetShit.y = FlxG.height - 20 - txtOffsetShit.height;
if (txtOffsetShit != null)
{
txtOffsetShit.text = 'Offset: ' + swagChar?.animOffsets;
txtOffsetShit.y = FlxG.height - 20 - txtOffsetShit.height;
}
dropDownSetup = true;
}
}
private var characterAnimNames:Array<String>;
private var characterAnimNames:Null<Array<String>>;
function playCharacterAnimation(str:String, setOnionSkin:Bool = true)
{
if (setOnionSkin)
if (setOnionSkin && onionSkinChar != null)
{
// clears the canvas
onionSkinChar.pixels.fillRect(new Rectangle(0, 0, FlxG.width * 2, FlxG.height * 2), 0x00000000);
onionSkinChar.stamp(swagChar, Std.int(swagChar.x), Std.int(swagChar.y));
if (swagChar != null) onionSkinChar.stamp(swagChar, Std.int(swagChar.x), Std.int(swagChar.y));
onionSkinChar.alpha = 0.6;
}
// var animName = characterAnimNames[Std.parseInt(str)];
var animName = str;
swagChar.playAnimation(animName, true); // trace();
trace(swagChar.animationOffsets.get(animName));
if (swagChar != null) swagChar.playAnimation(animName, true);
trace(swagChar?.animationOffsets.get(animName));
txtOffsetShit.text = 'Offset: ' + swagChar.animOffsets;
txtOffsetShit.y = FlxG.height - 20 - txtOffsetShit.height;
if (txtOffsetShit != null)
{
txtOffsetShit.text = 'Offset: ' + swagChar?.animOffsets;
txtOffsetShit.y = FlxG.height - 20 - txtOffsetShit.height;
}
}
var _file:FileReference;
var _file:Null<FileReference>;
function saveOffsets(saveString:String, fileName:String)
{
@ -578,11 +621,13 @@ class DebugBoundingState extends FlxState
function onSaveComplete(_):Void
{
if (_file != null){
_file.removeEventListener(Event.COMPLETE, onSaveComplete);
_file.removeEventListener(Event.CANCEL, onSaveCancel);
_file.removeEventListener(IOErrorEvent.IO_ERROR, onSaveError);
_file = null;
FlxG.log.notice("Successfully saved LEVEL DATA.");
}
}
/**
@ -590,10 +635,13 @@ class DebugBoundingState extends FlxState
*/
function onSaveCancel(_):Void
{
if (_file != null)
{
_file.removeEventListener(Event.COMPLETE, onSaveComplete);
_file.removeEventListener(Event.CANCEL, onSaveCancel);
_file.removeEventListener(IOErrorEvent.IO_ERROR, onSaveError);
_file = null;
}
}
/**
@ -601,11 +649,14 @@ class DebugBoundingState extends FlxState
*/
function onSaveError(_):Void
{
if (_file != null)
{
_file.removeEventListener(Event.COMPLETE, onSaveComplete);
_file.removeEventListener(Event.CANCEL, onSaveCancel);
_file.removeEventListener(IOErrorEvent.IO_ERROR, onSaveError);
_file = null;
FlxG.log.error("Problem saving Level data");
}
}
}

View file

@ -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";
}

View 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;
}
}

View file

@ -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;
}
/**