diff --git a/assets/images/editors/charter/event-icons/components/arrow-down.png b/assets/images/editors/charter/event-icons/components/arrow-down.png new file mode 100644 index 00000000..b5e3ce87 Binary files /dev/null and b/assets/images/editors/charter/event-icons/components/arrow-down.png differ diff --git a/assets/images/editors/charter/event-icons/components/arrow-down.xml b/assets/images/editors/charter/event-icons/components/arrow-down.xml new file mode 100644 index 00000000..1e58cc38 --- /dev/null +++ b/assets/images/editors/charter/event-icons/components/arrow-down.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/assets/images/editors/charter/event-icons/components/warning.png b/assets/images/editors/charter/event-icons/components/warning.png new file mode 100644 index 00000000..3558e0a3 Binary files /dev/null and b/assets/images/editors/charter/event-icons/components/warning.png differ diff --git a/source/funkin/backend/chart/EventsData.hx b/source/funkin/backend/chart/EventsData.hx index d1cac615..e9f5e79b 100644 --- a/source/funkin/backend/chart/EventsData.hx +++ b/source/funkin/backend/chart/EventsData.hx @@ -10,7 +10,7 @@ import funkin.backend.assets.Paths; using StringTools; class EventsData { - public static var defaultEventsList:Array = ["HScript Call", "Camera Movement", "Add Camera Zoom", "Camera Modulo Change", "Camera Flash", "BPM Change", "Scroll Speed Change", "Alt Animation Toggle", "Play Animation", "Time Signature Change"]; + public static var defaultEventsList:Array = ["HScript Call", "Camera Movement", "Add Camera Zoom", "Camera Modulo Change", "Camera Flash", "BPM Change", "Continuous BPM Change", "Time Signature Change", "Scroll Speed Change", "Alt Animation Toggle", "Play Animation"]; public static var defaultEventsParams:Map> = [ "HScript Call" => [ {name: "Function Name", type: TString, defValue: "myFunc"}, @@ -31,7 +31,9 @@ class EventsData { {name: "Time (Steps)", type: TFloat(0.25, 9999, 0.25, 2), defValue: 4}, {name: "Camera", type: TDropDown(['camGame', 'camHUD']), defValue: "camHUD"} ], - "BPM Change" => [{name: "Target BPM", type: TFloat(1.00, null, 0.001, 3), defValue: 100}], + "BPM Change" => [{name: "Target BPM", type: TFloat(1.00, 9999, 0.001, 3), defValue: 100}], + "Continuous BPM Change" => [{name: "Target BPM", type: TFloat(1.00, 9999, 0.001, 3), defValue: 100}, {name: "Time (steps)", type: TFloat(0.25, 9999, 0.25, 2), defValue: 4}], + "Time Signature Change" => [{name: "Target Beat Count", type: TFloat(1), defValue: 4}, {name: "Target Step Count", type: TFloat(1), defValue: 4}], "Scroll Speed Change" => [ {name: "Tween Speed?", type: TBool, defValue: true}, {name: "New Speed", type: TFloat(0.01, 99, 0.01, 2), defValue: 1.}, @@ -48,8 +50,7 @@ class EventsData { } ], "Alt Animation Toggle" => [{name: "Enable On Sing Poses", type: TBool, defValue: true}, {name: "Enable On Idle", type: TBool, defValue: true}, {name: "Strumline", type: TStrumLine, defValue: 0}], - "Play Animation" => [{name: "Character", type: TStrumLine, defValue: 0}, {name: "Animation", type: TString, defValue: "animation"}, {name: "Is forced?", type: TBool, defValue: true}], - "Time Signature Change" => [{name: "Target Beat Count", type: TFloat(1), defValue: 4}, {name: "Target Step Count", type: TFloat(1), defValue: 4}], + "Play Animation" => [{name: "Character", type: TStrumLine, defValue: 0}, {name: "Animation", type: TString, defValue: "animation"}, {name: "Is forced?", type: TBool, defValue: true}] ]; public static var eventsList:Array = defaultEventsList.copy(); diff --git a/source/funkin/backend/system/Conductor.hx b/source/funkin/backend/system/Conductor.hx index 2fbc6dfe..01281ef8 100644 --- a/source/funkin/backend/system/Conductor.hx +++ b/source/funkin/backend/system/Conductor.hx @@ -4,6 +4,7 @@ import flixel.FlxState; import flixel.util.FlxSignal.FlxTypedSignal; import funkin.backend.chart.ChartData; import funkin.backend.system.interfaces.IBeatReceiver; +import funkin.editors.charter.Charter; @:structInit class BPMChangeEvent @@ -14,6 +15,7 @@ class BPMChangeEvent public var stepsPerBeat:Int = 4; public var endSongTime:Float = 0; + public var endStepTime:Float = 0; public var continuous:Bool = false; public var stepTime:Float; @@ -139,6 +141,11 @@ final class Conductor */ public static var bpmChangeMap:Array; + /** + * Array of all events that have been rejected by the Conductor. + */ + public static var invalidEvents:Array = []; + @:dox(hide) public function new() {} public static function reset() { @@ -154,13 +161,9 @@ final class Conductor mapBPMChanges(SONG); } - private static function mapBPMChange(curChange:BPMChangeEvent, time:Float, bpm:Float, ?endTime:Float, ?prevChange:BPMChangeEvent):BPMChangeEvent { + private static function mapBPMChange(curChange:BPMChangeEvent, time:Float, bpm:Float):BPMChangeEvent { var beatTime:Float, measureTime:Float, stepTime:Float; - if (curChange.continuous) - stepTime = curChange.stepTime + (((curChange.endSongTime - curChange.songTime) * (curChange.bpm - prevChange.bpm)) - / Math.log(curChange.bpm / prevChange.bpm) + (time - curChange.endSongTime) * curChange.bpm) / 15000; - else - stepTime = curChange.stepTime + (time - curChange.songTime) / (15000 / curChange.bpm); + stepTime = (curChange.continuous ? curChange.endStepTime : curChange.stepTime) + (time - (curChange.continuous ? curChange.endSongTime : curChange.songTime)) / (15000 / curChange.bpm); beatTime = curChange.beatTime + (stepTime - curChange.stepTime) / curChange.stepsPerBeat; measureTime = curChange.measureTime + (beatTime - curChange.beatTime) / curChange.beatsPerMeasure; @@ -174,7 +177,6 @@ final class Conductor beatsPerMeasure: curChange.beatsPerMeasure, stepsPerBeat: curChange.stepsPerBeat }); - if (curChange.continuous = (endTime is Float && endTime != 0)) curChange.endSongTime = endTime; return curChange; } @@ -194,32 +196,97 @@ final class Conductor }; curChangeIndex = 0; bpmChangeMap = [curChange]; + invalidEvents = []; if (song.events == null) return; // fix the sort first... var events:Array = []; - for (e in song.events) if (e.params != null && (e.name == "BPM Change" || e.name == "Time Signature Change")) events.push(e); - events.sort(function(a, b) return Std.int(a.time - b.time)); + for (e in song.events) if (e.params != null && (e.name == "BPM Change" || e.name == "Time Signature Change" || e.name == "Continuous BPM Change")) events.push(e); + events.sort(function(a, b) { + if (a.time == b.time) { + if (a.name == "Continuous BPM Change") return 1; + if (b.name == "Continuous BPM Change") return -1; + } + return Std.int(a.time - b.time); + }); - var prevChange:BPMChangeEvent = null; for (e in events) { - if (bpmChangeMap.length > 3) prevChange = bpmChangeMap[bpmChangeMap.length - 2]; - var name = e.name, params = e.params, time = e.time; - if (name == "BPM Change" && params[0] is Float && curChange.bpm != params[0]) - curChange = mapBPMChange(curChange, time, params[0], params[1], prevChange); - else if (name == "Time Signature Change") { - //if (beatsPerMeasure == curChange.beatsPerMeasure && stepsPerBeat == curChange.stepsPerBeat) continue; - /* TODO: make so time sigs doesnt stop the bpm change if its in the duration of bpm change */ + curChange = mapEvent(e, curChange); + } + } - if (curChange.songTime != time) curChange = mapBPMChange(curChange, time, curChange.bpm, null, prevChange); - curChange.beatsPerMeasure = params[0]; - curChange.stepsPerBeat = params[1]; + private static function mapEvent(e:ChartEvent, curChange:BPMChangeEvent) { + var name = e.name, params = e.params, time = e.time; + if (curChange.continuous && time < curChange.endSongTime) { //ensure that you cannot place any conductor events during a continuous change + invalidEvents.push(e); + Logs.trace('Invalid Conductor event "${e.name}" at ${e.time} (Intersecting continuous change!)', WARNING); + return curChange; + } + + if (name == "BPM Change" && params[0] is Float && curChange.bpm != params[0]) + curChange = mapBPMChange(curChange, time, params[0]); + else if (name == "Time Signature Change") { + //if (beatsPerMeasure == curChange.beatsPerMeasure && stepsPerBeat == curChange.stepsPerBeat) continue; + /* TODO: make so time sigs doesnt stop the bpm change if its in the duration of bpm change */ + + if (curChange.songTime != time) curChange = mapBPMChange(curChange, time, curChange.bpm); + curChange.beatsPerMeasure = params[0]; + curChange.stepsPerBeat = params[1]; + + curChange.stepTime = CoolUtil.floorInt(curChange.stepTime + .99998); + curChange.beatTime = CoolUtil.floorInt(curChange.beatTime + .99998); + curChange.measureTime = CoolUtil.floorInt(curChange.measureTime + .99998); + } else if (name == "Continuous BPM Change") { + + var prevBPM = curChange.bpm; + if (curChange.bpm == params[0]) { + invalidEvents.push(e); + return curChange; //DO NOT!!!! + } + curChange = mapBPMChange(curChange, time, params[0]); + var endTime = time + (params[1]) / (curChange.bpm - prevBPM) * Math.log(curChange.bpm / prevBPM) * 15000; + curChange.endStepTime = curChange.stepTime + params[1]; + curChange.continuous = true; + curChange.endSongTime = endTime; + } + return curChange; + } + + public static function mapCharterBPMChanges(song:ChartData) { + var curChange:BPMChangeEvent = { + songTime: 0, + stepTime: 0, + beatTime: 0, + measureTime: 0, + bpm: song.meta.bpm, + beatsPerMeasure: song.meta.beatsPerMeasure.getDefault(4), + stepsPerBeat: CoolUtil.floorInt(song.meta.stepsPerBeat.getDefault(4)) + }; + curChangeIndex = 0; + bpmChangeMap = [curChange]; + invalidEvents = []; + + for(event in Charter.instance.eventsGroup.members) { + event.events.sort(function(a, b) { + if (a.time == b.time) { + if (a.name == "Continuous BPM Change") return 1; + if (b.name == "Continuous BPM Change") return -1; + } + return 0; + }); + + var eventTime = Conductor.getTimeForStep(event.step); + for (e in event.events) { + e.time = eventTime; - curChange.stepTime = CoolUtil.floorInt(curChange.stepTime + .99998); - curChange.beatTime = CoolUtil.floorInt(curChange.beatTime + .99998); - curChange.measureTime = CoolUtil.floorInt(curChange.measureTime + .99998); + if ((e.name == "BPM Change" || e.name == "Time Signature Change" || e.name == "Continuous BPM Change")) { + curChange = mapEvent(e, curChange); + } + } } + + } private static var elapsed:Float; diff --git a/source/funkin/editors/charter/Charter.hx b/source/funkin/editors/charter/Charter.hx index 3760b25a..55f74e70 100644 --- a/source/funkin/editors/charter/Charter.hx +++ b/source/funkin/editors/charter/Charter.hx @@ -843,6 +843,7 @@ class Charter extends UIState { if (s is CharterNote) cast(s, CharterNote).snappedToStrumline = true; if (s is UISprite) cast(s, UISprite).cursor = BUTTON; } + checkSelectionForBPMUpdates(); if (!(verticalChange == 0 && horizontalChange == 0)) { notesGroup.sortNotes(); eventsGroup.sortEvents(); undos.addToUndo(CSelectionDrag(undoDrags)); @@ -1031,11 +1032,7 @@ class Charter extends UIState { notesGroup.sortNotes(); notesGroup.autoSort = true; - for (s in selection) - if (s is CharterEvent) { - Charter.instance.updateBPMEvents(); - break; - } + checkSelectionForBPMUpdates(); if (addToUndo) undos.addToUndo(CCreateSelection(selection)); @@ -1059,11 +1056,7 @@ class Charter extends UIState { notesGroup.sortNotes(); notesGroup.autoSort = true; - for (s in selection) - if (s is CharterEvent) { - Charter.instance.updateBPMEvents(); - break; - } + checkSelectionForBPMUpdates(); if (addToUndo) undos.addToUndo(CDeleteSelection(selection)); @@ -1432,6 +1425,8 @@ class Charter extends UIState { selection = sObjects; _edit_copy(_); // to fix stupid bugs + checkSelectionForBPMUpdates(); + undos.addToUndo(CCreateSelection(sObjects.copy())); } @@ -1477,6 +1472,7 @@ class Charter extends UIState { if (s.selectable.draggable) s.selectable.handleDrag(s.change * -1); selection = [for (s in selectionDrags) s.selectable]; + Charter.instance.updateBPMEvents(); case CEditSustains(changes): for(n in changes) n.note.updatePos(n.note.step, n.note.id, n.before, n.note.type); @@ -1522,6 +1518,7 @@ class Charter extends UIState { for (s in selectionDrags) if (s.selectable.draggable) s.selectable.handleDrag(s.change); //this.selection = selection; + Charter.instance.updateBPMEvents(); case CEditSustains(changes): for(n in changes) n.note.updatePos(n.note.step, n.note.id, n.after, n.note.type); @@ -1836,14 +1833,30 @@ class Charter extends UIState { } public function updateBPMEvents() { + eventsGroup.sortEvents(); + Conductor.mapCharterBPMChanges(PlayState.SONG); buildEvents(); - Conductor.changeBPM(PlayState.SONG.meta.bpm, cast PlayState.SONG.meta.beatsPerMeasure.getDefault(4), cast PlayState.SONG.meta.stepsPerBeat.getDefault(4)); - Conductor.mapBPMChanges(PlayState.SONG); - + for(e in eventsGroup.members) { + for(event in e.events) { + if (event.name == "BPM Change" || event.name == "Time Signature Change" || event.name == "Continuous BPM Change") { + e.refreshEventIcons(); + break; + } + } + } + refreshBPMSensitive(); } + public inline function checkSelectionForBPMUpdates() { + for (s in selection) + if (s is CharterEvent) { + Charter.instance.updateBPMEvents(); + break; + } + } + public inline function hitsoundsEnabled(id:Int) return strumLines.members[id] != null && strumLines.members[id].hitsounds; diff --git a/source/funkin/editors/charter/CharterBackdropGroup.hx b/source/funkin/editors/charter/CharterBackdropGroup.hx index ac58a444..484b0380 100644 --- a/source/funkin/editors/charter/CharterBackdropGroup.hx +++ b/source/funkin/editors/charter/CharterBackdropGroup.hx @@ -294,10 +294,10 @@ class CharterGridSeperatorBase extends FlxSprite { for (i => change in Conductor.bpmChangeMap) { if (change.stepTime >= minStep && change.stepTime <= maxStep) { //get step while ignoring the current change - var index = CoolUtil.boundInt(i-1, 0, Conductor.bpmChangeMap.length - 1); - var step = Conductor.getTimeWithBPMInSteps(change.songTime, index, Conductor.getTimeWithIndexInBPM(change.songTime, index)); + var index:Int = CoolUtil.boundInt(i-1, 0, Conductor.bpmChangeMap.length - 1); + var step:Float = Conductor.getTimeWithBPMInSteps(change.songTime, index, Conductor.getTimeWithIndexInBPM(change.songTime, index)); - if (Math.ceil(step) - step > 0) { //mid step change + if (Math.ceil(step) - step > 0 && (step - Math.floor(step)) > FlxMath.EPSILON) { //mid step change timeSignatureChangeGaps.push(step); } } diff --git a/source/funkin/editors/charter/CharterEvent.hx b/source/funkin/editors/charter/CharterEvent.hx index 0eb0fc81..0e2dc002 100644 --- a/source/funkin/editors/charter/CharterEvent.hx +++ b/source/funkin/editors/charter/CharterEvent.hx @@ -1,5 +1,7 @@ package funkin.editors.charter; +import funkin.backend.shaders.CustomShader; +import funkin.backend.system.Conductor; import flixel.group.FlxSpriteGroup; import flixel.math.FlxPoint; import flixel.system.FlxAssets.FlxGraphicAsset; @@ -176,7 +178,7 @@ class CharterEvent extends UISliceSprite implements ICharterSelectable { return healthIcon; } - public static function generateEventIcon(event:ChartEvent):FlxSprite { + public static function generateEventIcon(event:ChartEvent, inMenu:Bool = true):FlxSprite { var script = getUIScript(event, "event-icon"); if(script != null && !(script is DummyScript)) { if(script.get("generateIcon") != null) { @@ -201,8 +203,43 @@ class CharterEvent extends UISliceSprite implements ICharterSelectable { num.active = false; num; }); + if (Conductor.invalidEvents.contains(event)) generateEventIconWarning(group); return group; } + case "Continuous BPM Change": + if(event.params != null && event.params[1] != null) { + if (inMenu) { + var group = new EventIconGroup(); + group.add(generateDefaultIcon("BPM Change Start")); + if (Conductor.invalidEvents.contains(event)) generateEventIconWarning(group); + return group; + } + + var group = generateEventIconWithDuration(event.params[1], "BPM Change Start"); + group.members[0].y -= 2; + generateEventIconNumbers(group, Std.int(event.params[0]), 15); + if (Conductor.invalidEvents.contains(event)) generateEventIconWarning(group); + return group; + } else { + return generateDefaultIcon("BPM Change Start"); + } + case "BPM Change": + if(event.params != null && event.params[0] != null) { + if (inMenu) { + var group = new EventIconGroup(); + group.add(generateDefaultIcon(event.name)); + if (Conductor.invalidEvents.contains(event)) generateEventIconWarning(group); + return group; + } + + var group = new EventIconGroup(); + group.add(generateDefaultIcon(event.name)); + group.members[0].y -= 2; + generateEventIconNumbers(group, Std.int(event.params[0]), 15); + if (Conductor.invalidEvents.contains(event)) generateEventIconWarning(group); + return group; + } + case "Camera Movement": // camera movement, use health icon if(event.params != null) { @@ -213,6 +250,76 @@ class CharterEvent extends UISliceSprite implements ICharterSelectable { return generateDefaultIcon(event.name); } + private static function generateEventIconNumbers(group:EventIconGroup, number:Int, y:Float) { + var numString = Std.string(number); + var offset = (16 - (numString.length*5))/2; + for (num in 0...numString.length) { + group.add({ + var num = new EventNumber(offset + num*5, y, Std.parseInt(numString.charAt(num)), EventNumber.ALIGN_CENTER); + num.active = false; + num; + }); + } + } + + private static function generateEventIconWithDuration(duration:Float, startIcon:String, endIcon:String = "") { + var group = new EventIconGroup(); + group.add(generateDefaultIcon(startIcon)); + + var xOffset = 4; + var yGap = 24; + var endGap = 7; + if (endIcon == "") endGap = -2; + + if (duration >= 0.65) { //min time for showing arrow + var tail = new FlxSprite(xOffset, yGap); + var arrow = new FlxSprite(xOffset, (duration * 40) + endGap); + var arrowSegment = new FlxSprite(xOffset, yGap); + tail.frames = arrow.frames = arrowSegment.frames = Paths.getSparrowAtlas("editors/charter/event-icons/components/arrow-down"); + + group.add({ + tail.animation.addByPrefix("tail", "tail"); + tail.animation.play("tail"); + tail; + }); + + group.add({ + arrowSegment.animation.addByPrefix("segment", "segment"); + arrowSegment.animation.play("segment"); + arrowSegment.scale.y = (duration * 40) - (tail.height + endGap + yGap); + arrowSegment.updateHitbox(); + arrowSegment.y += tail.height; + arrowSegment; + }); + + group.add({ + arrow.animation.addByPrefix("arrow", "arrow"); + arrow.animation.play("arrow"); + arrow; + }); + } + + if (endIcon != "") { + group.add({ + var icon = generateDefaultIcon(endIcon); + icon.y = duration * 40; + icon; + }); + } + + + return group; + } + + private static function generateEventIconWarning(group:EventIconGroup) { + for (spr in group) { + spr.colorTransform.redMultiplier = spr.colorTransform.greenMultiplier = spr.colorTransform.blueMultiplier = 0.5; + spr.colorTransform.redOffset = 100; + } + group.add(getEventComponent("warning", 16, -8)); + group.copyColorTransformToChildren = false; + } + public override function onHovered() { super.onHovered(); /* @@ -238,26 +345,22 @@ class CharterEvent extends UISliceSprite implements ICharterSelectable { } for(event in events) { - var spr = generateEventIcon(event); + var spr = generateEventIcon(event, false); icons.push(spr); members.push(spr); } draggable = true; - for (event in events) - if (event.name == "BPM Change" || event.name == "Time Signature Change") { - draggable = false; - break; - } x = (snappedToGrid && eventsBackdrop != null ? eventsBackdrop.x : 0) - (bWidth = 37 + (icons.length * 22)); } } class EventIconGroup extends FlxSpriteGroup { - public var forceWidth:Float = 32; - public var forceHeight:Float = 32; + public var forceWidth:Float = 16; + public var forceHeight:Float = 16; public var dontTransformChildren:Bool = true; + public var copyColorTransformToChildren:Bool = true; public function new() { super(); @@ -298,6 +401,12 @@ class EventIconGroup extends FlxSpriteGroup { override function get_height() { return forceHeight; } + + override public function draw() { + @:privateAccess + if (copyColorTransformToChildren && colorTransform != null) for (child in members) child.colorTransform.__copyFrom(colorTransform); + super.draw(); + } } class EventNumber extends FlxSprite { @@ -312,10 +421,15 @@ class EventNumber extends FlxSprite { super(x, y); this.digits = []; this.align = align; - while (number > 0) { - this.digits.insert(0, number % 10); - number = Std.int(number / 10); + if (number == 0) { + this.digits.insert(0, 0); + } else { + while (number > 0) { + this.digits.insert(0, number % 10); + number = Std.int(number / 10); + } } + loadGraphic(Paths.image('editors/charter/event-icons/components/eventNums'), true, 6, 7); } diff --git a/source/funkin/editors/charter/CharterEventScreen.hx b/source/funkin/editors/charter/CharterEventScreen.hx index 307aa907..3c2ad340 100644 --- a/source/funkin/editors/charter/CharterEventScreen.hx +++ b/source/funkin/editors/charter/CharterEventScreen.hx @@ -58,7 +58,7 @@ class CharterEventScreen extends UISubstateWindow { }); eventsList.add(new EventButton(events[events.length-1], CharterEvent.generateEventIcon(events[events.length-1]), events.length-1, this, eventsList)); changeTab(events.length-1); - })); + }, chartEvent.step)); for (k=>i in events) eventsList.add(new EventButton(i, CharterEvent.generateEventIcon(i), k, this, eventsList)); add(eventsList); @@ -83,12 +83,12 @@ class CharterEventScreen extends UISubstateWindow { else { chartEvent.events = [for (i in eventsList.buttons.members) i.event]; chartEvent.refreshEventIcons(); - Charter.instance.updateBPMEvents(); Charter.undos.addToUndo(CEditEvent(chartEvent, oldEvents, [for (event in events) Reflect.copy(event)])); } } + Charter.instance.updateBPMEvents(); close(); }); saveButton.x -= saveButton.bWidth; diff --git a/source/funkin/editors/charter/CharterEventTypeSelection.hx b/source/funkin/editors/charter/CharterEventTypeSelection.hx index bfa6cfe7..0e836e77 100644 --- a/source/funkin/editors/charter/CharterEventTypeSelection.hx +++ b/source/funkin/editors/charter/CharterEventTypeSelection.hx @@ -1,9 +1,11 @@ package funkin.editors.charter; +import funkin.backend.system.Conductor; import funkin.backend.chart.EventsData; class CharterEventTypeSelection extends UISubstateWindow { var callback:String->Void; + var eventStepTime:Float; var buttons:Array = []; @@ -13,9 +15,10 @@ class CharterEventTypeSelection extends UISubstateWindow { var upIndicator:UIText; var downIndicator:UIText; - public function new(callback:String->Void) { + public function new(callback:String->Void, eventStepTime:Float) { super(); this.callback = callback; + this.eventStepTime = eventStepTime; } public override function create() { @@ -32,6 +35,15 @@ class CharterEventTypeSelection extends UISubstateWindow { buttonsBG.frames = Paths.getFrames('editors/ui/inputbox'); add(buttonsBG); + var disableConductorEvents:Bool = false; + var disableOnlyContinuousChanges:Bool = false; + for (change in Conductor.bpmChangeMap) { + if (change.continuous && eventStepTime >= change.stepTime && eventStepTime < change.endStepTime) { + disableOnlyContinuousChanges = eventStepTime == change.stepTime; //allow time sig and instant bpm changes on the same event + disableConductorEvents = !disableOnlyContinuousChanges; + } + } + for(k=>eventName in EventsData.eventsList) { var button = new UIButton(0, (32 * k), eventName, function() { close(); @@ -52,6 +64,11 @@ class CharterEventTypeSelection extends UISubstateWindow { icon.x = button.x + 8; icon.y = button.y + Math.abs(button.bHeight - icon.height) / 2; add(icon); + + if (disableConductorEvents && (eventName == "Time Signature Change" || eventName == "Continuous BPM Change" || eventName == "BPM Change") || (disableOnlyContinuousChanges && eventName == "Continuous BPM Change")) { + button.selectable = button.shouldPress = false; + button.autoAlpha = true; + } } windowSpr.bHeight = 61 + (32 * (17)); @@ -79,7 +96,7 @@ class CharterEventTypeSelection extends UISubstateWindow { sinner += elapsed; for (button in buttons) - button.selectable = buttonsBG.hovered; + if (button.shouldPress) button.selectable = buttonsBG.hovered; buttonCameras.zoom = subCam.zoom;