diff --git a/buildnumber.txt b/buildnumber.txt index 62cdfc6b..a67dc566 100644 --- a/buildnumber.txt +++ b/buildnumber.txt @@ -1 +1 @@ -1803 \ No newline at end of file +1813 \ No newline at end of file diff --git a/source/funkin/desktop/DesktopMain.hx b/source/funkin/desktop/DesktopMain.hx index 619deeba..7f341a92 100644 --- a/source/funkin/desktop/DesktopMain.hx +++ b/source/funkin/desktop/DesktopMain.hx @@ -114,6 +114,8 @@ class DesktopMain extends MusicBeatState { public override function destroy() { super.destroy(); + + FlxG.sound.soundTrayEnabled = (); contextMenu = FlxDestroyUtil.destroy(contextMenu); FlxG.scaleMode = oldScaleMode; @@ -209,4 +211,9 @@ class MouseInput { interface IDesktopFocusableObject { public function onFocus():Void; public function onFocusLost():Void; +} + +// useless, only here for +interface IDesktopInputObject { + } \ No newline at end of file diff --git a/source/funkin/desktop/editors/CharacterEditor.hx b/source/funkin/desktop/editors/CharacterEditor.hx index 98db603a..fc2ebea1 100644 --- a/source/funkin/desktop/editors/CharacterEditor.hx +++ b/source/funkin/desktop/editors/CharacterEditor.hx @@ -1,5 +1,6 @@ package funkin.desktop.editors; +import flixel.FlxBasic; import funkin.game.Character; import flixel.FlxSprite; import flixel.FlxObject; @@ -22,6 +23,8 @@ class CharacterEditor extends WindowContent { public var animList:Array = []; public var animListNames:Array = []; + public var curAnim:AnimData; + /** * ANIMATION TAB */ @@ -29,6 +32,9 @@ class CharacterEditor extends WindowContent { public var animNameInput:InputBox; public var animPrefixInput:InputBox; public var animLoopCheckbox:Checkbox; + public var animFpsStepper:NumericStepper; + public var animOffsetX:NumericStepper; + public var animOffsetY:NumericStepper; public override function create() { super.create(); @@ -78,42 +84,47 @@ class CharacterEditor extends WindowContent { tabView.updateAnchor(1, 0, [camHUD]); add(tabView); - var labels:Array = []; + var content:Array = []; var label:WindowText = null; - labels.push(label = new WindowText(10, 10, 0, "Animations")); - animsDropDown = new DropDown(10, label.y + label.height, 380, [], function(id) { + content.push(label = new WindowText(10, 10, 0, "Animations")); + content.push(animsDropDown = new DropDown(10, label.y + label.height, 380, [], function(id) { changeAnim(id); - }); + })); - labels.push(label = new WindowText(10, animsDropDown.y + animsDropDown.height + 10, 0, "Animation Name")); - animNameInput = new InputBox(10, label.y + label.height, 380, ""); + content.push(label = new WindowText(10, animsDropDown.y + animsDropDown.height + 10, 0, "Animation Name")); + content.push(animNameInput = new InputBox(10, label.y + label.height, 380, "")); - labels.push(label = new WindowText(10, animNameInput.y + animNameInput.height + 10, 0, "Animation Prefix")); - animPrefixInput = new InputBox(10, label.y + label.height, 380, ""); + content.push(label = new WindowText(10, animNameInput.y + animNameInput.height + 10, 0, "Animation Prefix")); + content.push(animPrefixInput = new InputBox(10, label.y + label.height, 380, "")); - animLoopCheckbox = new Checkbox(10, animPrefixInput.y + animPrefixInput.height + 10, 380, "Loop"); - - // labels.push(label = new WindowText(10, animNameInput.y + animNameInput.height + 10, 0, "Animation Prefix")); - // animPrefixInput = new InputBox(10, label.y + label.height, 380, ""); + content.push(animLoopCheckbox = new Checkbox(10, animPrefixInput.y + animPrefixInput.height + 10, 380, "Loop")); - for(l in labels) - tabView.tabs[0].add(l); + content.push(label = new WindowText(10, animLoopCheckbox.y + animLoopCheckbox.height + 10, 0, "Animation FPS (Frames per second)")); + content.push(animFpsStepper = new NumericStepper(10, label.y + label.height + 10, 380, 0)); - for(spr in [animsDropDown, animNameInput, animPrefixInput, animLoopCheckbox]) + content.push(label = new WindowText(10, animFpsStepper.y + animFpsStepper.height + 10, 0, "Animation Offset (Relative to scale)")); + content.push(animOffsetX = new NumericStepper(10, label.y + label.height + 10, 180, 0)); + content.push(animOffsetY = new NumericStepper(200, label.y + label.height + 10, 180, 0)); + + for(spr in content) tabView.tabs[0].add(spr); refreshAnims(); + changeAnim(0); } public function changeAnim(id:Int) { - var anim = animList[id]; - if (anim == null) return; - char.playAnim(anim.name, true); + curAnim = animList[id]; + if (curAnim == null) return; + char.playAnim(curAnim.name, true); - animNameInput.text = anim.name; - animPrefixInput.text = anim.name; - animLoopCheckbox.setChecked(anim.loop); + animNameInput.text = curAnim.name; + animPrefixInput.text = curAnim.anim; + animLoopCheckbox.setChecked(curAnim.loop); + animFpsStepper.value = curAnim.fps; + animOffsetX.value = curAnim.x; + animOffsetY.value = curAnim.y; } public function refreshAnims() { @@ -130,6 +141,33 @@ class CharacterEditor extends WindowContent { if (FlxG.keys.pressed.DOWN) camFollow.y += elapsed * 500; if (FlxG.keys.pressed.UP) camFollow.y -= elapsed * 500; } + + if (curAnim != null) { + // animation updating + var shouldUpdate = false; + var oldName:String = curAnim.name; + + var updateListAsWell = shouldUpdate = (curAnim.name != (curAnim.name = animNameInput.text)); + shouldUpdate = (curAnim.anim != (curAnim.anim = animPrefixInput.text)) || shouldUpdate; + shouldUpdate = (curAnim.fps != (curAnim.fps = Std.int(animFpsStepper.value))) || shouldUpdate; + shouldUpdate = (curAnim.loop != (curAnim.loop = animLoopCheckbox.checked)) || shouldUpdate; + shouldUpdate = (curAnim.x != (curAnim.x = animOffsetX.value)) || shouldUpdate; + shouldUpdate = (curAnim.y != (curAnim.y = animOffsetY.value)) || shouldUpdate; + + if (shouldUpdate) + updateCurAnim(oldName, updateListAsWell); + } + } + + public function updateCurAnim(?oldName:String, updateListAsWell:Bool = false) { + if (oldName == null) + oldName = char.animation.curAnim != null ? char.animation.curAnim.name : curAnim.name; + + char.animation.remove(oldName); + + XMLUtil.addAnimToSprite(char, curAnim); + + char.playAnim(curAnim.name, true); } public override function onWindowResize(width:Int, height:Int) { diff --git a/source/funkin/desktop/sprites/InputBox.hx b/source/funkin/desktop/sprites/InputBox.hx index c6dabc8d..463598c3 100644 --- a/source/funkin/desktop/sprites/InputBox.hx +++ b/source/funkin/desktop/sprites/InputBox.hx @@ -6,7 +6,7 @@ import openfl.events.KeyboardEvent; import flixel.FlxG; import lime.app.Application; -class InputBox extends Button { +class InputBox extends Button implements IDesktopInputObject { public var index:Int = 0; public static var insertMode:Bool = false; @@ -17,6 +17,7 @@ class InputBox extends Button { return label.text; public var onChange:Void->Void; + public var onConfirm:Void->Void; public function new(x:Float, y:Float, w:Float, t:String, ?onChange:Void->Void) { super(x, y, t, function() { @@ -49,11 +50,14 @@ class InputBox extends Button { text = text.substr(0, index) + text.substring(index + 1, text.length); onTextInput(""); case Keyboard.BACKSPACE: - text = text.substr(0, index - 1) + text.substring(index, text.length); + text = text.substr(0, index <= 0 ? 0 : index - 1) + text.substring(index, text.length); index--; onTextInput(""); case Keyboard.ENTER | Keyboard.NUMPAD_ENTER: - onTextInput("\n"); + if (onChange != null) + onChange(); + if (onConfirm != null) + onConfirm(); default: // nothing } @@ -62,26 +66,31 @@ class InputBox extends Button { private function onTextInput(input:String) { if (!this.hasFocus()) return; - text = text.substr(0, index) + input + text.substring(index, text.length); - - if (!insertMode) - index += input.length; + if (input.length > 0) { + text = text.substr(0, index) + input + text.substring(index, text.length); + + if (!insertMode) + index += input.length; + } if (onChange != null) onChange(); } public override function update(elapsed:Float) { - if (DesktopMain.mouseInput.justPressed && !DesktopMain.mouseInput.overlaps(this, camera)) { - disabled = false; + if (DesktopMain.mouseInput.justPressed && !DesktopMain.mouseInput.overlaps(this, camera)) this.loseFocus(); - } super.update(elapsed); } public override function onFocusLost() { super.onFocusLost(); - disabled = false; + if (disabled != (disabled = false)) { + if (onChange != null) + onChange(); + if (onConfirm != null) + onConfirm(); + } } public inline function changeIndex(change:Int) { @@ -90,11 +99,9 @@ class InputBox extends Button { public inline function setIndex(index:Int) { this.index = index; - } public override function destroy() { super.destroy(); - } } \ No newline at end of file diff --git a/source/funkin/desktop/sprites/NumericStepper.hx b/source/funkin/desktop/sprites/NumericStepper.hx new file mode 100644 index 00000000..24207d05 --- /dev/null +++ b/source/funkin/desktop/sprites/NumericStepper.hx @@ -0,0 +1,77 @@ +package funkin.desktop.sprites; + +import flixel.math.FlxMath; + +class NumericStepper extends InputBox { + public var decimals:Int = 0; + public var increment:Float = 1; + + @:isVar public var value(get, set):Float = 0; + + public var onValueChange:Void->Void; + + public var addButton:Button; + public var subButton:Button; + + public function new(x:Float, y:Float, w:Float, v:Float, ?onValueChange:Void->Void) { + super(x, y, w, Std.string(v)); + addButton = new Button(w - (height * 2), 0, "+", onAdd); + addButton.resize(height, height); + + subButton = new Button(w - (height), 0, "-", onSub); + subButton.resize(height, height); + + onConfirm = onManualChange; + } + + public override function resize(width:Float, height:Float) { + super.resize(width - (height * 2), height); + } + + public override function update(elapsed:Float) { + addButton.update(elapsed); + subButton.update(elapsed); + super.update(elapsed); + } + + public override function draw() { + addButton.copyProperties(this); + subButton.copyProperties(this); + addButton.x += width; + subButton.x += width + height; + + super.draw(); + addButton.draw(); + subButton.draw(); + } + + private inline function onSub() + value--; + + private inline function onAdd() + value++; + + private function onManualChange() { + var v = Std.parseFloat(text); + if (!v.isNaN()) { + value = round(v); + } else { + set_value(value); + } + } + + private function set_value(nv:Float) { + if (this.value != (this.value = nv)) + if (onValueChange != null) + onValueChange(); + text = Std.string(round(this.value)); + return this.value; + } + + private inline function get_value() + return this.value; + + private inline function round(value:Float) { + return decimals <= 0 ? Std.int(value) : FlxMath.roundDecimal(value, decimals); + } +} \ No newline at end of file diff --git a/source/funkin/system/Conductor.hx b/source/funkin/system/Conductor.hx index 72b8df78..cdbbd5c9 100644 --- a/source/funkin/system/Conductor.hx +++ b/source/funkin/system/Conductor.hx @@ -156,7 +156,7 @@ class Conductor // update conductor var timeUntilUpdate = -(lastSongPosTime - (lastSongPosTime = Main.time)); var elapsedAL = (lastSongPos - lastPos); - speed = FlxMath.bound(FlxMath.lerp(speed, timeUntilUpdate / elapsedAL, timeUntilUpdate / 1000), 0.925, 1.075); + speed = FlxMath.bound(FlxMath.lerp(speed, timeUntilUpdate / elapsedAL, FlxMath.bound(timeUntilUpdate / 1000, 0, 1)), 0.925, 1.075); songPosition = lastSongPos; } else { songPosition += elapsed * 1000 * speed; diff --git a/source/funkin/system/XMLUtil.hx b/source/funkin/system/XMLUtil.hx index 336e757d..96c9f608 100644 --- a/source/funkin/system/XMLUtil.hx +++ b/source/funkin/system/XMLUtil.hx @@ -130,8 +130,10 @@ class XMLUtil { if (sprite is XMLSprite) animType = cast(sprite, XMLSprite).spriteAnimType; - var animData = extractAnimFromXML(anim, animType, loop); + return addAnimToSprite(sprite, extractAnimFromXML(anim, animType, loop)); + } + public static function addAnimToSprite(sprite:FlxSprite, animData:AnimData):ErrorCode { if (animData.name != null && animData.anim != null) { if (animData.fps <= 0 #if web || animData.fps == null #end) animData.fps = 24;