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;