diff --git a/source/funkin/backend/chart/EventsData.hx b/source/funkin/backend/chart/EventsData.hx index 97eefd7f..a46de554 100644 --- a/source/funkin/backend/chart/EventsData.hx +++ b/source/funkin/backend/chart/EventsData.hx @@ -33,7 +33,7 @@ class EventsData { ], "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}], + "Time Signature Change" => [{name: "Target Numerator", type: TFloat(1), defValue: 4}, {name: "Target Denominator", 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.}, diff --git a/source/funkin/backend/system/Conductor.hx b/source/funkin/backend/system/Conductor.hx index 10bc8a42..d535a9a9 100644 --- a/source/funkin/backend/system/Conductor.hx +++ b/source/funkin/backend/system/Conductor.hx @@ -6,6 +6,12 @@ import funkin.backend.chart.ChartData; import funkin.backend.system.interfaces.IBeatReceiver; import funkin.editors.charter.Charter; +enum BeatType { + STEP; + BEAT; + MEASURE; +} + @:structInit class BPMChangeEvent { @@ -25,6 +31,16 @@ class BPMChangeEvent final class Conductor { + public static function getBeats(?every:BeatType, interval:Float, offset:Float = 0):Float { + final beat = switch(every) { + case MEASURE: curMeasureFloat; + case STEP: curStepFloat; + default: curBeatFloat; + } + if (interval <= 0) return beat - offset; + else return Math.floor((beat - offset) * interval) / interval; + } + /** * FlxSignals */ @@ -96,12 +112,21 @@ final class Conductor return bpmChangeMap.length == 0 ? 4 : bpmChangeMap[curChangeIndex].beatsPerMeasure; /** - * Number of steps per beat (bottom number in time signature). Defaults to 4. + * Number of steps per beat. Defaults to 4. + * Not a divisor number for time signature, it does the complete opposite. + * It's because CNE Conductor is based in sixteenth note instead of beat. */ - public static var stepsPerBeat(get, never):Float; + public static var stepsPerBeat(get, never):Int; private static function get_stepsPerBeat() return bpmChangeMap.length == 0 ? 4 : bpmChangeMap[curChangeIndex].stepsPerBeat; + /** + * How much value notes to divide for beat (bottom or divisor number in time signature). + * Only for a convinient way to access divisor instead of multiply by steps per beat. + */ + public static var denominator(get, never):Int; + private static function get_denominator() return Math.floor(16 / stepsPerBeat); + /** * Current step */ @@ -231,13 +256,13 @@ final class Conductor if (curChange.songTime != time) curChange = mapBPMChange(curChange, time, curChange.bpm); curChange.beatsPerMeasure = params[0]; - curChange.stepsPerBeat = params[1]; - + curChange.stepsPerBeat = Math.floor(16 / params[1]); // convert from denominator to stepsPerBeat + 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") { - + } + else if (name == "Continuous BPM Change") { var prevBPM = curChange.bpm; if (curChange.bpm == params[0]) { invalidEvents.push(e); @@ -266,7 +291,7 @@ final class Conductor bpmChangeMap = [curChange]; invalidEvents = []; - for(event in Charter.instance.eventsGroup.members) { + for (event in Charter.instance.eventsGroup.members) { event.events.sort(function(a, b) { if (MathUtil.equal(a.time, b.time)) { if (a.name == "Continuous BPM Change") return 1; @@ -282,11 +307,8 @@ final class Conductor if ((e.name == "BPM Change" || e.name == "Time Signature Change" || e.name == "Continuous BPM Change")) { curChange = mapEvent(e, curChange); } - } } - - } private static var elapsed:Float; @@ -303,12 +325,10 @@ final class Conductor return; } - if (lastSongPos != (lastSongPos = FlxG.sound.music.time - songOffset)) { - // update conductor + if (lastSongPos != (lastSongPos = FlxG.sound.music.time - songOffset)) songPosition = lastSongPos; - } else { + else songPosition += songOffset + elapsed * 1000; - } } private static function onStateSwitch(newState:FlxState) { diff --git a/source/funkin/backend/system/framerate/ConductorInfo.hx b/source/funkin/backend/system/framerate/ConductorInfo.hx index 96f4e39d..1afb043b 100644 --- a/source/funkin/backend/system/framerate/ConductorInfo.hx +++ b/source/funkin/backend/system/framerate/ConductorInfo.hx @@ -13,7 +13,7 @@ class ConductorInfo extends FramerateCategory { _text += '\n - ${Conductor.curStep} steps'; _text += '\n - ${Conductor.curMeasure} measures'; _text += '\nCurrent BPM: ${Conductor.bpm}'; - _text += '\nTime Signature: ${Conductor.beatsPerMeasure}/${Conductor.stepsPerBeat}'; + _text += '\nTime Signature: ${Conductor.beatsPerMeasure}/${Conductor.denominator}'; this.text.text = _text; super.__enterFrame(t); diff --git a/source/funkin/editors/charter/Charter.hx b/source/funkin/editors/charter/Charter.hx index 96855473..301e2306 100644 --- a/source/funkin/editors/charter/Charter.hx +++ b/source/funkin/editors/charter/Charter.hx @@ -1243,12 +1243,13 @@ class Charter extends UIState { for (strumLine in strumLines.members) strumLine.vocals.pause(); } - songPosInfo.text = '${CoolUtil.timeToStr(Conductor.songPosition)} / ${CoolUtil.timeToStr(songLength)}' - + '\nStep: ${curStep}' - + '\nBeat: ${curBeat}' - + '\nMeasure: ${curMeasure}' - + '\nBPM: ${Math.floor(Conductor.bpm*1000)/1000}' - + '\nTime Signature: ${Conductor.beatsPerMeasure}/${Conductor.stepsPerBeat}'; + var curChange = Conductor.curChange; + songPosInfo.text = '${CoolUtil.timeToStr(songPos)} / ${CoolUtil.timeToStr(songLength)}' + + '\nStep: $curStep' + + '\nBeat: $curBeat' + + '\nMeasure: $curMeasure' + + '\nBPM: ${(curChange != null && curChange.continuous && curChange.endSongTime > songPos) ? CoolUtil.quantize(Conductor.bpm, 2) : Conductor.bpm}' + + '\nTime Signature: ${Conductor.beatsPerMeasure}/${Conductor.denominator}'; if (charterCamera.zoom != (charterCamera.zoom = lerp(charterCamera.zoom, __camZoom, __firstFrame ? 1 : 0.125))) updateDisplaySprites(); diff --git a/source/funkin/editors/charter/CharterEvent.hx b/source/funkin/editors/charter/CharterEvent.hx index 9dcee4b1..8386f5e3 100644 --- a/source/funkin/editors/charter/CharterEvent.hx +++ b/source/funkin/editors/charter/CharterEvent.hx @@ -140,7 +140,7 @@ class CharterEvent extends UISliceSprite implements ICharterSelectable { graphic = defaultPath; } - return new FlxSprite().loadGraphic(graphic); + return new FlxSprite(graphic); } /** @@ -154,7 +154,7 @@ class CharterEvent extends UISliceSprite implements ICharterSelectable { public static function getEventComponent(type:String, x:Float = 0.0, y:Float = 0.0) { var componentPath = Paths.image("editors/charter/event-icons/components/" + type); if(Assets.exists(componentPath)) - return new FlxSprite(x, y).loadGraphic(componentPath); + return new FlxSprite(x, y, componentPath); Logs.trace('Could not find component $type', WARNING); return null; @@ -423,12 +423,10 @@ class EventNumber extends FlxSprite { this.align = align; this.spacing = spacing; - - if (number == 0) { this.digits.insert(0, 0); - } else { - + } + else { var decimals:Float = FlxMath.roundDecimal(Math.abs(number % 1), precision); if (decimals > 0) this.digits.insert(0, FRAME_POINT); while(decimals > 0) { diff --git a/source/funkin/editors/charter/CharterMetaDataScreen.hx b/source/funkin/editors/charter/CharterMetaDataScreen.hx index e58898c0..3266b3a2 100644 --- a/source/funkin/editors/charter/CharterMetaDataScreen.hx +++ b/source/funkin/editors/charter/CharterMetaDataScreen.hx @@ -15,7 +15,7 @@ class CharterMetaDataScreen extends UISubstateWindow { public var songNameTextBox:UITextBox; public var bpmStepper:UINumericStepper; public var beatsPerMeasureStepper:UINumericStepper; - public var stepsPerBeatStepper :UINumericStepper; + public var denominatorStepper :UINumericStepper; public var needsVoicesCheckbox:UICheckbox; public var customPropertiesButtonList:UIButtonList; @@ -63,10 +63,10 @@ class CharterMetaDataScreen extends UISubstateWindow { add(new UIText(beatsPerMeasureStepper.x + 30, beatsPerMeasureStepper.y + 3, 0, "/", 22)); - stepsPerBeatStepper = new UINumericStepper(beatsPerMeasureStepper.x + 30 + 24, beatsPerMeasureStepper.y, metadata.stepsPerBeat, 1, 0, 1, null, 54); - add(stepsPerBeatStepper); + denominatorStepper = new UINumericStepper(beatsPerMeasureStepper.x + 30 + 24, beatsPerMeasureStepper.y, Math.floor(16 / metadata.stepsPerBeat), 1, 0, 1, null, 54); + add(denominatorStepper); - needsVoicesCheckbox = new UICheckbox(stepsPerBeatStepper.x + 80 + 26, stepsPerBeatStepper.y, "Voices", metadata.needsVoices); + needsVoicesCheckbox = new UICheckbox(denominatorStepper.x + 80 + 26, denominatorStepper.y, "Voices", metadata.needsVoices); add(needsVoicesCheckbox); addLabelOn(needsVoicesCheckbox, "Needs Voices"); needsVoicesCheckbox.y += 6; needsVoicesCheckbox.x += 4; @@ -142,7 +142,7 @@ class CharterMetaDataScreen extends UISubstateWindow { } public function saveMeta() { - for (stepper in [bpmStepper, beatsPerMeasureStepper, stepsPerBeatStepper]) + for (stepper in [bpmStepper, beatsPerMeasureStepper, denominatorStepper]) @:privateAccess stepper.__onChange(stepper.label.text); var customVals = {}; @@ -154,7 +154,7 @@ class CharterMetaDataScreen extends UISubstateWindow { name: songNameTextBox.label.text, bpm: bpmStepper.value, beatsPerMeasure: Std.int(beatsPerMeasureStepper.value), - stepsPerBeat: Std.int(stepsPerBeatStepper.value), + stepsPerBeat: Std.int(16 / denominatorStepper.value), needsVoices: needsVoicesCheckbox.checked, displayName: displayNameTextBox.label.text, icon: iconTextBox.label.text, diff --git a/source/funkin/editors/charter/SongCreationScreen.hx b/source/funkin/editors/charter/SongCreationScreen.hx index 007c8f6c..d3c1b0bc 100644 --- a/source/funkin/editors/charter/SongCreationScreen.hx +++ b/source/funkin/editors/charter/SongCreationScreen.hx @@ -19,7 +19,7 @@ class SongCreationScreen extends UISubstateWindow { public var songNameTextBox:UITextBox; public var bpmStepper:UINumericStepper; public var beatsPerMeasureStepper:UINumericStepper; - public var stepsPerBeatStepper :UINumericStepper; + public var denominatorStepper :UINumericStepper; public var needsVoicesCheckbox:UICheckbox; public var instExplorer:UIFileExplorer; public var voicesExplorer:UIFileExplorer; @@ -79,12 +79,12 @@ class SongCreationScreen extends UISubstateWindow { songDataGroup.add(new UIText(beatsPerMeasureStepper.x + 30, beatsPerMeasureStepper.y + 3, 0, "/", 22)); - stepsPerBeatStepper = new UINumericStepper(beatsPerMeasureStepper.x + 30 + 24, beatsPerMeasureStepper.y, 4, 1, 0, 1, null, 54); - songDataGroup.add(stepsPerBeatStepper); + denominatorStepper = new UINumericStepper(beatsPerMeasureStepper.x + 30 + 24, beatsPerMeasureStepper.y, 4, 1, 0, 1, null, 54); + songDataGroup.add(denominatorStepper); var voicesUIText:UIText = null; - needsVoicesCheckbox = new UICheckbox(stepsPerBeatStepper.x + 80 + 26, stepsPerBeatStepper.y, "Voices", true); + needsVoicesCheckbox = new UICheckbox(denominatorStepper.x + 80 + 26, denominatorStepper.y, "Voices", true); needsVoicesCheckbox.onChecked = function(checked) { if (voicesExplorer == null) return; @@ -243,14 +243,14 @@ class SongCreationScreen extends UISubstateWindow { } function saveSongInfo() { - for (stepper in [bpmStepper, beatsPerMeasureStepper, stepsPerBeatStepper]) + for (stepper in [bpmStepper, beatsPerMeasureStepper, denominatorStepper]) @:privateAccess stepper.__onChange(stepper.label.text); var meta:ChartMetaData = { name: songNameTextBox.label.text, bpm: bpmStepper.value, beatsPerMeasure: Std.int(beatsPerMeasureStepper.value), - stepsPerBeat: Std.int(stepsPerBeatStepper.value), + stepsPerBeat: Std.int(16 / denominatorStepper.value), needsVoices: needsVoicesCheckbox.checked, displayName: displayNameTextBox.label.text, icon: iconTextBox.label.text, diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx index 1744c3ac..bbb95c49 100644 --- a/source/funkin/game/PlayState.hx +++ b/source/funkin/game/PlayState.hx @@ -224,23 +224,29 @@ class PlayState extends MusicBeatState */ public var camZooming:Bool = false; /** - * Interval of cam zooming (beats). - * For example: if set to 4, the camera will zoom every 4 beats. + * Interval of cam zooming (in conductor values). + * Example: If Interval is 1 and Beat Type is on MEASURE, it'll zoom every a measure. + * NOTE: Will set to 4 if not found any other time signatures unlike 4/4 */ - public var camZoomingInterval:Int = 4; + public var camZoomingInterval:Float = 1; + /** + * Number of Conductor values to offset camZooming by. + */ + public var camZoomingOffset:Float = 0; + /** + * Beat type for interval of cam zooming. + * Example: If Beat Type is on STEP and Interval is 2, it'll zoom every 2 steps. + * NOTE: Will set to BEAT if not found any other time signatures unlike 4/4 + */ + public var camZoomingEvery:BeatType = MEASURE; + /** + * Stores what was the last beat for the cam zooming intervals. + */ + public var camZoomingLastBeat:Float; /** * How strong the cam zooms should be (defaults to 1) */ public var camZoomingStrength:Float = 1; - /** - * Number of Beats to offset camZooming by. - * Will automatically be set when a Time Signature Change Occurs. - */ - public var camZoomingOffset:Float = 0; - /** - * The curBeat position of the last Time Signature Change that occured - */ - public var lastTimeSigBeat:Float = 0; /** * Maximum amount of zoom for the camera. */ @@ -1020,21 +1026,34 @@ class PlayState extends MusicBeatState { if (songData == null) songData = SONG; - events = songData.events != null ? [for(e in songData.events) e] : []; - // get first camera focus - for(e in events) { - if (e.time > 10) break; - if (e.name == "Camera Movement") { - executeEvent(e); - break; + var foundCam = false; + var foundSigs = songData.meta.beatsPerMeasure.getDefault(4) != 4 || songData.meta.stepsPerBeat.getDefault(4) != 4; + + if (events == null) events = []; + else events = [ + for (e in songData.events) { + switch (e.name) { + case "Camera Movement": if (!foundCam && e.time < 10) { + foundCam = true; + executeEvent(e); + } + case "Time Signature Change": if (!foundSigs && (e.params[0] != 4 || e.params[1] != 4)) { + foundSigs = true; + } + } + e; } + ]; + + if (!foundSigs) { + camZoomingInterval = 4; + camZoomingEvery = BEAT; } + events.sort(function(p1, p2) { return FlxSort.byValues(FlxSort.DESCENDING, p1.time, p2.time); }); - camZoomingInterval = cast songData.meta.beatsPerMeasure.getDefault(4); - curSong = songData.meta.name.toLowerCase(); inst = FlxG.sound.load(Paths.inst(SONG.meta.name, difficulty)); @@ -1284,6 +1303,15 @@ class PlayState extends MusicBeatState } } + if (Options.camZoomOnBeat && camZooming && FlxG.camera.zoom < maxCamZoom) { + var beat = Conductor.getBeats(camZoomingEvery, camZoomingInterval, camZoomingOffset); + if (camZoomingLastBeat != beat) { + camZoomingLastBeat = beat; + FlxG.camera.zoom += 0.015 * camZoomingStrength; + camHUD.zoom += 0.03 * camZoomingStrength; + } + } + if (doIconBop) for (icon in iconArray) if (icon.updateBump != null) @@ -1470,9 +1498,6 @@ class PlayState extends MusicBeatState if (strumLines.members[event.params[0]] != null && strumLines.members[event.params[0]].characters != null) for (char in strumLines.members[event.params[0]].characters) if (char != null) char.playAnim(event.params[1], event.params[2], null); - case "Time Signature Change": - lastTimeSigBeat = Conductor.getTimeInBeats(event.time); - // the rest is automatically handled by conductor case "Unknown": // nothing } } @@ -1885,13 +1910,6 @@ class PlayState extends MusicBeatState override function beatHit(curBeat:Int) { super.beatHit(curBeat); - - camZoomingOffset = curBeat - lastTimeSigBeat; - - if (Options.camZoomOnBeat && camZooming && FlxG.camera.zoom < maxCamZoom && camZoomingOffset % camZoomingInterval == 0) { - FlxG.camera.zoom += 0.015 * camZoomingStrength; - camHUD.zoom += 0.03 * camZoomingStrength; - } if (doIconBop) for (icon in iconArray) diff --git a/source/funkin/options/categories/GameplayOptions.hx b/source/funkin/options/categories/GameplayOptions.hx index 15c0ba60..8bd3c49b 100644 --- a/source/funkin/options/categories/GameplayOptions.hx +++ b/source/funkin/options/categories/GameplayOptions.hx @@ -53,7 +53,7 @@ class GameplayOptions extends OptionsScreen { __lastBeat = Conductor.curBeat; } - var beat = Math.floor(Conductor.getStepForTime(FlxG.sound.music.time) / Conductor.stepsPerBeat); + var beat = Math.floor(Conductor.getTimeInBeats(FlxG.sound.music.time)); if (__lastSongBeat != beat) { __metronome.replay(); __lastSongBeat = beat;