Reorganize a whole bunch of classes and perform syntax cleanup.

This commit is contained in:
EliteMasterEric
2023-11-07 04:04:22 -05:00
parent 618231954b
commit ce97a002cb
120 changed files with 462 additions and 938 deletions
+4 -3
View File
@@ -3,9 +3,10 @@ package funkin.ui.debug;
import flixel.math.FlxPoint;
import flixel.FlxObject;
import flixel.FlxSprite;
import funkin.MusicBeatSubState;
import funkin.ui.MusicBeatSubState;
import funkin.ui.TextMenuList;
import funkin.ui.debug.charting.ChartEditorState;
import funkin.ui.MusicBeatSubState;
class DebugMenuSubState extends MusicBeatSubState
{
@@ -85,13 +86,13 @@ class DebugMenuSubState extends MusicBeatSubState
function openAnimationEditor()
{
FlxG.switchState(new funkin.ui.animDebugShit.DebugBoundingState());
FlxG.switchState(new funkin.ui.debug.anim.DebugBoundingState());
trace('Animation Editor');
}
function testStickers()
{
openSubState(new funkin.ui.StickerSubState());
openSubState(new funkin.ui.transition.StickerSubState());
trace('opened stickers');
}
+44
View File
@@ -0,0 +1,44 @@
package funkin.ui.debug;
import openfl.text.TextFormat;
import openfl.system.System;
import openfl.text.TextField;
class MemoryCounter extends TextField
{
var memPeak:Float = 0;
static final BYTES_PER_MEG:Float = 1024 * 1024;
static final ROUND_TO:Float = 1 / 100;
public function new(x:Float = 10, y:Float = 10, color:Int = 0x000000)
{
super();
this.x = x;
this.y = y;
this.width = 500;
this.selectable = false;
this.mouseEnabled = false;
defaultTextFormat = new TextFormat("_sans", 12, color);
text = "RAM: ";
#if flash
addEventListener(Event.ENTER_FRAME, function(e) {
var time = Lib.getTimer();
__enterFrame(time - currentTime);
});
#end
}
// Event Handlers
@:noCompletion
#if !flash override #end function __enterFrame(deltaTime:Float):Void
{
var mem:Float = Math.round(System.totalMemory / BYTES_PER_MEG / ROUND_TO) * ROUND_TO;
if (mem > memPeak) memPeak = mem;
text = 'RAM: ${mem}mb / ${memPeak}mb';
}
}
@@ -0,0 +1,617 @@
package funkin.ui.debug.anim;
import funkin.util.SerializerUtil;
import funkin.play.character.CharacterData;
import flixel.FlxCamera;
import flixel.FlxSprite;
import flixel.FlxState;
import flixel.addons.display.FlxGridOverlay;
import flixel.addons.ui.FlxInputText;
import flixel.addons.ui.FlxUIDropDownMenu;
import flixel.graphics.frames.FlxAtlasFrames;
import flixel.graphics.frames.FlxFrame;
import flixel.group.FlxGroup;
import flixel.math.FlxPoint;
import flixel.sound.FlxSound;
import flixel.text.FlxText;
import flixel.util.FlxColor;
import funkin.util.MouseUtil;
import flixel.util.FlxSpriteUtil;
import flixel.util.FlxTimer;
import funkin.play.character.BaseCharacter;
import funkin.play.character.CharacterData.CharacterDataParser;
import funkin.play.character.SparrowCharacter;
import haxe.ui.RuntimeComponentBuilder;
import haxe.ui.components.DropDown;
import haxe.ui.core.Component;
import haxe.ui.events.ItemEvent;
import haxe.ui.events.UIEvent;
import funkin.ui.mainmenu.MainMenuState;
import lime.utils.Assets as LimeAssets;
import openfl.Assets;
import openfl.events.Event;
import openfl.events.IOErrorEvent;
import openfl.geom.Rectangle;
import openfl.net.FileReference;
import openfl.net.URLLoader;
import funkin.ui.mainmenu.MainMenuState;
import openfl.net.URLRequest;
import openfl.utils.ByteArray;
import funkin.input.Cursor;
import funkin.play.character.CharacterData.CharacterDataParser;
import funkin.util.SortUtil;
using flixel.util.FlxSpriteUtil;
#if web
import js.html.FileList;
#end
#if sys
import sys.io.File;
#end
class DebugBoundingState extends FlxState
{
/*
TODAY'S TO-DO
- Cleaner UI
*/
var bg:FlxSprite;
var fileInfo:FlxText;
var txtGrp:FlxGroup;
var hudCam:FlxCamera;
var curView:ANIMDEBUGVIEW = SPRITESHEET;
var spriteSheetView:FlxGroup;
var offsetView:FlxGroup;
var animDropDownMenu:FlxUIDropDownMenu;
var dropDownSetup:Bool = false;
var onionSkinChar:FlxSprite;
var txtOffsetShit:FlxText;
var uiStuff:Component;
override function create()
{
Paths.setCurrentLevel('week1');
var str = Paths.xml('ui/animation-editor/offset-editor-view');
uiStuff = RuntimeComponentBuilder.fromAsset(str);
// uiStuff.findComponent("btnViewSpriteSheet").onClick = _ -> curView = SPRITESHEET;
var dropdown:DropDown = cast uiStuff.findComponent("swapper");
dropdown.onChange = function(e:UIEvent) {
trace(e.type);
curView = cast e.data.curView;
trace(e.data);
// trace(e.data);
};
// lv.
// lv.onChange = function(e:UIEvent)
// {
// trace(e.type);
// // trace(e.data.curView);
// // var item:haxe.ui.core.ItemRenderer = cast e.target;
// trace(e.target);
// // if (e.type == "change")
// // {
// // curView = cast e.data;
// // }
// };
hudCam = new FlxCamera();
hudCam.bgColor.alpha = 0;
FlxG.cameras.add(hudCam, false);
bg = FlxGridOverlay.create(10, 10);
// bg = new FlxSprite().makeGraphic(FlxG.width, FlxG.height, FlxColor.GREEN);
bg.scrollFactor.set();
add(bg);
initSpritesheetView();
initOffsetView();
Cursor.show();
uiStuff.cameras = [hudCam];
add(uiStuff);
super.create();
}
var bf:FlxSprite;
var swagOutlines:FlxSprite;
function initSpritesheetView():Void
{
spriteSheetView = new FlxGroup();
add(spriteSheetView);
var tex = Paths.getSparrowAtlas('characters/BOYFRIEND');
// tex.frames[0].uv
bf = new FlxSprite();
bf.loadGraphic(tex.parent);
spriteSheetView.add(bf);
swagOutlines = new FlxSprite().makeGraphic(tex.parent.width, tex.parent.height, FlxColor.TRANSPARENT);
generateOutlines(tex.frames);
txtGrp = new FlxGroup();
txtGrp.cameras = [hudCam];
spriteSheetView.add(txtGrp);
addInfo('boyfriend.xml', "");
addInfo('Width', bf.width);
addInfo('Height', bf.height);
spriteSheetView.add(swagOutlines);
FlxG.stage.window.onDropFile.add(function(path:String) {
// WACKY ASS TESTING SHIT FOR WEB FILE LOADING??
#if web
var swagList:FileList = cast path;
var objShit = js.html.URL.createObjectURL(swagList.item(0));
trace(objShit);
var funnysound = new FlxSound().loadStream('https://cdn.discordapp.com/attachments/767500676166451231/817821618251759666/Flutter.mp3', false, false,
null, function() {
trace('LOADED SHIT??');
});
funnysound.volume = 1;
funnysound.play();
var urlShit = new URLLoader(new URLRequest(objShit));
new FlxTimer().start(3, function(tmr:FlxTimer) {
// music lol!
if (urlShit.dataFormat == BINARY)
{
// var daSwagBytes:ByteArray = urlShit.data;
// FlxG.sound.playMusic();
// trace('is binary!!');
}
trace(urlShit.dataFormat);
});
// remove(bf);
// FlxG.bitmap.removeByKey(Paths.image('characters/temp'));
// Assets.cache.clear();
// bf.loadGraphic(objShit);
// add(bf);
// trace(swagList.item(0).name);
// var urlShit = js.html.URL.createObjectURL(path);
#end
#if sys
trace("DROPPED FILE FROM: " + Std.string(path));
var newPath = "./" + Paths.image('characters/temp');
File.copy(path, newPath);
var swag = Paths.image('characters/temp');
if (bf != null) remove(bf);
FlxG.bitmap.removeByKey(Paths.image('characters/temp'));
Assets.cache.clear();
bf.loadGraphic(Paths.image('characters/temp'));
add(bf);
#end
});
}
function generateOutlines(frameShit:Array<FlxFrame>):Void
{
// swagOutlines.width = frameShit[0].parent.width;
// swagOutlines.height = frameShit[0].parent.height;
swagOutlines.pixels.fillRect(new Rectangle(0, 0, swagOutlines.width, swagOutlines.height), 0x00000000);
for (i in frameShit)
{
var lineStyle:LineStyle = {color: FlxColor.RED, thickness: 2};
var uvW:Float = (i.uv.width * i.parent.width) - (i.uv.x * i.parent.width);
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);
// swagGraphic.setPosition(, );
// trace(uvH);
}
}
function initOffsetView():Void
{
offsetView = new FlxGroup();
add(offsetView);
onionSkinChar = new FlxSprite().makeGraphic(FlxG.width * 2, FlxG.height * 2, FlxColor.TRANSPARENT);
onionSkinChar.visible = false;
offsetView.add(onionSkinChar);
txtOffsetShit = new FlxText(20, 20, 0, "", 20);
txtOffsetShit.setFormat(Paths.font("vcr.ttf"), 26, FlxColor.WHITE, LEFT, FlxTextBorderStyle.OUTLINE, FlxColor.BLACK);
txtOffsetShit.cameras = [hudCam];
offsetView.add(txtOffsetShit);
animDropDownMenu = new FlxUIDropDownMenu(0, 0, FlxUIDropDownMenu.makeStrIdLabelArray(['weed'], true));
animDropDownMenu.cameras = [hudCam];
// Move to bottom right corner
animDropDownMenu.x = FlxG.width - animDropDownMenu.width - 20;
animDropDownMenu.y = FlxG.height - animDropDownMenu.height - 20;
offsetView.add(animDropDownMenu);
var characters:Array<String> = CharacterDataParser.listCharacterIds();
characters = characters.filter(function(charId:String) {
var char = CharacterDataParser.fetchCharacterData(charId);
return char.renderType != AnimateAtlas;
});
characters.sort(SortUtil.alphabetically);
var charDropdown:DropDown = cast uiStuff.findComponent('characterDropdown');
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);
public var oldPos:FlxPoint = FlxPoint.get(0, 0);
function mouseOffsetMovement()
{
if (swagChar != null)
{
if (FlxG.mouse.justPressed)
{
mouseOffset.set(FlxG.mouse.x - -swagChar.animOffsets[0], FlxG.mouse.y - -swagChar.animOffsets[1]);
}
if (FlxG.mouse.pressed)
{
swagChar.animOffsets = [(FlxG.mouse.x - mouseOffset.x) * -1, (FlxG.mouse.y - mouseOffset.y) * -1];
swagChar.animationOffsets.set(animDropDownMenu.selectedLabel, swagChar.animOffsets);
txtOffsetShit.text = 'Offset: ' + swagChar.animOffsets;
}
}
}
function addInfo(str:String, value:Dynamic)
{
var swagText:FlxText = new FlxText(10, 10 + (28 * txtGrp.length));
swagText.setFormat(Paths.font("vcr.ttf"), 26, FlxColor.WHITE, LEFT, FlxTextBorderStyle.OUTLINE, FlxColor.BLACK);
swagText.scrollFactor.set();
txtGrp.add(swagText);
swagText.text = str + ": " + Std.string(value);
}
function clearInfo()
{
txtGrp.clear();
}
function checkLibrary(library:String)
{
trace(Assets.hasLibrary(library));
if (Assets.getLibrary(library) == null)
{
@:privateAccess
if (!LimeAssets.libraryPaths.exists(library)) throw "Missing library: " + library;
// var callback = callbacks.add("library:" + library);
Assets.loadLibrary(library).onComplete(function(_) {
trace('LOADED... awesomeness...');
// callback();
});
}
}
override function update(elapsed:Float)
{
if (FlxG.keys.justPressed.ONE)
{
var lv:DropDown = cast uiStuff.findComponent("swapper");
lv.selectedIndex = 0;
curView = SPRITESHEET;
}
if (FlxG.keys.justReleased.TWO)
{
var lv:DropDown = cast uiStuff.findComponent("swapper");
lv.selectedIndex = 1;
curView = ANIMATIONS;
if (swagChar != null)
{
FlxG.camera.focusOn(swagChar.getMidpoint());
FlxG.camera.zoom = 0.95;
}
}
switch (curView)
{
case SPRITESHEET:
spriteSheetView.visible = true;
offsetView.visible = false;
offsetView.active = false;
case ANIMATIONS:
spriteSheetView.visible = false;
offsetView.visible = true;
offsetView.active = true;
offsetControls();
mouseOffsetMovement();
}
if (FlxG.keys.justPressed.H) hudCam.visible = !hudCam.visible;
if (FlxG.keys.justPressed.F4) FlxG.switchState(new MainMenuState());
MouseUtil.mouseCamDrag();
MouseUtil.mouseWheelZoom();
// bg.scale.x = FlxG.camera.zoom;
// bg.scale.y = FlxG.camera.zoom;
bg.setGraphicSize(Std.int(bg.width / FlxG.camera.zoom));
super.update(elapsed);
}
function offsetControls():Void
{
if (FlxG.keys.justPressed.RBRACKET || FlxG.keys.justPressed.E)
{
if (Std.parseInt(animDropDownMenu.selectedId) + 1 <= animDropDownMenu.length)
animDropDownMenu.selectedId = Std.string(Std.parseInt(animDropDownMenu.selectedId)
+ 1);
else
animDropDownMenu.selectedId = Std.string(0);
playCharacterAnimation(animDropDownMenu.selectedId, true);
}
if (FlxG.keys.justPressed.LBRACKET || FlxG.keys.justPressed.Q)
{
if (Std.parseInt(animDropDownMenu.selectedId) - 1 >= 0) animDropDownMenu.selectedId = Std.string(Std.parseInt(animDropDownMenu.selectedId) - 1);
else
animDropDownMenu.selectedId = Std.string(animDropDownMenu.length - 1);
playCharacterAnimation(animDropDownMenu.selectedId, true);
}
// Keyboards controls for general WASD "movement"
// modifies the animDropDownMenu so that it's properly updated and shit
// and then it's just played and updated from the animDropDownMenu callback, which is set in the loadAnimShit() function probabbly
if (FlxG.keys.justPressed.W || FlxG.keys.justPressed.S || FlxG.keys.justPressed.D || FlxG.keys.justPressed.A)
{
var suffix:String = '';
var targetLabel:String = '';
if (FlxG.keys.pressed.SHIFT) suffix = 'miss';
if (FlxG.keys.justPressed.W) targetLabel = 'singUP$suffix';
if (FlxG.keys.justPressed.S) targetLabel = 'singDOWN$suffix';
if (FlxG.keys.justPressed.A) targetLabel = 'singLEFT$suffix';
if (FlxG.keys.justPressed.D) targetLabel = 'singRIGHT$suffix';
if (targetLabel != animDropDownMenu.selectedLabel)
{
// Play the new animation if the IDs are the different.
// Override the onion skin.
animDropDownMenu.selectedLabel = targetLabel;
playCharacterAnimation(animDropDownMenu.selectedId, true);
}
else
{
// Replay the current animation if the IDs are the same.
// Don't override the onion skin.
playCharacterAnimation(animDropDownMenu.selectedId, false);
}
}
if (FlxG.keys.justPressed.F)
{
onionSkinChar.visible = !onionSkinChar.visible;
}
// Plays the idle animation
if (FlxG.keys.justPressed.SPACE)
{
animDropDownMenu.selectedLabel = 'idle';
playCharacterAnimation(animDropDownMenu.selectedId, true);
}
// Playback the animation
if (FlxG.keys.justPressed.ENTER) playCharacterAnimation(animDropDownMenu.selectedId, false);
if (FlxG.keys.justPressed.RIGHT || FlxG.keys.justPressed.LEFT || FlxG.keys.justPressed.UP || FlxG.keys.justPressed.DOWN)
{
var animName = animDropDownMenu.selectedLabel;
var coolValues:Array<Float> = swagChar.animationOffsets.get(animName).copy();
var multiplier:Int = 5;
if (FlxG.keys.pressed.CONTROL) multiplier = 1;
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;
swagChar.animationOffsets.set(animDropDownMenu.selectedLabel, coolValues);
swagChar.playAnimation(animName);
txtOffsetShit.text = 'Offset: ' + coolValues;
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");
}
}
function buildOutputStringOld():String
{
var outputString:String = "";
for (i in swagChar.animationOffsets.keys())
{
outputString += i + " " + swagChar.animationOffsets.get(i)[0] + " " + swagChar.animationOffsets.get(i)[1] + "\n";
}
outputString.trim();
return outputString;
}
function buildOutputStringNew():String
{
var charData:CharacterData = Reflect.copy(swagChar._data);
for (charDataAnim in charData.animations)
{
var animName:String = charDataAnim.name;
charDataAnim.offsets = swagChar.animationOffsets.get(animName);
}
return SerializerUtil.toJSON(charData, true);
}
var swagChar:BaseCharacter;
/*
Called when animation dropdown is changed!
*/
function loadAnimShit(char:String)
{
if (swagChar != null)
{
offsetView.remove(swagChar);
swagChar.destroy();
}
swagChar = CharacterDataParser.fetchCharacter(char);
swagChar.x = 100;
swagChar.y = 100;
// swagChar.debugMode = true;
offsetView.add(swagChar);
generateOutlines(swagChar.frames.frames);
bf.pixels = swagChar.pixels;
clearInfo();
addInfo(swagChar._data.assetPath, "");
addInfo('Width', bf.width);
addInfo('Height', bf.height);
characterAnimNames = [];
for (i in swagChar.animationOffsets.keys())
{
characterAnimNames.push(i);
trace(i);
trace(swagChar.animationOffsets[i]);
}
animDropDownMenu.setData(FlxUIDropDownMenu.makeStrIdLabelArray(characterAnimNames, true));
animDropDownMenu.callback = function(str:String) {
playCharacterAnimation(str, true);
};
txtOffsetShit.text = 'Offset: ' + swagChar.animOffsets;
dropDownSetup = true;
}
private var characterAnimNames:Array<String>;
function playCharacterAnimation(str:String, setOnionSkin:Bool = true)
{
if (setOnionSkin)
{
// 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));
onionSkinChar.alpha = 0.6;
}
var animName = characterAnimNames[Std.parseInt(str)];
swagChar.playAnimation(animName, true); // trace();
trace(swagChar.animationOffsets.get(animName));
txtOffsetShit.text = 'Offset: ' + swagChar.animOffsets;
}
var _file:FileReference;
function saveOffsets(saveString:String, fileName:String)
{
if ((saveString != null) && (saveString.length > 0))
{
_file = new FileReference();
_file.addEventListener(Event.COMPLETE, onSaveComplete);
_file.addEventListener(Event.CANCEL, onSaveCancel);
_file.addEventListener(IOErrorEvent.IO_ERROR, onSaveError);
_file.save(saveString,);
}
}
function onSaveComplete(_):Void
{
_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.");
}
/**
* Called when the save file dialog is cancelled.
*/
function onSaveCancel(_):Void
{
_file.removeEventListener(Event.COMPLETE, onSaveComplete);
_file.removeEventListener(Event.CANCEL, onSaveCancel);
_file.removeEventListener(IOErrorEvent.IO_ERROR, onSaveError);
_file = null;
}
/**
* Called if there is an error while saving the gameplay recording.
*/
function onSaveError(_):Void
{
_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");
}
}
enum abstract ANIMDEBUGVIEW(String)
{
var SPRITESHEET;
var ANIMATIONS;
}
@@ -0,0 +1,49 @@
package funkin.ui.debug.anim;
import flixel.FlxG;
import funkin.graphics.adobeanimate.FlxAtlasSprite;
import funkin.ui.MusicBeatState;
/**
* A simple test of FlxAnimate.
* Delete this later?
*/
class FlxAnimateTest extends MusicBeatState
{
var sprite:FlxAtlasSprite;
public function new()
{
super();
this.bgColor = 0xFF999999;
}
public override function create():Void
{
super.create();
sprite = new FlxAtlasSprite(0, 0, 'shared:assets/shared/images/characters/tankman');
add(sprite);
sprite.playAnimation('idle');
}
public override function update(elapsed:Float):Void
{
super.update(elapsed);
if (FlxG.keys.justPressed.SPACE) sprite.playAnimation('idle');
if (FlxG.keys.justPressed.W) sprite.playAnimation('singUP');
if (FlxG.keys.justPressed.A) sprite.playAnimation('singLEFT');
if (FlxG.keys.justPressed.S) sprite.playAnimation('singDOWN');
if (FlxG.keys.justPressed.D) sprite.playAnimation('singRIGHT');
if (FlxG.keys.justPressed.J) sprite.playAnimation('hehPrettyGood');
if (FlxG.keys.justPressed.K) sprite.playAnimation('ugh');
}
}
@@ -16,6 +16,7 @@ import flixel.tweens.FlxEase;
import flixel.tweens.FlxTween;
import flixel.tweens.misc.VarTween;
import flixel.util.FlxColor;
import funkin.ui.mainmenu.MainMenuState;
import flixel.util.FlxSort;
import flixel.util.FlxTimer;
import funkin.audio.visualize.PolygonSpectogram;
@@ -31,7 +32,7 @@ import funkin.input.TurboKeyHandler;
import funkin.modding.events.ScriptEvent;
import funkin.play.character.BaseCharacter.CharacterType;
import funkin.play.character.CharacterData;
import funkin.play.HealthIcon;
import funkin.play.components.HealthIcon;
import funkin.play.notes.NoteSprite;
import funkin.play.PlayState;
import funkin.play.song.Song;
@@ -0,0 +1,160 @@
package funkin.ui.debug.latency;
import flash.display.Graphics;
import flash.display.Shape;
import flash.display.Sprite;
import flash.text.TextField;
import flash.text.TextFormatAlign;
import flixel.math.FlxMath;
import flixel.system.debug.DebuggerUtil;
import flixel.system.debug.stats.Stats;
import flixel.util.FlxColor;
import flixel.util.FlxDestroyUtil;
/**
* This is a helper function for the stats window to draw a graph with given values.
* SHAMELESSLY STOLEN FROM FLIXEL
* https://github.com/HaxeFlixel/flixel/blob/master/flixel/system/debug/stats/StatsGraph.hx
*/
#if FLX_DEBUG
class CoolStatsGraph extends Sprite
{
static inline var AXIS_COLOR:FlxColor = 0xffffff;
static inline var AXIS_ALPHA:Float = 0.5;
static inline var HISTORY_MAX:Int = 500;
public var minLabel:TextField;
public var curLabel:TextField;
public var maxLabel:TextField;
public var avgLabel:TextField;
public var minValue:Float = FlxMath.MAX_VALUE_FLOAT;
public var maxValue:Float = FlxMath.MIN_VALUE_FLOAT;
public var graphColor:FlxColor;
public var history:Array<Float> = [];
var _axis:Shape;
var _width:Int;
var _height:Int;
var _unit:String;
var _labelWidth:Int;
var _label:String;
public function new(X:Int, Y:Int, Width:Int, Height:Int, GraphColor:FlxColor, Unit:String, LabelWidth:Int = 45, ?Label:String)
{
super();
x = X;
y = Y;
_width = Width - LabelWidth;
_height = Height;
graphColor = GraphColor;
_unit = Unit;
_labelWidth = LabelWidth;
_label = (Label == null) ? "" : Label;
_axis = new Shape();
_axis.x = _labelWidth + 10;
maxLabel = DebuggerUtil.createTextField(0, 0, Stats.LABEL_COLOR, Stats.TEXT_SIZE);
curLabel = DebuggerUtil.createTextField(0, (_height / 2) - (Stats.TEXT_SIZE / 2), graphColor, Stats.TEXT_SIZE);
minLabel = DebuggerUtil.createTextField(0, _height - Stats.TEXT_SIZE, Stats.LABEL_COLOR, Stats.TEXT_SIZE);
avgLabel = DebuggerUtil.createTextField(_labelWidth + 20, (_height / 2) - (Stats.TEXT_SIZE / 2) - 10, Stats.LABEL_COLOR, Stats.TEXT_SIZE);
avgLabel.width = _width;
avgLabel.defaultTextFormat.align = TextFormatAlign.CENTER;
avgLabel.alpha = 0.5;
addChild(_axis);
addChild(maxLabel);
addChild(curLabel);
addChild(minLabel);
addChild(avgLabel);
drawAxes();
}
/**
* Redraws the axes of the graph.
*/
function drawAxes():Void
{
var gfx = _axis.graphics;
gfx.clear();
gfx.lineStyle(1, AXIS_COLOR, AXIS_ALPHA);
// y-Axis
gfx.moveTo(0, 0);
gfx.lineTo(0, _height);
// x-Axis
gfx.moveTo(0, _height);
gfx.lineTo(_width, _height);
}
/**
* Redraws the graph based on the values stored in the history.
*/
function drawGraph():Void
{
var gfx:Graphics = graphics;
gfx.clear();
gfx.lineStyle(1, graphColor, 1);
var inc:Float = _width / (HISTORY_MAX - 1);
var range:Float = Math.max(maxValue - minValue, maxValue * 0.1);
var graphX = _axis.x + 1;
for (i in 0...history.length)
{
var value = (history[i] - minValue) / range;
var pointY = (-value * _height - 1) + _height;
if (i == 0) gfx.moveTo(graphX, _axis.y + pointY);
gfx.lineTo(graphX + (i * inc), pointY);
}
}
public function update(Value:Float):Void
{
history.unshift(Value);
if (history.length > HISTORY_MAX) history.pop();
// Update range
maxValue = Math.max(maxValue, Value);
minValue = Math.min(minValue, Value);
minLabel.text = formatValue(minValue);
curLabel.text = formatValue(Value);
maxLabel.text = formatValue(maxValue);
avgLabel.text = _label + "\nAvg: " + formatValue(average());
drawGraph();
}
function formatValue(value:Float):String
{
return FlxMath.roundDecimal(value, Stats.DECIMALS) + " " + _unit;
}
public function average():Float
{
var sum:Float = 0;
for (value in history)
sum += value;
return sum / history.length;
}
public function destroy():Void
{
_axis = FlxDestroyUtil.removeChild(this, _axis);
minLabel = FlxDestroyUtil.removeChild(this, minLabel);
curLabel = FlxDestroyUtil.removeChild(this, curLabel);
maxLabel = FlxDestroyUtil.removeChild(this, maxLabel);
avgLabel = FlxDestroyUtil.removeChild(this, avgLabel);
history = null;
}
}
#end
@@ -0,0 +1,319 @@
package funkin.ui.debug.latency;
import funkin.data.notestyle.NoteStyleRegistry;
import flixel.FlxSprite;
import flixel.FlxSubState;
import flixel.group.FlxGroup;
import flixel.group.FlxGroup.FlxTypedGroup;
import flixel.math.FlxMath;
import funkin.ui.MusicBeatSubState;
import flixel.sound.FlxSound;
import flixel.system.debug.stats.StatsGraph;
import flixel.text.FlxText;
import flixel.util.FlxColor;
import funkin.audio.visualize.PolygonSpectogram;
import funkin.play.notes.NoteSprite;
import funkin.ui.debug.latency.CoolStatsGraph;
import haxe.Timer;
import openfl.events.KeyboardEvent;
class LatencyState extends MusicBeatSubState
{
var offsetText:FlxText;
var noteGrp:FlxTypedGroup<NoteSprite>;
var strumLine:FlxSprite;
var blocks:FlxTypedGroup<FlxSprite>;
var songPosVis:FlxSprite;
var songVisFollowVideo:FlxSprite;
var songVisFollowAudio:FlxSprite;
var beatTrail:FlxSprite;
var diffGrp:FlxTypedGroup<FlxText>;
var offsetsPerBeat:Array<Int> = [];
var swagSong:HomemadeMusic;
#if FLX_DEBUG
var funnyStatsGraph:CoolStatsGraph;
var realStats:CoolStatsGraph;
#end
override function create()
{
swagSong = new HomemadeMusic();
swagSong.loadEmbedded(Paths.sound('soundTest'), true);
FlxG.sound.music = swagSong;
FlxG.sound.music.play();
#if FLX_DEBUG
funnyStatsGraph = new CoolStatsGraph(0, Std.int(FlxG.height / 2), FlxG.width, Std.int(FlxG.height / 2), FlxColor.PINK, "time");
FlxG.addChildBelowMouse(funnyStatsGraph);
realStats = new CoolStatsGraph(0, Std.int(FlxG.height / 2), FlxG.width, Std.int(FlxG.height / 2), FlxColor.YELLOW, "REAL");
FlxG.addChildBelowMouse(realStats);
#end
FlxG.stage.addEventListener(KeyboardEvent.KEY_DOWN, key -> {
trace(key.charCode);
if (key.charCode == 120) generateBeatStuff();
trace("\tEVENT PRESS: \t" + FlxG.sound.music.time + " " + Timer.stamp());
// trace(FlxG.sound.music.prevTimestamp);
trace(FlxG.sound.music.time);
trace("\tFR FR PRESS: \t" + swagSong.getTimeWithDiff());
// trace("\tREDDIT: \t" + swagSong.frfrTime + " " + Timer.stamp());
@:privateAccess
trace("\tREDDIT: \t" + FlxG.sound.music._channel.position + " " + Timer.stamp());
// trace("EVENT LISTENER: " + key);
});
// FlxG.sound.playMusic(Paths.sound('soundTest'));
// funnyStatsGraph.hi
Conductor.forceBPM(60);
noteGrp = new FlxTypedGroup<NoteSprite>();
add(noteGrp);
diffGrp = new FlxTypedGroup<FlxText>();
add(diffGrp);
// var musSpec:PolygonSpectogram = new PolygonSpectogram(FlxG.sound.music, FlxColor.RED, FlxG.height, Math.floor(FlxG.height / 2));
// musSpec.x += 170;
// musSpec.scrollFactor.set();
// musSpec.waveAmplitude = 100;
// musSpec.realtimeVisLenght = 0.45;
// // musSpec.visType = FREQUENCIES;
// add(musSpec);
for (beat in 0...Math.floor(FlxG.sound.music.length / Conductor.beatLengthMs))
{
var beatTick:FlxSprite = new FlxSprite(songPosToX(beat * Conductor.beatLengthMs), FlxG.height - 15);
beatTick.makeGraphic(2, 15);
beatTick.alpha = 0.3;
add(beatTick);
var offsetTxt:FlxText = new FlxText(songPosToX(beat * Conductor.beatLengthMs), FlxG.height - 26, 0, "swag");
offsetTxt.alpha = 0.5;
diffGrp.add(offsetTxt);
offsetsPerBeat.push(0);
}
songVisFollowAudio = new FlxSprite(0, FlxG.height - 20).makeGraphic(2, 20, FlxColor.YELLOW);
add(songVisFollowAudio);
songVisFollowVideo = new FlxSprite(0, FlxG.height - 20).makeGraphic(2, 20, FlxColor.BLUE);
add(songVisFollowVideo);
songPosVis = new FlxSprite(0, FlxG.height - 20).makeGraphic(2, 20, FlxColor.RED);
add(songPosVis);
beatTrail = new FlxSprite(0, songPosVis.y).makeGraphic(2, 20, FlxColor.PURPLE);
beatTrail.alpha = 0.7;
add(beatTrail);
blocks = new FlxTypedGroup<FlxSprite>();
add(blocks);
for (i in 0...8)
{
var block = new FlxSprite(2, 50 * i).makeGraphic(48, 48);
block.alpha = 0;
blocks.add(block);
}
for (i in 0...32)
{
var note:NoteSprite = new NoteSprite(NoteStyleRegistry.instance.fetchDefault(), Conductor.beatLengthMs * i);
noteGrp.add(note);
}
offsetText = new FlxText();
offsetText.screenCenter();
add(offsetText);
strumLine = new FlxSprite(FlxG.width / 2, 100).makeGraphic(FlxG.width, 5);
add(strumLine);
super.create();
}
override function stepHit():Bool
{
if (Conductor.currentStep % 4 == 2)
{
blocks.members[((Conductor.currentBeat % 8) + 1) % 8].alpha = 0.5;
}
return super.stepHit();
}
override function beatHit():Bool
{
if (Conductor.currentBeat % 8 == 0) blocks.forEach(blok -> {
blok.alpha = 0;
});
blocks.members[Conductor.currentBeat % 8].alpha = 1;
// block.visible = !block.visible;
return super.beatHit();
}
override function update(elapsed:Float)
{
/* trace("1: " + swagSong.frfrTime);
@:privateAccess
trace(FlxG.sound.music._channel.position);
*/
#if FLX_DEBUG
funnyStatsGraph.update(FlxG.sound.music.time % 500);
realStats.update(swagSong.getTimeWithDiff() % 500);
#end
if (FlxG.keys.justPressed.S)
{
trace("\tUPDATE PRESS: \t" + FlxG.sound.music.time + " " + Timer.stamp());
}
if (FlxG.keys.justPressed.SPACE)
{
if (FlxG.sound.music.playing) FlxG.sound.music.pause();
else
FlxG.sound.music.resume();
}
if (FlxG.keys.pressed.D) FlxG.sound.music.time += 1000 * FlxG.elapsed;
Conductor.update(swagSong.getTimeWithDiff() - Conductor.offset);
// Conductor.songPosition += (Timer.stamp() * 1000) - FlxG.sound.music.prevTimestamp;
songPosVis.x = songPosToX(Conductor.songPosition);
songVisFollowAudio.x = songPosToX(Conductor.songPosition - Conductor.audioOffset);
songVisFollowVideo.x = songPosToX(Conductor.songPosition - Conductor.visualOffset);
offsetText.text = "AUDIO Offset: " + Conductor.audioOffset + "ms";
offsetText.text += "\nVIDOE Offset: " + Conductor.visualOffset + "ms";
offsetText.text += "\ncurrentStep: " + Conductor.currentStep;
offsetText.text += "\ncurrentBeat: " + Conductor.currentBeat;
var avgOffsetInput:Float = 0;
for (offsetThing in offsetsPerBeat)
avgOffsetInput += offsetThing;
avgOffsetInput /= offsetsPerBeat.length;
offsetText.text += "\naverage input offset needed: " + avgOffsetInput;
var multiply:Float = 10;
if (FlxG.keys.pressed.SHIFT) multiply = 1;
if (FlxG.keys.pressed.CONTROL)
{
if (FlxG.keys.justPressed.RIGHT)
{
Conductor.audioOffset += 1 * multiply;
}
if (FlxG.keys.justPressed.LEFT)
{
Conductor.audioOffset -= 1 * multiply;
}
}
else
{
if (FlxG.keys.justPressed.RIGHT)
{
Conductor.visualOffset += 1 * multiply;
}
if (FlxG.keys.justPressed.LEFT)
{
Conductor.visualOffset -= 1 * multiply;
}
}
/* if (FlxG.keys.justPressed.SPACE)
{
FlxG.sound.music.stop();
FlxG.resetState();
}*/
noteGrp.forEach(function(daNote:NoteSprite) {
daNote.y = (strumLine.y - ((Conductor.songPosition - Conductor.audioOffset) - daNote.noteData.time) * 0.45);
daNote.x = strumLine.x + 30;
if (daNote.y < strumLine.y) daNote.alpha = 0.5;
if (daNote.y < 0 - daNote.height)
{
daNote.alpha = 1;
// daNote.data.strumTime += Conductor.beatLengthMs * 8;
}
});
super.update(elapsed);
}
function generateBeatStuff()
{
Conductor.songPosition = swagSong.getTimeWithDiff();
var closestBeat:Int = Math.round(Conductor.songPosition / Conductor.beatLengthMs) % diffGrp.members.length;
var getDiff:Float = Conductor.songPosition - (closestBeat * Conductor.beatLengthMs);
getDiff -= Conductor.visualOffset;
// lil fix for end of song
if (closestBeat == 0 && getDiff >= Conductor.beatLengthMs * 2) getDiff -= FlxG.sound.music.length;
trace("\tDISTANCE TO CLOSEST BEAT: " + getDiff + "ms");
trace("\tCLOSEST BEAT: " + closestBeat);
beatTrail.x = songPosVis.x;
diffGrp.members[closestBeat].text = getDiff + "ms";
offsetsPerBeat[closestBeat] = Std.int(getDiff);
}
function songPosToX(pos:Float):Float
{
return FlxMath.remapToRange(pos, 0, FlxG.sound.music.length, 0, FlxG.width);
}
}
class HomemadeMusic extends FlxSound
{
public var prevTimestamp:Int = 0;
public var timeWithDiff:Float = 0;
public function new()
{
super();
}
var prevTime:Float = 0;
override function update(elapsed:Float)
{
super.update(elapsed);
if (prevTime != time)
{
prevTime = time;
prevTimestamp = Std.int(Timer.stamp() * 1000);
}
}
public function getTimeWithDiff():Float
{
return time + (Std.int(Timer.stamp() * 1000) - prevTimestamp);
}
}
@@ -0,0 +1,9 @@
package funkin.ui.debug.stage;
class CharStage extends SprStage
{
public function new(x:Float, y:Float, dragShitFunc:SprStage->Void)
{
super(x, y, dragShitFunc);
}
}
+49
View File
@@ -0,0 +1,49 @@
package funkin.ui.debug.stage;
import flixel.FlxSprite;
import flixel.input.mouse.FlxMouseEvent;
import flixel.math.FlxPoint;
class SprStage extends FlxSprite
{
public var imgName:String = '';
public var layer:Int = 0;
public var mousePressing:Bool = false;
public var mouseOffset:FlxPoint = FlxPoint.get(0, 0);
public var oldPos:FlxPoint = FlxPoint.get(0, 0);
public function new(?x:Float = 0, ?y:Float = 0, dragShitFunc:SprStage->Void)
{
super(x, y);
FlxMouseEvent.add(this, dragShitFunc, null, function(spr:SprStage) {
if (isSelected() || StageBuilderState.curTool == SELECT) alpha = 0.5;
}, function(spr:SprStage) {
alpha = 1;
}, false, true, true);
}
public function isSelected():Bool
{
return StageBuilderState.curSelectedSpr == this;
}
override function update(elapsed:Float)
{
super.update(elapsed);
if (mousePressing && isSelected())
{
this.x = FlxG.mouse.x - mouseOffset.x;
this.y = FlxG.mouse.y - mouseOffset.y;
}
if (FlxG.mouse.justReleased)
{
mousePressing = false;
StageBuilderState.changeTool(GRAB);
}
}
}
@@ -0,0 +1,435 @@
package funkin.ui.debug.stage;
import flixel.FlxCamera;
import flixel.FlxSprite;
import flixel.addons.display.FlxGridOverlay;
import flixel.group.FlxGroup;
import flixel.input.mouse.FlxMouseButton.FlxMouseButtonID;
import flixel.input.mouse.FlxMouseEvent;
import flixel.math.FlxPoint;
import funkin.ui.MusicBeatState;
import flixel.text.FlxText;
import flixel.ui.FlxButton;
import flixel.util.FlxColor;
import flixel.util.FlxSort;
import funkin.util.MouseUtil;
import flixel.util.FlxTimer;
class StageBuilderState extends MusicBeatState
{
var hudGrp:FlxGroup;
var textInfo:FlxText;
var sprGrp:FlxTypedGroup<SprStage>;
// var snd:Sound;
// var sndChannel:SoundChannel;
var hudCam:FlxCamera;
override function create()
{
hudCam = new FlxCamera();
hudCam.bgColor.alpha = 0;
FlxG.cameras.add(hudCam, false);
super.create();
FlxG.mouse.visible = true;
// var alsoSnd:FlxSound = new FlxSound();
// snd = new Sound();
// var swagBytes:ByteArray = new ByteArray(8192);
// for (shit in 0...8192)
// {
// swagBytes.writeFloat(Math.sin((shit) / Math.PI) * 0.25);
// swagBytes.writeFloat(Math.sin((shit) / Math.PI) * 0.25);
// trace('wweseosme???');
// }
// snd.__buffer = AudioBuffer.fromBytes(swagBytes);
// snd.dispatchEvent(new Event(Event.COMPLETE));
// swagBytes.writeFloat(Math.sin((shit + event.position) / Math.PI) * 0.25);
// swagBytes.writeFloat(Math.sin((shit + event.position) / Math.PI) * 0.25);
// function sineShit(event:SampleDataEvent):Void
// {
// for (shit in 0...8192)
// {
// event.data.writeFloat(Math.sin((shit + event.position) / Math.PI) * 0.25);
// event.data.writeFloat(Math.sin((shit + event.position) / Math.PI) * 0.25);
// trace('wweseosme???');
// }
// }
// snd.addEventListener(SampleDataEvent.SAMPLE_DATA, sineShit);
// snd.__buffer.
// snd = Assets.getSound(Paths.music('freakyMenu/freakyMenu'));
// for (thing in snd.load)
// thing = Std.int(thing / 2);
// snd.play();
// trace(snd.__buffer.data.toBytes().getData().bytes);
var bg:FlxSprite = FlxGridOverlay.create(10, 10);
add(bg);
sprGrp = new FlxTypedGroup<SprStage>();
add(sprGrp);
hudGrp = new FlxGroup();
hudGrp.cameras = [hudCam];
add(hudGrp);
textInfo = new FlxText(10, 80, 0, "", 24);
textInfo.setFormat(Paths.font("vcr.ttf"), 20, FlxColor.WHITE, LEFT, FlxTextBorderStyle.OUTLINE, FlxColor.BLACK);
textInfo.scrollFactor.set();
hudGrp.add(textInfo);
var imgBtn:FlxButton = new FlxButton(20, 20, "Load Image", loadImage);
hudGrp.add(imgBtn);
var saveSceneBtn:FlxButton = new FlxButton(20, 50, "Save Scene", saveScene);
hudGrp.add(saveSceneBtn);
#if desktop
FlxG.stage.window.onDropFile.add(function(path:String) {
trace("DROPPED FILE FROM: " + Std.string(path));
var fileName:String = path.split('\\').pop();
var fileNameNoExt:String = fileName.split('.')[0];
var newPath = './' + Paths.image('stageBuild/' + fileNameNoExt);
// sys.io.File.copy(path, newPath);
// trace(sys.io.File.getBytes(Std.string(path)).toString());
// FlxG.bitmap.add('assets/preload/images/stageBuild/eltonJohn.png');
sys.io.File.copy(path, './' + Paths.image('stageBuild/stageTempImg'));
var fo = sys.io.File.write(newPath);
fo.write(sys.io.File.getBytes(path));
new FlxTimer().start(0.2, function(tmr) {
var awesomeImg:SprStage = new SprStage(FlxG.mouse.x, FlxG.mouse.y, sprDragShitFunc);
awesomeImg.loadGraphic(Paths.image('stageBuild/stageTempImg'), false, 0, 0, true);
awesomeImg.layer = sprGrp.members.length;
awesomeImg.imgName = fileName;
sprGrp.add(awesomeImg);
curFocus = MOVEMENTS;
});
// Load the image shit by
// 1. reading the image file names
// 2. copy to stage temp like normal?
// var awesomeImg:FlxSprite = new FlxSprite().loadGraphic(Paths.image('stageBuild/swag'));
// sprGrp.add(awesomeImg);
// var swag = Paths.image('characters/temp');
// if (bf != null)
// remove(bf);
// FlxG.bitmap.removeByKey(Paths.image('characters/temp'));
// bf.loadGraphic(Paths.image('characters/temp'));
// add(bf);
});
#end
}
public static var curSelectedSpr:SprStage;
function loadImage():Void
{
// var img:FlxSprite = new FlxSprite().loadGraphic(Paths.image('newgrounds_logo'));
// img.scrollFactor.set(0.5, 2);
// sprGrp.add(img);
}
function saveScene():Void
{
// trace();
}
public static var curTool:TOOLS = SELECT;
var tempTool:TOOLS = SELECT;
override function update(elapsed:Float)
{
if (FlxG.keys.justPressed.H) hudGrp.visible = !hudGrp.visible;
if (FlxG.keys.justPressed.ESCAPE)
{
if (curFocus == ATTRIBUTES) curFocus = MOVEMENTS;
else
curFocus = ATTRIBUTES;
}
switch (curFocus)
{
case MOVEMENTS:
movementControls();
case ATTRIBUTES:
attributeControls();
default:
}
if (FlxG.keys.justPressed.DELETE)
{
if (curSelectedSpr != null) sprGrp.remove(curSelectedSpr, true);
}
MouseUtil.mouseCamDrag();
if (FlxG.keys.pressed.CONTROL) MouseUtil.mouseWheelZoom();
if (isShaking)
{
FlxG.stage.window.x = Std.int(shakePos.x + (FlxG.random.float(-1, 1) * shakeIntensity));
FlxG.stage.window.y = Std.int(shakePos.y + (FlxG.random.float(-1, 1) * shakeIntensity));
shakeIntensity -= 30 * elapsed;
if (shakeIntensity <= 0)
{
isShaking = false;
shakeIntensity = 60;
FlxG.stage.window.x = Std.int(shakePos.x);
FlxG.stage.window.y = Std.int(shakePos.y);
}
}
if (curTool == GRABBING && FlxG.mouse.justReleased)
{
moveSprPos([
curSelectedSpr.x - curSelectedSpr.oldPos.x,
curSelectedSpr.y - curSelectedSpr.oldPos.y
]);
}
if (FlxG.keys.justPressed.Z && actionQueue.length > 0)
{
// trace('UNDO - QUEUE LENGTH: ' + actionQueue.length);
isUndoRedo = true;
var daFunc = actionQueue.pop();
var daValue = posQueue.pop();
daFunc(daValue);
}
super.update(elapsed);
}
function attributeControls():Void
{
textInfo.alpha = 1;
if (FlxG.keys.justPressed.LEFT || FlxG.keys.justPressed.RIGHT)
{
if (FlxG.keys.justPressed.LEFT)
{
curSelectedSpr.scrollFactor.x -= 0.1;
curSelectedSpr.scrollFactor.y -= 0.1;
}
if (FlxG.keys.justPressed.RIGHT)
{
curSelectedSpr.scrollFactor.x += 0.1;
curSelectedSpr.scrollFactor.y += 0.1;
}
updateTextInfo();
}
}
function movementControls():Void
{
textInfo.alpha = 0.5;
if (FlxG.keys.justPressed.CONTROL)
{
tempTool = curTool;
changeTool(SELECT);
}
if (FlxG.keys.justReleased.CONTROL)
{
changeTool(tempTool);
}
if (FlxG.keys.justPressed.V)
{
changeTool(SELECT);
}
if (FlxG.keys.justPressed.R)
{
shakingScreen();
FlxG.stage.window.title = "YOU WILL DIE";
// FlxG.stage.window.x -= Std.int(10);
// FlxG.stage.window.y -= Std.int(10);
// FlxG.stage.window.width += Std.int(40);
// FlxG.stage.window.height += Std.int(20);
}
if (FlxG.keys.justPressed.RIGHT) moveSprPos([1, 0, true]);
if (FlxG.keys.justPressed.LEFT) moveSprPos([-1, 0, true]);
if (FlxG.keys.justPressed.UP) moveSprPos([0, -1, true]);
if (FlxG.keys.justPressed.DOWN) moveSprPos([0, 1, true]);
if (FlxG.keys.justPressed.LBRACKET)
{
if (curSelectedSpr != null) moveLayer(1);
}
if (FlxG.keys.justPressed.RBRACKET)
{
if (curSelectedSpr != null) moveLayer(-1);
}
}
var curFocus:FOCUS = MOVEMENTS;
static public function changeTool(newTool:TOOLS)
{
curTool = newTool;
switch (curTool)
{
// redo this later so it doesn't create brand new FlxSprites into memory or someshit??? this was lazy 3AM way
case SELECT:
FlxG.mouse.load(new FlxSprite().loadGraphic(Paths.image('stageBuild/cursorSelect')).pixels);
case GRABBING:
FlxG.mouse.load(new FlxSprite().loadGraphic(Paths.image('stageBuild/cursorGrabbing')).pixels);
case GRAB:
FlxG.mouse.load(new FlxSprite().loadGraphic(Paths.image('stageBuild/cursorGrab')).pixels);
default:
trace('swag');
}
}
function changeCurSelected(spr:SprStage)
{
undoRedoCheck(changeCurSelected, curSelectedSpr);
curSelectedSpr = spr;
updateTextInfo();
}
function updateTextInfo()
{
textInfo.text = curSelectedSpr.imgName;
textInfo.text += ' - parallax: ' + curSelectedSpr.scrollFactor;
}
// base check to see if its in a state of undo or redo
function undoRedoCheck(daFunc:Dynamic->Void, daValue:Dynamic)
{
if (!isUndoRedo)
{
actionQueue.push(daFunc);
posQueue.push(daValue);
}
else
isUndoRedo = false;
}
function sprDragShitFunc(spr:SprStage)
{
if (curTool == SELECT) changeCurSelected(spr);
spr.mousePressing = true;
if (spr.isSelected()) changeTool(GRABBING);
spr.mouseOffset.set(FlxG.mouse.x - spr.x, FlxG.mouse.y - spr.y);
spr.oldPos.set(spr.x, spr.y);
}
// make function for changing cur selection
function moveSprPos(dumbArray:Array<Dynamic>)
{
var xDiff:Float = dumbArray[0];
var yDiff:Float = dumbArray[1];
var forceMove:Bool = dumbArray[2];
// trace(xDiff);
// trace(yDiff);
// usually set to false for the MOUSE DRAG, merely to track movements from the mouse
if (forceMove)
{
curSelectedSpr.x += xDiff;
curSelectedSpr.y += yDiff;
}
undoRedoCheck(moveSprPos, [-xDiff, -yDiff, true]);
}
var isUndoRedo:Bool = false;
var actionQueue:Array<Dynamic->Void> = [];
var posQueue:Array<Dynamic> = [];
function moveLayer(layerMovement:Int = 0):Void
{
if (curSelectedSpr.layer == 0 && layerMovement > 0) return;
curSelectedSpr.layer -= layerMovement;
sprGrp.members[curSelectedSpr.layer].layer += layerMovement;
// NOTE: fix to account if only one layer is in?
sortSprGrp();
undoRedoCheck(moveLayer, layerMovement * -1);
}
var isShaking:Bool = false;
var shakeIntensity:Float = 60;
var shakePos:FlxPoint = new FlxPoint();
function shakingScreen()
{
if (!isShaking)
{
isShaking = true;
shakePos.set(FlxG.stage.window.x, FlxG.stage.window.y);
}
}
function sortSprGrp()
{
sprGrp.sort(daLayerSorting, FlxSort.ASCENDING);
FlxMouseEvent.reorder();
}
function daLayerSorting(order:Int = FlxSort.ASCENDING, layer1:SprStage, layer2:SprStage):Int
{
return FlxSort.byValues(FlxSort.ASCENDING, layer1.layer, layer2.layer);
}
}
enum FOCUS
{
ATTRIBUTES;
MOVEMENTS;
TOOLBAR;
}
enum TOOLS
{
SELECT;
MOVE;
GRAB;
GRABBING;
BOYFRIEND;
}
@@ -0,0 +1,82 @@
package funkin.ui.debug.stage;
import funkin.ui.debug.stage.StageOffsetSubState;
import flixel.FlxSprite;
/**
* Very similar to eric's implementation
* see funkin.ui.debug.charting.ChartEditorCommand and ChartEditorState
* for more documentation since I am lazy to document!
*/
interface StageEditorCommand
{
public function execute(state:StageOffsetSubState):Void;
public function undo(state:StageOffsetSubState):Void;
public function toString():String;
}
class MovePropCommand implements StageEditorCommand
{
var xDiff:Float;
var yDiff:Float;
var realMove:Bool; // if needs a move!
public function new(xDiff:Float = 0, yDiff:Float = 0, realMove:Bool = true)
{
this.xDiff = xDiff;
this.yDiff = yDiff;
this.realMove = realMove;
}
public function execute(state:StageOffsetSubState):Void
{
if (realMove)
{
state.char.x += xDiff;
state.char.y += yDiff;
}
}
public function undo(state:StageOffsetSubState):Void
{
state.char.x -= xDiff;
state.char.y -= yDiff;
}
public function toString():String
{
return "Moved char " + xDiff + " " + yDiff + " " + realMove;
}
}
class SelectPropCommand implements StageEditorCommand
{
var prop:FlxSprite;
var prevProp:FlxSprite;
public function new(prop:FlxSprite)
{
this.prop = prop;
}
public function execute(state:StageOffsetSubState):Void
{
this.prevProp = state.char;
state.char = prop;
}
public function undo(state:StageOffsetSubState):Void
{
var funnyShader = state.char.shader;
if (state.char != null) state.char.shader = null;
state.char = this.prevProp;
// I KNOW, TWO DAMN NULL CHECKS IN A SINGLE FUNCTION! FUK U
if (state.char != null) state.char.shader = funnyShader;
}
public function toString():String
{
return "Selected" + prop;
}
}
@@ -0,0 +1,383 @@
package funkin.ui.debug.stage;
import flixel.FlxSprite;
import flixel.input.mouse.FlxMouseEvent;
import flixel.math.FlxPoint;
import funkin.play.character.BaseCharacter;
import funkin.play.PlayState;
import funkin.play.stage.StageData;
import funkin.play.stage.StageProp;
import funkin.graphics.shaders.StrokeShader;
import funkin.ui.haxeui.HaxeUISubState;
import funkin.ui.debug.stage.StageEditorCommand;
import funkin.util.SerializerUtil;
import funkin.util.MouseUtil;
import haxe.ui.containers.ListView;
import haxe.ui.core.Component;
import haxe.ui.events.UIEvent;
import haxe.ui.RuntimeComponentBuilder;
import openfl.events.Event;
import openfl.events.IOErrorEvent;
import openfl.net.FileReference;
/**
* A substate dedicated to allowing the user to create and edit stages/props
* Built with HaxeUI for use by both developers and modders.
*
* All functionality is kept within this file to ruin my own sanity.
*
* @author ninjamuffin99
*/
// Give other classes access to private instance fields
@:allow(funkin.ui.debug.stage.StageEditorCommand)
class StageOffsetSubState extends HaxeUISubState
{
var uiStuff:Component;
var outlineShader:StrokeShader;
static final STAGE_EDITOR_LAYOUT = Paths.ui('stage-editor/stage-editor-view');
public function new()
{
super(STAGE_EDITOR_LAYOUT);
}
override function create()
{
super.create();
FlxG.mouse.visible = true;
PlayState.instance.pauseMusic();
FlxG.camera.target = null;
setupUIListeners();
// var str = Paths.xml('ui/stage-editor-view');
// uiStuff = RuntimeComponentBuilder.fromAsset(str);
// uiStuff.findComponent("lol").onClick = saveCharacterCompile;
// uiStuff.findComponent('saveAs').onClick = saveStageFileRef;
// add(uiStuff);
PlayState.instance.persistentUpdate = true;
component.cameras = [PlayState.instance.camHUD];
// uiStuff.cameras = [PlayState.instance.camHUD];
// btn.cameras = [PlayState.instance.camHUD];
outlineShader = new StrokeShader(0xFFFFFFFF, 4, 4);
var layerList:ListView = findComponent("prop-layers");
for (thing in PlayState.instance.currentStage)
{
var prop:StageProp = cast thing;
if (prop != null && prop.name != null)
{
layerList.dataSource.add(
{
item: prop.name,
complete: true,
id: 'swag'
});
}
FlxMouseEvent.add(thing, spr -> {
// onMouseClick
trace(spr);
var dyn:StageProp = cast spr;
if (dyn != null && dyn.name != null)
{
if (FlxG.keys.pressed.CONTROL && char != dyn) selectProp(dyn.name);
}
}, null, spr -> {
// onMouseHover
// ID tag is to see if currently overlapping hold basically!, a bit more reliable than checking transparency!
// used for bug where you can click, and if you click on NO sprite, it snaps the thing to position! unintended!
if (FlxG.keys.pressed.CONTROL)
{
spr.ID = 1;
spr.alpha = 0.5;
}
else
{
spr.ID = 0;
spr.alpha = 1;
}
}, spr -> {
// onOut
// this if statement is for when u move ur mouse too fast... figure out how to proper lock it to mouse!
if (char != spr)
{
spr.ID = 0;
spr.alpha = 1;
}
});
}
}
function selectProp(propName:String)
{
if (char != null && char.shader == outlineShader) char.shader = null;
var proptemp:FlxSprite = cast PlayState.instance.currentStage.getNamedProp(propName);
if (proptemp == null) return;
performCommand(new SelectPropCommand(proptemp));
char.shader = outlineShader;
// trace(thing);
// trace(spr);
trace("JUST PRESSED!");
sprOld.x = char.x;
sprOld.y = char.y;
mosPosOld.x = FlxG.mouse.x;
mosPosOld.y = FlxG.mouse.y;
setUIValue('propXPos', char.x);
setUIValue('propYPos', char.y);
}
function setupUIListeners()
{
addUIClickListener('lol', saveCharacterCompile);
addUIClickListener('saveAs', saveStageFileRef);
// addUIChangeListener('complete', (event:UIEvent) -> {
// trace(event.value);
// trace(event.type);
// trace(event.target);
// trace(event.data);
// });
addUIChangeListener('propXPos', (event:UIEvent) -> {
if (char != null)
{
char.x = event.value;
// var xDiff = event.value - char.x;
// performCommand(new MovePropCommand(xDiff, 0));
}
});
addUIChangeListener('propYPos', (event:UIEvent) -> {
if (char != null)
{
char.y = event.value;
// var yDiff = event.value - char.y;
// performCommand(new MovePropCommand(0, yDiff));
}
});
addUIChangeListener('prop-layers', (event:UIEvent) -> {
trace(event.value);
trace(event.type);
trace(event.target);
trace(event.data);
trace(event.relatedEvent);
});
setUICheckboxSelected('complete', false);
}
var mosPosOld:FlxPoint = new FlxPoint();
var sprOld:FlxPoint = new FlxPoint();
private var char:FlxSprite = null;
var overlappingChar:Bool = false;
override function update(elapsed:Float)
{
super.update(elapsed);
if (FlxG.keys.justPressed.O)
{
trace("COMMANDS!!!");
trace("-/-/-/-/-/-");
for (ind => cmd in commandStack)
{
var output:String = cmd.toString();
if (curOperation == ind) output += " <---";
trace(output);
}
trace("-/-/-/-/-/-");
}
if (char != null)
{
setUIValue('propXPos', char.x);
setUIValue('propYPos', char.y);
}
if (char != null && char.ID == 1)
{
if (FlxG.mouse.pressed)
{
char.x = sprOld.x - (mosPosOld.x - FlxG.mouse.x);
char.y = sprOld.y - (mosPosOld.y - FlxG.mouse.y);
}
if (FlxG.mouse.justReleased)
{
trace("LOL");
var xDiff = (mosPosOld.x - FlxG.mouse.x);
var yDiff = (mosPosOld.y - FlxG.mouse.y);
performCommand(new MovePropCommand(-xDiff, -yDiff, false));
}
}
if (char != null)
{
var zoomShitLol:Float = 2 / FlxG.camera.zoom;
if (FlxG.keys.justPressed.LEFT) performCommand(new MovePropCommand(-zoomShitLol, 0));
if (FlxG.keys.justPressed.RIGHT) performCommand(new MovePropCommand(zoomShitLol, 0));
if (FlxG.keys.justPressed.UP) performCommand(new MovePropCommand(0, -zoomShitLol));
if (FlxG.keys.justPressed.DOWN) performCommand(new MovePropCommand(0, zoomShitLol));
}
FlxG.mouse.visible = true;
MouseUtil.mouseCamDrag();
if (FlxG.keys.pressed.CONTROL) MouseUtil.mouseWheelZoom();
if (FlxG.keys.pressed.CONTROL && FlxG.keys.justPressed.Z) undoLastCommand();
if (FlxG.keys.justPressed.Y)
{
for (thing in PlayState.instance.currentStage)
{
FlxMouseEvent.remove(thing);
thing.alpha = 1;
}
// if (uiStuff != null) remove(uiStuff);
// uiStuff = null;
PlayState.instance.disableKeys = false;
PlayState.instance.resetCamera();
FlxG.mouse.visible = false;
close();
}
}
var commandStack:Array<StageEditorCommand> = [];
var curOperation:Int = -1; // -1 at default, arrays start at 0
function performCommand(command:StageEditorCommand):Void
{
command.execute(this);
commandStack.push(command);
curOperation++;
if (curOperation < commandStack.length - 1) commandStack = commandStack.slice(0, curOperation + 1);
}
function undoCommand(command:StageEditorCommand):Void
{
command.undo(this);
curOperation--;
}
function undoLastCommand():Void
{
trace(curOperation);
trace(commandStack.length);
// trace(commandStack[commandStack.length]);
if (curOperation == -1 || commandStack.length == 0)
{
trace('no actions to undo');
return;
}
var command = commandStack[curOperation];
undoCommand(command);
}
var _file:FileReference;
function saveStageFileRef(_):Void
{
var jsonStr = prepStageStuff();
_file = new FileReference();
_file.addEventListener(Event.COMPLETE, onSaveComplete);
_file.addEventListener(Event.CANCEL, onSaveCancel);
_file.addEventListener(IOErrorEvent.IO_ERROR, onSaveError);
_file.save(jsonStr, PlayState.instance.currentStageId + ".json");
}
function onSaveComplete(_)
{
fileRemoveListens();
FlxG.log.notice("Successfully saved!");
}
function onSaveCancel(_)
{
fileRemoveListens();
}
function onSaveError(_)
{
fileRemoveListens();
FlxG.log.error("Problem saving Stage file!");
}
function fileRemoveListens()
{
_file.removeEventListener(Event.COMPLETE, onSaveComplete);
_file.removeEventListener(Event.CANCEL, onSaveCancel);
_file.removeEventListener(IOErrorEvent.IO_ERROR, onSaveError);
_file = null;
}
function saveCharacterCompile(_):Void
{
var outputJson:String = prepStageStuff();
#if sys
// save "local" to the current export.
sys.io.File.saveContent('./assets/data/stages/' + PlayState.instance.currentStageId + '.json', outputJson);
// save to the dev version
sys.io.File.saveContent('../../../../assets/preload/data/stages/' + PlayState.instance.currentStageId + '.json', outputJson);
#end
}
function prepStageStuff():String
{
var stageLol:StageData = StageDataParser.parseStageData(PlayState.instance.currentStageId);
for (prop in stageLol.props)
{
@:privateAccess
var posStuff = PlayState.instance.currentStage.namedProps.get(prop.name);
prop.position[0] = posStuff.x;
prop.position[1] = posStuff.y;
}
var bfPos = PlayState.instance.currentStage.getBoyfriend().feetPosition;
stageLol.characters.bf.position[0] = Std.int(bfPos.x);
stageLol.characters.bf.position[1] = Std.int(bfPos.y);
var dadPos = PlayState.instance.currentStage.getDad().feetPosition;
stageLol.characters.dad.position[0] = Std.int(dadPos.x);
stageLol.characters.dad.position[1] = Std.int(dadPos.y);
var GF_FEET_SNIIIIIIIIIIIIIFFFF = PlayState.instance.currentStage.getGirlfriend().feetPosition;
stageLol.characters.gf.position[0] = Std.int(GF_FEET_SNIIIIIIIIIIIIIFFFF.x);
stageLol.characters.gf.position[1] = Std.int(GF_FEET_SNIIIIIIIIIIIIIFFFF.y);
return SerializerUtil.toJSON(stageLol);
}
}
@@ -0,0 +1,14 @@
package funkin.ui.debug.stage;
import flixel.group.FlxGroup;
class StagetoolBar extends FlxGroup
{
var icons:Array<StageBuilderState.TOOLS> = [SELECT, MOVE, GRAB, BOYFRIEND];
var iconSprs:Array<String> = ['cursorSelect', 'cursorGrab', 'cursorMove', 'toolbarBF'];
public function new()
{
super();
}
}