diff --git a/source/funkin/backend/system/macros/SoftcodedClassMacro.hx b/source/funkin/backend/system/macros/SoftcodedClassMacro.hx deleted file mode 100644 index 5f52c0c8..00000000 --- a/source/funkin/backend/system/macros/SoftcodedClassMacro.hx +++ /dev/null @@ -1,246 +0,0 @@ -package funkin.backend.system.macros; - -using StringTools; -class SoftcodedClassMacro { - #if macro - private static var nonAllowedSuffixes:Array = ["__Softcoded", "_HSC", "_HSX", "_Impl_"]; - public static function init() { - Compiler.addGlobalMetadata('funkin.game.cutscenes', '@:build(funkin.backend.system.macros.SoftcodedClassMacro.build())'); - Compiler.addGlobalMetadata('funkin.game', '@:build(funkin.backend.system.macros.SoftcodedClassMacro.build())'); - Compiler.addGlobalMetadata('funkin.menus', '@:build(funkin.backend.system.macros.SoftcodedClassMacro.build())'); - Compiler.addGlobalMetadata('funkin.options', '@:build(funkin.backend.system.macros.SoftcodedClassMacro.build())'); - Compiler.addGlobalMetadata('funkin.backend.scripting', '@:build(funkin.backend.system.macros.SoftcodedClassMacro.build())'); - Compiler.addGlobalMetadata('funkin.backend.shaders', '@:build(funkin.backend.system.macros.SoftcodedClassMacro.build())'); - Compiler.addGlobalMetadata('flixel', '@:build(funkin.backend.system.macros.SoftcodedClassMacro.build())', false); - } - - public static function build():Array { - var fields = Context.getBuildFields(); - var cl = Context.getLocalClass(); - - if (cl != null) { - var scriptClassType = TPath({ - pack: ['funkin', 'scripting'], - name: 'Script', - sub: 'ScriptClass' - }); - - var funcs:Array = []; - funcs.push({ - pos: Context.currentPos(), - name: '__scriptClass', - kind: FVar(scriptClassType, macro null) - }); - var c = cl.get(); - c.isPrivate = false; - switch(c.kind) { - case KNormal: - // do nothing - default: - return fields; - } - if (c.isPrivate || c.isInterface || c.isFinal || c.isExtern || c.isAbstract) return fields; - - for(s in nonAllowedSuffixes) - if (c.name.endsWith(s)) - return fields; - if (c.params.length > 0) return fields; // NOT SUPPORTED - if (c.isAbstract) return fields; - - for(m in c.meta.get()) { - switch(m.name) { - case ":final", ":noSoftcodedExtend": - return fields; - default: - // cool - } - } - - var shadowClass = macro class { - - }; - - - // var fixedPack = [for(p in c.pack) if (p.startsWith("_")) p.substr(1) else p]; - - shadowClass.name = '${c.name}__Softcoded'; - shadowClass.fields = funcs; - shadowClass.pack = c.pack.copy(); - shadowClass.kind = TDClass({ - pack: c.pack.copy(), - name: c.name - }, [], false, false, false); - - var addedFields:Array = []; - - #if !display - for(f in [for(f in fields) f]) { - var valid = true; - if (f.access != null) for(e in f.access) { - switch (e) { - case AStatic | ADynamic | AInline | AMacro | AFinal | AExtern | AAbstract: - valid = false; - break; - default: - // nothing - } - } - - if (!valid) continue; - - switch(f.kind) { - case FFun(fun): - if (fun.params != null && fun.params.length > 0) continue; - var funcExpr = macro { - ${{ - pos: Context.currentPos(), - expr: ECall({ - pos: Context.currentPos(), - expr: EField({ - pos: Context.currentPos(), - expr: EConst(CIdent("super")) - }, f.name) - }, fun.args != null ? [for(a in fun.args) { - pos: Context.currentPos(), - expr: EConst(CIdent(a.name)) - }] : []) - }}; - }; - - var isVoid = fun.ret.match(TPath({name: "Void"})); - - if (!isVoid) - funcExpr = macro return ${funcExpr}; - - var accessors = []; - if (f.access != null) for(f in f.access) accessors.push(f); - accessors.remove(AOverride); - - if (f.name == "new") { - var constructorExpr = macro { - ${{ - expr: ECall({ - pos: Context.currentPos(), - expr: EConst(CIdent("super")) - }, fun.args != null ? [for(a in fun.args) { - pos: Context.currentPos(), - expr: EConst(CIdent(a.name)) - }] : []), - pos: Context.currentPos() - }} - - __scriptClass = scrObj; - - __scriptClass.call("new", ${{ - pos: Context.currentPos(), - expr: EArrayDecl([for(a in fun.args) { - pos: Context.currentPos(), - expr: EConst(CIdent(a.name)) - }]) - }}, this); - - } - var func:Function = { - expr: constructorExpr, - args: [for(a in fun.args) a] - }; - func.args.insert(0, { - name: "scrObj", - type: scriptClassType - }); - funcs.push({ - pos: Context.currentPos(), - name: 'new', - kind: FFun(func), - access: [for(a in accessors) a] - }); - - - var printer = new Printer(); - - continue; - } else { - var func:Function = { - expr: funcExpr, - args: fun.args - }; - funcs.push({ - pos: Context.currentPos(), - name: '__super__${f.name}', - kind: FFun(func), - access: [for(a in accessors) a] - }); - } - - accessors.push(AOverride); - - var overrideFuncExpr = isVoid ? macro { - var fieldName = ${{ - pos: Context.currentPos(), - expr: EConst(CString(f.name, DoubleQuotes)) - }}; - if (__scriptClass != null && __scriptClass.hasField(fieldName) && Reflect.isFunction(__scriptClass.get(fieldName))) { - __scriptClass.call(fieldName, ${{ - pos: Context.currentPos(), - expr: EArrayDecl([for(a in fun.args) { - pos: Context.currentPos(), - expr: EConst(CIdent(a.name)) - }]) - }}, this); - } else ${funcExpr} - } : macro { - var fieldName = ${{ - pos: Context.currentPos(), - expr: EConst(CString(f.name, DoubleQuotes)) - }}; - if (__scriptClass != null && __scriptClass.hasField(fieldName) && Reflect.isFunction(__scriptClass.get(fieldName))) { - return __scriptClass.call(fieldName, ${{ - pos: Context.currentPos(), - expr: EArrayDecl([for(a in fun.args) { - pos: Context.currentPos(), - expr: EConst(CIdent(a.name)) - }]) - }}, this); - } else ${funcExpr} - }; - - var overrideFunc:Function = { - expr: overrideFuncExpr, - args: fun.args - }; - funcs.push({ - pos: Context.currentPos(), - name: '${f.name}', - kind: FFun(overrideFunc), - access: accessors - }); - - default: - - } - } - #end - - var imports = [for(i in Context.getLocalImports()) i]; - imports.push({ - path: [for(p in c.module.split(".")) { - pos: Context.currentPos(), - name: p - }], - mode: IAll - }); - imports.push({ - path: [for(p in c.module.split(".")) { - pos: Context.currentPos(), - name: p - }], - mode: INormal - }); - - - Context.defineModule(c.module + "__Softcoded", [shadowClass], imports); - } - return fields; - } - #end -} \ No newline at end of file diff --git a/source/funkin/editors/charter/CharterEvent.hx b/source/funkin/editors/charter/CharterEvent.hx index 7594e82d..d35224d6 100644 --- a/source/funkin/editors/charter/CharterEvent.hx +++ b/source/funkin/editors/charter/CharterEvent.hx @@ -138,7 +138,7 @@ typedef EventParamInfo = { enum EventParamType { TBool; - TInt(?min:Int, ?max:Int); + TInt(?min:Int, ?max:Int, ?step:Float); TFloat(?min:Int, ?max:Int, ?step:Float, ?precision:Int); TString; TArrayOfString; diff --git a/source/funkin/editors/charter/CharterEventScreen.hx b/source/funkin/editors/charter/CharterEventScreen.hx index b32fb882..ea14ca12 100644 --- a/source/funkin/editors/charter/CharterEventScreen.hx +++ b/source/funkin/editors/charter/CharterEventScreen.hx @@ -27,19 +27,6 @@ class CharterEventScreen extends UISubstateWindow { FlxG.sound.music.pause(); // prevent the song from continuing iconsPanel = new FlxGroup(); - - for(k=>e in chartEvent.events) { - var butt = new UIButton(windowSpr.x + 1, windowSpr.y + 31 + (k*30), "", function() { - changeTab(k); - }); - butt.bWidth = butt.bHeight = 30; - iconsPanel.add(butt); - - var icon = CharterEvent.generateEventIcon(e); - icon.setPosition(butt.x + ((butt.bWidth - icon.width) / 2), butt.y + ((butt.bHeight - icon.height) / 2)); - iconsPanel.add(icon); - } - add(iconsPanel); paramsPanel = new FlxGroup(); @@ -55,6 +42,7 @@ class CharterEventScreen extends UISubstateWindow { public function changeTab(id:Int) { saveCurTab(); + // destroy old elements paramsFields = []; @@ -83,8 +71,32 @@ class CharterEventScreen extends UISubstateWindow { paramsFields.push(textBox); case TBool: var checkbox = new UICheckbox(eventName.x, y, param.name, cast curEvent.params[k]); + y += checkbox.height + 10; paramsPanel.add(checkbox); paramsFields.push(checkbox); + case TInt(min, max, step): + var label:UIText = new UIText(eventName.x, y, 0, param.name); + y += label.height + 4; + paramsPanel.add(label); + + var numericStepper = new UINumericStepper(eventName.x, y, cast curEvent.params[k], step.getDefault(1), 0, min, max); + y += numericStepper.height + 10; + paramsPanel.add(numericStepper); + paramsFields.push(numericStepper); + case TFloat(min, max, step, precision): + var label:UIText = new UIText(eventName.x, y, 0, param.name); + y += label.height + 4; + paramsPanel.add(label); + + var numericStepper = new UINumericStepper(eventName.x, y, cast curEvent.params[k], step.getDefault(1), precision, min, max); + y += numericStepper.height + 10; + paramsPanel.add(numericStepper); + paramsFields.push(numericStepper); + case TStrumLine: + var dropdown = new UIDropDown(eventName.x, y, 320, 32, [for(k=>s in cast(FlxG.state, Charter).strumLines.members) 'Strumline #${k+1} (${s.strumLine.characters[0]})'], cast curEvent.params[k]); + y += dropdown.height + 10; + paramsPanel.add(dropdown); + paramsFields.push(dropdown); default: // none paramsFields.push(null); @@ -93,19 +105,49 @@ class CharterEventScreen extends UISubstateWindow { } else curEvent = -1; + refreshIcons(); + } + + public function refreshIcons() { + while(iconsPanel.members.length > 0) + iconsPanel.remove(iconsPanel.members[0], true).destroy(); + + for(k=>e in chartEvent.events) { + var butt = new UIButton(windowSpr.x + 1, windowSpr.y + 31 + (k*30), "", function() { + changeTab(k); + }); + butt.bWidth = butt.bHeight = 30; + butt.visible = (k != curEvent); + iconsPanel.add(butt); + + var icon = CharterEvent.generateEventIcon(e); + icon.setPosition(butt.x + ((butt.bWidth - icon.width) / 2), butt.y + ((butt.bHeight - icon.height) / 2)); + iconsPanel.add(icon); + } } public function saveCurTab() { if (curEvent < 0) return; - chartEvent.events[curEvent].params = [for(p in paramsFields) { - if (p is UITextBox) - cast(p, UITextBox).label.text; - else if (p is UICheckbox) - cast(p, UICheckbox).checked; - else - null; - } + chartEvent.events[curEvent].params = [ + for(p in paramsFields) { + if (p is UIDropDown) + cast(p, UIDropDown).index; + else if (p is UINumericStepper) { + var stepper = cast(p, UINumericStepper); + // int + if (stepper.precision == 0) + Std.int(stepper.value); + else + stepper.value; + } + else if (p is UITextBox) + cast(p, UITextBox).label.text; + else if (p is UICheckbox) + cast(p, UICheckbox).checked; + else + null; + } ]; } diff --git a/source/funkin/editors/charter/CharterNoteGroup.hx b/source/funkin/editors/charter/CharterNoteGroup.hx index 00cd7e9e..747b08f6 100644 --- a/source/funkin/editors/charter/CharterNoteGroup.hx +++ b/source/funkin/editors/charter/CharterNoteGroup.hx @@ -20,8 +20,8 @@ class CharterNoteGroup extends FlxTypedGroup { if (FlxG.state is Charter && !FlxG.sound.music.playing) curStep = cast(FlxG.state, Charter).conductorFollowerSpr.y / 40; - var begin = SortedArrayUtil.binarySearch(members, curStep - max, getVarForEach); - var end = SortedArrayUtil.binarySearch(members, curStep + max, getVarForEach); + var begin = SortedArrayUtil.binarySearch(members, curStep - max, getVarForEachAdd); + var end = SortedArrayUtil.binarySearch(members, curStep + max, getVarForEachRemove); for(i in begin...end) { __loopSprite = members[i]; @@ -45,8 +45,10 @@ class CharterNoteGroup extends FlxTypedGroup { private static function getVar(n:CharterNote) return n.step; - private static function getVarForEach(n:CharterNote) + private static function getVarForEachAdd(n:CharterNote) return n.step + n.susLength; + private static function getVarForEachRemove(n:CharterNote) + return n.step - n.susLength; public override function draw() { @:privateAccess var oldDefaultCameras = FlxCamera._defaultCameras; diff --git a/source/funkin/editors/ui/UIDropDown.hx b/source/funkin/editors/ui/UIDropDown.hx new file mode 100644 index 00000000..5acad965 --- /dev/null +++ b/source/funkin/editors/ui/UIDropDown.hx @@ -0,0 +1,64 @@ +package funkin.editors.ui; + +class UIDropDown extends UISliceSprite { + public var dropButton:UIButton; + public var label:UIText; + + public var index:Int = 0; + public var options:Array; + + public var onChange:Int->Void; + + var curMenu:UIContextMenu = null; + + public function new(x:Float, y:Float, width:Int = 320, height:Int = 32, options:Array, index:Int = 0) { + super(x, y, width - height, height, 'editors/ui/inputbox'); // using same sprite cause fuck you + + this.options = options; + this.index = index; + + label = new UIText(0, 0, width - height, options[index]); + members.push(label); + + dropButton = new UIButton(0, 0, "V", null, height, height); + members.push(dropButton); + } + + public function setOption(newIndex:Int) { + if (index != (index = newIndex)) { + label.text = options[index]; + if (onChange != null) + onChange(index); + } + } + + public override function update(elapsed:Float) { + + var opened = curMenu.contextMenuOpened(); + framesOffset = (opened || (hovered && FlxG.mouse.pressed)) ? 18 : (hovered ? 9 : 0); + if (FlxG.mouse.justReleased && (hovered || dropButton.hovered)) { + if (opened) + UIState.state.curContextMenu.preventOutOfBoxClickDeletion(); + else + openContextMenu(); + } + + super.update(elapsed); + + label.follow(this, 4, Std.int((bHeight - label.height) / 2)); + dropButton.follow(this, bWidth - bHeight, 0); + } + + public function openContextMenu() { + var screenPos = getScreenPosition(null, __lastDrawCameras[0] == null ? FlxG.camera : __lastDrawCameras[0]); + curMenu = UIState.state.openContextMenu([ + for(k=>o in options) { + icon: (k == index) ? 1 : 0, + label: o + } + ], function(_, i, _) { + setOption(i); + }, screenPos.x, screenPos.y + bHeight); + } + +} \ No newline at end of file diff --git a/source/funkin/editors/ui/UINumericStepper.hx b/source/funkin/editors/ui/UINumericStepper.hx new file mode 100644 index 00000000..c561846c --- /dev/null +++ b/source/funkin/editors/ui/UINumericStepper.hx @@ -0,0 +1,43 @@ +package funkin.editors.ui; + +class UINumericStepper extends UITextBox { + public var value(default, set):Float; + public var precision:Int = 0; + public var min:Null = null; + public var max:Null = null; + public var step:Float = 1; + + public function new(x:Float, y:Float, value:Float = 0, step:Float = 1, precision:Int = 0, ?min:Float, ?max:Float, w:Int = 180, h:Int = 32) { + super(x, y, "", w - h, h, false); + + onChange = __onChange; + + this.precision = precision; + this.min = min; + this.max = max; + this.step = step; + + set_value(value); + + // TODO: add stepper buttons + } + + private function __onChange(text:String) { + var f = Std.parseFloat(text); + if (Math.isNaN(f)) + return; + value = f; + } + + private function set_value(v:Float) { + if (min != null && max != null) { + v = FlxMath.bound(v, min, max); + } else if (min != null) { + v = Math.max(v, min); + } else if (max != null) { + v = Math.min(v, max); + } + label.text = Std.string(FlxMath.roundDecimal(v, precision)); + return value = v; + } +} \ No newline at end of file diff --git a/source/funkin/editors/ui/UIState.hx b/source/funkin/editors/ui/UIState.hx index cf95db44..3cd2caf2 100644 --- a/source/funkin/editors/ui/UIState.hx +++ b/source/funkin/editors/ui/UIState.hx @@ -119,7 +119,7 @@ class UIState extends MusicBeatState { state.persistentDraw = true; state.persistentUpdate = true; - openSubState(curContextMenu = new UIContextMenu(options, callback, x.getDefault(__mousePos.x), y.getDefault(__mousePos.y))); + state.openSubState(curContextMenu = new UIContextMenu(options, callback, x.getDefault(__mousePos.x), y.getDefault(__mousePos.y))); return curContextMenu; } } \ No newline at end of file diff --git a/source/funkin/editors/ui/UITextBox.hx b/source/funkin/editors/ui/UITextBox.hx index 887ef063..2659f7d0 100644 --- a/source/funkin/editors/ui/UITextBox.hx +++ b/source/funkin/editors/ui/UITextBox.hx @@ -13,6 +13,10 @@ class UITextBox extends UISliceSprite implements IUIFocusable { public var multiline:Bool = false; public var caretSpr:FlxSprite; + public var onChange:String->Void; + + var __wasFocused:Bool = false; + public function new(x:Float, y:Float, text:String = "", width:Int = 320, height:Int = 32, multiline:Bool = false) { super(x, y, width, height, 'editors/ui/inputbox'); @@ -25,30 +29,38 @@ class UITextBox extends UISliceSprite implements IUIFocusable { caretSpr.updateHitbox(); members.push(caretSpr); this.multiline = multiline; + position = text.length; } var cacheRect:Rectangle = new Rectangle(); public override function update(elapsed:Float) { - super.update(elapsed); - var off = multiline ? 4 : ((bHeight - label.height) / 2); - label.follow(this, 4, off); - - if (hovered && FlxG.mouse.justReleased) { + if (hovered && FlxG.mouse.justReleased && __lastDrawCameras.length > 0) { // get caret pos - var pos = FlxG.mouse.getScreenPosition(camera, FlxPoint.get()); + var pos = FlxG.mouse.getScreenPosition(__lastDrawCameras[0], FlxPoint.get()); pos.x -= label.x; pos.y -= label.y; - var index = label.textField.getCharIndexAtPoint(pos.x, pos.y); - if (index > -1) - position = index; + if (pos.x < 0) + position = 0; + else { + var index = label.textField.getCharIndexAtPoint(pos.x, pos.y); + if (index > -1) + position = index; + else + position = label.text.length; + } pos.put(); } + + super.update(elapsed); + var off = multiline ? 4 : ((bHeight - label.height) / 2); + label.follow(this, 4, off); framesOffset = (focused ? 18 : (hovered ? 9 : 0)); @:privateAccess { if (focused) { + __wasFocused = true; caretSpr.alpha = (FlxG.game.ticks % 666) >= 333 ? 1 : 0; var curPos = switch(position) { @@ -66,6 +78,11 @@ class UITextBox extends UISliceSprite implements IUIFocusable { caretSpr.follow(this, 4 + curPos.x, off + curPos.y); curPos.put(); } else { + if (__wasFocused) { + __wasFocused = false; + if (onChange != null) + onChange(label.text); + } caretSpr.alpha = 0; } } @@ -73,6 +90,8 @@ class UITextBox extends UISliceSprite implements IUIFocusable { public function onKeyDown(e:KeyCode, modifier:KeyModifier) { switch(e) { + case RETURN: + focused = false; case LEFT: changeSelection(-1); case RIGHT: @@ -87,7 +106,7 @@ class UITextBox extends UISliceSprite implements IUIFocusable { case END: position = label.text.length; case V: - if (modifier == KeyModifier.LEFT_CTRL || modifier == KeyModifier.RIGHT_CTRL ) { + if (modifier == KeyModifier.LEFT_CTRL || modifier == KeyModifier.RIGHT_CTRL) { // paste var data:String = Clipboard.generalClipboard.getData(TEXT_FORMAT); if (data != null) diff --git a/source/funkin/editors/ui/UITopMenu.hx b/source/funkin/editors/ui/UITopMenu.hx index b98476d9..fba0bf0d 100644 --- a/source/funkin/editors/ui/UITopMenu.hx +++ b/source/funkin/editors/ui/UITopMenu.hx @@ -62,13 +62,11 @@ class UITopMenuButton extends UISliceSprite { public override function onHovered() { super.onHovered(); - if (hovered) { - if (curMenu.contextMenuOpened()) { - UIState.state.curContextMenu.preventOutOfBoxClickDeletion(); - } else { - if ((parent != null && parent.anyMenuOpened) || FlxG.mouse.justReleased) { - openContextMenu(); - } + if (curMenu.contextMenuOpened()) { + UIState.state.curContextMenu.preventOutOfBoxClickDeletion(); + } else { + if ((parent != null && parent.anyMenuOpened) || FlxG.mouse.justReleased) { + openContextMenu(); } } }