mirror of
https://github.com/ninjamuffin99/Funkin.git
synced 2024-12-12 08:25:13 +00:00
Merge branch 'rewrite/master' into bugfix/audio-offsets
This commit is contained in:
commit
82258cfa1d
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
|
@ -85,7 +85,7 @@
|
||||||
},
|
},
|
||||||
"projectManager.git.baseFolders": ["./"],
|
"projectManager.git.baseFolders": ["./"],
|
||||||
|
|
||||||
"haxecheckstyle.sourceFolders": ["src", "Source"],
|
"haxecheckstyle.sourceFolders": ["src", "source"],
|
||||||
"haxecheckstyle.externalSourceRoots": [],
|
"haxecheckstyle.externalSourceRoots": [],
|
||||||
"haxecheckstyle.configurationFile": "checkstyle.json",
|
"haxecheckstyle.configurationFile": "checkstyle.json",
|
||||||
"haxecheckstyle.codeSimilarityBufferSize": 100,
|
"haxecheckstyle.codeSimilarityBufferSize": 100,
|
||||||
|
|
|
@ -100,8 +100,15 @@ class Main extends Sprite
|
||||||
|
|
||||||
// George recommends binding the save before FlxGame is created.
|
// George recommends binding the save before FlxGame is created.
|
||||||
Save.load();
|
Save.load();
|
||||||
|
var game:FlxGame = new FlxGame(gameWidth, gameHeight, initialState, framerate, framerate, skipSplash, startFullscreen);
|
||||||
|
|
||||||
addChild(new FlxGame(gameWidth, gameHeight, initialState, framerate, framerate, skipSplash, startFullscreen));
|
// FlxG.game._customSoundTray wants just the class, it calls new from
|
||||||
|
// create() in there, which gets called when it's added to stage
|
||||||
|
// which is why it needs to be added before addChild(game) here
|
||||||
|
@:privateAccess
|
||||||
|
game._customSoundTray = funkin.ui.options.FunkinSoundTray;
|
||||||
|
|
||||||
|
addChild(game);
|
||||||
|
|
||||||
#if hxcpp_debug_server
|
#if hxcpp_debug_server
|
||||||
trace('hxcpp_debug_server is enabled! You can now connect to the game with a debugger.');
|
trace('hxcpp_debug_server is enabled! You can now connect to the game with a debugger.');
|
||||||
|
|
|
@ -24,6 +24,7 @@ import funkin.data.stage.StageRegistry;
|
||||||
import funkin.data.dialogue.ConversationRegistry;
|
import funkin.data.dialogue.ConversationRegistry;
|
||||||
import funkin.data.dialogue.DialogueBoxRegistry;
|
import funkin.data.dialogue.DialogueBoxRegistry;
|
||||||
import funkin.data.dialogue.SpeakerRegistry;
|
import funkin.data.dialogue.SpeakerRegistry;
|
||||||
|
import funkin.data.freeplay.AlbumRegistry;
|
||||||
import funkin.data.song.SongRegistry;
|
import funkin.data.song.SongRegistry;
|
||||||
import funkin.play.character.CharacterData.CharacterDataParser;
|
import funkin.play.character.CharacterData.CharacterDataParser;
|
||||||
import funkin.modding.module.ModuleHandler;
|
import funkin.modding.module.ModuleHandler;
|
||||||
|
@ -167,6 +168,7 @@ class InitState extends FlxState
|
||||||
ConversationRegistry.instance.loadEntries();
|
ConversationRegistry.instance.loadEntries();
|
||||||
DialogueBoxRegistry.instance.loadEntries();
|
DialogueBoxRegistry.instance.loadEntries();
|
||||||
SpeakerRegistry.instance.loadEntries();
|
SpeakerRegistry.instance.loadEntries();
|
||||||
|
AlbumRegistry.instance.loadEntries();
|
||||||
StageRegistry.instance.loadEntries();
|
StageRegistry.instance.loadEntries();
|
||||||
|
|
||||||
// TODO: CharacterDataParser doesn't use json2object, so it's way slower than the other parsers.
|
// TODO: CharacterDataParser doesn't use json2object, so it's way slower than the other parsers.
|
||||||
|
|
36
source/funkin/data/freeplay/AlbumData.hx
Normal file
36
source/funkin/data/freeplay/AlbumData.hx
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
package funkin.data.freeplay;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A type definition for the data for an album of songs.
|
||||||
|
* It includes things like what graphics to display in Freeplay.
|
||||||
|
* @see https://lib.haxe.org/p/json2object/
|
||||||
|
*/
|
||||||
|
typedef AlbumData =
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Semantic version for album data.
|
||||||
|
*/
|
||||||
|
public var version:String;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Readable name of the album.
|
||||||
|
*/
|
||||||
|
public var name:String;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Readable name of the artist(s) of the album.
|
||||||
|
*/
|
||||||
|
public var artists:Array<String>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asset key for the album art.
|
||||||
|
* The album art will be displayed in Freeplay.
|
||||||
|
*/
|
||||||
|
public var albumArtAsset:String;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asset key for the album title.
|
||||||
|
* The album title will be displayed below the album art in Freeplay.
|
||||||
|
*/
|
||||||
|
public var albumTitleAsset:String;
|
||||||
|
}
|
84
source/funkin/data/freeplay/AlbumRegistry.hx
Normal file
84
source/funkin/data/freeplay/AlbumRegistry.hx
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
package funkin.data.freeplay;
|
||||||
|
|
||||||
|
import funkin.ui.freeplay.Album;
|
||||||
|
import funkin.data.freeplay.AlbumData;
|
||||||
|
import funkin.ui.freeplay.ScriptedAlbum;
|
||||||
|
|
||||||
|
class AlbumRegistry extends BaseRegistry<Album, AlbumData>
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The current version string for the album data format.
|
||||||
|
* Handle breaking changes by incrementing this value
|
||||||
|
* and adding migration to the `migrateAlbumData()` function.
|
||||||
|
*/
|
||||||
|
public static final ALBUM_DATA_VERSION:thx.semver.Version = '1.0.0';
|
||||||
|
|
||||||
|
public static final ALBUM_DATA_VERSION_RULE:thx.semver.VersionRule = '1.0.x';
|
||||||
|
|
||||||
|
public static final instance:AlbumRegistry = new AlbumRegistry();
|
||||||
|
|
||||||
|
public function new()
|
||||||
|
{
|
||||||
|
super('ALBUM', 'ui/freeplay/albums', ALBUM_DATA_VERSION_RULE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read, parse, and validate the JSON data and produce the corresponding data object.
|
||||||
|
* @param id The ID of the entry to load.
|
||||||
|
* @return The parsed data object.
|
||||||
|
*/
|
||||||
|
public function parseEntryData(id:String):Null<AlbumData>
|
||||||
|
{
|
||||||
|
// JsonParser does not take type parameters,
|
||||||
|
// otherwise this function would be in BaseRegistry.
|
||||||
|
var parser:json2object.JsonParser<AlbumData> = new json2object.JsonParser<AlbumData>();
|
||||||
|
parser.ignoreUnknownVariables = false;
|
||||||
|
|
||||||
|
switch (loadEntryFile(id))
|
||||||
|
{
|
||||||
|
case {fileName: fileName, contents: contents}:
|
||||||
|
parser.fromJson(contents, fileName);
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parser.errors.length > 0)
|
||||||
|
{
|
||||||
|
printErrors(parser.errors, id);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return parser.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse and validate the JSON data and produce the corresponding data object.
|
||||||
|
*
|
||||||
|
* NOTE: Must be implemented on the implementation class.
|
||||||
|
* @param contents The JSON as a string.
|
||||||
|
* @param fileName An optional file name for error reporting.
|
||||||
|
* @return The parsed data object.
|
||||||
|
*/
|
||||||
|
public function parseEntryDataRaw(contents:String, ?fileName:String):Null<AlbumData>
|
||||||
|
{
|
||||||
|
var parser:json2object.JsonParser<AlbumData> = new json2object.JsonParser<AlbumData>();
|
||||||
|
parser.ignoreUnknownVariables = false;
|
||||||
|
parser.fromJson(contents, fileName);
|
||||||
|
|
||||||
|
if (parser.errors.length > 0)
|
||||||
|
{
|
||||||
|
printErrors(parser.errors, fileName);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return parser.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createScriptedEntry(clsName:String):Album
|
||||||
|
{
|
||||||
|
return ScriptedAlbum.init(clsName, 'unknown');
|
||||||
|
}
|
||||||
|
|
||||||
|
function getScriptedClassNames():Array<String>
|
||||||
|
{
|
||||||
|
return ScriptedAlbum.listScriptClasses();
|
||||||
|
}
|
||||||
|
}
|
|
@ -706,7 +706,7 @@ abstract SongEventData(SongEventDataRaw) from SongEventDataRaw to SongEventDataR
|
||||||
this = new SongEventDataRaw(time, eventKind, value);
|
this = new SongEventDataRaw(time, eventKind, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public inline function valueAsStruct(?defaultKey:String = "key"):Dynamic
|
public function valueAsStruct(?defaultKey:String = "key"):Dynamic
|
||||||
{
|
{
|
||||||
if (this.value == null) return {};
|
if (this.value == null) return {};
|
||||||
if (Std.isOfType(this.value, Array))
|
if (Std.isOfType(this.value, Array))
|
||||||
|
|
|
@ -3,6 +3,7 @@ package funkin.graphics;
|
||||||
import flixel.FlxSprite;
|
import flixel.FlxSprite;
|
||||||
import flixel.util.FlxColor;
|
import flixel.util.FlxColor;
|
||||||
import flixel.graphics.FlxGraphic;
|
import flixel.graphics.FlxGraphic;
|
||||||
|
import flixel.tweens.FlxTween;
|
||||||
import openfl.display3D.textures.TextureBase;
|
import openfl.display3D.textures.TextureBase;
|
||||||
import funkin.graphics.framebuffer.FixedBitmapData;
|
import funkin.graphics.framebuffer.FixedBitmapData;
|
||||||
import openfl.display.BitmapData;
|
import openfl.display.BitmapData;
|
||||||
|
@ -253,7 +254,7 @@ class FunkinSprite extends FlxSprite
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ensure scale is applied when cloning a sprite.
|
* Ensure scale is applied when cloning a sprite.R
|
||||||
* The default `clone()` method acts kinda weird TBH.
|
* The default `clone()` method acts kinda weird TBH.
|
||||||
* @return A clone of this sprite.
|
* @return A clone of this sprite.
|
||||||
*/
|
*/
|
||||||
|
@ -266,4 +267,13 @@ class FunkinSprite extends FlxSprite
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override function destroy():Void
|
||||||
|
{
|
||||||
|
frames = null;
|
||||||
|
// Cancel all tweens so they don't continue to run on a destroyed sprite.
|
||||||
|
// This prevents crashes.
|
||||||
|
FlxTween.cancelTweensOf(this);
|
||||||
|
super.destroy();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,9 @@ package funkin.graphics.adobeanimate;
|
||||||
import flixel.util.FlxSignal.FlxTypedSignal;
|
import flixel.util.FlxSignal.FlxTypedSignal;
|
||||||
import flxanimate.FlxAnimate;
|
import flxanimate.FlxAnimate;
|
||||||
import flxanimate.FlxAnimate.Settings;
|
import flxanimate.FlxAnimate.Settings;
|
||||||
import flixel.math.FlxPoint;
|
import flxanimate.frames.FlxAnimateFrames;
|
||||||
|
import openfl.display.BitmapData;
|
||||||
|
import openfl.utils.Assets;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A sprite which provides convenience functions for rendering a texture atlas with animations.
|
* A sprite which provides convenience functions for rendering a texture atlas with animations.
|
||||||
|
@ -31,7 +33,7 @@ class FlxAtlasSprite extends FlxAnimate
|
||||||
|
|
||||||
var canPlayOtherAnims:Bool = true;
|
var canPlayOtherAnims:Bool = true;
|
||||||
|
|
||||||
public function new(x:Float, y:Float, path:String, ?settings:Settings)
|
public function new(x:Float, y:Float, ?path:String, ?settings:Settings)
|
||||||
{
|
{
|
||||||
if (settings == null) settings = SETTINGS;
|
if (settings == null) settings = SETTINGS;
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ import funkin.data.level.LevelRegistry;
|
||||||
import funkin.data.notestyle.NoteStyleRegistry;
|
import funkin.data.notestyle.NoteStyleRegistry;
|
||||||
import funkin.data.song.SongRegistry;
|
import funkin.data.song.SongRegistry;
|
||||||
import funkin.data.stage.StageRegistry;
|
import funkin.data.stage.StageRegistry;
|
||||||
|
import funkin.data.freeplay.AlbumRegistry;
|
||||||
import funkin.modding.module.ModuleHandler;
|
import funkin.modding.module.ModuleHandler;
|
||||||
import funkin.play.character.CharacterData.CharacterDataParser;
|
import funkin.play.character.CharacterData.CharacterDataParser;
|
||||||
import funkin.save.Save;
|
import funkin.save.Save;
|
||||||
|
@ -324,6 +325,7 @@ class PolymodHandler
|
||||||
ConversationRegistry.instance.loadEntries();
|
ConversationRegistry.instance.loadEntries();
|
||||||
DialogueBoxRegistry.instance.loadEntries();
|
DialogueBoxRegistry.instance.loadEntries();
|
||||||
SpeakerRegistry.instance.loadEntries();
|
SpeakerRegistry.instance.loadEntries();
|
||||||
|
AlbumRegistry.instance.loadEntries();
|
||||||
StageRegistry.instance.loadEntries();
|
StageRegistry.instance.loadEntries();
|
||||||
CharacterDataParser.loadCharacterCache(); // TODO: Migrate characters to BaseRegistry.
|
CharacterDataParser.loadCharacterCache(); // TODO: Migrate characters to BaseRegistry.
|
||||||
ModuleHandler.loadModuleCache();
|
ModuleHandler.loadModuleCache();
|
||||||
|
|
|
@ -119,6 +119,8 @@ class GameOverSubState extends MusicBeatSubState
|
||||||
// Set up the visuals
|
// Set up the visuals
|
||||||
//
|
//
|
||||||
|
|
||||||
|
var playState = PlayState.instance;
|
||||||
|
|
||||||
// Add a black background to the screen.
|
// Add a black background to the screen.
|
||||||
var bg = new FunkinSprite().makeSolidColor(FlxG.width * 2, FlxG.height * 2, FlxColor.BLACK);
|
var bg = new FunkinSprite().makeSolidColor(FlxG.width * 2, FlxG.height * 2, FlxColor.BLACK);
|
||||||
// We make this transparent so that we can see the stage underneath during debugging,
|
// We make this transparent so that we can see the stage underneath during debugging,
|
||||||
|
@ -130,13 +132,16 @@ class GameOverSubState extends MusicBeatSubState
|
||||||
|
|
||||||
// Pluck Boyfriend from the PlayState and place him (in the same position) in the GameOverSubState.
|
// Pluck Boyfriend from the PlayState and place him (in the same position) in the GameOverSubState.
|
||||||
// We can then play the character's `firstDeath` animation.
|
// We can then play the character's `firstDeath` animation.
|
||||||
boyfriend = PlayState.instance.currentStage.getBoyfriend(true);
|
boyfriend = playState.currentStage.getBoyfriend(true);
|
||||||
boyfriend.isDead = true;
|
boyfriend.isDead = true;
|
||||||
add(boyfriend);
|
add(boyfriend);
|
||||||
boyfriend.resetCharacter();
|
boyfriend.resetCharacter();
|
||||||
|
|
||||||
|
// Cancel camera tweening if it's currently active.
|
||||||
|
playState.cancelAllCameraTweens();
|
||||||
|
|
||||||
// Assign a camera follow point to the boyfriend's position.
|
// Assign a camera follow point to the boyfriend's position.
|
||||||
cameraFollowPoint = new FlxObject(PlayState.instance.cameraFollowPoint.x, PlayState.instance.cameraFollowPoint.y, 1, 1);
|
cameraFollowPoint = new FlxObject(playState.cameraFollowPoint.x, playState.cameraFollowPoint.y, 1, 1);
|
||||||
cameraFollowPoint.x = boyfriend.getGraphicMidpoint().x;
|
cameraFollowPoint.x = boyfriend.getGraphicMidpoint().x;
|
||||||
cameraFollowPoint.y = boyfriend.getGraphicMidpoint().y;
|
cameraFollowPoint.y = boyfriend.getGraphicMidpoint().y;
|
||||||
var offsets:Array<Float> = boyfriend.getDeathCameraOffsets();
|
var offsets:Array<Float> = boyfriend.getDeathCameraOffsets();
|
||||||
|
|
|
@ -237,6 +237,17 @@ class PlayState extends MusicBeatSubState
|
||||||
*/
|
*/
|
||||||
public var cameraFollowPoint:FlxObject;
|
public var cameraFollowPoint:FlxObject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An FlxTween that tweens the camera to the follow point.
|
||||||
|
* Only used when tweening the camera manually, rather than tweening via follow.
|
||||||
|
*/
|
||||||
|
public var cameraFollowTween:FlxTween;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An FlxTween that zooms the camera to the desired amount.
|
||||||
|
*/
|
||||||
|
public var cameraZoomTween: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.
|
||||||
|
@ -244,14 +255,23 @@ class PlayState extends MusicBeatSubState
|
||||||
public var previousCameraFollowPoint:FlxPoint = null;
|
public var previousCameraFollowPoint:FlxPoint = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The current camera zoom level.
|
* The current camera zoom level without any modifiers applied.
|
||||||
*
|
*/
|
||||||
* The camera zoom is increased every beat, and lerped back to this value every frame, creating a smooth 'zoom-in' effect.
|
public var currentCameraZoom:Float = FlxCamera.defaultZoom * 1.05;
|
||||||
* Defaults to 1.05 but may be larger or smaller depending on the current stage,
|
|
||||||
* and may be changed by the `ZoomCamera` song event.
|
/**
|
||||||
|
* 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.
|
||||||
*/
|
*/
|
||||||
public var defaultCameraZoom:Float = FlxCamera.defaultZoom * 1.05;
|
public var defaultCameraZoom:Float = FlxCamera.defaultZoom * 1.05;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Camera zoom applied on top of currentCameraZoom.
|
||||||
|
* Tweened via the `ZoomCamera` song event in additive mode.
|
||||||
|
*/
|
||||||
|
public var additiveCameraZoom:Float = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The current HUD camera zoom level.
|
* The current HUD camera zoom level.
|
||||||
*
|
*
|
||||||
|
@ -397,10 +417,15 @@ class PlayState extends MusicBeatSubState
|
||||||
var startingSong:Bool = false;
|
var startingSong:Bool = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* False if `FlxG.sound.music`
|
* Track if we currently have the music paused for a Pause substate, so we can unpause it when we return.
|
||||||
*/
|
*/
|
||||||
var musicPausedBySubState:Bool = false;
|
var musicPausedBySubState:Bool = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Track any camera tweens we've paused for a Pause substate, so we can unpause them when we return.
|
||||||
|
*/
|
||||||
|
var cameraTweensPausedBySubState:List<FlxTween> = new List<FlxTween>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* False until `create()` has completed.
|
* False until `create()` has completed.
|
||||||
*/
|
*/
|
||||||
|
@ -943,7 +968,9 @@ class PlayState extends MusicBeatSubState
|
||||||
// Lerp the camera zoom towards the target level.
|
// Lerp the camera zoom towards the target level.
|
||||||
if (subState == null)
|
if (subState == null)
|
||||||
{
|
{
|
||||||
FlxG.camera.zoom = FlxMath.lerp(defaultCameraZoom, FlxG.camera.zoom, 0.95);
|
currentCameraZoom = FlxMath.lerp(defaultCameraZoom, currentCameraZoom, 0.95);
|
||||||
|
FlxG.camera.zoom = currentCameraZoom + additiveCameraZoom;
|
||||||
|
|
||||||
camHUD.zoom = FlxMath.lerp(defaultHUDCameraZoom, camHUD.zoom, 0.95);
|
camHUD.zoom = FlxMath.lerp(defaultHUDCameraZoom, camHUD.zoom, 0.95);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1121,14 +1148,30 @@ class PlayState extends MusicBeatSubState
|
||||||
// Pause the music.
|
// Pause the music.
|
||||||
if (FlxG.sound.music != null)
|
if (FlxG.sound.music != null)
|
||||||
{
|
{
|
||||||
musicPausedBySubState = FlxG.sound.music.playing;
|
if (FlxG.sound.music.playing)
|
||||||
if (musicPausedBySubState)
|
|
||||||
{
|
{
|
||||||
FlxG.sound.music.pause();
|
FlxG.sound.music.pause();
|
||||||
|
musicPausedBySubState = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pause vocals.
|
||||||
|
// Not tracking that we've done this via a bool because vocal re-syncing involves pausing the vocals anyway.
|
||||||
if (vocals != null) vocals.pause();
|
if (vocals != null) vocals.pause();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pause camera tweening, and keep track of which tweens we pause.
|
||||||
|
if (cameraFollowTween != null && cameraFollowTween.active)
|
||||||
|
{
|
||||||
|
cameraFollowTween.active = false;
|
||||||
|
cameraTweensPausedBySubState.add(cameraFollowTween);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cameraZoomTween != null && cameraZoomTween.active)
|
||||||
|
{
|
||||||
|
cameraZoomTween.active = false;
|
||||||
|
cameraTweensPausedBySubState.add(cameraZoomTween);
|
||||||
|
}
|
||||||
|
|
||||||
// Pause the countdown.
|
// Pause the countdown.
|
||||||
Countdown.pauseCountdown();
|
Countdown.pauseCountdown();
|
||||||
}
|
}
|
||||||
|
@ -1150,17 +1193,26 @@ class PlayState extends MusicBeatSubState
|
||||||
|
|
||||||
if (event.eventCanceled) return;
|
if (event.eventCanceled) return;
|
||||||
|
|
||||||
// Resume
|
// Resume music if we paused it.
|
||||||
if (musicPausedBySubState)
|
if (musicPausedBySubState)
|
||||||
{
|
{
|
||||||
FlxG.sound.music.play();
|
FlxG.sound.music.play();
|
||||||
|
musicPausedBySubState = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Resume camera tweens if we paused any.
|
||||||
|
for (camTween in cameraTweensPausedBySubState)
|
||||||
|
{
|
||||||
|
camTween.active = true;
|
||||||
|
}
|
||||||
|
cameraTweensPausedBySubState.clear();
|
||||||
|
|
||||||
if (currentConversation != null)
|
if (currentConversation != null)
|
||||||
{
|
{
|
||||||
currentConversation.resumeMusic();
|
currentConversation.resumeMusic();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Re-sync vocals.
|
||||||
if (FlxG.sound.music != null && !startingSong && !isInCutscene) resyncVocals();
|
if (FlxG.sound.music != null && !startingSong && !isInCutscene) resyncVocals();
|
||||||
|
|
||||||
// Resume the countdown.
|
// Resume the countdown.
|
||||||
|
@ -1308,7 +1360,7 @@ class PlayState extends MusicBeatSubState
|
||||||
if (FlxG.camera.zoom < (1.35 * defaultCameraZoom) && cameraZoomRate > 0 && Conductor.instance.currentBeat % cameraZoomRate == 0)
|
if (FlxG.camera.zoom < (1.35 * defaultCameraZoom) && cameraZoomRate > 0 && Conductor.instance.currentBeat % cameraZoomRate == 0)
|
||||||
{
|
{
|
||||||
// Zoom camera in (1.5%)
|
// Zoom camera in (1.5%)
|
||||||
FlxG.camera.zoom += cameraZoomIntensity * defaultCameraZoom;
|
currentCameraZoom += cameraZoomIntensity * defaultCameraZoom;
|
||||||
// Hud zooms double (3%)
|
// Hud zooms double (3%)
|
||||||
camHUD.zoom += hudCameraZoomIntensity * defaultHUDCameraZoom;
|
camHUD.zoom += hudCameraZoomIntensity * defaultHUDCameraZoom;
|
||||||
}
|
}
|
||||||
|
@ -1500,6 +1552,11 @@ class PlayState extends MusicBeatSubState
|
||||||
{
|
{
|
||||||
// Apply camera zoom level from stage data.
|
// Apply camera zoom level from stage data.
|
||||||
defaultCameraZoom = currentStage.camZoom;
|
defaultCameraZoom = currentStage.camZoom;
|
||||||
|
currentCameraZoom = defaultCameraZoom;
|
||||||
|
FlxG.camera.zoom = currentCameraZoom;
|
||||||
|
|
||||||
|
// Reset additive zoom.
|
||||||
|
additiveCameraZoom = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2847,6 +2904,9 @@ class PlayState extends MusicBeatSubState
|
||||||
*/
|
*/
|
||||||
function performCleanup():Void
|
function performCleanup():Void
|
||||||
{
|
{
|
||||||
|
// If the camera is being tweened, stop it.
|
||||||
|
cancelAllCameraTweens();
|
||||||
|
|
||||||
if (currentConversation != null)
|
if (currentConversation != null)
|
||||||
{
|
{
|
||||||
remove(currentConversation);
|
remove(currentConversation);
|
||||||
|
@ -2905,6 +2965,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.
|
||||||
|
cancelAllCameraTweens();
|
||||||
|
|
||||||
// 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.
|
||||||
// Else, zoom in on GF.
|
// Else, zoom in on GF.
|
||||||
|
@ -2991,15 +3054,119 @@ class PlayState extends MusicBeatSubState
|
||||||
/**
|
/**
|
||||||
* Resets the camera's zoom level and focus point.
|
* Resets the camera's zoom level and focus point.
|
||||||
*/
|
*/
|
||||||
public function resetCamera():Void
|
public function resetCamera(?resetZoom:Bool = true, ?cancelTweens:Bool = true):Void
|
||||||
{
|
{
|
||||||
|
// Cancel camera tweens if any are active.
|
||||||
|
if (cancelTweens)
|
||||||
|
{
|
||||||
|
cancelAllCameraTweens();
|
||||||
|
}
|
||||||
|
|
||||||
FlxG.camera.follow(cameraFollowPoint, LOCKON, 0.04);
|
FlxG.camera.follow(cameraFollowPoint, LOCKON, 0.04);
|
||||||
FlxG.camera.targetOffset.set();
|
FlxG.camera.targetOffset.set();
|
||||||
FlxG.camera.zoom = defaultCameraZoom;
|
|
||||||
|
if (resetZoom)
|
||||||
|
{
|
||||||
|
resetCameraZoom();
|
||||||
|
}
|
||||||
|
|
||||||
// Snap the camera to the follow point immediately.
|
// Snap the camera to the follow point immediately.
|
||||||
FlxG.camera.focusOn(cameraFollowPoint.getPosition());
|
FlxG.camera.focusOn(cameraFollowPoint.getPosition());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disables camera following and tweens the camera to the follow point manually.
|
||||||
|
*/
|
||||||
|
public function tweenCameraToFollowPoint(?duration:Float, ?ease:Null<Float->Float>):Void
|
||||||
|
{
|
||||||
|
// Cancel the current tween if it's active.
|
||||||
|
cancelCameraFollowTween();
|
||||||
|
|
||||||
|
if (duration == 0)
|
||||||
|
{
|
||||||
|
// Instant movement. Just reset the camera to force it to the follow point.
|
||||||
|
resetCamera(false, false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Disable camera following for the duration of the tween.
|
||||||
|
FlxG.camera.target = null;
|
||||||
|
|
||||||
|
// Follow tween! Caching it so we can cancel/pause it later if needed.
|
||||||
|
var followPos:FlxPoint = cameraFollowPoint.getPosition() - FlxPoint.weak(FlxG.camera.width * 0.5, FlxG.camera.height * 0.5);
|
||||||
|
cameraFollowTween = FlxTween.tween(FlxG.camera.scroll, {x: followPos.x, y: followPos.y}, duration,
|
||||||
|
{
|
||||||
|
ease: ease,
|
||||||
|
onComplete: function(_) {
|
||||||
|
resetCamera(false, false); // Re-enable camera following when the tween is complete.
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function cancelCameraFollowTween()
|
||||||
|
{
|
||||||
|
if (cameraFollowTween != null)
|
||||||
|
{
|
||||||
|
cameraFollowTween.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tweens the camera zoom to the desired amount.
|
||||||
|
*/
|
||||||
|
public function tweenCameraZoom(?zoom:Float, ?duration:Float, ?directMode:Bool, ?ease:Null<Float->Float>):Void
|
||||||
|
{
|
||||||
|
// Cancel the current tween if it's active.
|
||||||
|
cancelCameraZoomTween();
|
||||||
|
|
||||||
|
var targetZoom = zoom * FlxCamera.defaultZoom;
|
||||||
|
|
||||||
|
if (directMode) // Direct mode: Tween defaultCameraZoom for basic "smooth" zooms.
|
||||||
|
{
|
||||||
|
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});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else // Additive mode: Tween additiveCameraZoom for ease-based zooms.
|
||||||
|
{
|
||||||
|
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});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function cancelCameraZoomTween()
|
||||||
|
{
|
||||||
|
if (cameraZoomTween != null)
|
||||||
|
{
|
||||||
|
cameraZoomTween.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancel all active camera tweens simultaneously.
|
||||||
|
*/
|
||||||
|
public function cancelAllCameraTweens()
|
||||||
|
{
|
||||||
|
cancelCameraFollowTween();
|
||||||
|
cancelCameraZoomTween();
|
||||||
|
}
|
||||||
|
|
||||||
#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.
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package funkin.play.event;
|
package funkin.play.event;
|
||||||
|
|
||||||
|
import flixel.tweens.FlxEase;
|
||||||
// Data from the chart
|
// Data from the chart
|
||||||
import funkin.data.song.SongData;
|
import funkin.data.song.SongData;
|
||||||
import funkin.data.song.SongData.SongEventData;
|
import funkin.data.song.SongData.SongEventData;
|
||||||
|
@ -69,6 +70,13 @@ class FocusCameraSongEvent extends SongEvent
|
||||||
|
|
||||||
if (char == null) char = cast data.value;
|
if (char == null) char = cast data.value;
|
||||||
|
|
||||||
|
var useTween:Null<Bool> = data.getBool('useTween');
|
||||||
|
if (useTween == null) useTween = false;
|
||||||
|
var duration:Null<Float> = data.getFloat('duration');
|
||||||
|
if (duration == null) duration = 4.0;
|
||||||
|
var ease:Null<String> = data.getString('ease');
|
||||||
|
if (ease == null) ease = 'linear';
|
||||||
|
|
||||||
switch (char)
|
switch (char)
|
||||||
{
|
{
|
||||||
case -1: // Position
|
case -1: // Position
|
||||||
|
@ -117,6 +125,26 @@ class FocusCameraSongEvent extends SongEvent
|
||||||
default:
|
default:
|
||||||
trace('Unknown camera focus: ' + data);
|
trace('Unknown camera focus: ' + data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (useTween)
|
||||||
|
{
|
||||||
|
switch (ease)
|
||||||
|
{
|
||||||
|
case 'INSTANT':
|
||||||
|
PlayState.instance.tweenCameraToFollowPoint(0);
|
||||||
|
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.tweenCameraToFollowPoint(durSeconds, easeFunction);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override function getTitle():String
|
public override function getTitle():String
|
||||||
|
@ -158,6 +186,51 @@ class FocusCameraSongEvent extends SongEvent
|
||||||
step: 10.0,
|
step: 10.0,
|
||||||
type: SongEventFieldType.FLOAT,
|
type: SongEventFieldType.FLOAT,
|
||||||
units: "px"
|
units: "px"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'useTween',
|
||||||
|
title: 'Use Tween',
|
||||||
|
type: SongEventFieldType.BOOL,
|
||||||
|
defaultValue: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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' => 'INSTANT',
|
||||||
|
'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',
|
||||||
|
'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',
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,19 +62,26 @@ class ZoomCameraSongEvent extends SongEvent
|
||||||
|
|
||||||
var zoom:Null<Float> = data.getFloat('zoom');
|
var zoom:Null<Float> = data.getFloat('zoom');
|
||||||
if (zoom == null) zoom = 1.0;
|
if (zoom == null) zoom = 1.0;
|
||||||
|
|
||||||
var duration:Null<Float> = data.getFloat('duration');
|
var duration:Null<Float> = data.getFloat('duration');
|
||||||
if (duration == null) duration = 4.0;
|
if (duration == null) duration = 4.0;
|
||||||
|
|
||||||
|
var mode:Null<String> = data.getString('mode');
|
||||||
|
if (mode == null) mode = 'additive';
|
||||||
|
|
||||||
var ease:Null<String> = data.getString('ease');
|
var ease:Null<String> = data.getString('ease');
|
||||||
if (ease == null) ease = 'linear';
|
if (ease == null) ease = 'linear';
|
||||||
|
|
||||||
|
var directMode:Bool = mode == 'direct';
|
||||||
|
|
||||||
// If it's a string, check the value.
|
// If it's a string, check the value.
|
||||||
switch (ease)
|
switch (ease)
|
||||||
{
|
{
|
||||||
case 'INSTANT':
|
case 'INSTANT':
|
||||||
// Set the zoom. Use defaultCameraZoom to prevent breaking camera bops.
|
PlayState.instance.tweenCameraZoom(zoom, 0, directMode);
|
||||||
PlayState.instance.defaultCameraZoom = zoom * FlxCamera.defaultZoom;
|
|
||||||
default:
|
default:
|
||||||
|
var durSeconds = Conductor.instance.stepLengthMs * duration / 1000;
|
||||||
|
|
||||||
var easeFunction:Null<Float->Float> = Reflect.field(FlxEase, ease);
|
var easeFunction:Null<Float->Float> = Reflect.field(FlxEase, ease);
|
||||||
if (easeFunction == null)
|
if (easeFunction == null)
|
||||||
{
|
{
|
||||||
|
@ -82,8 +89,7 @@ class ZoomCameraSongEvent extends SongEvent
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
FlxTween.tween(PlayState.instance, {defaultCameraZoom: zoom * FlxCamera.defaultZoom}, (Conductor.instance.stepLengthMs * duration / 1000),
|
PlayState.instance.tweenCameraZoom(zoom, durSeconds, directMode, easeFunction);
|
||||||
{ease: easeFunction});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,8 +102,9 @@ class ZoomCameraSongEvent extends SongEvent
|
||||||
* ```
|
* ```
|
||||||
* {
|
* {
|
||||||
* 'zoom': FLOAT, // Target zoom level.
|
* 'zoom': FLOAT, // Target zoom level.
|
||||||
* 'duration': FLOAT, // Optional duration in steps
|
* 'duration': FLOAT, // Optional duration in steps.
|
||||||
* 'ease': ENUM, // Optional easing function
|
* 'mode': ENUM, // Whether to set additive zoom or direct zoom.
|
||||||
|
* 'ease': ENUM, // Optional easing function.
|
||||||
* }
|
* }
|
||||||
* @return SongEventSchema
|
* @return SongEventSchema
|
||||||
*/
|
*/
|
||||||
|
@ -120,6 +127,13 @@ class ZoomCameraSongEvent extends SongEvent
|
||||||
type: SongEventFieldType.FLOAT,
|
type: SongEventFieldType.FLOAT,
|
||||||
units: 'steps'
|
units: 'steps'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'mode',
|
||||||
|
title: 'Mode',
|
||||||
|
defaultValue: 'additive',
|
||||||
|
type: SongEventFieldType.ENUM,
|
||||||
|
keys: ['Additive' => 'additive', 'Direct' => 'direct']
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'ease',
|
name: 'ease',
|
||||||
title: 'Easing Type',
|
title: 'Easing Type',
|
||||||
|
|
|
@ -213,6 +213,26 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
|
||||||
return _metadata.values();
|
return _metadata.values();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List the album IDs for each variation of the song.
|
||||||
|
* @return A map of variation IDs to album IDs.
|
||||||
|
*/
|
||||||
|
public function listAlbums():Map<String, String>
|
||||||
|
{
|
||||||
|
var result:Map<String, String> = new Map<String, String>();
|
||||||
|
|
||||||
|
for (difficultyId in difficulties.keys())
|
||||||
|
{
|
||||||
|
var meta:Null<SongDifficulty> = difficulties.get(difficultyId);
|
||||||
|
if (meta != null && meta.album != null)
|
||||||
|
{
|
||||||
|
result.set(difficultyId, meta.album);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Populate the difficulty data from the provided metadata.
|
* Populate the difficulty data from the provided metadata.
|
||||||
* Does not load chart data (that is triggered later when we want to play the song).
|
* Does not load chart data (that is triggered later when we want to play the song).
|
||||||
|
@ -367,11 +387,14 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List all the difficulties in this song.
|
* List all the difficulties in this song.
|
||||||
|
*
|
||||||
* @param variationId Optionally filter by a single variation.
|
* @param variationId Optionally filter by a single variation.
|
||||||
* @param variationIds Optionally filter by multiple variations.
|
* @param variationIds Optionally filter by multiple variations.
|
||||||
|
* @param showHidden Include charts which are not accessible to the player.
|
||||||
|
*
|
||||||
* @return The list of difficulties.
|
* @return The list of difficulties.
|
||||||
*/
|
*/
|
||||||
public function listDifficulties(?variationId:String, ?variationIds:Array<String>):Array<String>
|
public function listDifficulties(?variationId:String, ?variationIds:Array<String>, showHidden:Bool = false):Array<String>
|
||||||
{
|
{
|
||||||
if (variationIds == null) variationIds = [];
|
if (variationIds == null) variationIds = [];
|
||||||
if (variationId != null) variationIds.push(variationId);
|
if (variationId != null) variationIds.push(variationId);
|
||||||
|
@ -387,6 +410,15 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
|
||||||
return difficulty.difficulty;
|
return difficulty.difficulty;
|
||||||
}).nonNull().unique();
|
}).nonNull().unique();
|
||||||
|
|
||||||
|
diffFiltered = diffFiltered.filter(function(diffId:String):Bool {
|
||||||
|
if (showHidden) return true;
|
||||||
|
for (targetVariation in variationIds)
|
||||||
|
{
|
||||||
|
if (isDifficultyVisible(diffId, targetVariation)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
diffFiltered.sort(SortUtil.defaultsThenAlphabetically.bind(Constants.DEFAULT_DIFFICULTY_LIST));
|
diffFiltered.sort(SortUtil.defaultsThenAlphabetically.bind(Constants.DEFAULT_DIFFICULTY_LIST));
|
||||||
|
|
||||||
return diffFiltered;
|
return diffFiltered;
|
||||||
|
@ -405,6 +437,13 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isDifficultyVisible(diffId:String, variationId:String):Bool
|
||||||
|
{
|
||||||
|
var variation = _metadata.get(variationId);
|
||||||
|
if (variation == null) return false;
|
||||||
|
return variation.playData.difficulties.contains(diffId);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Purge the cached chart data for each difficulty of this song.
|
* Purge the cached chart data for each difficulty of this song.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -878,6 +878,8 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
*/
|
*/
|
||||||
var noteDisplayDirty:Bool = true;
|
var noteDisplayDirty:Bool = true;
|
||||||
|
|
||||||
|
var noteTooltipsDirty:Bool = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the selected charactesr have been modified and the health icons need to be updated.
|
* Whether the selected charactesr have been modified and the health icons need to be updated.
|
||||||
*/
|
*/
|
||||||
|
@ -1541,6 +1543,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
// Make sure view is updated when the variation changes.
|
// Make sure view is updated when the variation changes.
|
||||||
noteDisplayDirty = true;
|
noteDisplayDirty = true;
|
||||||
notePreviewDirty = true;
|
notePreviewDirty = true;
|
||||||
|
noteTooltipsDirty = true;
|
||||||
notePreviewViewportBoundsDirty = true;
|
notePreviewViewportBoundsDirty = true;
|
||||||
|
|
||||||
switchToCurrentInstrumental();
|
switchToCurrentInstrumental();
|
||||||
|
@ -1562,6 +1565,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
// Make sure view is updated when the difficulty changes.
|
// Make sure view is updated when the difficulty changes.
|
||||||
noteDisplayDirty = true;
|
noteDisplayDirty = true;
|
||||||
notePreviewDirty = true;
|
notePreviewDirty = true;
|
||||||
|
noteTooltipsDirty = true;
|
||||||
notePreviewViewportBoundsDirty = true;
|
notePreviewViewportBoundsDirty = true;
|
||||||
|
|
||||||
// Make sure the difficulty we selected is in the list of difficulties.
|
// Make sure the difficulty we selected is in the list of difficulties.
|
||||||
|
@ -3663,8 +3667,13 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
selectionSquare.width = eventSprite.width;
|
selectionSquare.width = eventSprite.width;
|
||||||
selectionSquare.height = eventSprite.height;
|
selectionSquare.height = eventSprite.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Additional cleanup on notes.
|
||||||
|
if (noteTooltipsDirty) eventSprite.updateTooltipText();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
noteTooltipsDirty = false;
|
||||||
|
|
||||||
// Sort the notes DESCENDING. This keeps the sustain behind the associated note.
|
// Sort the notes DESCENDING. This keeps the sustain behind the associated note.
|
||||||
renderedNotes.sort(FlxSort.byY, FlxSort.DESCENDING); // TODO: .group.insertionSort()
|
renderedNotes.sort(FlxSort.byY, FlxSort.DESCENDING); // TODO: .group.insertionSort()
|
||||||
|
|
||||||
|
|
|
@ -51,7 +51,12 @@ class SetItemSelectionCommand implements ChartEditorCommand
|
||||||
}
|
}
|
||||||
var eventData = eventSelected.valueAsStruct(defaultKey);
|
var eventData = eventSelected.valueAsStruct(defaultKey);
|
||||||
|
|
||||||
state.eventDataToPlace = eventData;
|
var eventDataClone = Reflect.copy(eventData);
|
||||||
|
|
||||||
|
if (eventDataClone != null)
|
||||||
|
{
|
||||||
|
state.eventDataToPlace = eventDataClone;
|
||||||
|
}
|
||||||
|
|
||||||
state.refreshToolbox(ChartEditorState.CHART_EDITOR_TOOLBOX_EVENT_DATA_LAYOUT);
|
state.refreshToolbox(ChartEditorState.CHART_EDITOR_TOOLBOX_EVENT_DATA_LAYOUT);
|
||||||
}
|
}
|
||||||
|
|
|
@ -164,8 +164,7 @@ class ChartEditorEventSprite extends FlxSprite
|
||||||
this.eventData = value;
|
this.eventData = value;
|
||||||
// Update the position to match the note data.
|
// Update the position to match the note data.
|
||||||
updateEventPosition();
|
updateEventPosition();
|
||||||
// Update the tooltip text.
|
updateTooltipText();
|
||||||
this.tooltip.tipData = {text: this.eventData.buildTooltip()};
|
|
||||||
return this.eventData;
|
return this.eventData;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -188,6 +187,13 @@ class ChartEditorEventSprite extends FlxSprite
|
||||||
this.updateTooltipPosition();
|
this.updateTooltipPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function updateTooltipText():Void
|
||||||
|
{
|
||||||
|
if (this.eventData == null) return;
|
||||||
|
if (this.isGhost) return;
|
||||||
|
this.tooltip.tipData = {text: this.eventData.buildTooltip()};
|
||||||
|
}
|
||||||
|
|
||||||
public function updateTooltipPosition():Void
|
public function updateTooltipPosition():Void
|
||||||
{
|
{
|
||||||
// No tooltip for ghost sprites.
|
// No tooltip for ghost sprites.
|
||||||
|
|
|
@ -237,6 +237,11 @@ class ChartEditorEventDataToolbox extends ChartEditorBaseToolbox
|
||||||
{
|
{
|
||||||
value = event.target.value.value;
|
value = event.target.value.value;
|
||||||
}
|
}
|
||||||
|
else if (field.type == BOOL)
|
||||||
|
{
|
||||||
|
var chk:CheckBox = cast event.target;
|
||||||
|
value = cast(chk.selected, Null<Bool>); // Need to cast to nullable bool or the compiler will get mad.
|
||||||
|
}
|
||||||
|
|
||||||
trace('ChartEditorToolboxHandler.buildEventDataFormFromSchema() - ${event.target.id} = ${value}');
|
trace('ChartEditorToolboxHandler.buildEventDataFormFromSchema() - ${event.target.id} = ${value}');
|
||||||
|
|
||||||
|
@ -253,14 +258,15 @@ class ChartEditorEventDataToolbox extends ChartEditorBaseToolbox
|
||||||
// Edit the event data of any existing events.
|
// Edit the event data of any existing events.
|
||||||
if (!_initializing && chartEditorState.currentEventSelection.length > 0)
|
if (!_initializing && chartEditorState.currentEventSelection.length > 0)
|
||||||
{
|
{
|
||||||
for (event in chartEditorState.currentEventSelection)
|
for (songEvent in chartEditorState.currentEventSelection)
|
||||||
{
|
{
|
||||||
event.eventKind = chartEditorState.eventKindToPlace;
|
songEvent.eventKind = chartEditorState.eventKindToPlace;
|
||||||
event.value = chartEditorState.eventDataToPlace;
|
songEvent.value = Reflect.copy(chartEditorState.eventDataToPlace);
|
||||||
}
|
}
|
||||||
chartEditorState.saveDataDirty = true;
|
chartEditorState.saveDataDirty = true;
|
||||||
chartEditorState.noteDisplayDirty = true;
|
chartEditorState.noteDisplayDirty = true;
|
||||||
chartEditorState.notePreviewDirty = true;
|
chartEditorState.notePreviewDirty = true;
|
||||||
|
chartEditorState.noteTooltipsDirty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import funkin.data.dialogue.DialogueBoxData;
|
||||||
import funkin.data.dialogue.DialogueBoxRegistry;
|
import funkin.data.dialogue.DialogueBoxRegistry;
|
||||||
import funkin.data.dialogue.SpeakerData;
|
import funkin.data.dialogue.SpeakerData;
|
||||||
import funkin.data.dialogue.SpeakerRegistry;
|
import funkin.data.dialogue.SpeakerRegistry;
|
||||||
|
import funkin.data.freeplay.AlbumRegistry;
|
||||||
import funkin.play.cutscene.dialogue.Conversation;
|
import funkin.play.cutscene.dialogue.Conversation;
|
||||||
import funkin.play.cutscene.dialogue.DialogueBox;
|
import funkin.play.cutscene.dialogue.DialogueBox;
|
||||||
import funkin.play.cutscene.dialogue.Speaker;
|
import funkin.play.cutscene.dialogue.Speaker;
|
||||||
|
|
|
@ -49,8 +49,11 @@ class StageOffsetSubState extends HaxeUISubState
|
||||||
{
|
{
|
||||||
super.create();
|
super.create();
|
||||||
|
|
||||||
|
var playState = PlayState.instance;
|
||||||
|
|
||||||
FlxG.mouse.visible = true;
|
FlxG.mouse.visible = true;
|
||||||
PlayState.instance.pauseMusic();
|
playState.pauseMusic();
|
||||||
|
playState.cancelAllCameraTweens();
|
||||||
FlxG.camera.target = null;
|
FlxG.camera.target = null;
|
||||||
|
|
||||||
setupUIListeners();
|
setupUIListeners();
|
||||||
|
@ -63,8 +66,8 @@ class StageOffsetSubState extends HaxeUISubState
|
||||||
|
|
||||||
// add(uiStuff);
|
// add(uiStuff);
|
||||||
|
|
||||||
PlayState.instance.persistentUpdate = true;
|
playState.persistentUpdate = true;
|
||||||
component.cameras = [PlayState.instance.camHUD];
|
component.cameras = [playState.camHUD];
|
||||||
// uiStuff.cameras = [PlayState.instance.camHUD];
|
// uiStuff.cameras = [PlayState.instance.camHUD];
|
||||||
// btn.cameras = [PlayState.instance.camHUD];
|
// btn.cameras = [PlayState.instance.camHUD];
|
||||||
|
|
||||||
|
@ -72,7 +75,7 @@ class StageOffsetSubState extends HaxeUISubState
|
||||||
|
|
||||||
var layerList:ListView = findComponent("prop-layers");
|
var layerList:ListView = findComponent("prop-layers");
|
||||||
|
|
||||||
for (thing in PlayState.instance.currentStage)
|
for (thing in playState.currentStage)
|
||||||
{
|
{
|
||||||
var prop:StageProp = cast thing;
|
var prop:StageProp = cast thing;
|
||||||
if (prop != null && prop.name != null)
|
if (prop != null && prop.name != null)
|
||||||
|
|
89
source/funkin/ui/freeplay/Album.hx
Normal file
89
source/funkin/ui/freeplay/Album.hx
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
package funkin.ui.freeplay;
|
||||||
|
|
||||||
|
import funkin.data.freeplay.AlbumData;
|
||||||
|
import funkin.data.freeplay.AlbumRegistry;
|
||||||
|
import funkin.data.IRegistryEntry;
|
||||||
|
import flixel.graphics.FlxGraphic;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class representing the data for an album as displayed in Freeplay.
|
||||||
|
*/
|
||||||
|
class Album implements IRegistryEntry<AlbumData>
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The internal ID for this album.
|
||||||
|
*/
|
||||||
|
public final id:String;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The full data for an album.
|
||||||
|
*/
|
||||||
|
public final _data:AlbumData;
|
||||||
|
|
||||||
|
public function new(id:String)
|
||||||
|
{
|
||||||
|
this.id = id;
|
||||||
|
this._data = _fetchData(id);
|
||||||
|
|
||||||
|
if (_data == null)
|
||||||
|
{
|
||||||
|
throw 'Could not parse album data for id: $id';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the name of the album.
|
||||||
|
* @
|
||||||
|
*/
|
||||||
|
public function getAlbumName():String
|
||||||
|
{
|
||||||
|
return _data.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the artists of the album.
|
||||||
|
* @return The list of artists
|
||||||
|
*/
|
||||||
|
public function getAlbumArtists():Array<String>
|
||||||
|
{
|
||||||
|
return _data.artists;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the asset key for the album art.
|
||||||
|
* @return The asset key
|
||||||
|
*/
|
||||||
|
public function getAlbumArtAssetKey():String
|
||||||
|
{
|
||||||
|
return _data.albumArtAsset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the album art as a graphic, ready to apply to a sprite.
|
||||||
|
* @return The built graphic
|
||||||
|
*/
|
||||||
|
public function getAlbumArtGraphic():FlxGraphic
|
||||||
|
{
|
||||||
|
return FlxG.bitmap.add(Paths.image(getAlbumArtAssetKey()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the asset key for the album title.
|
||||||
|
*/
|
||||||
|
public function getAlbumTitleAssetKey():String
|
||||||
|
{
|
||||||
|
return _data.albumTitleAsset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toString():String
|
||||||
|
{
|
||||||
|
return 'Album($id)';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function destroy():Void {}
|
||||||
|
|
||||||
|
static function _fetchData(id:String):Null<AlbumData>
|
||||||
|
{
|
||||||
|
return AlbumRegistry.instance.parseEntryDataWithMigration(id, AlbumRegistry.instance.fetchEntryVersion(id));
|
||||||
|
}
|
||||||
|
}
|
192
source/funkin/ui/freeplay/AlbumRoll.hx
Normal file
192
source/funkin/ui/freeplay/AlbumRoll.hx
Normal file
|
@ -0,0 +1,192 @@
|
||||||
|
package funkin.ui.freeplay;
|
||||||
|
|
||||||
|
import flixel.FlxSprite;
|
||||||
|
import flixel.group.FlxSpriteGroup;
|
||||||
|
import flixel.util.FlxSort;
|
||||||
|
import flixel.tweens.FlxTween;
|
||||||
|
import flixel.util.FlxTimer;
|
||||||
|
import flixel.tweens.FlxEase;
|
||||||
|
import funkin.data.freeplay.AlbumRegistry;
|
||||||
|
import funkin.graphics.FunkinSprite;
|
||||||
|
import funkin.util.SortUtil;
|
||||||
|
import openfl.utils.Assets;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The graphic for the album roll in the FreeplayState.
|
||||||
|
* Simply set `albumID` to fetch the required data and update the textures.
|
||||||
|
*/
|
||||||
|
class AlbumRoll extends FlxSpriteGroup
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The ID of the album to display.
|
||||||
|
* Modify this value to automatically update the album art and title.
|
||||||
|
*/
|
||||||
|
public var albumId(default, set):String;
|
||||||
|
|
||||||
|
function set_albumId(value:String):String
|
||||||
|
{
|
||||||
|
if (this.albumId != value)
|
||||||
|
{
|
||||||
|
this.albumId = value;
|
||||||
|
updateAlbum();
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
var albumArt:FunkinSprite;
|
||||||
|
var albumTitle:FunkinSprite;
|
||||||
|
var difficultyStars:DifficultyStars;
|
||||||
|
|
||||||
|
var _exitMovers:Null<FreeplayState.ExitMoverData>;
|
||||||
|
|
||||||
|
var albumData:Album;
|
||||||
|
|
||||||
|
public function new()
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
|
||||||
|
albumTitle = new FunkinSprite(947, 491);
|
||||||
|
albumTitle.visible = true;
|
||||||
|
albumTitle.zIndex = 200;
|
||||||
|
add(albumTitle);
|
||||||
|
|
||||||
|
difficultyStars = new DifficultyStars(140, 39);
|
||||||
|
|
||||||
|
difficultyStars.stars.visible = true;
|
||||||
|
albumTitle.visible = false;
|
||||||
|
// albumArtist.visible = false;
|
||||||
|
|
||||||
|
// var albumArtist:FlxSprite = new FlxSprite(1010, 607).loadGraphic(Paths.image('freeplay/albumArtist-kawaisprite'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load the album data by ID and update the textures.
|
||||||
|
*/
|
||||||
|
function updateAlbum():Void
|
||||||
|
{
|
||||||
|
albumData = AlbumRegistry.instance.fetchEntry(albumId);
|
||||||
|
|
||||||
|
if (albumData == null)
|
||||||
|
{
|
||||||
|
FlxG.log.warn('Could not find album data for album ID: ${albumId}');
|
||||||
|
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (albumArt != null)
|
||||||
|
{
|
||||||
|
FlxTween.cancelTweensOf(albumArt);
|
||||||
|
albumArt.visible = false;
|
||||||
|
albumArt.destroy();
|
||||||
|
remove(albumArt);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Paths.animateAtlas('freeplay/albumRoll'),
|
||||||
|
albumArt = FunkinSprite.create(1500, 360, albumData.getAlbumArtAssetKey());
|
||||||
|
albumArt.setGraphicSize(262, 262); // Magic number for size IG
|
||||||
|
albumArt.zIndex = 100;
|
||||||
|
|
||||||
|
// playIntro();
|
||||||
|
add(albumArt);
|
||||||
|
|
||||||
|
applyExitMovers();
|
||||||
|
|
||||||
|
if (Assets.exists(Paths.image(albumData.getAlbumTitleAssetKey())))
|
||||||
|
{
|
||||||
|
albumTitle.loadGraphic(Paths.image(albumData.getAlbumTitleAssetKey()));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
albumTitle.visible = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function refresh():Void
|
||||||
|
{
|
||||||
|
sort(SortUtil.byZIndex, FlxSort.ASCENDING);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply exit movers for the album roll.
|
||||||
|
* @param exitMovers The exit movers to apply.
|
||||||
|
*/
|
||||||
|
public function applyExitMovers(?exitMovers:FreeplayState.ExitMoverData):Void
|
||||||
|
{
|
||||||
|
if (exitMovers == null)
|
||||||
|
{
|
||||||
|
exitMovers = _exitMovers;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_exitMovers = exitMovers;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exitMovers == null) return;
|
||||||
|
|
||||||
|
exitMovers.set([albumArt],
|
||||||
|
{
|
||||||
|
x: FlxG.width,
|
||||||
|
speed: 0.4,
|
||||||
|
wait: 0
|
||||||
|
});
|
||||||
|
exitMovers.set([albumTitle],
|
||||||
|
{
|
||||||
|
x: FlxG.width,
|
||||||
|
speed: 0.2,
|
||||||
|
wait: 0.1
|
||||||
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
exitMovers.set([albumArtist],
|
||||||
|
{
|
||||||
|
x: FlxG.width * 1.1,
|
||||||
|
speed: 0.2,
|
||||||
|
wait: 0.2
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
exitMovers.set([difficultyStars],
|
||||||
|
{
|
||||||
|
x: FlxG.width * 1.2,
|
||||||
|
speed: 0.2,
|
||||||
|
wait: 0.3
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Play the intro animation on the album art.
|
||||||
|
*/
|
||||||
|
public function playIntro():Void
|
||||||
|
{
|
||||||
|
albumArt.visible = true;
|
||||||
|
FlxTween.tween(albumArt, {x: 950, y: 320, angle: -340}, 0.5, {ease: FlxEase.elasticOut});
|
||||||
|
|
||||||
|
albumTitle.visible = false;
|
||||||
|
new FlxTimer().start(0.75, function(_) {
|
||||||
|
showTitle();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setDifficultyStars(?difficulty:Int):Void
|
||||||
|
{
|
||||||
|
if (difficulty == null) return;
|
||||||
|
|
||||||
|
difficultyStars.difficulty = difficulty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function showTitle():Void
|
||||||
|
{
|
||||||
|
albumTitle.visible = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make the album stars visible.
|
||||||
|
*/
|
||||||
|
public function showStars():Void
|
||||||
|
{
|
||||||
|
// albumArtist.visible = false;
|
||||||
|
difficultyStars.stars.visible = false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,19 +1,14 @@
|
||||||
package funkin.ui.freeplay;
|
package funkin.ui.freeplay;
|
||||||
|
|
||||||
import openfl.text.TextField;
|
|
||||||
import flixel.addons.display.FlxGridOverlay;
|
|
||||||
import flixel.addons.transition.FlxTransitionableState;
|
import flixel.addons.transition.FlxTransitionableState;
|
||||||
import flixel.addons.ui.FlxInputText;
|
import flixel.addons.ui.FlxInputText;
|
||||||
import flixel.FlxCamera;
|
import flixel.FlxCamera;
|
||||||
import flixel.FlxGame;
|
|
||||||
import flixel.FlxSprite;
|
import flixel.FlxSprite;
|
||||||
import flixel.FlxState;
|
|
||||||
import flixel.group.FlxGroup;
|
import flixel.group.FlxGroup;
|
||||||
import flixel.group.FlxGroup.FlxTypedGroup;
|
import flixel.group.FlxGroup.FlxTypedGroup;
|
||||||
import flixel.group.FlxSpriteGroup.FlxTypedSpriteGroup;
|
import flixel.group.FlxSpriteGroup.FlxTypedSpriteGroup;
|
||||||
import flixel.input.touch.FlxTouch;
|
import flixel.input.touch.FlxTouch;
|
||||||
import flixel.math.FlxAngle;
|
import flixel.math.FlxAngle;
|
||||||
import flixel.math.FlxMath;
|
|
||||||
import flixel.math.FlxPoint;
|
import flixel.math.FlxPoint;
|
||||||
import flixel.system.debug.watch.Tracker.TrackerProfile;
|
import flixel.system.debug.watch.Tracker.TrackerProfile;
|
||||||
import flixel.text.FlxText;
|
import flixel.text.FlxText;
|
||||||
|
@ -25,7 +20,6 @@ import flixel.util.FlxTimer;
|
||||||
import funkin.audio.FunkinSound;
|
import funkin.audio.FunkinSound;
|
||||||
import funkin.data.level.LevelRegistry;
|
import funkin.data.level.LevelRegistry;
|
||||||
import funkin.data.song.SongRegistry;
|
import funkin.data.song.SongRegistry;
|
||||||
import funkin.graphics.adobeanimate.FlxAtlasSprite;
|
|
||||||
import funkin.graphics.FunkinCamera;
|
import funkin.graphics.FunkinCamera;
|
||||||
import funkin.graphics.FunkinSprite;
|
import funkin.graphics.FunkinSprite;
|
||||||
import funkin.graphics.shaders.AngleMask;
|
import funkin.graphics.shaders.AngleMask;
|
||||||
|
@ -33,28 +27,16 @@ import funkin.graphics.shaders.HSVShader;
|
||||||
import funkin.graphics.shaders.PureColor;
|
import funkin.graphics.shaders.PureColor;
|
||||||
import funkin.graphics.shaders.StrokeShader;
|
import funkin.graphics.shaders.StrokeShader;
|
||||||
import funkin.input.Controls;
|
import funkin.input.Controls;
|
||||||
import funkin.input.Controls.Control;
|
|
||||||
import funkin.play.components.HealthIcon;
|
|
||||||
import funkin.play.PlayState;
|
|
||||||
import funkin.play.PlayStatePlaylist;
|
import funkin.play.PlayStatePlaylist;
|
||||||
import funkin.play.song.Song;
|
import funkin.play.song.Song;
|
||||||
import funkin.save.Save;
|
import funkin.save.Save;
|
||||||
import funkin.save.Save.SaveScoreData;
|
import funkin.save.Save.SaveScoreData;
|
||||||
import funkin.ui.AtlasText;
|
import funkin.ui.AtlasText;
|
||||||
import funkin.ui.freeplay.BGScrollingText;
|
|
||||||
import funkin.ui.freeplay.DifficultyStars;
|
|
||||||
import funkin.ui.freeplay.DJBoyfriend;
|
|
||||||
import funkin.ui.freeplay.FreeplayScore;
|
|
||||||
import funkin.ui.freeplay.LetterSort;
|
|
||||||
import funkin.ui.freeplay.SongMenuItem;
|
|
||||||
import funkin.ui.mainmenu.MainMenuState;
|
import funkin.ui.mainmenu.MainMenuState;
|
||||||
import funkin.ui.MusicBeatState;
|
|
||||||
import funkin.ui.MusicBeatSubState;
|
import funkin.ui.MusicBeatSubState;
|
||||||
import funkin.ui.transition.LoadingState;
|
import funkin.ui.transition.LoadingState;
|
||||||
import funkin.ui.transition.StickerSubState;
|
import funkin.ui.transition.StickerSubState;
|
||||||
import funkin.util.MathUtil;
|
import funkin.util.MathUtil;
|
||||||
import funkin.util.MathUtil;
|
|
||||||
import lime.app.Future;
|
|
||||||
import lime.utils.Assets;
|
import lime.utils.Assets;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -65,6 +47,9 @@ typedef FreeplayStateParams =
|
||||||
?character:String,
|
?character:String,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The state for the freeplay menu, allowing the player to select any song to play.
|
||||||
|
*/
|
||||||
class FreeplayState extends MusicBeatSubState
|
class FreeplayState extends MusicBeatSubState
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
|
@ -120,30 +105,31 @@ class FreeplayState extends MusicBeatSubState
|
||||||
var grpDifficulties:FlxTypedSpriteGroup<DifficultySprite>;
|
var grpDifficulties:FlxTypedSpriteGroup<DifficultySprite>;
|
||||||
|
|
||||||
var coolColors:Array<Int> = [
|
var coolColors:Array<Int> = [
|
||||||
0xff9271fd,
|
0xFF9271FD,
|
||||||
0xff9271fd,
|
0xFF9271FD,
|
||||||
0xff223344,
|
0xFF223344,
|
||||||
0xFF941653,
|
0xFF941653,
|
||||||
0xFFfc96d7,
|
0xFFFC96D7,
|
||||||
0xFFa0d1ff,
|
0xFFA0D1FF,
|
||||||
0xffff78bf,
|
0xFFFF78BF,
|
||||||
0xfff6b604
|
0xFFF6B604
|
||||||
];
|
];
|
||||||
|
|
||||||
var grpSongs:FlxTypedGroup<Alphabet>;
|
var grpSongs:FlxTypedGroup<Alphabet>;
|
||||||
var grpCapsules:FlxTypedGroup<SongMenuItem>;
|
var grpCapsules:FlxTypedGroup<SongMenuItem>;
|
||||||
var curCapsule:SongMenuItem;
|
var curCapsule:SongMenuItem;
|
||||||
var curPlaying:Bool = false;
|
var curPlaying:Bool = false;
|
||||||
var ostName:FlxText;
|
|
||||||
var difficultyStars:DifficultyStars;
|
|
||||||
|
|
||||||
var displayedVariations:Array<String>;
|
var displayedVariations:Array<String>;
|
||||||
|
|
||||||
var dj:DJBoyfriend;
|
var dj:DJBoyfriend;
|
||||||
|
|
||||||
|
var ostName:FlxText;
|
||||||
|
var albumRoll:AlbumRoll;
|
||||||
|
|
||||||
var letterSort:LetterSort;
|
var letterSort:LetterSort;
|
||||||
var typing:FlxInputText;
|
var typing:FlxInputText;
|
||||||
var exitMovers:Map<Array<FlxSprite>, MoveData> = new Map();
|
var exitMovers:ExitMoverData = new Map();
|
||||||
|
|
||||||
var stickerSubState:StickerSubState;
|
var stickerSubState:StickerSubState;
|
||||||
|
|
||||||
|
@ -179,7 +165,7 @@ class FreeplayState extends MusicBeatSubState
|
||||||
|
|
||||||
#if discord_rpc
|
#if discord_rpc
|
||||||
// Updating Discord Rich Presence
|
// Updating Discord Rich Presence
|
||||||
DiscordClient.changePresence("In the Menus", null);
|
DiscordClient.changePresence('In the Menus', null);
|
||||||
#end
|
#end
|
||||||
|
|
||||||
var isDebug:Bool = false;
|
var isDebug:Bool = false;
|
||||||
|
@ -195,7 +181,7 @@ class FreeplayState extends MusicBeatSubState
|
||||||
|
|
||||||
// TODO: This makes custom variations disappear from Freeplay. Figure out a better solution later.
|
// TODO: This makes custom variations disappear from Freeplay. Figure out a better solution later.
|
||||||
// Default character (BF) shows default and Erect variations. Pico shows only Pico variations.
|
// Default character (BF) shows default and Erect variations. Pico shows only Pico variations.
|
||||||
displayedVariations = (currentCharacter == "bf") ? [Constants.DEFAULT_VARIATION, "erect"] : [currentCharacter];
|
displayedVariations = (currentCharacter == 'bf') ? [Constants.DEFAULT_VARIATION, 'erect'] : [currentCharacter];
|
||||||
|
|
||||||
// programmatically adds the songs via LevelRegistry and SongRegistry
|
// programmatically adds the songs via LevelRegistry and SongRegistry
|
||||||
for (levelId in LevelRegistry.instance.listBaseGameLevelIds())
|
for (levelId in LevelRegistry.instance.listBaseGameLevelIds())
|
||||||
|
@ -205,7 +191,7 @@ class FreeplayState extends MusicBeatSubState
|
||||||
var song:Song = SongRegistry.instance.fetchEntry(songId);
|
var song:Song = SongRegistry.instance.fetchEntry(songId);
|
||||||
|
|
||||||
// Only display songs which actually have available charts for the current character.
|
// Only display songs which actually have available charts for the current character.
|
||||||
var availableDifficultiesForSong = song.listDifficulties(displayedVariations);
|
var availableDifficultiesForSong:Array<String> = song.listDifficulties(displayedVariations);
|
||||||
if (availableDifficultiesForSong.length == 0) continue;
|
if (availableDifficultiesForSong.length == 0) continue;
|
||||||
|
|
||||||
songs.push(new FreeplaySongData(levelId, songId, song, displayedVariations));
|
songs.push(new FreeplaySongData(levelId, songId, song, displayedVariations));
|
||||||
|
@ -226,16 +212,16 @@ class FreeplayState extends MusicBeatSubState
|
||||||
trace(FlxCamera.defaultZoom);
|
trace(FlxCamera.defaultZoom);
|
||||||
|
|
||||||
var pinkBack:FunkinSprite = FunkinSprite.create('freeplay/pinkBack');
|
var pinkBack:FunkinSprite = FunkinSprite.create('freeplay/pinkBack');
|
||||||
pinkBack.color = 0xFFffd4e9; // sets it to pink!
|
pinkBack.color = 0xFFFFD4E9; // sets it to pink!
|
||||||
pinkBack.x -= pinkBack.width;
|
pinkBack.x -= pinkBack.width;
|
||||||
|
|
||||||
FlxTween.tween(pinkBack, {x: 0}, 0.6, {ease: FlxEase.quartOut});
|
FlxTween.tween(pinkBack, {x: 0}, 0.6, {ease: FlxEase.quartOut});
|
||||||
add(pinkBack);
|
add(pinkBack);
|
||||||
|
|
||||||
var orangeBackShit:FunkinSprite = new FunkinSprite(84, 440).makeSolidColor(Std.int(pinkBack.width), 75, 0xFFfeda00);
|
var orangeBackShit:FunkinSprite = new FunkinSprite(84, 440).makeSolidColor(Std.int(pinkBack.width), 75, 0xFFFEDA00);
|
||||||
add(orangeBackShit);
|
add(orangeBackShit);
|
||||||
|
|
||||||
var alsoOrangeLOL:FunkinSprite = new FunkinSprite(0, orangeBackShit.y).makeSolidColor(100, Std.int(orangeBackShit.height), 0xFFffd400);
|
var alsoOrangeLOL:FunkinSprite = new FunkinSprite(0, orangeBackShit.y).makeSolidColor(100, Std.int(orangeBackShit.height), 0xFFFFD400);
|
||||||
add(alsoOrangeLOL);
|
add(alsoOrangeLOL);
|
||||||
|
|
||||||
exitMovers.set([pinkBack, orangeBackShit, alsoOrangeLOL],
|
exitMovers.set([pinkBack, orangeBackShit, alsoOrangeLOL],
|
||||||
|
@ -254,10 +240,10 @@ class FreeplayState extends MusicBeatSubState
|
||||||
add(grpTxtScrolls);
|
add(grpTxtScrolls);
|
||||||
grpTxtScrolls.visible = false;
|
grpTxtScrolls.visible = false;
|
||||||
|
|
||||||
FlxG.debugger.addTrackerProfile(new TrackerProfile(BGScrollingText, ["x", "y", "speed", "size"]));
|
FlxG.debugger.addTrackerProfile(new TrackerProfile(BGScrollingText, ['x', 'y', 'speed', 'size']));
|
||||||
|
|
||||||
var moreWays:BGScrollingText = new BGScrollingText(0, 160, "HOT BLOODED IN MORE WAYS THAN ONE", FlxG.width, true, 43);
|
var moreWays:BGScrollingText = new BGScrollingText(0, 160, 'HOT BLOODED IN MORE WAYS THAN ONE', FlxG.width, true, 43);
|
||||||
moreWays.funnyColor = 0xFFfff383;
|
moreWays.funnyColor = 0xFFFFF383;
|
||||||
moreWays.speed = 6.8;
|
moreWays.speed = 6.8;
|
||||||
grpTxtScrolls.add(moreWays);
|
grpTxtScrolls.add(moreWays);
|
||||||
|
|
||||||
|
@ -267,8 +253,8 @@ class FreeplayState extends MusicBeatSubState
|
||||||
speed: 0.4,
|
speed: 0.4,
|
||||||
});
|
});
|
||||||
|
|
||||||
var funnyScroll:BGScrollingText = new BGScrollingText(0, 220, "BOYFRIEND", FlxG.width / 2, false, 60);
|
var funnyScroll:BGScrollingText = new BGScrollingText(0, 220, 'BOYFRIEND', FlxG.width / 2, false, 60);
|
||||||
funnyScroll.funnyColor = 0xFFff9963;
|
funnyScroll.funnyColor = 0xFFFF9963;
|
||||||
funnyScroll.speed = -3.8;
|
funnyScroll.speed = -3.8;
|
||||||
grpTxtScrolls.add(funnyScroll);
|
grpTxtScrolls.add(funnyScroll);
|
||||||
|
|
||||||
|
@ -280,7 +266,7 @@ class FreeplayState extends MusicBeatSubState
|
||||||
wait: 0
|
wait: 0
|
||||||
});
|
});
|
||||||
|
|
||||||
var txtNuts:BGScrollingText = new BGScrollingText(0, 285, "PROTECT YO NUTS", FlxG.width / 2, true, 43);
|
var txtNuts:BGScrollingText = new BGScrollingText(0, 285, 'PROTECT YO NUTS', FlxG.width / 2, true, 43);
|
||||||
txtNuts.speed = 3.5;
|
txtNuts.speed = 3.5;
|
||||||
grpTxtScrolls.add(txtNuts);
|
grpTxtScrolls.add(txtNuts);
|
||||||
exitMovers.set([txtNuts],
|
exitMovers.set([txtNuts],
|
||||||
|
@ -289,8 +275,8 @@ class FreeplayState extends MusicBeatSubState
|
||||||
speed: 0.4,
|
speed: 0.4,
|
||||||
});
|
});
|
||||||
|
|
||||||
var funnyScroll2:BGScrollingText = new BGScrollingText(0, 335, "BOYFRIEND", FlxG.width / 2, false, 60);
|
var funnyScroll2:BGScrollingText = new BGScrollingText(0, 335, 'BOYFRIEND', FlxG.width / 2, false, 60);
|
||||||
funnyScroll2.funnyColor = 0xFFff9963;
|
funnyScroll2.funnyColor = 0xFFFF9963;
|
||||||
funnyScroll2.speed = -3.8;
|
funnyScroll2.speed = -3.8;
|
||||||
grpTxtScrolls.add(funnyScroll2);
|
grpTxtScrolls.add(funnyScroll2);
|
||||||
|
|
||||||
|
@ -300,8 +286,8 @@ class FreeplayState extends MusicBeatSubState
|
||||||
speed: 0.5,
|
speed: 0.5,
|
||||||
});
|
});
|
||||||
|
|
||||||
var moreWays2:BGScrollingText = new BGScrollingText(0, 397, "HOT BLOODED IN MORE WAYS THAN ONE", FlxG.width, true, 43);
|
var moreWays2:BGScrollingText = new BGScrollingText(0, 397, 'HOT BLOODED IN MORE WAYS THAN ONE', FlxG.width, true, 43);
|
||||||
moreWays2.funnyColor = 0xFFfff383;
|
moreWays2.funnyColor = 0xFFFFF383;
|
||||||
moreWays2.speed = 6.8;
|
moreWays2.speed = 6.8;
|
||||||
grpTxtScrolls.add(moreWays2);
|
grpTxtScrolls.add(moreWays2);
|
||||||
|
|
||||||
|
@ -311,8 +297,8 @@ class FreeplayState extends MusicBeatSubState
|
||||||
speed: 0.4
|
speed: 0.4
|
||||||
});
|
});
|
||||||
|
|
||||||
var funnyScroll3:BGScrollingText = new BGScrollingText(0, orangeBackShit.y + 10, "BOYFRIEND", FlxG.width / 2, 60);
|
var funnyScroll3:BGScrollingText = new BGScrollingText(0, orangeBackShit.y + 10, 'BOYFRIEND', FlxG.width / 2, 60);
|
||||||
funnyScroll3.funnyColor = 0xFFfea400;
|
funnyScroll3.funnyColor = 0xFFFEA400;
|
||||||
funnyScroll3.speed = -3.8;
|
funnyScroll3.speed = -3.8;
|
||||||
grpTxtScrolls.add(funnyScroll3);
|
grpTxtScrolls.add(funnyScroll3);
|
||||||
|
|
||||||
|
@ -328,8 +314,10 @@ class FreeplayState extends MusicBeatSubState
|
||||||
x: -dj.width * 1.6,
|
x: -dj.width * 1.6,
|
||||||
speed: 0.5
|
speed: 0.5
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: Replace this.
|
// TODO: Replace this.
|
||||||
if (currentCharacter == "pico") dj.visible = false;
|
if (currentCharacter == 'pico') dj.visible = false;
|
||||||
|
|
||||||
add(dj);
|
add(dj);
|
||||||
|
|
||||||
var bgDad:FlxSprite = new FlxSprite(pinkBack.width * 0.75, 0).loadGraphic(Paths.image('freeplay/freeplayBGdad'));
|
var bgDad:FlxSprite = new FlxSprite(pinkBack.width * 0.75, 0).loadGraphic(Paths.image('freeplay/freeplayBGdad'));
|
||||||
|
@ -387,62 +375,23 @@ class FreeplayState extends MusicBeatSubState
|
||||||
if (diffSprite.difficultyId == currentDifficulty) diffSprite.visible = true;
|
if (diffSprite.difficultyId == currentDifficulty) diffSprite.visible = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: This is an AtlasSprite because we use an animation to bring it into view.
|
albumRoll = new AlbumRoll();
|
||||||
// TODO: Add the ability to select the album graphic.
|
albumRoll.albumId = 'volume1';
|
||||||
var albumArt:FlxAtlasSprite = new FlxAtlasSprite(640, 360, Paths.animateAtlas("freeplay/albumRoll"));
|
add(albumRoll);
|
||||||
albumArt.visible = false;
|
|
||||||
add(albumArt);
|
|
||||||
|
|
||||||
exitMovers.set([albumArt],
|
albumRoll.applyExitMovers(exitMovers);
|
||||||
{
|
|
||||||
x: FlxG.width,
|
|
||||||
speed: 0.4,
|
|
||||||
wait: 0
|
|
||||||
});
|
|
||||||
|
|
||||||
var albumTitle:FlxSprite = new FlxSprite(947, 491).loadGraphic(Paths.image('freeplay/albumTitle-fnfvol1'));
|
|
||||||
var albumArtist:FlxSprite = new FlxSprite(1010, 607).loadGraphic(Paths.image('freeplay/albumArtist-kawaisprite'));
|
|
||||||
difficultyStars = new DifficultyStars(140, 39);
|
|
||||||
|
|
||||||
difficultyStars.stars.visible = false;
|
|
||||||
albumTitle.visible = false;
|
|
||||||
albumArtist.visible = false;
|
|
||||||
|
|
||||||
exitMovers.set([albumTitle],
|
|
||||||
{
|
|
||||||
x: FlxG.width,
|
|
||||||
speed: 0.2,
|
|
||||||
wait: 0.1
|
|
||||||
});
|
|
||||||
|
|
||||||
exitMovers.set([albumArtist],
|
|
||||||
{
|
|
||||||
x: FlxG.width * 1.1,
|
|
||||||
speed: 0.2,
|
|
||||||
wait: 0.2
|
|
||||||
});
|
|
||||||
exitMovers.set([difficultyStars],
|
|
||||||
{
|
|
||||||
x: FlxG.width * 1.2,
|
|
||||||
speed: 0.2,
|
|
||||||
wait: 0.3
|
|
||||||
});
|
|
||||||
|
|
||||||
add(albumTitle);
|
|
||||||
add(albumArtist);
|
|
||||||
add(difficultyStars);
|
|
||||||
|
|
||||||
var overhangStuff:FlxSprite = new FlxSprite().makeGraphic(FlxG.width, 64, FlxColor.BLACK);
|
var overhangStuff:FlxSprite = new FlxSprite().makeGraphic(FlxG.width, 64, FlxColor.BLACK);
|
||||||
overhangStuff.y -= overhangStuff.height;
|
overhangStuff.y -= overhangStuff.height;
|
||||||
add(overhangStuff);
|
add(overhangStuff);
|
||||||
FlxTween.tween(overhangStuff, {y: 0}, 0.3, {ease: FlxEase.quartOut});
|
FlxTween.tween(overhangStuff, {y: 0}, 0.3, {ease: FlxEase.quartOut});
|
||||||
|
|
||||||
var fnfFreeplay:FlxText = new FlxText(8, 8, 0, "FREEPLAY", 48);
|
var fnfFreeplay:FlxText = new FlxText(8, 8, 0, 'FREEPLAY', 48);
|
||||||
fnfFreeplay.font = "VCR OSD Mono";
|
fnfFreeplay.font = 'VCR OSD Mono';
|
||||||
fnfFreeplay.visible = false;
|
fnfFreeplay.visible = false;
|
||||||
|
|
||||||
ostName = new FlxText(8, 8, FlxG.width - 8 - 8, "OFFICIAL OST", 48);
|
ostName = new FlxText(8, 8, FlxG.width - 8 - 8, 'OFFICIAL OST', 48);
|
||||||
ostName.font = "VCR OSD Mono";
|
ostName.font = 'VCR OSD Mono';
|
||||||
ostName.alignment = RIGHT;
|
ostName.alignment = RIGHT;
|
||||||
ostName.visible = false;
|
ostName.visible = false;
|
||||||
|
|
||||||
|
@ -454,21 +403,21 @@ class FreeplayState extends MusicBeatSubState
|
||||||
wait: 0
|
wait: 0
|
||||||
});
|
});
|
||||||
|
|
||||||
var sillyStroke = new StrokeShader(0xFFFFFFFF, 2, 2);
|
var sillyStroke:StrokeShader = new StrokeShader(0xFFFFFFFF, 2, 2);
|
||||||
fnfFreeplay.shader = sillyStroke;
|
fnfFreeplay.shader = sillyStroke;
|
||||||
add(fnfFreeplay);
|
add(fnfFreeplay);
|
||||||
add(ostName);
|
add(ostName);
|
||||||
|
|
||||||
var fnfHighscoreSpr:FlxSprite = new FlxSprite(860, 70);
|
var fnfHighscoreSpr:FlxSprite = new FlxSprite(860, 70);
|
||||||
fnfHighscoreSpr.frames = Paths.getSparrowAtlas('freeplay/highscore');
|
fnfHighscoreSpr.frames = Paths.getSparrowAtlas('freeplay/highscore');
|
||||||
fnfHighscoreSpr.animation.addByPrefix("highscore", "highscore small instance 1", 24, false);
|
fnfHighscoreSpr.animation.addByPrefix('highscore', 'highscore small instance 1', 24, false);
|
||||||
fnfHighscoreSpr.visible = false;
|
fnfHighscoreSpr.visible = false;
|
||||||
fnfHighscoreSpr.setGraphicSize(0, Std.int(fnfHighscoreSpr.height * 1));
|
fnfHighscoreSpr.setGraphicSize(0, Std.int(fnfHighscoreSpr.height * 1));
|
||||||
fnfHighscoreSpr.updateHitbox();
|
fnfHighscoreSpr.updateHitbox();
|
||||||
add(fnfHighscoreSpr);
|
add(fnfHighscoreSpr);
|
||||||
|
|
||||||
new FlxTimer().start(FlxG.random.float(12, 50), function(tmr) {
|
new FlxTimer().start(FlxG.random.float(12, 50), function(tmr) {
|
||||||
fnfHighscoreSpr.animation.play("highscore");
|
fnfHighscoreSpr.animation.play('highscore');
|
||||||
tmr.time = FlxG.random.float(20, 60);
|
tmr.time = FlxG.random.float(20, 60);
|
||||||
}, 0);
|
}, 0);
|
||||||
|
|
||||||
|
@ -479,7 +428,7 @@ class FreeplayState extends MusicBeatSubState
|
||||||
var clearBoxSprite:FlxSprite = new FlxSprite(1165, 65).loadGraphic(Paths.image('freeplay/clearBox'));
|
var clearBoxSprite:FlxSprite = new FlxSprite(1165, 65).loadGraphic(Paths.image('freeplay/clearBox'));
|
||||||
add(clearBoxSprite);
|
add(clearBoxSprite);
|
||||||
|
|
||||||
txtCompletion = new AtlasText(1185, 87, "69", AtlasFont.FREEPLAY_CLEAR);
|
txtCompletion = new AtlasText(1185, 87, '69', AtlasFont.FREEPLAY_CLEAR);
|
||||||
txtCompletion.visible = false;
|
txtCompletion.visible = false;
|
||||||
add(txtCompletion);
|
add(txtCompletion);
|
||||||
|
|
||||||
|
@ -496,9 +445,9 @@ class FreeplayState extends MusicBeatSubState
|
||||||
letterSort.changeSelectionCallback = (str) -> {
|
letterSort.changeSelectionCallback = (str) -> {
|
||||||
switch (str)
|
switch (str)
|
||||||
{
|
{
|
||||||
case "fav":
|
case 'fav':
|
||||||
generateSongList({filterType: FAVORITE}, true);
|
generateSongList({filterType: FAVORITE}, true);
|
||||||
case "ALL":
|
case 'ALL':
|
||||||
generateSongList(null, true);
|
generateSongList(null, true);
|
||||||
default:
|
default:
|
||||||
generateSongList({filterType: REGEXP, filterData: str}, true);
|
generateSongList({filterType: REGEXP, filterData: str}, true);
|
||||||
|
@ -514,25 +463,20 @@ class FreeplayState extends MusicBeatSubState
|
||||||
dj.onIntroDone.add(function() {
|
dj.onIntroDone.add(function() {
|
||||||
// when boyfriend hits dat shiii
|
// when boyfriend hits dat shiii
|
||||||
|
|
||||||
albumArt.visible = true;
|
albumRoll.playIntro();
|
||||||
albumArt.anim.play("");
|
|
||||||
albumArt.anim.onComplete = function() {
|
|
||||||
albumArt.anim.pause();
|
|
||||||
};
|
|
||||||
|
|
||||||
new FlxTimer().start(1, function(_) {
|
new FlxTimer().start(0.75, function(_) {
|
||||||
albumTitle.visible = true;
|
albumRoll.showTitle();
|
||||||
});
|
});
|
||||||
|
|
||||||
new FlxTimer().start(35 / 24, function(_) {
|
new FlxTimer().start(35 / 24, function(_) {
|
||||||
albumArtist.visible = true;
|
albumRoll.showStars();
|
||||||
difficultyStars.stars.visible = true;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
FlxTween.tween(grpDifficulties, {x: 90}, 0.6, {ease: FlxEase.quartOut});
|
FlxTween.tween(grpDifficulties, {x: 90}, 0.6, {ease: FlxEase.quartOut});
|
||||||
|
|
||||||
var diffSelLeft = new DifficultySelector(20, grpDifficulties.y - 10, false, controls);
|
var diffSelLeft:DifficultySelector = new DifficultySelector(20, grpDifficulties.y - 10, false, controls);
|
||||||
var diffSelRight = new DifficultySelector(325, grpDifficulties.y - 10, true, controls);
|
var diffSelRight:DifficultySelector = new DifficultySelector(325, grpDifficulties.y - 10, true, controls);
|
||||||
|
|
||||||
add(diffSelLeft);
|
add(diffSelLeft);
|
||||||
add(diffSelRight);
|
add(diffSelRight);
|
||||||
|
@ -562,7 +506,7 @@ class FreeplayState extends MusicBeatSubState
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
pinkBack.color = 0xFFffd863;
|
pinkBack.color = 0xFFFFD863;
|
||||||
bgDad.visible = true;
|
bgDad.visible = true;
|
||||||
orangeBackShit.visible = true;
|
orangeBackShit.visible = true;
|
||||||
alsoOrangeLOL.visible = true;
|
alsoOrangeLOL.visible = true;
|
||||||
|
@ -571,9 +515,9 @@ class FreeplayState extends MusicBeatSubState
|
||||||
|
|
||||||
generateSongList(null, false);
|
generateSongList(null, false);
|
||||||
|
|
||||||
var swag:Alphabet = new Alphabet(1, 0, "swag");
|
// var swag:Alphabet = new Alphabet(1, 0, 'swag');
|
||||||
|
|
||||||
var funnyCam = new FunkinCamera(0, 0, FlxG.width, FlxG.height);
|
var funnyCam:FunkinCamera = new FunkinCamera(0, 0, FlxG.width, FlxG.height);
|
||||||
funnyCam.bgColor = FlxColor.TRANSPARENT;
|
funnyCam.bgColor = FlxColor.TRANSPARENT;
|
||||||
FlxG.cameras.add(funnyCam);
|
FlxG.cameras.add(funnyCam);
|
||||||
|
|
||||||
|
@ -588,12 +532,20 @@ class FreeplayState extends MusicBeatSubState
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given the current filter, rebuild the current song list.
|
||||||
|
*
|
||||||
|
* @param filterStuff A filter to apply to the song list (regex, startswith, all, favorite)
|
||||||
|
* @param force
|
||||||
|
*/
|
||||||
public function generateSongList(?filterStuff:SongFilter, force:Bool = false):Void
|
public function generateSongList(?filterStuff:SongFilter, force:Bool = false):Void
|
||||||
{
|
{
|
||||||
curSelected = 1;
|
curSelected = 1;
|
||||||
|
|
||||||
for (cap in grpCapsules.members)
|
for (cap in grpCapsules.members)
|
||||||
|
{
|
||||||
cap.kill();
|
cap.kill();
|
||||||
|
}
|
||||||
|
|
||||||
var tempSongs:Array<FreeplaySongData> = songs;
|
var tempSongs:Array<FreeplaySongData> = songs;
|
||||||
|
|
||||||
|
@ -604,7 +556,7 @@ class FreeplayState extends MusicBeatSubState
|
||||||
case REGEXP:
|
case REGEXP:
|
||||||
// filterStuff.filterData has a string with the first letter of the sorting range, and the second one
|
// filterStuff.filterData has a string with the first letter of the sorting range, and the second one
|
||||||
// this creates a filter to return all the songs that start with a letter between those two
|
// this creates a filter to return all the songs that start with a letter between those two
|
||||||
var filterRegexp = new EReg("^[" + filterStuff.filterData + "].*", "i");
|
var filterRegexp:EReg = new EReg('^[' + filterStuff.filterData + '].*', 'i');
|
||||||
tempSongs = tempSongs.filter(str -> {
|
tempSongs = tempSongs.filter(str -> {
|
||||||
if (str == null) return true; // Random
|
if (str == null) return true; // Random
|
||||||
return filterRegexp.match(str.songName);
|
return filterRegexp.match(str.songName);
|
||||||
|
@ -660,14 +612,19 @@ class FreeplayState extends MusicBeatSubState
|
||||||
funnyMenu.favIcon.visible = tempSongs[i].isFav;
|
funnyMenu.favIcon.visible = tempSongs[i].isFav;
|
||||||
funnyMenu.hsvShader = hsvShader;
|
funnyMenu.hsvShader = hsvShader;
|
||||||
|
|
||||||
if (i < 8) funnyMenu.initJumpIn(Math.min(i, 4), force);
|
if (i < 8)
|
||||||
|
{
|
||||||
|
funnyMenu.initJumpIn(Math.min(i, 4), force);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
funnyMenu.forcePosition();
|
funnyMenu.forcePosition();
|
||||||
|
}
|
||||||
|
|
||||||
grpCapsules.add(funnyMenu);
|
grpCapsules.add(funnyMenu);
|
||||||
}
|
}
|
||||||
|
|
||||||
FlxG.console.registerFunction("changeSelection", changeSelection);
|
FlxG.console.registerFunction('changeSelection', changeSelection);
|
||||||
|
|
||||||
rememberSelection();
|
rememberSelection();
|
||||||
|
|
||||||
|
@ -699,7 +656,7 @@ class FreeplayState extends MusicBeatSubState
|
||||||
{
|
{
|
||||||
if (songs[curSelected] != null)
|
if (songs[curSelected] != null)
|
||||||
{
|
{
|
||||||
var realShit = curSelected;
|
var realShit:Int = curSelected;
|
||||||
songs[curSelected].isFav = !songs[curSelected].isFav;
|
songs[curSelected].isFav = !songs[curSelected].isFav;
|
||||||
if (songs[curSelected].isFav)
|
if (songs[curSelected].isFav)
|
||||||
{
|
{
|
||||||
|
@ -708,7 +665,7 @@ class FreeplayState extends MusicBeatSubState
|
||||||
ease: FlxEase.elasticOut,
|
ease: FlxEase.elasticOut,
|
||||||
onComplete: _ -> {
|
onComplete: _ -> {
|
||||||
grpCapsules.members[realShit].favIcon.visible = true;
|
grpCapsules.members[realShit].favIcon.visible = true;
|
||||||
grpCapsules.members[realShit].favIcon.animation.play("fav");
|
grpCapsules.members[realShit].favIcon.animation.play('fav');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -772,9 +729,9 @@ class FreeplayState extends MusicBeatSubState
|
||||||
{
|
{
|
||||||
if (busy) return;
|
if (busy) return;
|
||||||
|
|
||||||
var upP = controls.UI_UP_P;
|
var upP:Bool = controls.UI_UP_P;
|
||||||
var downP = controls.UI_DOWN_P;
|
var downP:Bool = controls.UI_DOWN_P;
|
||||||
var accepted = controls.ACCEPT;
|
var accepted:Bool = controls.ACCEPT;
|
||||||
|
|
||||||
if (FlxG.onMobile)
|
if (FlxG.onMobile)
|
||||||
{
|
{
|
||||||
|
@ -786,14 +743,14 @@ class FreeplayState extends MusicBeatSubState
|
||||||
}
|
}
|
||||||
if (touch.pressed)
|
if (touch.pressed)
|
||||||
{
|
{
|
||||||
var dx = initTouchPos.x - touch.screenX;
|
var dx:Float = initTouchPos.x - touch.screenX;
|
||||||
var dy = initTouchPos.y - touch.screenY;
|
var dy:Float = initTouchPos.y - touch.screenY;
|
||||||
|
|
||||||
var angle = Math.atan2(dy, dx);
|
var angle:Float = Math.atan2(dy, dx);
|
||||||
var length = Math.sqrt(dx * dx + dy * dy);
|
var length:Float = Math.sqrt(dx * dx + dy * dy);
|
||||||
|
|
||||||
FlxG.watch.addQuick("LENGTH", length);
|
FlxG.watch.addQuick('LENGTH', length);
|
||||||
FlxG.watch.addQuick("ANGLE", Math.round(FlxAngle.asDegrees(angle)));
|
FlxG.watch.addQuick('ANGLE', Math.round(FlxAngle.asDegrees(angle)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -858,11 +815,16 @@ class FreeplayState extends MusicBeatSubState
|
||||||
{
|
{
|
||||||
spamTimer = 0;
|
spamTimer = 0;
|
||||||
|
|
||||||
if (controls.UI_UP) changeSelection(-1);
|
if (controls.UI_UP)
|
||||||
|
{
|
||||||
|
changeSelection(-1);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
changeSelection(1);
|
changeSelection(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else if (spamTimer >= 0.9) spamming = true;
|
else if (spamTimer >= 0.9) spamming = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -899,16 +861,6 @@ class FreeplayState extends MusicBeatSubState
|
||||||
changeDiff(1);
|
changeDiff(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: DEBUG REMOVE THIS
|
|
||||||
if (FlxG.keys.justPressed.P)
|
|
||||||
{
|
|
||||||
var newParams:FreeplayStateParams =
|
|
||||||
{
|
|
||||||
character: currentCharacter == "bf" ? "pico" : "bf",
|
|
||||||
};
|
|
||||||
openSubState(new funkin.ui.transition.StickerSubState(null, (sticker) -> new funkin.ui.freeplay.FreeplayState(newParams, sticker)));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (controls.BACK && !typing.hasFocus)
|
if (controls.BACK && !typing.hasFocus)
|
||||||
{
|
{
|
||||||
FlxTween.globalManager.clear();
|
FlxTween.globalManager.clear();
|
||||||
|
@ -974,7 +926,7 @@ class FreeplayState extends MusicBeatSubState
|
||||||
public override function destroy():Void
|
public override function destroy():Void
|
||||||
{
|
{
|
||||||
super.destroy();
|
super.destroy();
|
||||||
var daSong = songs[curSelected];
|
var daSong:Null<FreeplaySongData> = songs[curSelected];
|
||||||
if (daSong != null)
|
if (daSong != null)
|
||||||
{
|
{
|
||||||
clearDaCache(daSong.songName);
|
clearDaCache(daSong.songName);
|
||||||
|
@ -985,7 +937,7 @@ class FreeplayState extends MusicBeatSubState
|
||||||
{
|
{
|
||||||
touchTimer = 0;
|
touchTimer = 0;
|
||||||
|
|
||||||
var currentDifficultyIndex = diffIdsCurrent.indexOf(currentDifficulty);
|
var currentDifficultyIndex:Int = diffIdsCurrent.indexOf(currentDifficulty);
|
||||||
|
|
||||||
if (currentDifficultyIndex == -1) currentDifficultyIndex = diffIdsCurrent.indexOf(Constants.DEFAULT_DIFFICULTY);
|
if (currentDifficultyIndex == -1) currentDifficultyIndex = diffIdsCurrent.indexOf(Constants.DEFAULT_DIFFICULTY);
|
||||||
|
|
||||||
|
@ -996,7 +948,7 @@ class FreeplayState extends MusicBeatSubState
|
||||||
|
|
||||||
currentDifficulty = diffIdsCurrent[currentDifficultyIndex];
|
currentDifficulty = diffIdsCurrent[currentDifficultyIndex];
|
||||||
|
|
||||||
var daSong = songs[curSelected];
|
var daSong:Null<FreeplaySongData> = songs[curSelected];
|
||||||
if (daSong != null)
|
if (daSong != null)
|
||||||
{
|
{
|
||||||
var songScore:SaveScoreData = Save.instance.getSongScore(songs[curSelected].songId, currentDifficulty);
|
var songScore:SaveScoreData = Save.instance.getSongScore(songs[curSelected].songId, currentDifficulty);
|
||||||
|
@ -1060,11 +1012,12 @@ class FreeplayState extends MusicBeatSubState
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the difficulty star count on the right.
|
// Set the difficulty star count on the right.
|
||||||
difficultyStars.difficulty = daSong?.songRating ?? difficultyStars.difficulty; // yay haxe 4.3
|
albumRoll.setDifficultyStars(daSong?.songRating);
|
||||||
|
albumRoll.albumId = daSong?.albumId ?? Constants.DEFAULT_ALBUM_ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clears the cache of songs, frees up memory, they' ll have to be loaded in later tho function clearDaCache(actualSongTho:String)
|
// Clears the cache of songs, frees up memory, they' ll have to be loaded in later tho function clearDaCache(actualSongTho:String)
|
||||||
function clearDaCache(actualSongTho:String)
|
function clearDaCache(actualSongTho:String):Void
|
||||||
{
|
{
|
||||||
for (song in songs)
|
for (song in songs)
|
||||||
{
|
{
|
||||||
|
@ -1079,7 +1032,7 @@ class FreeplayState extends MusicBeatSubState
|
||||||
|
|
||||||
function capsuleOnConfirmRandom(randomCapsule:SongMenuItem):Void
|
function capsuleOnConfirmRandom(randomCapsule:SongMenuItem):Void
|
||||||
{
|
{
|
||||||
trace("RANDOM SELECTED");
|
trace('RANDOM SELECTED');
|
||||||
|
|
||||||
busy = true;
|
busy = true;
|
||||||
letterSort.inputEnabled = false;
|
letterSort.inputEnabled = false;
|
||||||
|
@ -1095,7 +1048,7 @@ class FreeplayState extends MusicBeatSubState
|
||||||
|
|
||||||
if (availableSongCapsules.length == 0)
|
if (availableSongCapsules.length == 0)
|
||||||
{
|
{
|
||||||
trace("No songs available!");
|
trace('No songs available!');
|
||||||
busy = false;
|
busy = false;
|
||||||
letterSort.inputEnabled = true;
|
letterSort.inputEnabled = true;
|
||||||
FlxG.sound.play(Paths.sound('cancelMenu'));
|
FlxG.sound.play(Paths.sound('cancelMenu'));
|
||||||
|
@ -1167,24 +1120,23 @@ class FreeplayState extends MusicBeatSubState
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the difficulty star count on the right.
|
// Set the difficulty star count on the right.
|
||||||
var daSong = songs[curSelected];
|
var daSong:Null<FreeplaySongData> = songs[curSelected];
|
||||||
difficultyStars.difficulty = daSong?.songRating ?? 0;
|
albumRoll.setDifficultyStars(daSong?.songRating ?? 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
function changeSelection(change:Int = 0):Void
|
function changeSelection(change:Int = 0):Void
|
||||||
{
|
{
|
||||||
// NGio.logEvent('Fresh');
|
|
||||||
FlxG.sound.play(Paths.sound('scrollMenu'), 0.4);
|
FlxG.sound.play(Paths.sound('scrollMenu'), 0.4);
|
||||||
// FlxG.sound.playMusic(Paths.inst(songs[curSelected].songName));
|
// FlxG.sound.playMusic(Paths.inst(songs[curSelected].songName));
|
||||||
|
|
||||||
var prevSelected = curSelected;
|
var prevSelected:Int = curSelected;
|
||||||
|
|
||||||
curSelected += change;
|
curSelected += change;
|
||||||
|
|
||||||
if (curSelected < 0) curSelected = grpCapsules.countLiving() - 1;
|
if (curSelected < 0) curSelected = grpCapsules.countLiving() - 1;
|
||||||
if (curSelected >= grpCapsules.countLiving()) curSelected = 0;
|
if (curSelected >= grpCapsules.countLiving()) curSelected = 0;
|
||||||
|
|
||||||
var daSongCapsule = grpCapsules.members[curSelected];
|
var daSongCapsule:SongMenuItem = grpCapsules.members[curSelected];
|
||||||
if (daSongCapsule.songData != null)
|
if (daSongCapsule.songData != null)
|
||||||
{
|
{
|
||||||
var songScore:SaveScoreData = Save.instance.getSongScore(daSongCapsule.songData.songId, currentDifficulty);
|
var songScore:SaveScoreData = Save.instance.getSongScore(daSongCapsule.songData.songId, currentDifficulty);
|
||||||
|
@ -1235,6 +1187,9 @@ class FreeplayState extends MusicBeatSubState
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The difficulty selector arrows to the left and right of the difficulty.
|
||||||
|
*/
|
||||||
class DifficultySelector extends FlxSprite
|
class DifficultySelector extends FlxSprite
|
||||||
{
|
{
|
||||||
var controls:Controls;
|
var controls:Controls;
|
||||||
|
@ -1247,7 +1202,7 @@ class DifficultySelector extends FlxSprite
|
||||||
this.controls = controls;
|
this.controls = controls;
|
||||||
|
|
||||||
frames = Paths.getSparrowAtlas('freeplay/freeplaySelector');
|
frames = Paths.getSparrowAtlas('freeplay/freeplaySelector');
|
||||||
animation.addByPrefix('shine', "arrow pointer loop", 24);
|
animation.addByPrefix('shine', 'arrow pointer loop', 24);
|
||||||
animation.play('shine');
|
animation.play('shine');
|
||||||
|
|
||||||
whiteShader = new PureColor(FlxColor.WHITE);
|
whiteShader = new PureColor(FlxColor.WHITE);
|
||||||
|
@ -1281,34 +1236,62 @@ class DifficultySelector extends FlxSprite
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Structure for the current song filter.
|
||||||
|
*/
|
||||||
typedef SongFilter =
|
typedef SongFilter =
|
||||||
{
|
{
|
||||||
var filterType:FilterType;
|
var filterType:FilterType;
|
||||||
var ?filterData:Dynamic;
|
var ?filterData:Dynamic;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Possible types to use for the song filter.
|
||||||
|
*/
|
||||||
enum abstract FilterType(String)
|
enum abstract FilterType(String)
|
||||||
{
|
{
|
||||||
var STARTSWITH;
|
/**
|
||||||
var REGEXP;
|
* Filter to songs which start with a string
|
||||||
var FAVORITE;
|
*/
|
||||||
var ALL;
|
public var STARTSWITH;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter to songs which match a regular expression
|
||||||
|
*/
|
||||||
|
public var REGEXP;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter to songs which are favorited
|
||||||
|
*/
|
||||||
|
public var FAVORITE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter to all songs
|
||||||
|
*/
|
||||||
|
public var ALL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data about a specific song in the freeplay menu.
|
||||||
|
*/
|
||||||
class FreeplaySongData
|
class FreeplaySongData
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* Whether or not the song has been favorited.
|
||||||
|
*/
|
||||||
public var isFav:Bool = false;
|
public var isFav:Bool = false;
|
||||||
|
|
||||||
var song:Song;
|
var song:Song;
|
||||||
|
|
||||||
public var levelId(default, null):String = "";
|
public var levelId(default, null):String = '';
|
||||||
public var songId(default, null):String = "";
|
public var songId(default, null):String = '';
|
||||||
|
|
||||||
public var songDifficulties(default, null):Array<String> = [];
|
public var songDifficulties(default, null):Array<String> = [];
|
||||||
|
|
||||||
public var songName(default, null):String = "";
|
public var songName(default, null):String = '';
|
||||||
public var songCharacter(default, null):String = "";
|
public var songCharacter(default, null):String = '';
|
||||||
public var songRating(default, null):Int = 0;
|
public var songRating(default, null):Int = 0;
|
||||||
|
public var albumId(default, null):String = '';
|
||||||
|
|
||||||
public var currentDifficulty(default, set):String = Constants.DEFAULT_DIFFICULTY;
|
public var currentDifficulty(default, set):String = Constants.DEFAULT_DIFFICULTY;
|
||||||
public var displayedVariations(default, null):Array<String> = [Constants.DEFAULT_VARIATION];
|
public var displayedVariations(default, null):Array<String> = [Constants.DEFAULT_VARIATION];
|
||||||
|
@ -1332,19 +1315,28 @@ class FreeplaySongData
|
||||||
updateValues(displayedVariations);
|
updateValues(displayedVariations);
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateValues(displayedVariations:Array<String>):Void
|
function updateValues(variations:Array<String>):Void
|
||||||
{
|
{
|
||||||
this.songDifficulties = song.listDifficulties(displayedVariations);
|
this.songDifficulties = song.listDifficulties(variations);
|
||||||
if (!this.songDifficulties.contains(currentDifficulty)) currentDifficulty = Constants.DEFAULT_DIFFICULTY;
|
if (!this.songDifficulties.contains(currentDifficulty)) currentDifficulty = Constants.DEFAULT_DIFFICULTY;
|
||||||
|
|
||||||
var songDifficulty:SongDifficulty = song.getDifficulty(currentDifficulty, displayedVariations);
|
var songDifficulty:SongDifficulty = song.getDifficulty(currentDifficulty, variations);
|
||||||
if (songDifficulty == null) return;
|
if (songDifficulty == null) return;
|
||||||
this.songName = songDifficulty.songName;
|
this.songName = songDifficulty.songName;
|
||||||
this.songCharacter = songDifficulty.characters.opponent;
|
this.songCharacter = songDifficulty.characters.opponent;
|
||||||
this.songRating = songDifficulty.difficultyRating;
|
this.songRating = songDifficulty.difficultyRating;
|
||||||
|
this.albumId = songDifficulty.album;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The map storing information about the exit movers.
|
||||||
|
*/
|
||||||
|
typedef ExitMoverData = Map<Array<FlxSprite>, MoveData>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The data for an exit mover.
|
||||||
|
*/
|
||||||
typedef MoveData =
|
typedef MoveData =
|
||||||
{
|
{
|
||||||
var ?x:Float;
|
var ?x:Float;
|
||||||
|
@ -1353,8 +1345,14 @@ typedef MoveData =
|
||||||
var ?wait:Float;
|
var ?wait:Float;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The sprite for the difficulty
|
||||||
|
*/
|
||||||
class DifficultySprite extends FlxSprite
|
class DifficultySprite extends FlxSprite
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* The difficulty id which this sprite represents.
|
||||||
|
*/
|
||||||
public var difficultyId:String;
|
public var difficultyId:String;
|
||||||
|
|
||||||
public function new(diffId:String)
|
public function new(diffId:String)
|
||||||
|
|
9
source/funkin/ui/freeplay/ScriptedAlbum.hx
Normal file
9
source/funkin/ui/freeplay/ScriptedAlbum.hx
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
package funkin.ui.freeplay;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A script that can be tied to an Album.
|
||||||
|
* Create a scripted class that extends Album to use this.
|
||||||
|
* This allows you to customize how a specific album appears.
|
||||||
|
*/
|
||||||
|
@:hscriptClass
|
||||||
|
class ScriptedAlbum extends funkin.ui.freeplay.Album implements polymod.hscript.HScriptedClass {}
|
|
@ -65,25 +65,26 @@ class SongMenuItem extends FlxSpriteGroup
|
||||||
var rank:String = FlxG.random.getObject(ranks);
|
var rank:String = FlxG.random.getObject(ranks);
|
||||||
|
|
||||||
ranking = new FlxSprite(capsule.width * 0.84, 30);
|
ranking = new FlxSprite(capsule.width * 0.84, 30);
|
||||||
ranking.loadGraphic(Paths.image("freeplay/ranks/" + rank));
|
ranking.loadGraphic(Paths.image('freeplay/ranks/' + rank));
|
||||||
ranking.scale.x = ranking.scale.y = realScaled;
|
ranking.scale.x = ranking.scale.y = realScaled;
|
||||||
ranking.alpha = 0.75;
|
// ranking.alpha = 0.75;
|
||||||
|
ranking.visible = false;
|
||||||
ranking.origin.set(capsule.origin.x - ranking.x, capsule.origin.y - ranking.y);
|
ranking.origin.set(capsule.origin.x - ranking.x, capsule.origin.y - ranking.y);
|
||||||
add(ranking);
|
add(ranking);
|
||||||
grpHide.add(ranking);
|
grpHide.add(ranking);
|
||||||
|
|
||||||
switch (rank)
|
switch (rank)
|
||||||
{
|
{
|
||||||
case "perfect":
|
case 'perfect':
|
||||||
ranking.x -= 10;
|
ranking.x -= 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
grayscaleShader = new Grayscale(1);
|
grayscaleShader = new Grayscale(1);
|
||||||
|
|
||||||
diffRatingSprite = new FlxSprite(145, 90).loadGraphic(Paths.image("freeplay/diffRatings/diff00"));
|
diffRatingSprite = new FlxSprite(145, 90).loadGraphic(Paths.image('freeplay/diffRatings/diff00'));
|
||||||
diffRatingSprite.shader = grayscaleShader;
|
diffRatingSprite.shader = grayscaleShader;
|
||||||
diffRatingSprite.visible = false;
|
// TODO: Readd once ratings are fully implemented
|
||||||
add(diffRatingSprite);
|
// add(diffRatingSprite);
|
||||||
diffRatingSprite.origin.set(capsule.origin.x - diffRatingSprite.x, capsule.origin.y - diffRatingSprite.y);
|
diffRatingSprite.origin.set(capsule.origin.x - diffRatingSprite.x, capsule.origin.y - diffRatingSprite.y);
|
||||||
grpHide.add(diffRatingSprite);
|
grpHide.add(diffRatingSprite);
|
||||||
|
|
||||||
|
@ -104,7 +105,7 @@ class SongMenuItem extends FlxSpriteGroup
|
||||||
|
|
||||||
favIcon = new FlxSprite(400, 40);
|
favIcon = new FlxSprite(400, 40);
|
||||||
favIcon.frames = Paths.getSparrowAtlas('freeplay/favHeart');
|
favIcon.frames = Paths.getSparrowAtlas('freeplay/favHeart');
|
||||||
favIcon.animation.addByPrefix('fav', "favorite heart", 24, false);
|
favIcon.animation.addByPrefix('fav', 'favorite heart', 24, false);
|
||||||
favIcon.animation.play('fav');
|
favIcon.animation.play('fav');
|
||||||
favIcon.setGraphicSize(50, 50);
|
favIcon.setGraphicSize(50, 50);
|
||||||
favIcon.visible = false;
|
favIcon.visible = false;
|
||||||
|
@ -114,10 +115,11 @@ class SongMenuItem extends FlxSpriteGroup
|
||||||
setVisibleGrp(false);
|
setVisibleGrp(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateDifficultyRating(newRating:Int)
|
function updateDifficultyRating(newRating:Int):Void
|
||||||
{
|
{
|
||||||
var ratingPadded:String = newRating < 10 ? '0$newRating' : '$newRating';
|
var ratingPadded:String = newRating < 10 ? '0$newRating' : '$newRating';
|
||||||
diffRatingSprite.loadGraphic(Paths.image('freeplay/diffRatings/diff${ratingPadded}'));
|
diffRatingSprite.loadGraphic(Paths.image('freeplay/diffRatings/diff${ratingPadded}'));
|
||||||
|
diffRatingSprite.visible = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function set_hsvShader(value:HSVShader):HSVShader
|
function set_hsvShader(value:HSVShader):HSVShader
|
||||||
|
@ -129,7 +131,7 @@ class SongMenuItem extends FlxSpriteGroup
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
function textAppear()
|
function textAppear():Void
|
||||||
{
|
{
|
||||||
songText.scale.x = 1.7;
|
songText.scale.x = 1.7;
|
||||||
songText.scale.y = 0.2;
|
songText.scale.y = 0.2;
|
||||||
|
@ -144,7 +146,7 @@ class SongMenuItem extends FlxSpriteGroup
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function setVisibleGrp(value:Bool)
|
function setVisibleGrp(value:Bool):Void
|
||||||
{
|
{
|
||||||
for (spr in grpHide.members)
|
for (spr in grpHide.members)
|
||||||
{
|
{
|
||||||
|
@ -156,7 +158,7 @@ class SongMenuItem extends FlxSpriteGroup
|
||||||
updateSelected();
|
updateSelected();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function init(?x:Float, ?y:Float, songData:Null<FreeplaySongData>)
|
public function init(?x:Float, ?y:Float, songData:Null<FreeplaySongData>):Void
|
||||||
{
|
{
|
||||||
if (x != null) this.x = x;
|
if (x != null) this.x = x;
|
||||||
if (y != null) this.y = y;
|
if (y != null) this.y = y;
|
||||||
|
@ -176,7 +178,7 @@ class SongMenuItem extends FlxSpriteGroup
|
||||||
* @param char The character ID used by this song.
|
* @param char The character ID used by this song.
|
||||||
* If the character has no freeplay icon, a warning will be thrown and nothing will display.
|
* If the character has no freeplay icon, a warning will be thrown and nothing will display.
|
||||||
*/
|
*/
|
||||||
public function setCharacter(char:String)
|
public function setCharacter(char:String):Void
|
||||||
{
|
{
|
||||||
var charPath:String = "freeplay/icons/";
|
var charPath:String = "freeplay/icons/";
|
||||||
|
|
||||||
|
@ -186,18 +188,18 @@ class SongMenuItem extends FlxSpriteGroup
|
||||||
// TODO: Also, can use CharacterDataParser.getCharPixelIconAsset()
|
// TODO: Also, can use CharacterDataParser.getCharPixelIconAsset()
|
||||||
switch (char)
|
switch (char)
|
||||||
{
|
{
|
||||||
case "monster-christmas":
|
case 'monster-christmas':
|
||||||
charPath += "monsterpixel";
|
charPath += 'monsterpixel';
|
||||||
case "mom-car":
|
case 'mom-car':
|
||||||
charPath += "mommypixel";
|
charPath += 'mommypixel';
|
||||||
case "dad":
|
case 'dad':
|
||||||
charPath += "daddypixel";
|
charPath += 'daddypixel';
|
||||||
case "darnell-blazin":
|
case 'darnell-blazin':
|
||||||
charPath += "darnellpixel";
|
charPath += 'darnellpixel';
|
||||||
case "senpai-angry":
|
case 'senpai-angry':
|
||||||
charPath += "senpaipixel";
|
charPath += 'senpaipixel';
|
||||||
default:
|
default:
|
||||||
charPath += char + "pixel";
|
charPath += '${char}pixel';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!openfl.utils.Assets.exists(Paths.image(charPath)))
|
if (!openfl.utils.Assets.exists(Paths.image(charPath)))
|
||||||
|
@ -211,7 +213,7 @@ class SongMenuItem extends FlxSpriteGroup
|
||||||
|
|
||||||
switch (char)
|
switch (char)
|
||||||
{
|
{
|
||||||
case "parents-christmas":
|
case 'parents-christmas':
|
||||||
pixelIcon.origin.x = 140;
|
pixelIcon.origin.x = 140;
|
||||||
default:
|
default:
|
||||||
pixelIcon.origin.x = 100;
|
pixelIcon.origin.x = 100;
|
||||||
|
@ -262,7 +264,7 @@ class SongMenuItem extends FlxSpriteGroup
|
||||||
|
|
||||||
var grpHide:FlxGroup;
|
var grpHide:FlxGroup;
|
||||||
|
|
||||||
public function forcePosition()
|
public function forcePosition():Void
|
||||||
{
|
{
|
||||||
visible = true;
|
visible = true;
|
||||||
capsule.alpha = 1;
|
capsule.alpha = 1;
|
||||||
|
@ -287,7 +289,7 @@ class SongMenuItem extends FlxSpriteGroup
|
||||||
setVisibleGrp(true);
|
setVisibleGrp(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
override function update(elapsed:Float)
|
override function update(elapsed:Float):Void
|
||||||
{
|
{
|
||||||
if (doJumpIn)
|
if (doJumpIn)
|
||||||
{
|
{
|
||||||
|
|
147
source/funkin/ui/options/FunkinSoundTray.hx
Normal file
147
source/funkin/ui/options/FunkinSoundTray.hx
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
package funkin.ui.options;
|
||||||
|
|
||||||
|
import flixel.system.ui.FlxSoundTray;
|
||||||
|
import flixel.tweens.FlxTween;
|
||||||
|
import flixel.system.FlxAssets;
|
||||||
|
import flixel.tweens.FlxEase;
|
||||||
|
import openfl.display.Bitmap;
|
||||||
|
import openfl.display.BitmapData;
|
||||||
|
import openfl.utils.Assets;
|
||||||
|
import funkin.util.MathUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extends the default flixel soundtray, but with some art
|
||||||
|
* and lil polish!
|
||||||
|
*
|
||||||
|
* Gets added to the game in Main.hx, right after FlxGame is new'd
|
||||||
|
* since it's a Sprite rather than Flixel related object
|
||||||
|
*/
|
||||||
|
class FunkinSoundTray extends FlxSoundTray
|
||||||
|
{
|
||||||
|
var graphicScale:Float = 0.30;
|
||||||
|
var lerpYPos:Float = 0;
|
||||||
|
|
||||||
|
var volumeMaxSound:String;
|
||||||
|
|
||||||
|
public function new()
|
||||||
|
{
|
||||||
|
// calls super, then removes all children to add our own
|
||||||
|
// graphics
|
||||||
|
super();
|
||||||
|
removeChildren();
|
||||||
|
|
||||||
|
var bg:Bitmap = new Bitmap(Assets.getBitmapData(Paths.image("soundtray/volumebox")));
|
||||||
|
bg.scaleX = graphicScale;
|
||||||
|
bg.scaleY = graphicScale;
|
||||||
|
addChild(bg);
|
||||||
|
|
||||||
|
y = -height;
|
||||||
|
visible = false;
|
||||||
|
|
||||||
|
// makes an alpha'd version of all the bars (bar_10.png)
|
||||||
|
var backingBar:Bitmap = new Bitmap(Assets.getBitmapData(Paths.image("soundtray/bars_10")));
|
||||||
|
backingBar.x = 10;
|
||||||
|
backingBar.y = 5;
|
||||||
|
backingBar.scaleX = graphicScale;
|
||||||
|
backingBar.scaleY = graphicScale;
|
||||||
|
addChild(backingBar);
|
||||||
|
backingBar.alpha = 0.4;
|
||||||
|
|
||||||
|
// clear the bars array entirely, it was initialized
|
||||||
|
// in the super class
|
||||||
|
_bars = [];
|
||||||
|
|
||||||
|
// 1...11 due to how block named the assets,
|
||||||
|
// we are trying to get assets bars_1-10
|
||||||
|
for (i in 1...11)
|
||||||
|
{
|
||||||
|
var bar:Bitmap = new Bitmap(Assets.getBitmapData(Paths.image("soundtray/bars_" + i)));
|
||||||
|
bar.x = 10;
|
||||||
|
bar.y = 5;
|
||||||
|
bar.scaleX = graphicScale;
|
||||||
|
bar.scaleY = graphicScale;
|
||||||
|
addChild(bar);
|
||||||
|
_bars.push(bar);
|
||||||
|
}
|
||||||
|
|
||||||
|
y = -height;
|
||||||
|
screenCenter();
|
||||||
|
|
||||||
|
volumeUpSound = Paths.sound("soundtray/Volup");
|
||||||
|
volumeDownSound = Paths.sound("soundtray/Voldown");
|
||||||
|
volumeMaxSound = Paths.sound("soundtray/VolMAX");
|
||||||
|
|
||||||
|
trace("Custom tray added!");
|
||||||
|
}
|
||||||
|
|
||||||
|
override public function update(MS:Float):Void
|
||||||
|
{
|
||||||
|
y = MathUtil.coolLerp(y, lerpYPos, 0.1);
|
||||||
|
|
||||||
|
// Animate sound tray thing
|
||||||
|
if (_timer > 0)
|
||||||
|
{
|
||||||
|
_timer -= (MS / 1000);
|
||||||
|
}
|
||||||
|
else if (y > -height)
|
||||||
|
{
|
||||||
|
lerpYPos = -height - 10;
|
||||||
|
|
||||||
|
if (y <= -height)
|
||||||
|
{
|
||||||
|
visible = false;
|
||||||
|
active = false;
|
||||||
|
|
||||||
|
#if FLX_SAVE
|
||||||
|
// Save sound preferences
|
||||||
|
if (FlxG.save.isBound)
|
||||||
|
{
|
||||||
|
FlxG.save.data.mute = FlxG.sound.muted;
|
||||||
|
FlxG.save.data.volume = FlxG.sound.volume;
|
||||||
|
FlxG.save.flush();
|
||||||
|
}
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes the little volume tray slide out.
|
||||||
|
*
|
||||||
|
* @param up Whether the volume is increasing.
|
||||||
|
*/
|
||||||
|
override public function show(up:Bool = false):Void
|
||||||
|
{
|
||||||
|
_timer = 1;
|
||||||
|
lerpYPos = 10;
|
||||||
|
visible = true;
|
||||||
|
active = true;
|
||||||
|
var globalVolume:Int = Math.round(FlxG.sound.volume * 10);
|
||||||
|
|
||||||
|
if (FlxG.sound.muted)
|
||||||
|
{
|
||||||
|
globalVolume = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!silent)
|
||||||
|
{
|
||||||
|
var sound = up ? volumeUpSound : volumeDownSound;
|
||||||
|
|
||||||
|
if (globalVolume == 10) sound = volumeMaxSound;
|
||||||
|
|
||||||
|
if (sound != null) FlxG.sound.load(sound).play();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i in 0..._bars.length)
|
||||||
|
{
|
||||||
|
if (i < globalVolume)
|
||||||
|
{
|
||||||
|
_bars[i].visible = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_bars[i].visible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,37 +1,33 @@
|
||||||
package funkin.ui.story;
|
package funkin.ui.story;
|
||||||
|
|
||||||
import funkin.ui.mainmenu.MainMenuState;
|
|
||||||
import funkin.save.Save;
|
|
||||||
import funkin.save.Save.SaveScoreData;
|
|
||||||
import openfl.utils.Assets;
|
|
||||||
import flixel.addons.transition.FlxTransitionableState;
|
import flixel.addons.transition.FlxTransitionableState;
|
||||||
import flixel.FlxSprite;
|
import flixel.FlxSprite;
|
||||||
import flixel.group.FlxGroup.FlxTypedGroup;
|
import flixel.group.FlxGroup.FlxTypedGroup;
|
||||||
import flixel.text.FlxText;
|
import flixel.text.FlxText;
|
||||||
import flixel.addons.transition.FlxTransitionableState;
|
|
||||||
import flixel.tweens.FlxEase;
|
import flixel.tweens.FlxEase;
|
||||||
import funkin.graphics.FunkinSprite;
|
|
||||||
import funkin.ui.MusicBeatState;
|
|
||||||
import flixel.tweens.FlxTween;
|
import flixel.tweens.FlxTween;
|
||||||
import flixel.util.FlxColor;
|
import flixel.util.FlxColor;
|
||||||
import flixel.util.FlxTimer;
|
import flixel.util.FlxTimer;
|
||||||
import funkin.data.level.LevelRegistry;
|
|
||||||
import funkin.audio.FunkinSound;
|
import funkin.audio.FunkinSound;
|
||||||
|
import funkin.data.level.LevelRegistry;
|
||||||
|
import funkin.data.song.SongRegistry;
|
||||||
|
import funkin.graphics.FunkinSprite;
|
||||||
import funkin.modding.events.ScriptEvent;
|
import funkin.modding.events.ScriptEvent;
|
||||||
import funkin.modding.events.ScriptEventDispatcher;
|
import funkin.modding.events.ScriptEventDispatcher;
|
||||||
import funkin.play.PlayState;
|
|
||||||
import funkin.play.PlayStatePlaylist;
|
import funkin.play.PlayStatePlaylist;
|
||||||
import funkin.ui.mainmenu.MainMenuState;
|
|
||||||
import funkin.play.song.Song;
|
import funkin.play.song.Song;
|
||||||
import funkin.data.song.SongData.SongMusicData;
|
import funkin.save.Save;
|
||||||
import funkin.data.song.SongRegistry;
|
import funkin.save.Save.SaveScoreData;
|
||||||
import funkin.util.MathUtil;
|
import funkin.ui.mainmenu.MainMenuState;
|
||||||
|
import funkin.ui.MusicBeatState;
|
||||||
import funkin.ui.transition.LoadingState;
|
import funkin.ui.transition.LoadingState;
|
||||||
import funkin.ui.transition.StickerSubState;
|
import funkin.ui.transition.StickerSubState;
|
||||||
|
import funkin.util.MathUtil;
|
||||||
|
import openfl.utils.Assets;
|
||||||
|
|
||||||
class StoryMenuState extends MusicBeatState
|
class StoryMenuState extends MusicBeatState
|
||||||
{
|
{
|
||||||
static final DEFAULT_BACKGROUND_COLOR:FlxColor = FlxColor.fromString("#F9CF51");
|
static final DEFAULT_BACKGROUND_COLOR:FlxColor = FlxColor.fromString('#F9CF51');
|
||||||
static final BACKGROUND_HEIGHT:Int = 400;
|
static final BACKGROUND_HEIGHT:Int = 400;
|
||||||
|
|
||||||
var currentDifficultyId:String = 'normal';
|
var currentDifficultyId:String = 'normal';
|
||||||
|
@ -166,25 +162,25 @@ class StoryMenuState extends MusicBeatState
|
||||||
updateProps();
|
updateProps();
|
||||||
|
|
||||||
tracklistText = new FlxText(FlxG.width * 0.05, levelBackground.x + levelBackground.height + 100, 0, "Tracks", 32);
|
tracklistText = new FlxText(FlxG.width * 0.05, levelBackground.x + levelBackground.height + 100, 0, "Tracks", 32);
|
||||||
tracklistText.setFormat("VCR OSD Mono", 32);
|
tracklistText.setFormat('VCR OSD Mono', 32);
|
||||||
tracklistText.alignment = CENTER;
|
tracklistText.alignment = CENTER;
|
||||||
tracklistText.color = 0xFFe55777;
|
tracklistText.color = 0xFFE55777;
|
||||||
add(tracklistText);
|
add(tracklistText);
|
||||||
|
|
||||||
scoreText = new FlxText(10, 10, 0, 'HIGH SCORE: 42069420');
|
scoreText = new FlxText(10, 10, 0, 'HIGH SCORE: 42069420');
|
||||||
scoreText.setFormat("VCR OSD Mono", 32);
|
scoreText.setFormat('VCR OSD Mono', 32);
|
||||||
scoreText.zIndex = 1000;
|
scoreText.zIndex = 1000;
|
||||||
add(scoreText);
|
add(scoreText);
|
||||||
|
|
||||||
modeText = new FlxText(10, 10, 0, 'Base Game Levels [TAB to switch]');
|
modeText = new FlxText(10, 10, 0, 'Base Game Levels [TAB to switch]');
|
||||||
modeText.setFormat("VCR OSD Mono", 32);
|
modeText.setFormat('VCR OSD Mono', 32);
|
||||||
modeText.screenCenter(X);
|
modeText.screenCenter(X);
|
||||||
modeText.visible = hasModdedLevels();
|
modeText.visible = hasModdedLevels();
|
||||||
modeText.zIndex = 1000;
|
modeText.zIndex = 1000;
|
||||||
add(modeText);
|
add(modeText);
|
||||||
|
|
||||||
levelTitleText = new FlxText(FlxG.width * 0.7, 10, 0, 'LEVEL 1');
|
levelTitleText = new FlxText(FlxG.width * 0.7, 10, 0, 'LEVEL 1');
|
||||||
levelTitleText.setFormat("VCR OSD Mono", 32, FlxColor.WHITE, RIGHT);
|
levelTitleText.setFormat('VCR OSD Mono', 32, FlxColor.WHITE, RIGHT);
|
||||||
levelTitleText.alpha = 0.7;
|
levelTitleText.alpha = 0.7;
|
||||||
levelTitleText.zIndex = 1000;
|
levelTitleText.zIndex = 1000;
|
||||||
add(levelTitleText);
|
add(levelTitleText);
|
||||||
|
@ -217,7 +213,7 @@ class StoryMenuState extends MusicBeatState
|
||||||
|
|
||||||
#if discord_rpc
|
#if discord_rpc
|
||||||
// Updating Discord Rich Presence
|
// Updating Discord Rich Presence
|
||||||
DiscordClient.changePresence("In the Menus", null);
|
DiscordClient.changePresence('In the Menus', null);
|
||||||
#end
|
#end
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -307,11 +303,11 @@ class StoryMenuState extends MusicBeatState
|
||||||
changeDifficulty(0);
|
changeDifficulty(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
override function update(elapsed:Float)
|
override function update(elapsed:Float):Void
|
||||||
{
|
{
|
||||||
Conductor.instance.update();
|
Conductor.instance.update();
|
||||||
|
|
||||||
highScoreLerp = Std.int(MathUtil.coolLerp(highScoreLerp, highScore, 0.5));
|
highScoreLerp = Std.int(MathUtil.smoothLerp(highScoreLerp, highScore, elapsed, 0.5));
|
||||||
|
|
||||||
scoreText.text = 'LEVEL SCORE: ${Math.round(highScoreLerp)}';
|
scoreText.text = 'LEVEL SCORE: ${Math.round(highScoreLerp)}';
|
||||||
|
|
||||||
|
@ -552,10 +548,13 @@ class StoryMenuState extends MusicBeatState
|
||||||
FlxTransitionableState.skipNextTransIn = false;
|
FlxTransitionableState.skipNextTransIn = false;
|
||||||
FlxTransitionableState.skipNextTransOut = false;
|
FlxTransitionableState.skipNextTransOut = false;
|
||||||
|
|
||||||
|
var targetVariation:String = targetSong.getFirstValidVariation(PlayStatePlaylist.campaignDifficulty);
|
||||||
|
|
||||||
LoadingState.loadPlayState(
|
LoadingState.loadPlayState(
|
||||||
{
|
{
|
||||||
targetSong: targetSong,
|
targetSong: targetSong,
|
||||||
targetDifficulty: PlayStatePlaylist.campaignDifficulty,
|
targetDifficulty: PlayStatePlaylist.campaignDifficulty,
|
||||||
|
targetVariation: targetVariation
|
||||||
}, true);
|
}, true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -223,6 +223,7 @@ class TitleState extends MusicBeatState
|
||||||
var shouldFadeIn = (FlxG.sound.music == null);
|
var shouldFadeIn = (FlxG.sound.music == null);
|
||||||
// Load music. Includes logic to handle BPM changes.
|
// Load music. Includes logic to handle BPM changes.
|
||||||
FunkinSound.playMusic('freakyMenu', false, true);
|
FunkinSound.playMusic('freakyMenu', false, true);
|
||||||
|
FlxG.sound.music.volume = 0;
|
||||||
// Fade from 0.0 to 0.7 over 4 seconds
|
// Fade from 0.0 to 0.7 over 4 seconds
|
||||||
if (shouldFadeIn) FlxG.sound.music.fadeIn(4, 0, 0.7);
|
if (shouldFadeIn) FlxG.sound.music.fadeIn(4, 0, 0.7);
|
||||||
}
|
}
|
||||||
|
|
|
@ -175,17 +175,22 @@ class Constants
|
||||||
/**
|
/**
|
||||||
* The default name for songs.
|
* The default name for songs.
|
||||||
*/
|
*/
|
||||||
public static final DEFAULT_SONGNAME:String = "Unknown";
|
public static final DEFAULT_SONGNAME:String = 'Unknown';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The default artist for songs.
|
* The default artist for songs.
|
||||||
*/
|
*/
|
||||||
public static final DEFAULT_ARTIST:String = "Unknown";
|
public static final DEFAULT_ARTIST:String = 'Unknown';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The default note style for songs.
|
* The default note style for songs.
|
||||||
*/
|
*/
|
||||||
public static final DEFAULT_NOTE_STYLE:String = "funkin";
|
public static final DEFAULT_NOTE_STYLE:String = 'funkin';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default album for songs in Freeplay.
|
||||||
|
*/
|
||||||
|
public static final DEFAULT_ALBUM_ID:String = 'volume1';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The default timing format for songs.
|
* The default timing format for songs.
|
||||||
|
|
|
@ -62,12 +62,22 @@ class MathUtil
|
||||||
* @param duration The total duration of the interpolation. Nominal duration until remaining distance is less than `precision`.
|
* @param duration The total duration of the interpolation. Nominal duration until remaining distance is less than `precision`.
|
||||||
* @param precision The target precision of the interpolation. Defaults to 1% of distance remaining.
|
* @param precision The target precision of the interpolation. Defaults to 1% of distance remaining.
|
||||||
* @see https://twitter.com/FreyaHolmer/status/1757918211679650262
|
* @see https://twitter.com/FreyaHolmer/status/1757918211679650262
|
||||||
|
*
|
||||||
|
* @return A value between the current value and the target value.
|
||||||
*/
|
*/
|
||||||
public static function smoothLerp(current:Float, target:Float, elapsed:Float, duration:Float, precision:Float = 1 / 100):Float
|
public static function smoothLerp(current:Float, target:Float, elapsed:Float, duration:Float, precision:Float = 1 / 100):Float
|
||||||
{
|
{
|
||||||
// var halfLife:Float = -duration / logBase(2, precision);
|
// var halfLife:Float = -duration / logBase(2, precision);
|
||||||
// lerp(current, target, 1 - exp2(-elapsed / halfLife));
|
// lerp(current, target, 1 - exp2(-elapsed / halfLife));
|
||||||
|
|
||||||
return lerp(current, target, 1 - Math.pow(precision, elapsed / duration));
|
if (current == target) return target;
|
||||||
|
|
||||||
|
var result:Float = lerp(current, target, 1 - Math.pow(precision, elapsed / duration));
|
||||||
|
|
||||||
|
// TODO: Is there a better way to ensure a lerp which actually reaches the target?
|
||||||
|
// Research a framerate-independent PID lerp.
|
||||||
|
if (Math.abs(result - target) < (precision * target)) result = target;
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue