Reorganize a whole bunch of classes and perform syntax cleanup.
This commit is contained in:
@@ -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');
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user