diff --git a/Project.xml b/Project.xml index 4b81fd07b..8ba14e7dc 100644 --- a/Project.xml +++ b/Project.xml @@ -119,7 +119,7 @@ - + diff --git a/source/funkin/graphics/shaders/GaussianBlurShader.hx b/source/funkin/graphics/shaders/GaussianBlurShader.hx index 81167655b..cecfdab80 100644 --- a/source/funkin/graphics/shaders/GaussianBlurShader.hx +++ b/source/funkin/graphics/shaders/GaussianBlurShader.hx @@ -20,6 +20,6 @@ class GaussianBlurShader extends FlxRuntimeShader public function setAmount(value:Float):Void { this.amount = value; - this.setFloat("amount", amount); + this.setFloat("_amount", amount); } } diff --git a/source/funkin/graphics/shaders/Grayscale.hx b/source/funkin/graphics/shaders/Grayscale.hx index 6673ace24..fbd0970e5 100644 --- a/source/funkin/graphics/shaders/Grayscale.hx +++ b/source/funkin/graphics/shaders/Grayscale.hx @@ -17,6 +17,6 @@ class Grayscale extends FlxRuntimeShader public function setAmount(value:Float):Void { amount = value; - this.setFloat("amount", amount); + this.setFloat("_amount", amount); } } diff --git a/source/funkin/graphics/shaders/HSVShader.hx b/source/funkin/graphics/shaders/HSVShader.hx index 733bbca7f..2dfdac2c9 100644 --- a/source/funkin/graphics/shaders/HSVShader.hx +++ b/source/funkin/graphics/shaders/HSVShader.hx @@ -20,7 +20,7 @@ class HSVShader extends FlxRuntimeShader function set_hue(value:Float):Float { - this.setFloat('hue', value); + this.setFloat('_hue', value); this.hue = value; return this.hue; diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index cb1cacbc1..b59c48888 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -245,20 +245,26 @@ class PlayState extends MusicBeatSubState /** * The current camera zoom level without any modifiers applied. */ - public var currentCameraZoom:Float = FlxCamera.defaultZoom * 1.05; + public var currentCameraZoom:Float = FlxCamera.defaultZoom; /** - * currentCameraZoom is increased every beat, and lerped back to this value every frame, creating a smooth 'zoom-in' effect. - * Defaults to 1.05, but may be larger or smaller depending on the current stage. - * Tweened via the `ZoomCamera` song event in direct mode. + * Multiplier for currentCameraZoom for camera bops. + * Lerped back to 1.0x every frame. */ - public var defaultCameraZoom:Float = FlxCamera.defaultZoom * 1.05; + public var cameraBopMultiplier:Float = 1.0; /** - * Camera zoom applied on top of currentCameraZoom. - * Tweened via the `ZoomCamera` song event in additive mode. + * Default camera zoom for the current stage. + * If we aren't in a stage, just use the default zoom (1.05x). */ - public var additiveCameraZoom:Float = 0; + public var stageZoom(get, never):Float; + + function get_stageZoom():Float + { + if (currentStage != null) return currentStage.camZoom; + else + return FlxCamera.defaultZoom * 1.05; + } /** * The current HUD camera zoom level. @@ -268,16 +274,18 @@ class PlayState extends MusicBeatSubState public var defaultHUDCameraZoom:Float = FlxCamera.defaultZoom * 1.0; /** - * Intensity of the gameplay camera zoom. - * @default `1.5%` + * Camera bop intensity multiplier. + * Applied to cameraBopMultiplier on camera bops (usually every beat). + * @default `101.5%` */ - public var cameraZoomIntensity:Float = Constants.DEFAULT_ZOOM_INTENSITY; + public var cameraBopIntensity:Float = Constants.DEFAULT_BOP_INTENSITY; /** * Intensity of the HUD camera zoom. + * Need to make this a multiplier later. Just shoving in 0.015 for now so it doesn't break. * @default `3.0%` */ - public var hudCameraZoomIntensity:Float = Constants.DEFAULT_ZOOM_INTENSITY * 2.0; + public var hudCameraZoomIntensity:Float = 0.015 * 2.0; /** * How many beats (quarter notes) between camera zooms. @@ -861,8 +869,8 @@ class PlayState extends MusicBeatSubState regenNoteData(); // Reset camera zooming - cameraZoomIntensity = Constants.DEFAULT_ZOOM_INTENSITY; - hudCameraZoomIntensity = Constants.DEFAULT_ZOOM_INTENSITY * 2.0; + cameraBopIntensity = Constants.DEFAULT_BOP_INTENSITY; + hudCameraZoomIntensity = (cameraBopIntensity - 1.0) * 2.0; cameraZoomRate = Constants.DEFAULT_ZOOM_RATE; health = Constants.HEALTH_STARTING; @@ -957,11 +965,12 @@ class PlayState extends MusicBeatSubState if (health > Constants.HEALTH_MAX) health = Constants.HEALTH_MAX; if (health < Constants.HEALTH_MIN) health = Constants.HEALTH_MIN; - // Lerp the camera zoom towards the target level. + // Apply camera zoom + multipliers. if (subState == null) { - currentCameraZoom = FlxMath.lerp(defaultCameraZoom, currentCameraZoom, 0.95); - FlxG.camera.zoom = currentCameraZoom + additiveCameraZoom; + cameraBopMultiplier = FlxMath.lerp(1.0, cameraBopMultiplier, 0.95); // Lerp bop multiplier back to 1.0x + var zoomPlusBop = currentCameraZoom * cameraBopMultiplier; // Apply camera bop multiplier. + FlxG.camera.zoom = zoomPlusBop; // Actually apply the zoom to the camera. camHUD.zoom = FlxMath.lerp(defaultHUDCameraZoom, camHUD.zoom, 0.95); } @@ -971,6 +980,7 @@ class PlayState extends MusicBeatSubState FlxG.watch.addQuick('bfAnim', currentStage.getBoyfriend().getCurrentAnimation()); } FlxG.watch.addQuick('health', health); + FlxG.watch.addQuick('cameraBopIntensity', cameraBopIntensity); // TODO: Add a song event for Handle GF dance speed. @@ -1367,15 +1377,15 @@ class PlayState extends MusicBeatSubState // activeNotes.sort(SortUtil.byStrumtime, FlxSort.DESCENDING); } - // Only zoom camera if we are zoomed by less than 35%. + // Only bop camera if zoom level is below 135% if (Preferences.zoomCamera - && FlxG.camera.zoom < (1.35 * defaultCameraZoom) + && FlxG.camera.zoom < (1.35 * FlxCamera.defaultZoom) && cameraZoomRate > 0 && Conductor.instance.currentBeat % cameraZoomRate == 0) { - // Zoom camera in (1.5%) - currentCameraZoom += cameraZoomIntensity * defaultCameraZoom; - // Hud zooms double (3%) + // Set zoom multiplier for camera bop. + cameraBopMultiplier = cameraBopIntensity; + // HUD camera zoom still uses old system. To change. (+3%) camHUD.zoom += hudCameraZoomIntensity * defaultHUDCameraZoom; } // trace('Not bopping camera: ${FlxG.camera.zoom} < ${(1.35 * defaultCameraZoom)} && ${cameraZoomRate} > 0 && ${Conductor.instance.currentBeat} % ${cameraZoomRate} == ${Conductor.instance.currentBeat % cameraZoomRate}}'); @@ -1566,12 +1576,11 @@ class PlayState extends MusicBeatSubState { if (PlayState.instance.isMinimalMode) return; // Apply camera zoom level from stage data. - defaultCameraZoom = currentStage?.camZoom ?? 1.0; - currentCameraZoom = defaultCameraZoom; + currentCameraZoom = stageZoom; FlxG.camera.zoom = currentCameraZoom; - // Reset additive zoom. - additiveCameraZoom = 0; + // Reset bop multiplier. + cameraBopMultiplier = 1.0; } /** @@ -3116,6 +3125,15 @@ class PlayState extends MusicBeatSubState FlxG.camera.focusOn(cameraFollowPoint.getPosition()); } + /** + * Sets the camera follow point's position and tweens the camera there. + */ + public function tweenCameraToPosition(?x:Float, ?y:Float, ?duration:Float, ?ease:NullFloat>):Void + { + cameraFollowPoint.setPosition(x, y); + tweenCameraToFollowPoint(duration, ease); + } + /** * Disables camera following and tweens the camera to the follow point manually. */ @@ -3157,38 +3175,24 @@ class PlayState extends MusicBeatSubState /** * Tweens the camera zoom to the desired amount. */ - public function tweenCameraZoom(?zoom:Float, ?duration:Float, ?directMode:Bool, ?ease:NullFloat>):Void + public function tweenCameraZoom(?zoom:Float, ?duration:Float, ?direct:Bool, ?ease:NullFloat>):Void { // Cancel the current tween if it's active. cancelCameraZoomTween(); - var targetZoom = zoom * FlxCamera.defaultZoom; + // Direct mode: Set zoom directly. + // Stage mode: Set zoom as a multiplier of the current stage's default zoom. + var targetZoom = zoom * (direct ? FlxCamera.defaultZoom : stageZoom); - if (directMode) // Direct mode: Tween defaultCameraZoom for basic "smooth" zooms. + if (duration == 0) { - if (duration == 0) - { - // Instant zoom. No tween needed. - defaultCameraZoom = targetZoom; - } - else - { - // Zoom tween! Caching it so we can cancel/pause it later if needed. - cameraZoomTween = FlxTween.tween(this, {defaultCameraZoom: targetZoom}, duration, {ease: ease}); - } + // Instant zoom. No tween needed. + currentCameraZoom = targetZoom; } - else // Additive mode: Tween additiveCameraZoom for ease-based zooms. + else { - if (duration == 0) - { - // Instant zoom. No tween needed. - additiveCameraZoom = targetZoom; - } - else - { - // Zoom tween! Caching it so we can cancel/pause it later if needed. - cameraZoomTween = FlxTween.tween(this, {additiveCameraZoom: targetZoom}, duration, {ease: ease}); - } + // Zoom tween! Caching it so we can cancel/pause it later if needed. + cameraZoomTween = FlxTween.tween(this, {currentCameraZoom: targetZoom}, duration, {ease: ease}); } } diff --git a/source/funkin/play/cutscene/VideoCutscene.hx b/source/funkin/play/cutscene/VideoCutscene.hx index 3da51185f..0c05bc876 100644 --- a/source/funkin/play/cutscene/VideoCutscene.hx +++ b/source/funkin/play/cutscene/VideoCutscene.hx @@ -311,7 +311,7 @@ class VideoCutscene blackScreen = null; } }); - FlxTween.tween(FlxG.camera, {zoom: PlayState.instance.defaultCameraZoom}, transitionTime, + FlxTween.tween(FlxG.camera, {zoom: PlayState.instance.stageZoom}, transitionTime, { ease: FlxEase.quadInOut, onComplete: function(twn:FlxTween) { diff --git a/source/funkin/play/cutscene/dialogue/Conversation.hx b/source/funkin/play/cutscene/dialogue/Conversation.hx index c520c3e25..2c59eaba0 100644 --- a/source/funkin/play/cutscene/dialogue/Conversation.hx +++ b/source/funkin/play/cutscene/dialogue/Conversation.hx @@ -23,6 +23,7 @@ import funkin.modding.IScriptedClass.IDialogueScriptedClass; import funkin.modding.IScriptedClass.IEventHandler; import funkin.play.cutscene.dialogue.DialogueBox; import funkin.util.SortUtil; +import funkin.util.EaseUtil; /** * A high-level handler for dialogue. @@ -179,7 +180,7 @@ class Conversation extends FlxSpriteGroup implements IDialogueScriptedClass impl if (backdropData.fadeTime > 0.0) { backdrop.alpha = 0.0; - FlxTween.tween(backdrop, {alpha: 1.0}, backdropData.fadeTime, {ease: FlxEase.linear}); + FlxTween.tween(backdrop, {alpha: 1.0}, backdropData.fadeTime, {ease: EaseUtil.stepped(10)}); } else { @@ -403,6 +404,7 @@ class Conversation extends FlxSpriteGroup implements IDialogueScriptedClass impl type: ONESHOT, // holy shit like the game no way startDelay: 0, onComplete: (_) -> endOutro(), + ease: EaseUtil.stepped(8) }); FlxTween.tween(this.music, {volume: 0.0}, outroData.fadeTime); diff --git a/source/funkin/play/event/FocusCameraSongEvent.hx b/source/funkin/play/event/FocusCameraSongEvent.hx index d4ab4f24f..1bcac9ad3 100644 --- a/source/funkin/play/event/FocusCameraSongEvent.hx +++ b/source/funkin/play/event/FocusCameraSongEvent.hx @@ -70,80 +70,76 @@ class FocusCameraSongEvent extends SongEvent if (char == null) char = cast data.value; - var useTween:Null = data.getBool('useTween'); - if (useTween == null) useTween = false; var duration:Null = data.getFloat('duration'); if (duration == null) duration = 4.0; var ease:Null = data.getString('ease'); - if (ease == null) ease = 'linear'; + if (ease == null) ease = 'CLASSIC'; + + var currentStage = PlayState.instance.currentStage; + + // Get target position based on char. + var targetX:Float = posX; + var targetY:Float = posY; switch (char) { - case -1: // Position + case -1: // Position ("focus" on origin) trace('Focusing camera on static position.'); - var xTarget:Float = posX; - var yTarget:Float = posY; - PlayState.instance.cameraFollowPoint.setPosition(xTarget, yTarget); - case 0: // Boyfriend - // Focus the camera on the player. - if (PlayState.instance.currentStage.getBoyfriend() == null) + case 0: // Boyfriend (focus on player) + if (currentStage.getBoyfriend() == null) { trace('No BF to focus on.'); return; } trace('Focusing camera on player.'); - var xTarget:Float = PlayState.instance.currentStage.getBoyfriend().cameraFocusPoint.x + posX; - var yTarget:Float = PlayState.instance.currentStage.getBoyfriend().cameraFocusPoint.y + posY; + var bfPoint = currentStage.getBoyfriend().cameraFocusPoint; + targetX += bfPoint.x; + targetY += bfPoint.y; - PlayState.instance.cameraFollowPoint.setPosition(xTarget, yTarget); - case 1: // Dad - // Focus the camera on the dad. - if (PlayState.instance.currentStage.getDad() == null) + case 1: // Dad (focus on opponent) + if (currentStage.getDad() == null) { trace('No dad to focus on.'); return; } - trace('Focusing camera on dad.'); - trace(PlayState.instance.currentStage.getDad()); - var xTarget:Float = PlayState.instance.currentStage.getDad().cameraFocusPoint.x + posX; - var yTarget:Float = PlayState.instance.currentStage.getDad().cameraFocusPoint.y + posY; + trace('Focusing camera on opponent.'); + var dadPoint = currentStage.getDad().cameraFocusPoint; + targetX += dadPoint.x; + targetY += dadPoint.y; - PlayState.instance.cameraFollowPoint.setPosition(xTarget, yTarget); - case 2: // Girlfriend - // Focus the camera on the girlfriend. - if (PlayState.instance.currentStage.getGirlfriend() == null) + case 2: // Girlfriend (focus on girlfriend) + if (currentStage.getGirlfriend() == null) { trace('No GF to focus on.'); return; } trace('Focusing camera on girlfriend.'); - var xTarget:Float = PlayState.instance.currentStage.getGirlfriend().cameraFocusPoint.x + posX; - var yTarget:Float = PlayState.instance.currentStage.getGirlfriend().cameraFocusPoint.y + posY; + var gfPoint = currentStage.getGirlfriend().cameraFocusPoint; + targetX += gfPoint.x; + targetY += gfPoint.y; - PlayState.instance.cameraFollowPoint.setPosition(xTarget, yTarget); default: trace('Unknown camera focus: ' + data); } - if (useTween) + // Apply tween based on ease. + switch (ease) { - switch (ease) - { - case 'INSTANT': - PlayState.instance.tweenCameraToFollowPoint(0); - default: - var durSeconds = Conductor.instance.stepLengthMs * duration / 1000; - - var easeFunction:NullFloat> = Reflect.field(FlxEase, ease); - if (easeFunction == null) - { - trace('Invalid ease function: $ease'); - return; - } - - PlayState.instance.tweenCameraToFollowPoint(durSeconds, easeFunction); - } + case 'CLASSIC': // Old-school. No ease. Just set follow point. + PlayState.instance.cancelCameraFollowTween(); + PlayState.instance.cameraFollowPoint.setPosition(targetX, targetY); + case 'INSTANT': // Instant ease. Duration is automatically 0. + PlayState.instance.tweenCameraToPosition(targetX, targetY, 0); + default: + var durSeconds = Conductor.instance.stepLengthMs * duration / 1000; + var easeFunction:NullFloat> = Reflect.field(FlxEase, ease); + if (easeFunction == null) + { + trace('Invalid ease function: $ease'); + return; + } + PlayState.instance.tweenCameraToPosition(targetX, targetY, durSeconds, easeFunction); } } @@ -187,12 +183,6 @@ class FocusCameraSongEvent extends SongEvent type: SongEventFieldType.FLOAT, units: "px" }, - { - name: 'useTween', - title: 'Use Tween', - type: SongEventFieldType.BOOL, - defaultValue: false - }, { name: 'duration', title: 'Duration', @@ -208,7 +198,9 @@ class FocusCameraSongEvent extends SongEvent type: SongEventFieldType.ENUM, keys: [ 'Linear' => 'linear', - 'Instant' => 'INSTANT', + 'Sine In' => 'sineIn', + 'Sine Out' => 'sineOut', + 'Sine In/Out' => 'sineInOut', 'Quad In' => 'quadIn', 'Quad Out' => 'quadOut', 'Quad In/Out' => 'quadInOut', @@ -221,15 +213,17 @@ class FocusCameraSongEvent extends SongEvent 'Quint In' => 'quintIn', 'Quint Out' => 'quintOut', 'Quint In/Out' => 'quintInOut', + 'Expo In' => 'expoIn', + 'Expo Out' => 'expoOut', + 'Expo In/Out' => 'expoInOut', 'Smooth Step In' => 'smoothStepIn', 'Smooth Step Out' => 'smoothStepOut', 'Smooth Step In/Out' => 'smoothStepInOut', - 'Sine In' => 'sineIn', - 'Sine Out' => 'sineOut', - 'Sine In/Out' => 'sineInOut', 'Elastic In' => 'elasticIn', 'Elastic Out' => 'elasticOut', 'Elastic In/Out' => 'elasticInOut', + 'Instant (Ignores duration)' => 'INSTANT', + 'Classic (Ignores duration)' => 'CLASSIC' ] } ]); diff --git a/source/funkin/play/event/SetCameraBopSongEvent.hx b/source/funkin/play/event/SetCameraBopSongEvent.hx index a82577a5f..f3efc04e3 100644 --- a/source/funkin/play/event/SetCameraBopSongEvent.hx +++ b/source/funkin/play/event/SetCameraBopSongEvent.hx @@ -50,8 +50,8 @@ class SetCameraBopSongEvent extends SongEvent var intensity:Null = data.getFloat('intensity'); if (intensity == null) intensity = 1.0; - PlayState.instance.cameraZoomIntensity = Constants.DEFAULT_ZOOM_INTENSITY * intensity; - PlayState.instance.hudCameraZoomIntensity = Constants.DEFAULT_ZOOM_INTENSITY * intensity * 2.0; + PlayState.instance.cameraBopIntensity = (Constants.DEFAULT_BOP_INTENSITY - 1.0) * intensity + 1.0; + PlayState.instance.hudCameraZoomIntensity = (Constants.DEFAULT_BOP_INTENSITY - 1.0) * intensity * 2.0; PlayState.instance.cameraZoomRate = rate; trace('Set camera zoom rate to ${PlayState.instance.cameraZoomRate}'); } diff --git a/source/funkin/play/event/ZoomCameraSongEvent.hx b/source/funkin/play/event/ZoomCameraSongEvent.hx index b913aebe7..748abda19 100644 --- a/source/funkin/play/event/ZoomCameraSongEvent.hx +++ b/source/funkin/play/event/ZoomCameraSongEvent.hx @@ -81,7 +81,6 @@ class ZoomCameraSongEvent extends SongEvent PlayState.instance.tweenCameraZoom(zoom, 0, isDirectMode); default: var durSeconds = Conductor.instance.stepLengthMs * duration / 1000; - var easeFunction:NullFloat> = Reflect.field(FlxEase, ease); if (easeFunction == null) { @@ -102,9 +101,9 @@ class ZoomCameraSongEvent extends SongEvent * ``` * { * 'zoom': FLOAT, // Target zoom level. - * 'duration': FLOAT, // Optional duration in steps. - * 'mode': ENUM, // Whether to set additive zoom or direct zoom. - * 'ease': ENUM, // Optional easing function. + * 'duration': FLOAT, // Duration in steps. + * 'mode': ENUM, // Whether zoom is relative to the stage or absolute zoom. + * 'ease': ENUM, // Easing function. * } * @return SongEventSchema */ @@ -130,9 +129,9 @@ class ZoomCameraSongEvent extends SongEvent { name: 'mode', title: 'Mode', - defaultValue: 'direct', + defaultValue: 'stage', type: SongEventFieldType.ENUM, - keys: ['Additive' => 'additive', 'Direct' => 'direct'] + keys: ['Stage zoom' => 'stage', 'Absolute zoom' => 'direct'] }, { name: 'ease', @@ -142,6 +141,9 @@ class ZoomCameraSongEvent extends SongEvent keys: [ 'Linear' => 'linear', 'Instant' => 'INSTANT', + 'Sine In' => 'sineIn', + 'Sine Out' => 'sineOut', + 'Sine In/Out' => 'sineInOut', 'Quad In' => 'quadIn', 'Quad Out' => 'quadOut', 'Quad In/Out' => 'quadInOut', @@ -154,15 +156,15 @@ class ZoomCameraSongEvent extends SongEvent 'Quint In' => 'quintIn', 'Quint Out' => 'quintOut', 'Quint In/Out' => 'quintInOut', + 'Expo In' => 'expoIn', + 'Expo Out' => 'expoOut', + 'Expo In/Out' => 'expoInOut', 'Smooth Step In' => 'smoothStepIn', 'Smooth Step Out' => 'smoothStepOut', 'Smooth Step In/Out' => 'smoothStepInOut', - 'Sine In' => 'sineIn', - 'Sine Out' => 'sineOut', - 'Sine In/Out' => 'sineInOut', 'Elastic In' => 'elasticIn', 'Elastic Out' => 'elasticOut', - 'Elastic In/Out' => 'elasticInOut', + 'Elastic In/Out' => 'elasticInOut' ] } ]); diff --git a/source/funkin/util/Constants.hx b/source/funkin/util/Constants.hx index 47410b9c5..75766a75a 100644 --- a/source/funkin/util/Constants.hx +++ b/source/funkin/util/Constants.hx @@ -223,9 +223,10 @@ class Constants public static final DEFAULT_VARIATION_LIST:Array = ['default', 'erect', 'pico']; /** - * The default intensity for camera zooms. + * The default intensity multiplier for camera bops. + * Prolly needs to be tuned bc it's a multiplier now. */ - public static final DEFAULT_ZOOM_INTENSITY:Float = 0.015; + public static final DEFAULT_BOP_INTENSITY:Float = 1.015; /** * The default rate for camera zooms (in beats per zoom). diff --git a/source/funkin/util/EaseUtil.hx b/source/funkin/util/EaseUtil.hx new file mode 100644 index 000000000..200e74d07 --- /dev/null +++ b/source/funkin/util/EaseUtil.hx @@ -0,0 +1,17 @@ +package funkin.util; + +class EaseUtil +{ + /** + * Returns an ease function that eases via steps. + * Useful for "retro" style fades (week 6!) + * @param steps how many steps to ease over + * @return Float->Float + */ + public static inline function stepped(steps:Int):Float->Float + { + return function(t:Float):Float { + return Math.floor(t * steps) / steps; + } + } +}