mirror of
https://github.com/ninjamuffin99/Funkin.git
synced 2024-11-25 00:06:40 +00:00
First iteration of song playtesting from editor!
This commit is contained in:
parent
a6daf3b0d6
commit
5ff546bacc
234
source/flixel/addons/transition/FlxTransitionableSubState.hx
Normal file
234
source/flixel/addons/transition/FlxTransitionableSubState.hx
Normal file
|
@ -0,0 +1,234 @@
|
|||
package flixel.addons.transition;
|
||||
|
||||
import flixel.FlxSubState;
|
||||
import flixel.addons.transition.FlxTransitionableState;
|
||||
|
||||
/**
|
||||
* A `FlxSubState` which can perform visual transitions
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* First, extend `FlxTransitionableSubState` as ie, `FooState`.
|
||||
*
|
||||
* Method 1:
|
||||
*
|
||||
* ```haxe
|
||||
* var in:TransitionData = new TransitionData(...); // add your data where "..." is
|
||||
* var out:TransitionData = new TransitionData(...);
|
||||
*
|
||||
* FlxG.switchState(new FooState(in,out));
|
||||
* ```
|
||||
*
|
||||
* Method 2:
|
||||
*
|
||||
* ```haxe
|
||||
* FlxTransitionableSubState.defaultTransIn = new TransitionData(...);
|
||||
* FlxTransitionableSubState.defaultTransOut = new TransitionData(...);
|
||||
*
|
||||
* FlxG.switchState(new FooState());
|
||||
* ```
|
||||
*/
|
||||
class FlxTransitionableSubState extends FlxSubState
|
||||
{
|
||||
// global default transitions for ALL states, used if transIn/transOut are null
|
||||
public static var defaultTransIn(get, set):TransitionData;
|
||||
|
||||
static function get_defaultTransIn():TransitionData
|
||||
{
|
||||
return FlxTransitionableState.defaultTransIn;
|
||||
}
|
||||
|
||||
static function set_defaultTransIn(value:TransitionData):TransitionData
|
||||
{
|
||||
return FlxTransitionableState.defaultTransIn = value;
|
||||
}
|
||||
|
||||
public static var defaultTransOut(get, set):TransitionData;
|
||||
|
||||
static function get_defaultTransOut():TransitionData
|
||||
{
|
||||
return FlxTransitionableState.defaultTransOut;
|
||||
}
|
||||
|
||||
static function set_defaultTransOut(value:TransitionData):TransitionData
|
||||
{
|
||||
return FlxTransitionableState.defaultTransOut = value;
|
||||
}
|
||||
|
||||
public static var skipNextTransIn(get, set):Bool;
|
||||
|
||||
static function get_skipNextTransIn():Bool
|
||||
{
|
||||
return FlxTransitionableState.skipNextTransIn;
|
||||
}
|
||||
|
||||
static function set_skipNextTransIn(value:Bool):Bool
|
||||
{
|
||||
return FlxTransitionableState.skipNextTransIn = value;
|
||||
}
|
||||
|
||||
public static var skipNextTransOut(get, set):Bool;
|
||||
|
||||
static function get_skipNextTransOut():Bool
|
||||
{
|
||||
return FlxTransitionableState.skipNextTransOut;
|
||||
}
|
||||
|
||||
static function set_skipNextTransOut(value:Bool):Bool
|
||||
{
|
||||
return FlxTransitionableState.skipNextTransOut = value;
|
||||
}
|
||||
|
||||
// beginning & ending transitions for THIS state:
|
||||
public var transIn:TransitionData;
|
||||
public var transOut:TransitionData;
|
||||
|
||||
public var hasTransIn(get, never):Bool;
|
||||
public var hasTransOut(get, never):Bool;
|
||||
|
||||
/**
|
||||
* Create a state with the ability to do visual transitions
|
||||
* @param TransIn Plays when the state begins
|
||||
* @param TransOut Plays when the state ends
|
||||
*/
|
||||
public function new(?TransIn:TransitionData, ?TransOut:TransitionData)
|
||||
{
|
||||
transIn = TransIn;
|
||||
transOut = TransOut;
|
||||
|
||||
if (transIn == null && defaultTransIn != null)
|
||||
{
|
||||
transIn = defaultTransIn;
|
||||
}
|
||||
if (transOut == null && defaultTransOut != null)
|
||||
{
|
||||
transOut = defaultTransOut;
|
||||
}
|
||||
super();
|
||||
}
|
||||
|
||||
override function destroy():Void
|
||||
{
|
||||
super.destroy();
|
||||
transIn = null;
|
||||
transOut = null;
|
||||
_onExit = null;
|
||||
}
|
||||
|
||||
override function create():Void
|
||||
{
|
||||
super.create();
|
||||
transitionIn();
|
||||
}
|
||||
|
||||
override function startOutro(onOutroComplete:() -> Void)
|
||||
{
|
||||
if (!hasTransOut) onOutroComplete();
|
||||
else if (!_exiting)
|
||||
{
|
||||
// play the exit transition, and when it's done call FlxG.switchState
|
||||
_exiting = true;
|
||||
transitionOut(onOutroComplete);
|
||||
|
||||
if (skipNextTransOut)
|
||||
{
|
||||
skipNextTransOut = false;
|
||||
finishTransOut();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the in-transition. Can be called manually at any time.
|
||||
*/
|
||||
public function transitionIn():Void
|
||||
{
|
||||
if (transIn != null && transIn.type != NONE)
|
||||
{
|
||||
if (skipNextTransIn)
|
||||
{
|
||||
skipNextTransIn = false;
|
||||
if (finishTransIn != null)
|
||||
{
|
||||
finishTransIn();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var _trans = createTransition(transIn);
|
||||
|
||||
_trans.setStatus(FULL);
|
||||
openSubState(_trans);
|
||||
|
||||
_trans.finishCallback = finishTransIn;
|
||||
_trans.start(OUT);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the out-transition. Can be called manually at any time.
|
||||
*/
|
||||
public function transitionOut(?OnExit:Void->Void):Void
|
||||
{
|
||||
_onExit = OnExit;
|
||||
if (hasTransOut)
|
||||
{
|
||||
var _trans = createTransition(transOut);
|
||||
|
||||
_trans.setStatus(EMPTY);
|
||||
openSubState(_trans);
|
||||
|
||||
_trans.finishCallback = finishTransOut;
|
||||
_trans.start(IN);
|
||||
}
|
||||
else
|
||||
{
|
||||
_onExit();
|
||||
}
|
||||
}
|
||||
|
||||
var transOutFinished:Bool = false;
|
||||
|
||||
var _exiting:Bool = false;
|
||||
var _onExit:Void->Void;
|
||||
|
||||
function get_hasTransIn():Bool
|
||||
{
|
||||
return transIn != null && transIn.type != NONE;
|
||||
}
|
||||
|
||||
function get_hasTransOut():Bool
|
||||
{
|
||||
return transOut != null && transOut.type != NONE;
|
||||
}
|
||||
|
||||
function createTransition(data:TransitionData):Transition
|
||||
{
|
||||
return switch (data.type)
|
||||
{
|
||||
case TILES: new Transition(data);
|
||||
case FADE: new Transition(data);
|
||||
default: null;
|
||||
}
|
||||
}
|
||||
|
||||
function finishTransIn()
|
||||
{
|
||||
closeSubState();
|
||||
}
|
||||
|
||||
function finishTransOut()
|
||||
{
|
||||
transOutFinished = true;
|
||||
|
||||
if (!_exiting)
|
||||
{
|
||||
closeSubState();
|
||||
}
|
||||
|
||||
if (_onExit != null)
|
||||
{
|
||||
_onExit();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,7 +16,6 @@ import flixel.input.keyboard.FlxKey;
|
|||
import flixel.input.mouse.FlxMouseButton.FlxMouseButtonID;
|
||||
import flixel.math.FlxAngle;
|
||||
import flixel.math.FlxPoint;
|
||||
import flixel.ui.FlxVirtualPad;
|
||||
import flixel.util.FlxColor;
|
||||
import flixel.util.FlxTimer;
|
||||
import lime.ui.Haptic;
|
||||
|
|
|
@ -890,6 +890,13 @@ class FreeplayState extends MusicBeatSubState
|
|||
FlxG.sound.play(Paths.sound('confirmMenu'));
|
||||
dj.confirm();
|
||||
|
||||
if (targetSong != null)
|
||||
{
|
||||
// Load and cache the song's charts.
|
||||
// TODO: Do this in the loading state.
|
||||
targetSong.cacheCharts(true);
|
||||
}
|
||||
|
||||
new FlxTimer().start(1, function(tmr:FlxTimer) {
|
||||
LoadingState.loadAndSwitchState(new PlayState(
|
||||
{
|
||||
|
|
|
@ -12,7 +12,6 @@ import flixel.input.touch.FlxTouch;
|
|||
import flixel.text.FlxText;
|
||||
import flixel.tweens.FlxEase;
|
||||
import flixel.tweens.FlxTween;
|
||||
import flixel.ui.FlxButton;
|
||||
import flixel.util.FlxColor;
|
||||
import flixel.util.FlxTimer;
|
||||
import funkin.NGio;
|
||||
|
|
|
@ -3,7 +3,7 @@ package funkin;
|
|||
import funkin.modding.IScriptedClass.IEventHandler;
|
||||
import flixel.FlxState;
|
||||
import flixel.FlxSubState;
|
||||
import flixel.addons.ui.FlxUIState;
|
||||
import flixel.addons.transition.FlxTransitionableState;
|
||||
import flixel.text.FlxText;
|
||||
import flixel.util.FlxColor;
|
||||
import flixel.util.FlxSort;
|
||||
|
@ -16,7 +16,7 @@ import funkin.util.SortUtil;
|
|||
* MusicBeatState actually represents the core utility FlxState of the game.
|
||||
* It includes functionality for event handling, as well as maintaining BPM-based update events.
|
||||
*/
|
||||
class MusicBeatState extends FlxUIState implements IEventHandler
|
||||
class MusicBeatState extends FlxTransitionableState implements IEventHandler
|
||||
{
|
||||
var controls(get, never):Controls;
|
||||
|
||||
|
|
|
@ -1,24 +1,28 @@
|
|||
package funkin;
|
||||
|
||||
import flixel.addons.transition.FlxTransitionableSubState;
|
||||
import flixel.FlxSubState;
|
||||
import funkin.modding.IScriptedClass.IEventHandler;
|
||||
import flixel.text.FlxText;
|
||||
import flixel.util.FlxColor;
|
||||
import funkin.modding.events.ScriptEvent;
|
||||
import funkin.modding.IScriptedClass.IEventHandler;
|
||||
import funkin.modding.module.ModuleHandler;
|
||||
import flixel.text.FlxText;
|
||||
import funkin.modding.PolymodHandler;
|
||||
import funkin.util.SortUtil;
|
||||
import flixel.util.FlxSort;
|
||||
|
||||
/**
|
||||
* MusicBeatSubState reincorporates the functionality of MusicBeatState into an FlxSubState.
|
||||
*/
|
||||
class MusicBeatSubState extends FlxSubState implements IEventHandler
|
||||
class MusicBeatSubState extends FlxTransitionableSubState implements IEventHandler
|
||||
{
|
||||
public var leftWatermarkText:FlxText = null;
|
||||
public var rightWatermarkText:FlxText = null;
|
||||
|
||||
public function new(bgColor:FlxColor = FlxColor.TRANSPARENT)
|
||||
{
|
||||
super(bgColor);
|
||||
super();
|
||||
this.bgColor = bgColor;
|
||||
}
|
||||
|
||||
var controls(get, never):Controls;
|
||||
|
@ -67,6 +71,15 @@ class MusicBeatSubState extends FlxSubState implements IEventHandler
|
|||
FlxG.resetState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the state, by redoing the render order of all sprites.
|
||||
* It does this based on the `zIndex` of each prop.
|
||||
*/
|
||||
public function refresh()
|
||||
{
|
||||
sort(SortUtil.byZIndex, FlxSort.ASCENDING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a step is hit in the current song.
|
||||
* Continues outside of PlayState, for things like animations in menus.
|
||||
|
|
|
@ -16,14 +16,17 @@ class PauseSubState extends MusicBeatSubState
|
|||
{
|
||||
var grpMenuShit:FlxTypedGroup<Alphabet>;
|
||||
|
||||
var pauseOG:Array<String> = [
|
||||
var pauseOptionsBase:Array<String> = [
|
||||
'Resume',
|
||||
'Restart Song',
|
||||
'Change Difficulty',
|
||||
'Toggle Practice Mode',
|
||||
'Exit to Menu'
|
||||
];
|
||||
var difficultyChoices:Array<String> = ['EASY', 'NORMAL', 'HARD', 'ERECT', 'BACK'];
|
||||
|
||||
var pauseOptionsDifficulty:Array<String> = ['EASY', 'NORMAL', 'HARD', 'ERECT', 'BACK'];
|
||||
|
||||
var pauseOptionsCharting:Array<String> = ['Resume', 'Restart Song', 'Exit to Chart Editor'];
|
||||
|
||||
var menuItems:Array<String> = [];
|
||||
var curSelected:Int = 0;
|
||||
|
@ -36,11 +39,15 @@ class PauseSubState extends MusicBeatSubState
|
|||
var bg:FlxSprite;
|
||||
var metaDataGrp:FlxTypedGroup<FlxSprite>;
|
||||
|
||||
public function new()
|
||||
var isChartingMode:Bool;
|
||||
|
||||
public function new(?isChartingMode:Bool = false)
|
||||
{
|
||||
super();
|
||||
|
||||
menuItems = pauseOG;
|
||||
this.isChartingMode = isChartingMode;
|
||||
|
||||
menuItems = this.isChartingMode ? pauseOptionsCharting : pauseOptionsBase;
|
||||
|
||||
if (PlayStatePlaylist.campaignId == 'week6')
|
||||
{
|
||||
|
@ -180,14 +187,13 @@ class PauseSubState extends MusicBeatSubState
|
|||
{
|
||||
var daSelected:String = menuItems[curSelected];
|
||||
|
||||
// TODO: Why is this based on the menu item's name? Make this an enum or something.
|
||||
switch (daSelected)
|
||||
{
|
||||
case 'Resume':
|
||||
close();
|
||||
|
||||
case 'Change Difficulty':
|
||||
menuItems = difficultyChoices;
|
||||
menuItems = pauseOptionsDifficulty;
|
||||
regenMenu();
|
||||
|
||||
case 'EASY' | 'NORMAL' | 'HARD' | 'ERECT':
|
||||
|
@ -199,7 +205,7 @@ class PauseSubState extends MusicBeatSubState
|
|||
|
||||
close();
|
||||
case 'BACK':
|
||||
menuItems = pauseOG;
|
||||
menuItems = this.isChartingMode ? pauseOptionsCharting : pauseOptionsBase;
|
||||
regenMenu();
|
||||
|
||||
case 'Toggle Practice Mode':
|
||||
|
@ -226,6 +232,11 @@ class PauseSubState extends MusicBeatSubState
|
|||
if (PlayStatePlaylist.isStoryMode) openSubState(new funkin.ui.StickerSubState(null, STORY));
|
||||
else
|
||||
openSubState(new funkin.ui.StickerSubState(null, FREEPLAY));
|
||||
|
||||
case 'Exit to Chart Editor':
|
||||
this.close();
|
||||
if (FlxG.sound.music != null) FlxG.sound.music.stop();
|
||||
PlayState.instance.close(); // This only works because PlayState is a substate!
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package funkin.play;
|
||||
|
||||
import funkin.ui.debug.charting.ChartEditorState;
|
||||
import haxe.Int64;
|
||||
import funkin.play.notes.notestyle.NoteStyle;
|
||||
import funkin.data.notestyle.NoteStyleData;
|
||||
|
@ -77,12 +78,23 @@ typedef PlayStateParams =
|
|||
* @default `bf`, or the first character in the song's character list.
|
||||
*/
|
||||
?targetCharacter:String,
|
||||
/**
|
||||
* Whether the song should start in Practice Mode.
|
||||
* @default `false`
|
||||
*/
|
||||
?practiceMode:Bool,
|
||||
/**
|
||||
* Whether the song should be in minimal mode.
|
||||
* @default `false`
|
||||
*/
|
||||
?minimalMode:Bool,
|
||||
}
|
||||
|
||||
/**
|
||||
* The gameplay state, where all the rhythm gaming happens.
|
||||
* SubState so it can be loaded as a child of the chart editor.
|
||||
*/
|
||||
class PlayState extends MusicBeatState
|
||||
class PlayState extends MusicBeatSubState
|
||||
{
|
||||
/**
|
||||
* STATIC VARIABLES
|
||||
|
@ -209,6 +221,11 @@ class PlayState extends MusicBeatState
|
|||
*/
|
||||
public var isPracticeMode:Bool = false;
|
||||
|
||||
/**
|
||||
* In Minimal Mode, the stage and characters are not loaded and a standard background is used.
|
||||
*/
|
||||
public var isMinimalMode:Bool = false;
|
||||
|
||||
/**
|
||||
* Whether the game is currently in an animated cutscene, and gameplay should be stopped.
|
||||
*/
|
||||
|
@ -219,6 +236,20 @@ class PlayState extends MusicBeatState
|
|||
*/
|
||||
public var disableKeys:Bool = false;
|
||||
|
||||
public var isSubState(get, null):Bool;
|
||||
|
||||
function get_isSubState():Bool
|
||||
{
|
||||
return this._parentState != null;
|
||||
}
|
||||
|
||||
public var isChartingMode(get, null):Bool;
|
||||
|
||||
function get_isChartingMode():Bool
|
||||
{
|
||||
return this._parentState != null && Std.isOfType(this._parentState, ChartEditorState);
|
||||
}
|
||||
|
||||
/**
|
||||
* The current dialogue.
|
||||
*/
|
||||
|
@ -438,6 +469,8 @@ class PlayState extends MusicBeatState
|
|||
currentSong = params.targetSong;
|
||||
if (params.targetDifficulty != null) currentDifficulty = params.targetDifficulty;
|
||||
if (params.targetCharacter != null) currentPlayerId = params.targetCharacter;
|
||||
isPracticeMode = params.practiceMode ?? false;
|
||||
isMinimalMode = params.minimalMode ?? false;
|
||||
|
||||
// Don't do anything else here! Wait until create() when we attach to the camera.
|
||||
}
|
||||
|
@ -458,13 +491,6 @@ class PlayState extends MusicBeatState
|
|||
|
||||
NoteSplash.buildSplashFrames();
|
||||
|
||||
if (currentSong != null)
|
||||
{
|
||||
// Load and cache the song's charts.
|
||||
// TODO: Do this in the loading state.
|
||||
currentSong.cacheCharts(true);
|
||||
}
|
||||
|
||||
// Returns null if the song failed to load or doesn't have the selected difficulty.
|
||||
if (currentSong == null || currentChart == null)
|
||||
{
|
||||
|
@ -490,7 +516,14 @@ class PlayState extends MusicBeatState
|
|||
lime.app.Application.current.window.alert(message, 'Error loading PlayState');
|
||||
|
||||
// Force the user back to the main menu.
|
||||
FlxG.switchState(new MainMenuState());
|
||||
if (isSubState)
|
||||
{
|
||||
this.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
FlxG.switchState(new MainMenuState());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -532,8 +565,15 @@ class PlayState extends MusicBeatState
|
|||
// The song is now loaded. We can continue to initialize the play state.
|
||||
initCameras();
|
||||
initHealthBar();
|
||||
initStage();
|
||||
initCharacters();
|
||||
if (!isMinimalMode)
|
||||
{
|
||||
initStage();
|
||||
initCharacters();
|
||||
}
|
||||
else
|
||||
{
|
||||
initMinimalMode();
|
||||
}
|
||||
initStrumlines();
|
||||
|
||||
// Initialize the judgements and combo meter.
|
||||
|
@ -706,7 +746,7 @@ class PlayState extends MusicBeatState
|
|||
// There is a 1/1000 change to use a special pause menu.
|
||||
// This prevents the player from resuming, but that's the point.
|
||||
// It's a reference to Gitaroo Man, which doesn't let you pause the game.
|
||||
if (event.gitaroo)
|
||||
if (!isSubState && event.gitaroo)
|
||||
{
|
||||
FlxG.switchState(new GitarooPause(
|
||||
{
|
||||
|
@ -725,7 +765,7 @@ class PlayState extends MusicBeatState
|
|||
boyfriendPos = currentStage.getBoyfriend().getScreenPosition();
|
||||
}
|
||||
|
||||
var pauseSubState:FlxSubState = new PauseSubState();
|
||||
var pauseSubState:FlxSubState = new PauseSubState(isChartingMode);
|
||||
|
||||
openSubState(pauseSubState);
|
||||
pauseSubState.camera = camHUD;
|
||||
|
@ -1202,6 +1242,19 @@ class PlayState extends MusicBeatState
|
|||
loadStage(currentStageId);
|
||||
}
|
||||
|
||||
function initMinimalMode():Void
|
||||
{
|
||||
// Create the green background.
|
||||
var menuBG = new FlxSprite().loadGraphic(Paths.image('menuDesat'));
|
||||
menuBG.color = 0xFF4CAF50;
|
||||
menuBG.setGraphicSize(Std.int(menuBG.width * 1.1));
|
||||
menuBG.updateHitbox();
|
||||
menuBG.screenCenter();
|
||||
menuBG.scrollFactor.set(0, 0);
|
||||
menuBG.zIndex = -1000;
|
||||
add(menuBG);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads stage data from cache, assembles the props,
|
||||
* and adds it to the state.
|
||||
|
@ -2132,6 +2185,7 @@ class PlayState extends MusicBeatState
|
|||
if (FlxG.keys.justPressed.H) camHUD.visible = !camHUD.visible;
|
||||
#end
|
||||
|
||||
// Eject button
|
||||
if (FlxG.keys.justPressed.F4) FlxG.switchState(new MainMenuState());
|
||||
|
||||
if (FlxG.keys.justPressed.F5) debug_refreshModules();
|
||||
|
@ -2163,7 +2217,10 @@ class PlayState extends MusicBeatState
|
|||
}
|
||||
|
||||
// 8: Move to the offset editor.
|
||||
if (FlxG.keys.justPressed.EIGHT) FlxG.switchState(new funkin.ui.animDebugShit.DebugBoundingState());
|
||||
if (FlxG.keys.justPressed.EIGHT)
|
||||
{
|
||||
lime.app.Application.current.window.alert("Press ~ on the main menu to get to the editor", 'LOL');
|
||||
}
|
||||
|
||||
// 9: Toggle the old icon.
|
||||
if (FlxG.keys.justPressed.NINE) iconP1.toggleOldIcon();
|
||||
|
@ -2384,7 +2441,14 @@ class PlayState extends MusicBeatState
|
|||
// FlxG.save.data.weekUnlocked = StoryMenuState.weekUnlocked;
|
||||
FlxG.save.flush();
|
||||
|
||||
moveToResultsScreen();
|
||||
if (isSubState)
|
||||
{
|
||||
this.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
moveToResultsScreen();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -2438,10 +2502,23 @@ class PlayState extends MusicBeatState
|
|||
}
|
||||
else
|
||||
{
|
||||
moveToResultsScreen();
|
||||
if (isSubState)
|
||||
{
|
||||
this.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
moveToResultsScreen();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override function close():Void
|
||||
{
|
||||
performCleanup();
|
||||
super.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform necessary cleanup before leaving the PlayState.
|
||||
*/
|
||||
|
@ -2552,6 +2629,7 @@ class PlayState extends MusicBeatState
|
|||
FlxG.camera.follow(cameraFollowPoint, LOCKON, 0.04);
|
||||
FlxG.camera.targetOffset.set();
|
||||
FlxG.camera.zoom = defaultCameraZoom;
|
||||
// Snap the camera to the follow point immediately.
|
||||
FlxG.camera.focusOn(cameraFollowPoint.getPosition());
|
||||
}
|
||||
|
||||
|
|
|
@ -56,6 +56,32 @@ class Song implements IPlayStateScriptedClass
|
|||
populateFromMetadata();
|
||||
}
|
||||
|
||||
@:allow(funkin.play.song.Song)
|
||||
public static function buildRaw(songId:String, metadata:Array<SongMetadata>, variations:Array<String>, charts:Map<String, SongChartData>,
|
||||
?validScore:Bool = false):Song
|
||||
{
|
||||
var result:Song = new Song(songId);
|
||||
|
||||
result._metadata.clear();
|
||||
for (meta in metadata)
|
||||
result._metadata.push(meta);
|
||||
|
||||
result.variations.clear();
|
||||
for (vari in variations)
|
||||
result.variations.push(vari);
|
||||
|
||||
result.difficultyIds.clear();
|
||||
|
||||
result.populateFromMetadata();
|
||||
|
||||
for (variation => chartData in charts)
|
||||
result.applyChartData(chartData, variation);
|
||||
|
||||
result.validScore = validScore;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public function getRawMetadata():Array<SongMetadata>
|
||||
{
|
||||
return _metadata;
|
||||
|
@ -119,28 +145,33 @@ class Song implements IPlayStateScriptedClass
|
|||
for (variation in variations)
|
||||
{
|
||||
var chartData:SongChartData = SongDataParser.parseSongChartData(songId, variation);
|
||||
var chartNotes = chartData.notes;
|
||||
|
||||
for (diffId in chartNotes.keys())
|
||||
{
|
||||
// Retrieve the cached difficulty data.
|
||||
var difficulty:Null<SongDifficulty> = difficulties.get(diffId);
|
||||
if (difficulty == null)
|
||||
{
|
||||
trace('Fabricated new difficulty for $diffId.');
|
||||
difficulty = new SongDifficulty(this, diffId, variation);
|
||||
difficulties.set(diffId, difficulty);
|
||||
}
|
||||
// Add the chart data to the difficulty.
|
||||
difficulty.notes = chartData.notes.get(diffId);
|
||||
difficulty.scrollSpeed = chartData.getScrollSpeed(diffId);
|
||||
|
||||
difficulty.events = chartData.events;
|
||||
}
|
||||
applyChartData(chartData, variation);
|
||||
}
|
||||
trace('Done caching charts.');
|
||||
}
|
||||
|
||||
function applyChartData(chartData:SongChartData, variation:String):Void
|
||||
{
|
||||
var chartNotes = chartData.notes;
|
||||
|
||||
for (diffId in chartNotes.keys())
|
||||
{
|
||||
// Retrieve the cached difficulty data.
|
||||
var difficulty:Null<SongDifficulty> = difficulties.get(diffId);
|
||||
if (difficulty == null)
|
||||
{
|
||||
trace('Fabricated new difficulty for $diffId.');
|
||||
difficulty = new SongDifficulty(this, diffId, variation);
|
||||
difficulties.set(diffId, difficulty);
|
||||
}
|
||||
// Add the chart data to the difficulty.
|
||||
difficulty.notes = chartData.notes.get(diffId);
|
||||
difficulty.scrollSpeed = chartData.getScrollSpeed(diffId);
|
||||
|
||||
difficulty.events = chartData.events;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the metadata for a specific difficulty, including the chart if it is loaded.
|
||||
* @param diffId The difficulty ID, such as `easy` or `hard`.
|
||||
|
|
|
@ -202,7 +202,7 @@ class SongDataParser
|
|||
|
||||
static function loadMusicMetadataFile(musicPath:String, variation:String = ''):String
|
||||
{
|
||||
var musicMetadataFilePath:String = (variation != '') ? Paths.file('$MUSIC_DATA_PATH$musicPath/$musicPath-metadata-$variation.json') : Paths.file('$MUSIC_DATA_PATH$musicPath/$musicPath-metadata.json');
|
||||
var musicMetadataFilePath:String = (variation != '' || variation == "default") ? Paths.file('$MUSIC_DATA_PATH$musicPath/$musicPath-metadata-$variation.json') : Paths.file('$MUSIC_DATA_PATH$musicPath/$musicPath-metadata.json');
|
||||
|
||||
var rawJson:String = Assets.getText(musicMetadataFilePath).trim();
|
||||
|
||||
|
@ -238,7 +238,7 @@ class SongDataParser
|
|||
|
||||
static function loadSongChartDataFile(songPath:String, variation:String = ''):String
|
||||
{
|
||||
var songChartDataFilePath:String = (variation != '') ? Paths.json('$SONG_DATA_PATH$songPath/$songPath-chart-$variation') : Paths.json('$SONG_DATA_PATH$songPath/$songPath-chart');
|
||||
var songChartDataFilePath:String = (variation != '' || variation == 'default') ? Paths.json('$SONG_DATA_PATH$songPath/$songPath-chart-$variation') : Paths.json('$SONG_DATA_PATH$songPath/$songPath-chart');
|
||||
|
||||
var rawJson:String = Assets.getText(songChartDataFilePath).trim();
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package funkin.ui.debug;
|
||||
|
||||
import flixel.math.FlxPoint;
|
||||
import flixel.FlxObject;
|
||||
import flixel.FlxSprite;
|
||||
import funkin.MusicBeatSubState;
|
||||
|
@ -48,6 +49,9 @@ class DebugMenuSubState extends MusicBeatSubState
|
|||
createItem("ANIMATION EDITOR", openAnimationEditor);
|
||||
createItem("STAGE EDITOR", openStageEditor);
|
||||
createItem("TEST STICKERS", testStickers);
|
||||
|
||||
FlxG.camera.focusOn(new FlxPoint(camFocusPoint.x, camFocusPoint.y));
|
||||
FlxG.camera.focusOn(new FlxPoint(camFocusPoint.x, camFocusPoint.y + 500));
|
||||
}
|
||||
|
||||
function onMenuChange(selected:TextMenuItem)
|
||||
|
|
|
@ -580,6 +580,8 @@ class ChartEditorDialogHandler
|
|||
state.isHaxeUIDialogOpen = false;
|
||||
};
|
||||
|
||||
dialog.zIndex = 1000;
|
||||
|
||||
return dialog;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,9 @@ package funkin.ui.debug.charting;
|
|||
|
||||
import flixel.addons.display.FlxSliceSprite;
|
||||
import flixel.addons.display.FlxTiledSprite;
|
||||
import flixel.FlxCamera;
|
||||
import flixel.FlxSprite;
|
||||
import flixel.FlxSubState;
|
||||
import flixel.group.FlxSpriteGroup;
|
||||
import flixel.input.keyboard.FlxKey;
|
||||
import flixel.math.FlxPoint;
|
||||
|
@ -20,6 +22,7 @@ import funkin.modding.events.ScriptEvent;
|
|||
import funkin.play.HealthIcon;
|
||||
import funkin.play.notes.NoteSprite;
|
||||
import funkin.play.notes.Strumline;
|
||||
import funkin.play.PlayState;
|
||||
import funkin.play.song.Song;
|
||||
import funkin.play.song.SongData.SongChartData;
|
||||
import funkin.play.song.SongData.SongDataParser;
|
||||
|
@ -977,6 +980,13 @@ class ChartEditorState extends HaxeUIState
|
|||
|
||||
override function create():Void
|
||||
{
|
||||
// super.create() must be called first, the HaxeUI components get created here.
|
||||
super.create();
|
||||
// Set the z-index of the HaxeUI.
|
||||
this.component.zIndex = 100;
|
||||
|
||||
fixCamera();
|
||||
|
||||
// Get rid of any music from the previous state.
|
||||
FlxG.sound.music.stop();
|
||||
|
||||
|
@ -989,8 +999,6 @@ class ChartEditorState extends HaxeUIState
|
|||
buildGrid();
|
||||
buildSelectionBox();
|
||||
|
||||
// Add the HaxeUI components after the grid so they're on top.
|
||||
super.create();
|
||||
buildAdditionalUI();
|
||||
|
||||
// Setup the onClick listeners for the UI after it's been created.
|
||||
|
@ -999,6 +1007,8 @@ class ChartEditorState extends HaxeUIState
|
|||
|
||||
setupAutoSave();
|
||||
|
||||
refresh();
|
||||
|
||||
ChartEditorDialogHandler.openWelcomeDialog(this, false);
|
||||
}
|
||||
|
||||
|
@ -1028,6 +1038,7 @@ class ChartEditorState extends HaxeUIState
|
|||
menuBG.updateHitbox();
|
||||
menuBG.screenCenter();
|
||||
menuBG.scrollFactor.set(0, 0);
|
||||
menuBG.zIndex = -100;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1039,28 +1050,33 @@ class ChartEditorState extends HaxeUIState
|
|||
gridTiledSprite.x = FlxG.width / 2 - GRID_SIZE * STRUMLINE_SIZE; // Center the grid.
|
||||
gridTiledSprite.y = MENU_BAR_HEIGHT + GRID_TOP_PAD; // Push down to account for the menu bar.
|
||||
add(gridTiledSprite);
|
||||
gridTiledSprite.zIndex = 10;
|
||||
|
||||
gridGhostNote = new ChartEditorNoteSprite(this);
|
||||
gridGhostNote.alpha = 0.6;
|
||||
gridGhostNote.noteData = new SongNoteData(0, 0, 0, "");
|
||||
gridGhostNote.visible = false;
|
||||
add(gridGhostNote);
|
||||
gridGhostNote.zIndex = 11;
|
||||
|
||||
gridGhostEvent = new ChartEditorEventSprite(this);
|
||||
gridGhostEvent.alpha = 0.6;
|
||||
gridGhostEvent.eventData = new SongEventData(-1, "", {});
|
||||
gridGhostEvent.visible = false;
|
||||
add(gridGhostEvent);
|
||||
gridGhostEvent.zIndex = 12;
|
||||
|
||||
buildNoteGroup();
|
||||
|
||||
gridPlayheadScrollArea = new FlxSprite(gridTiledSprite.x - PLAYHEAD_SCROLL_AREA_WIDTH,
|
||||
MENU_BAR_HEIGHT).makeGraphic(PLAYHEAD_SCROLL_AREA_WIDTH, FlxG.height - MENU_BAR_HEIGHT, PLAYHEAD_SCROLL_AREA_COLOR);
|
||||
add(gridPlayheadScrollArea);
|
||||
gridPlayheadScrollArea.zIndex = 25;
|
||||
|
||||
// The playhead that show the current position in the song.
|
||||
gridPlayhead = new FlxSpriteGroup();
|
||||
add(gridPlayhead);
|
||||
gridPlayhead.zIndex = 30;
|
||||
|
||||
var playheadWidth = GRID_SIZE * (STRUMLINE_SIZE * 2 + 1) + (PLAYHEAD_SCROLL_AREA_WIDTH * 2);
|
||||
var playheadBaseYPos = MENU_BAR_HEIGHT + GRID_TOP_PAD;
|
||||
|
@ -1082,6 +1098,7 @@ class ChartEditorState extends HaxeUIState
|
|||
healthIconDad.x = gridTiledSprite.x - 15 - (HealthIcon.HEALTH_ICON_SIZE * 0.5);
|
||||
healthIconDad.y = gridTiledSprite.y + 5;
|
||||
add(healthIconDad);
|
||||
healthIconDad.zIndex = 30;
|
||||
|
||||
healthIconBF = new HealthIcon('bf');
|
||||
healthIconBF.autoUpdate = false;
|
||||
|
@ -1090,12 +1107,14 @@ class ChartEditorState extends HaxeUIState
|
|||
healthIconBF.y = gridTiledSprite.y + 5;
|
||||
healthIconBF.flipX = true;
|
||||
add(healthIconBF);
|
||||
healthIconBF.zIndex = 30;
|
||||
}
|
||||
|
||||
function buildSelectionBox():Void
|
||||
{
|
||||
selectionBoxSprite.scrollFactor.set(0, 0);
|
||||
add(selectionBoxSprite);
|
||||
selectionBoxSprite.zIndex = 30;
|
||||
|
||||
setSelectionBoxBounds();
|
||||
}
|
||||
|
@ -1140,18 +1159,22 @@ class ChartEditorState extends HaxeUIState
|
|||
renderedHoldNotes = new FlxTypedSpriteGroup<ChartEditorHoldNoteSprite>();
|
||||
renderedHoldNotes.setPosition(gridTiledSprite.x, gridTiledSprite.y);
|
||||
add(renderedHoldNotes);
|
||||
renderedHoldNotes.zIndex = 24;
|
||||
|
||||
renderedNotes = new FlxTypedSpriteGroup<ChartEditorNoteSprite>();
|
||||
renderedNotes.setPosition(gridTiledSprite.x, gridTiledSprite.y);
|
||||
add(renderedNotes);
|
||||
renderedNotes.zIndex = 25;
|
||||
|
||||
renderedEvents = new FlxTypedSpriteGroup<ChartEditorEventSprite>();
|
||||
renderedEvents.setPosition(gridTiledSprite.x, gridTiledSprite.y);
|
||||
add(renderedEvents);
|
||||
renderedNotes.zIndex = 25;
|
||||
|
||||
renderedSelectionSquares = new FlxTypedSpriteGroup<FlxSprite>();
|
||||
renderedSelectionSquares.setPosition(gridTiledSprite.x, gridTiledSprite.y);
|
||||
add(renderedSelectionSquares);
|
||||
renderedNotes.zIndex = 26;
|
||||
}
|
||||
|
||||
var playbarHeadLayout:Component;
|
||||
|
@ -1159,6 +1182,7 @@ class ChartEditorState extends HaxeUIState
|
|||
function buildAdditionalUI():Void
|
||||
{
|
||||
playbarHeadLayout = buildComponent(CHART_EDITOR_PLAYBARHEAD_LAYOUT);
|
||||
playbarHeadLayout.zIndex = 110;
|
||||
|
||||
playbarHeadLayout.width = FlxG.width - 8;
|
||||
playbarHeadLayout.height = 10;
|
||||
|
@ -1271,6 +1295,9 @@ class ChartEditorState extends HaxeUIState
|
|||
// addUIClickListener('menubarItemSelectBeforeCursor', _ -> doSomething());
|
||||
// addUIClickListener('menubarItemSelectAfterCursor', _ -> doSomething());
|
||||
|
||||
addUIClickListener('menubarItemPlaytestFull', _ -> testSongInPlayState(false));
|
||||
addUIClickListener('menubarItemPlaytestMinimal', _ -> testSongInPlayState(true));
|
||||
|
||||
addUIClickListener('menubarItemAbout', _ -> ChartEditorDialogHandler.openAboutDialog(this));
|
||||
|
||||
addUIClickListener('menubarItemUserGuide', _ -> ChartEditorDialogHandler.openUserGuideDialog(this));
|
||||
|
@ -1423,6 +1450,7 @@ class ChartEditorState extends HaxeUIState
|
|||
handleFileKeybinds();
|
||||
handleEditKeybinds();
|
||||
handleViewKeybinds();
|
||||
handleTestKeybinds();
|
||||
handleHelpKeybinds();
|
||||
|
||||
// DEBUG
|
||||
|
@ -2536,6 +2564,18 @@ class ChartEditorState extends HaxeUIState
|
|||
*/
|
||||
function handleViewKeybinds():Void {}
|
||||
|
||||
/**
|
||||
* Handle keybinds for the Test menu items.
|
||||
*/
|
||||
function handleTestKeybinds():Void
|
||||
{
|
||||
if (FlxG.keys.justPressed.ENTER)
|
||||
{
|
||||
var minimal = FlxG.keys.pressed.SHIFT;
|
||||
testSongInPlayState(minimal);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle keybinds for Help menu items.
|
||||
*/
|
||||
|
@ -2907,9 +2947,9 @@ class ChartEditorState extends HaxeUIState
|
|||
|
||||
function startAudioPlayback():Void
|
||||
{
|
||||
if (audioInstTrack != null) audioInstTrack.play();
|
||||
if (audioVocalTrackGroup != null) audioVocalTrackGroup.play();
|
||||
if (audioVocalTrackGroup != null) audioVocalTrackGroup.play();
|
||||
if (audioInstTrack != null) audioInstTrack.play(false, audioInstTrack.time);
|
||||
if (audioVocalTrackGroup != null) audioVocalTrackGroup.play(false, audioInstTrack.time);
|
||||
if (audioVocalTrackGroup != null) audioVocalTrackGroup.play(false, audioInstTrack.time);
|
||||
|
||||
setComponentText('playbarPlay', '||');
|
||||
}
|
||||
|
@ -3004,6 +3044,34 @@ class ChartEditorState extends HaxeUIState
|
|||
return this.scrollPositionInPixels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transitions to the Play State to test the song
|
||||
*/
|
||||
public function testSongInPlayState(?minimal:Bool = false):Void
|
||||
{
|
||||
var targetSong:Song = Song.buildRaw(currentSongId, songMetadata.values(), availableVariations, songChartData, false);
|
||||
|
||||
subStateClosed.add(fixCamera);
|
||||
|
||||
openSubState(new PlayState(
|
||||
{
|
||||
targetSong: targetSong,
|
||||
targetDifficulty: selectedDifficulty,
|
||||
// TODO: Add this.
|
||||
// targetCharacter: targetCharacter,
|
||||
practiceMode: true,
|
||||
minimalMode: minimal,
|
||||
}));
|
||||
}
|
||||
|
||||
function fixCamera(_:FlxSubState = null):Void
|
||||
{
|
||||
FlxG.cameras.reset(new FlxCamera());
|
||||
FlxG.camera.focusOn(new FlxPoint(FlxG.width / 2, FlxG.height / 2));
|
||||
|
||||
add(this.component);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads an instrumental from an absolute file path, replacing the current instrumental.
|
||||
*
|
||||
|
|
|
@ -513,6 +513,13 @@ class StoryMenuState extends MusicBeatState
|
|||
PlayStatePlaylist.campaignId = currentLevel.id;
|
||||
PlayStatePlaylist.campaignTitle = currentLevel.getTitle();
|
||||
|
||||
if (targetSong != null)
|
||||
{
|
||||
// Load and cache the song's charts.
|
||||
// TODO: Do this in the loading state.
|
||||
targetSong.cacheCharts(true);
|
||||
}
|
||||
|
||||
new FlxTimer().start(1, function(tmr:FlxTimer) {
|
||||
LoadingState.loadAndSwitchState(new PlayState(
|
||||
{
|
||||
|
|
|
@ -37,4 +37,15 @@ class ArrayTools
|
|||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all elements from the array, without creating a new array.
|
||||
* @param array The array to clear.
|
||||
*/
|
||||
public static function clear<T>(array:Array<T>):Void
|
||||
{
|
||||
// This method is faster than array.splice(0, array.length)
|
||||
while (array.length > 0)
|
||||
array.pop();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue