mirror of
https://github.com/ninjamuffin99/Funkin.git
synced 2024-11-15 19:33:36 +00:00
Merge pull request #2409 from Burgerballs/scroll-speed-fix
[FEATURE] Scroll Speed Event
This commit is contained in:
commit
6cc9c3d968
|
@ -236,6 +236,11 @@ class PlayState extends MusicBeatSubState
|
||||||
*/
|
*/
|
||||||
public var cameraZoomTween:FlxTween;
|
public var cameraZoomTween:FlxTween;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An FlxTween that changes the additive speed to the desired amount.
|
||||||
|
*/
|
||||||
|
public var scrollSpeedTweens:Array<FlxTween> = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The camera follow point from the last stage.
|
* The camera follow point from the last stage.
|
||||||
* Used to persist the position of the `cameraFollowPosition` between levels.
|
* Used to persist the position of the `cameraFollowPosition` between levels.
|
||||||
|
@ -822,6 +827,8 @@ class PlayState extends MusicBeatSubState
|
||||||
{
|
{
|
||||||
if (!assertChartExists()) return;
|
if (!assertChartExists()) return;
|
||||||
|
|
||||||
|
prevScrollTargets = [];
|
||||||
|
|
||||||
dispatchEvent(new ScriptEvent(SONG_RETRY));
|
dispatchEvent(new ScriptEvent(SONG_RETRY));
|
||||||
|
|
||||||
resetCamera();
|
resetCamera();
|
||||||
|
@ -1204,6 +1211,15 @@ class PlayState extends MusicBeatSubState
|
||||||
cameraTweensPausedBySubState.add(cameraZoomTween);
|
cameraTweensPausedBySubState.add(cameraZoomTween);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (tween in scrollSpeedTweens)
|
||||||
|
{
|
||||||
|
if (tween != null && tween.active)
|
||||||
|
{
|
||||||
|
tween.active = false;
|
||||||
|
cameraTweensPausedBySubState.add(tween);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Pause the countdown.
|
// Pause the countdown.
|
||||||
Countdown.pauseCountdown();
|
Countdown.pauseCountdown();
|
||||||
}
|
}
|
||||||
|
@ -3042,8 +3058,9 @@ class PlayState extends MusicBeatSubState
|
||||||
// Stop camera zooming on beat.
|
// Stop camera zooming on beat.
|
||||||
cameraZoomRate = 0;
|
cameraZoomRate = 0;
|
||||||
|
|
||||||
// Cancel camera tweening if it's active.
|
// Cancel camera and scroll tweening if it's active.
|
||||||
cancelAllCameraTweens();
|
cancelAllCameraTweens();
|
||||||
|
cancelScrollSpeedTweens();
|
||||||
|
|
||||||
// If the opponent is GF, zoom in on the opponent.
|
// If the opponent is GF, zoom in on the opponent.
|
||||||
// Else, if there is no GF, zoom in on BF.
|
// Else, if there is no GF, zoom in on BF.
|
||||||
|
@ -3263,6 +3280,60 @@ class PlayState extends MusicBeatSubState
|
||||||
cancelCameraZoomTween();
|
cancelCameraZoomTween();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var prevScrollTargets:Array<Dynamic> = []; // used to snap scroll speed when things go unruely
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The magical function that shall tween the scroll speed.
|
||||||
|
*/
|
||||||
|
public function tweenScrollSpeed(?speed:Float, ?duration:Float, ?ease:Null<Float->Float>, strumlines:Array<String>):Void
|
||||||
|
{
|
||||||
|
// Cancel the current tween if it's active.
|
||||||
|
cancelScrollSpeedTweens();
|
||||||
|
|
||||||
|
// Snap to previous event value to prevent the tween breaking when another event cancels the previous tween.
|
||||||
|
for (i in prevScrollTargets)
|
||||||
|
{
|
||||||
|
var value:Float = i[0];
|
||||||
|
var strum:Strumline = Reflect.getProperty(this, i[1]);
|
||||||
|
strum.scrollSpeed = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// for next event, clean array.
|
||||||
|
prevScrollTargets = [];
|
||||||
|
|
||||||
|
for (i in strumlines)
|
||||||
|
{
|
||||||
|
var value:Float = speed;
|
||||||
|
var strum:Strumline = Reflect.getProperty(this, i);
|
||||||
|
|
||||||
|
if (duration == 0)
|
||||||
|
{
|
||||||
|
strum.scrollSpeed = value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
scrollSpeedTweens.push(FlxTween.tween(strum,
|
||||||
|
{
|
||||||
|
'scrollSpeed': value
|
||||||
|
}, duration, {ease: ease}));
|
||||||
|
}
|
||||||
|
// make sure charts dont break if the charter is dumb and stupid
|
||||||
|
prevScrollTargets.push([value, i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function cancelScrollSpeedTweens()
|
||||||
|
{
|
||||||
|
for (tween in scrollSpeedTweens)
|
||||||
|
{
|
||||||
|
if (tween != null)
|
||||||
|
{
|
||||||
|
tween.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scrollSpeedTweens = [];
|
||||||
|
}
|
||||||
|
|
||||||
#if (debug || FORCE_DEBUG_VERSION)
|
#if (debug || FORCE_DEBUG_VERSION)
|
||||||
/**
|
/**
|
||||||
* Jumps forward or backward a number of sections in the song.
|
* Jumps forward or backward a number of sections in the song.
|
||||||
|
|
172
source/funkin/play/event/ScrollSpeedEvent.hx
Normal file
172
source/funkin/play/event/ScrollSpeedEvent.hx
Normal file
|
@ -0,0 +1,172 @@
|
||||||
|
package funkin.play.event;
|
||||||
|
|
||||||
|
import flixel.tweens.FlxTween;
|
||||||
|
import flixel.FlxCamera;
|
||||||
|
import flixel.tweens.FlxEase;
|
||||||
|
// Data from the chart
|
||||||
|
import funkin.data.song.SongData;
|
||||||
|
import funkin.data.song.SongData.SongEventData;
|
||||||
|
// Data from the event schema
|
||||||
|
import funkin.play.event.SongEvent;
|
||||||
|
import funkin.data.event.SongEventSchema;
|
||||||
|
import funkin.data.event.SongEventSchema.SongEventFieldType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class represents a handler for scroll speed events.
|
||||||
|
*
|
||||||
|
* Example: Scroll speed change of both strums from 1x to 1.3x:
|
||||||
|
* ```
|
||||||
|
* {
|
||||||
|
* 'e': 'ScrollSpeed',
|
||||||
|
* "v": {
|
||||||
|
* "scroll": "1.3",
|
||||||
|
* "duration": "4",
|
||||||
|
* "ease": "linear"
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
class ScrollSpeedEvent extends SongEvent
|
||||||
|
{
|
||||||
|
public function new()
|
||||||
|
{
|
||||||
|
super('ScrollSpeed');
|
||||||
|
}
|
||||||
|
|
||||||
|
static final DEFAULT_SCROLL:Float = 1;
|
||||||
|
static final DEFAULT_DURATION:Float = 4.0;
|
||||||
|
static final DEFAULT_EASE:String = 'linear';
|
||||||
|
static final DEFAULT_ABSOLUTE:Bool = false;
|
||||||
|
static final DEFAULT_STRUMLINE:String = 'both'; // my special little trick
|
||||||
|
|
||||||
|
public override function handleEvent(data:SongEventData):Void
|
||||||
|
{
|
||||||
|
// Does nothing if there is no PlayState.
|
||||||
|
if (PlayState.instance == null) return;
|
||||||
|
|
||||||
|
var scroll:Float = data.getFloat('scroll') ?? DEFAULT_SCROLL;
|
||||||
|
|
||||||
|
var duration:Float = data.getFloat('duration') ?? DEFAULT_DURATION;
|
||||||
|
|
||||||
|
var ease:String = data.getString('ease') ?? DEFAULT_EASE;
|
||||||
|
|
||||||
|
var strumline:String = data.getString('strumline') ?? DEFAULT_STRUMLINE;
|
||||||
|
|
||||||
|
var absolute:Bool = data.getBool('absolute') ?? DEFAULT_ABSOLUTE;
|
||||||
|
|
||||||
|
var strumlineNames:Array<String> = [];
|
||||||
|
|
||||||
|
if (!absolute)
|
||||||
|
{
|
||||||
|
// If absolute is set to false, do the awesome multiplicative thing
|
||||||
|
scroll = scroll * (PlayState.instance?.currentChart?.scrollSpeed ?? 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (strumline)
|
||||||
|
{
|
||||||
|
case 'both':
|
||||||
|
strumlineNames = ['playerStrumline', 'opponentStrumline'];
|
||||||
|
default:
|
||||||
|
strumlineNames = [strumline + 'Strumline'];
|
||||||
|
}
|
||||||
|
// If it's a string, check the value.
|
||||||
|
switch (ease)
|
||||||
|
{
|
||||||
|
case 'INSTANT':
|
||||||
|
PlayState.instance.tweenScrollSpeed(scroll, 0, null, strumlineNames);
|
||||||
|
default:
|
||||||
|
var durSeconds = Conductor.instance.stepLengthMs * duration / 1000;
|
||||||
|
var easeFunction:Null<Float->Float> = Reflect.field(FlxEase, ease);
|
||||||
|
if (easeFunction == null)
|
||||||
|
{
|
||||||
|
trace('Invalid ease function: $ease');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayState.instance.tweenScrollSpeed(scroll, durSeconds, easeFunction, strumlineNames);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override function getTitle():String
|
||||||
|
{
|
||||||
|
return 'Scroll Speed';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ```
|
||||||
|
* {
|
||||||
|
* 'scroll': FLOAT, // Target scroll level.
|
||||||
|
* 'duration': FLOAT, // Duration in steps.
|
||||||
|
* 'ease': ENUM, // Easing function.
|
||||||
|
* }
|
||||||
|
* @return SongEventSchema
|
||||||
|
*/
|
||||||
|
public override function getEventSchema():SongEventSchema
|
||||||
|
{
|
||||||
|
return new SongEventSchema([
|
||||||
|
{
|
||||||
|
name: 'scroll',
|
||||||
|
title: 'Target Value',
|
||||||
|
defaultValue: 1.0,
|
||||||
|
step: 0.1,
|
||||||
|
type: SongEventFieldType.FLOAT,
|
||||||
|
units: 'x'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'duration',
|
||||||
|
title: 'Duration',
|
||||||
|
defaultValue: 4.0,
|
||||||
|
step: 0.5,
|
||||||
|
type: SongEventFieldType.FLOAT,
|
||||||
|
units: 'steps'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'ease',
|
||||||
|
title: 'Easing Type',
|
||||||
|
defaultValue: 'linear',
|
||||||
|
type: SongEventFieldType.ENUM,
|
||||||
|
keys: [
|
||||||
|
'Linear' => 'linear',
|
||||||
|
'Instant (Ignores Duration)' => 'INSTANT',
|
||||||
|
'Sine In' => 'sineIn',
|
||||||
|
'Sine Out' => 'sineOut',
|
||||||
|
'Sine In/Out' => 'sineInOut',
|
||||||
|
'Quad In' => 'quadIn',
|
||||||
|
'Quad Out' => 'quadOut',
|
||||||
|
'Quad In/Out' => 'quadInOut',
|
||||||
|
'Cube In' => 'cubeIn',
|
||||||
|
'Cube Out' => 'cubeOut',
|
||||||
|
'Cube In/Out' => 'cubeInOut',
|
||||||
|
'Quart In' => 'quartIn',
|
||||||
|
'Quart Out' => 'quartOut',
|
||||||
|
'Quart In/Out' => 'quartInOut',
|
||||||
|
'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',
|
||||||
|
'Elastic In' => 'elasticIn',
|
||||||
|
'Elastic Out' => 'elasticOut',
|
||||||
|
'Elastic In/Out' => 'elasticInOut'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'strumline',
|
||||||
|
title: 'Target Strumline',
|
||||||
|
defaultValue: 'both',
|
||||||
|
type: SongEventFieldType.ENUM,
|
||||||
|
keys: ['Both' => 'both', 'Player' => 'player', 'Opponent' => 'opponent']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'absolute',
|
||||||
|
title: 'Absolute',
|
||||||
|
defaultValue: false,
|
||||||
|
type: SongEventFieldType.BOOL,
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -52,6 +52,14 @@ class Strumline extends FlxSpriteGroup
|
||||||
*/
|
*/
|
||||||
public var conductorInUse(get, set):Conductor;
|
public var conductorInUse(get, set):Conductor;
|
||||||
|
|
||||||
|
// Used in-game to control the scroll speed within a song
|
||||||
|
public var scrollSpeed:Float = 1.0;
|
||||||
|
|
||||||
|
public function resetScrollSpeed():Void
|
||||||
|
{
|
||||||
|
scrollSpeed = PlayState.instance?.currentChart?.scrollSpeed ?? 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
var _conductorInUse:Null<Conductor>;
|
var _conductorInUse:Null<Conductor>;
|
||||||
|
|
||||||
function get_conductorInUse():Conductor
|
function get_conductorInUse():Conductor
|
||||||
|
@ -134,6 +142,7 @@ class Strumline extends FlxSpriteGroup
|
||||||
this.refresh();
|
this.refresh();
|
||||||
|
|
||||||
this.onNoteIncoming = new FlxTypedSignal<NoteSprite->Void>();
|
this.onNoteIncoming = new FlxTypedSignal<NoteSprite->Void>();
|
||||||
|
resetScrollSpeed();
|
||||||
|
|
||||||
for (i in 0...KEY_COUNT)
|
for (i in 0...KEY_COUNT)
|
||||||
{
|
{
|
||||||
|
@ -283,7 +292,6 @@ class Strumline extends FlxSpriteGroup
|
||||||
// var vwoosh:Float = (strumTime < Conductor.songPosition) && vwoosh ? 2.0 : 1.0;
|
// var vwoosh:Float = (strumTime < Conductor.songPosition) && vwoosh ? 2.0 : 1.0;
|
||||||
// ^^^ commented this out... do NOT make it move faster as it moves offscreen!
|
// ^^^ commented this out... do NOT make it move faster as it moves offscreen!
|
||||||
var vwoosh:Float = 1.0;
|
var vwoosh:Float = 1.0;
|
||||||
var scrollSpeed:Float = PlayState.instance?.currentChart?.scrollSpeed ?? 1.0;
|
|
||||||
|
|
||||||
return
|
return
|
||||||
Constants.PIXELS_PER_MS * (conductorInUse.songPosition - strumTime - Conductor.instance.inputOffset) * scrollSpeed * vwoosh * (Preferences.downscroll ? 1 : -1);
|
Constants.PIXELS_PER_MS * (conductorInUse.songPosition - strumTime - Conductor.instance.inputOffset) * scrollSpeed * vwoosh * (Preferences.downscroll ? 1 : -1);
|
||||||
|
@ -539,6 +547,7 @@ class Strumline extends FlxSpriteGroup
|
||||||
{
|
{
|
||||||
playStatic(dir);
|
playStatic(dir);
|
||||||
}
|
}
|
||||||
|
resetScrollSpeed();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function applyNoteData(data:Array<SongNoteData>):Void
|
public function applyNoteData(data:Array<SongNoteData>):Void
|
||||||
|
@ -705,6 +714,7 @@ class Strumline extends FlxSpriteGroup
|
||||||
|
|
||||||
if (holdNoteSprite != null)
|
if (holdNoteSprite != null)
|
||||||
{
|
{
|
||||||
|
holdNoteSprite.parentStrumline = this;
|
||||||
holdNoteSprite.noteData = note;
|
holdNoteSprite.noteData = note;
|
||||||
holdNoteSprite.strumTime = note.time;
|
holdNoteSprite.strumTime = note.time;
|
||||||
holdNoteSprite.noteDirection = note.getDirection();
|
holdNoteSprite.noteDirection = note.getDirection();
|
||||||
|
|
|
@ -32,6 +32,7 @@ class SustainTrail extends FlxSprite
|
||||||
public var sustainLength(default, set):Float = 0; // millis
|
public var sustainLength(default, set):Float = 0; // millis
|
||||||
public var fullSustainLength:Float = 0;
|
public var fullSustainLength:Float = 0;
|
||||||
public var noteData:Null<SongNoteData>;
|
public var noteData:Null<SongNoteData>;
|
||||||
|
public var parentStrumline:Strumline;
|
||||||
|
|
||||||
public var cover:NoteHoldCover = null;
|
public var cover:NoteHoldCover = null;
|
||||||
|
|
||||||
|
@ -119,7 +120,7 @@ class SustainTrail extends FlxSprite
|
||||||
|
|
||||||
// CALCULATE SIZE
|
// CALCULATE SIZE
|
||||||
graphicWidth = graphic.width / 8 * zoom; // amount of notes * 2
|
graphicWidth = graphic.width / 8 * zoom; // amount of notes * 2
|
||||||
graphicHeight = sustainHeight(sustainLength, getScrollSpeed());
|
graphicHeight = sustainHeight(sustainLength, parentStrumline?.scrollSpeed ?? 1.0);
|
||||||
// instead of scrollSpeed, PlayState.SONG.speed
|
// instead of scrollSpeed, PlayState.SONG.speed
|
||||||
|
|
||||||
flipY = Preferences.downscroll;
|
flipY = Preferences.downscroll;
|
||||||
|
@ -135,9 +136,21 @@ class SustainTrail extends FlxSprite
|
||||||
this.active = true; // This NEEDS to be true for the note to be drawn!
|
this.active = true; // This NEEDS to be true for the note to be drawn!
|
||||||
}
|
}
|
||||||
|
|
||||||
function getScrollSpeed():Float
|
function getBaseScrollSpeed()
|
||||||
{
|
{
|
||||||
return PlayState?.instance?.currentChart?.scrollSpeed ?? 1.0;
|
return (PlayState.instance?.currentChart?.scrollSpeed ?? 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
var previousScrollSpeed:Float = 1;
|
||||||
|
|
||||||
|
override function update(elapsed)
|
||||||
|
{
|
||||||
|
super.update(elapsed);
|
||||||
|
if (previousScrollSpeed != (parentStrumline?.scrollSpeed ?? 1.0))
|
||||||
|
{
|
||||||
|
triggerRedraw();
|
||||||
|
}
|
||||||
|
previousScrollSpeed = parentStrumline?.scrollSpeed ?? 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -155,12 +168,16 @@ class SustainTrail extends FlxSprite
|
||||||
if (s < 0.0) s = 0.0;
|
if (s < 0.0) s = 0.0;
|
||||||
|
|
||||||
if (sustainLength == s) return s;
|
if (sustainLength == s) return s;
|
||||||
|
|
||||||
graphicHeight = sustainHeight(s, getScrollSpeed());
|
|
||||||
this.sustainLength = s;
|
this.sustainLength = s;
|
||||||
|
triggerRedraw();
|
||||||
|
return this.sustainLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
function triggerRedraw()
|
||||||
|
{
|
||||||
|
graphicHeight = sustainHeight(sustainLength, parentStrumline?.scrollSpeed ?? 1.0);
|
||||||
updateClipping();
|
updateClipping();
|
||||||
updateHitbox();
|
updateHitbox();
|
||||||
return this.sustainLength;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override function updateHitbox():Void
|
public override function updateHitbox():Void
|
||||||
|
@ -178,7 +195,7 @@ class SustainTrail extends FlxSprite
|
||||||
*/
|
*/
|
||||||
public function updateClipping(songTime:Float = 0):Void
|
public function updateClipping(songTime:Float = 0):Void
|
||||||
{
|
{
|
||||||
var clipHeight:Float = FlxMath.bound(sustainHeight(sustainLength - (songTime - strumTime), getScrollSpeed()), 0, graphicHeight);
|
var clipHeight:Float = FlxMath.bound(sustainHeight(sustainLength - (songTime - strumTime), parentStrumline?.scrollSpeed ?? 1.0), 0, graphicHeight);
|
||||||
if (clipHeight <= 0.1)
|
if (clipHeight <= 0.1)
|
||||||
{
|
{
|
||||||
visible = false;
|
visible = false;
|
||||||
|
|
|
@ -60,11 +60,11 @@ class ChartEditorHoldNoteSprite extends SustainTrail
|
||||||
{
|
{
|
||||||
if (lerp)
|
if (lerp)
|
||||||
{
|
{
|
||||||
sustainLength = FlxMath.lerp(sustainLength, h / (getScrollSpeed() * Constants.PIXELS_PER_MS), 0.25);
|
sustainLength = FlxMath.lerp(sustainLength, h / (getBaseScrollSpeed() * Constants.PIXELS_PER_MS), 0.25);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
sustainLength = h / (getScrollSpeed() * Constants.PIXELS_PER_MS);
|
sustainLength = h / (getBaseScrollSpeed() * Constants.PIXELS_PER_MS);
|
||||||
}
|
}
|
||||||
|
|
||||||
fullSustainLength = sustainLength;
|
fullSustainLength = sustainLength;
|
||||||
|
|
Loading…
Reference in a new issue