Funkin/source/funkin/InitState.hx

364 lines
11 KiB
Haxe
Raw Normal View History

package funkin;
import funkin.ui.debug.charting.ChartEditorState;
import funkin.ui.transition.LoadingState;
import flixel.FlxState;
import flixel.addons.transition.FlxTransitionableState;
2023-06-16 17:37:56 -04:00
import flixel.addons.transition.FlxTransitionSprite.GraphicTransTileDiamond;
import flixel.addons.transition.TransitionData;
import flixel.graphics.FlxGraphic;
import flixel.math.FlxPoint;
import flixel.math.FlxRect;
import flixel.FlxSprite;
2023-01-06 03:21:54 -05:00
import flixel.system.debug.log.LogStyle;
import flixel.util.FlxColor;
import funkin.ui.options.PreferencesMenu;
2023-01-06 03:21:54 -05:00
import funkin.util.macro.MacroUtil;
2023-06-16 17:37:56 -04:00
import funkin.util.WindowUtil;
import funkin.play.PlayStatePlaylist;
2022-03-25 17:56:47 -04:00
import openfl.display.BitmapData;
import funkin.data.level.LevelRegistry;
import funkin.data.notestyle.NoteStyleRegistry;
import funkin.data.event.SongEventRegistry;
import funkin.data.stage.StageRegistry;
import funkin.data.dialogue.ConversationRegistry;
import funkin.data.dialogue.DialogueBoxRegistry;
import funkin.data.dialogue.SpeakerRegistry;
import funkin.data.song.SongRegistry;
import funkin.play.character.CharacterData.CharacterDataParser;
import funkin.modding.module.ModuleHandler;
2023-08-08 15:41:25 -04:00
import funkin.ui.title.TitleState;
import funkin.util.CLIUtil;
import funkin.util.CLIUtil.CLIParams;
import funkin.ui.transition.LoadingState;
#if discord_rpc
import Discord.DiscordClient;
#end
2022-02-23 16:49:54 -05:00
/**
* A core class which performs initialization of the game.
* The initialization state has several functions:
* - Calls code to set up the game, including loading saves and parsing game data.
* - Chooses whether to start via debug or via launching normally.
*
* It should not contain any sprites or rendering.
2022-02-23 16:49:54 -05:00
*/
class InitState extends FlxState
{
/**
* Perform a bunch of game setup, then immediately transition to the title screen.
*/
public override function create():Void
{
// Setup a bunch of important Flixel stuff.
setupShit();
// Load player options from save data.
// Flixel has already loaded the save data, so we can just use it.
Preferences.init();
// Load controls from save data.
PlayerSettings.init();
startGame();
}
/**
* Setup a bunch of important Flixel stuff.
*/
function setupShit()
2023-01-22 19:55:30 -05:00
{
//
// GAME SETUP
//
// Setup window events (like callbacks for onWindowClose)
WindowUtil.initWindowEvents();
// Disable the thing on Windows where it tries to send a bug report to Microsoft because why do they care?
WindowUtil.disableCrashHandler();
// This ain't a pixel art game! (most of the time)
FlxSprite.defaultAntialiasing = true;
2023-01-22 19:55:30 -05:00
// Disable default keybinds for volume (we manually control volume in MusicBeatState with custom binds)
FlxG.sound.volumeUpKeys = [];
FlxG.sound.volumeDownKeys = [];
FlxG.sound.muteKeys = [];
2023-01-22 19:55:30 -05:00
// Set the game to a lower frame rate while it is in the background.
FlxG.game.focusLostFramerate = 30;
//
// FLIXEL DEBUG SETUP
//
2024-02-13 00:29:46 -05:00
#if (debug || FORCE_DEBUG_VERSION)
// Disable using ~ to open the console (we use that for the Editor menu)
FlxG.debugger.toggleKeys = [F2];
// Adds an additional Close Debugger button.
2023-01-22 19:55:30 -05:00
// This big obnoxious white button is for MOBILE, so that you can press it
// easily with your finger when debug bullshit pops up during testing lol!
FlxG.debugger.addButton(LEFT, new BitmapData(200, 200), function() {
2023-01-22 19:55:30 -05:00
FlxG.debugger.visible = false;
});
// Adds a red button to the debugger.
// This pauses the game AND the music! This ensures the Conductor stops.
FlxG.debugger.addButton(CENTER, new BitmapData(20, 20, true, 0xFFCC2233), function() {
if (FlxG.vcr.paused)
{
FlxG.vcr.resume();
for (snd in FlxG.sound.list)
{
snd.resume();
}
FlxG.sound.music.resume();
}
else
{
FlxG.vcr.pause();
for (snd in FlxG.sound.list)
{
snd.pause();
}
FlxG.sound.music.pause();
}
});
// Adds a blue button to the debugger.
// This skips forward in the song.
FlxG.debugger.addButton(CENTER, new BitmapData(20, 20, true, 0xFF2222CC), function() {
FlxG.game.debugger.vcr.onStep();
for (snd in FlxG.sound.list)
{
snd.pause();
snd.time += FlxG.elapsed * 1000;
}
FlxG.sound.music.pause();
FlxG.sound.music.time += FlxG.elapsed * 1000;
});
2024-02-13 00:29:46 -05:00
#end
// Make errors and warnings less annoying.
2024-02-27 22:02:04 -05:00
// Forcing this always since I have never been happy to have the debugger to pop up
2024-02-12 23:15:18 -05:00
LogStyle.ERROR.openConsole = false;
LogStyle.ERROR.errorSound = null;
LogStyle.WARNING.openConsole = false;
LogStyle.WARNING.errorSound = null;
2023-01-22 19:55:30 -05:00
//
// FLIXEL TRANSITIONS
//
2023-01-22 19:55:30 -05:00
// Diamond Transition
2023-01-22 19:55:30 -05:00
var diamond:FlxGraphic = FlxGraphic.fromClass(GraphicTransTileDiamond);
diamond.persist = true;
diamond.destroyOnNoUse = false;
2023-06-27 21:29:58 -04:00
// NOTE: tileData is ignored if TransitionData.type is FADE instead of TILES.
var tileData:TransitionTileData = {asset: diamond, width: 32, height: 32};
2023-07-04 16:38:10 -04:00
FlxTransitionableState.defaultTransIn = new TransitionData(FADE, FlxColor.BLACK, 1, new FlxPoint(0, -1), tileData,
2023-06-27 21:29:58 -04:00
new FlxRect(-200, -200, FlxG.width * 1.4, FlxG.height * 1.4));
2023-07-04 16:38:10 -04:00
FlxTransitionableState.defaultTransOut = new TransitionData(FADE, FlxColor.BLACK, 0.7, new FlxPoint(0, 1), tileData,
2023-06-27 21:29:58 -04:00
new FlxRect(-200, -200, FlxG.width * 1.4, FlxG.height * 1.4));
// Don't play transition in when entering the title state.
FlxTransitionableState.skipNextTransIn = true;
2023-01-22 19:55:30 -05:00
//
// NEWGROUNDS API SETUP
//
#if newgrounds
NGio.init();
#end
2023-01-22 19:55:30 -05:00
//
// DISCORD API SETUP
//
#if discord_rpc
DiscordClient.initialize();
2023-01-22 19:55:30 -05:00
Application.current.onExit.add(function(exitCode) {
DiscordClient.shutdown();
});
#end
2023-01-22 19:55:30 -05:00
//
// ANDROID SETUP
//
#if android
FlxG.android.preventDefaultKeys = [flixel.input.android.FlxAndroidKey.BACK];
#end
2023-01-22 19:55:30 -05:00
//
// FLIXEL PLUGINS
//
// Plugins provide a useful interface for globally active Flixel objects,
// that receive update events regardless of the current state.
// TODO: Move scripted Module behavior to a Flixel plugin.
funkin.util.plugins.EvacuateDebugPlugin.initialize();
funkin.util.plugins.ForceCrashPlugin.initialize();
funkin.util.plugins.ReloadAssetsDebugPlugin.initialize();
funkin.util.plugins.ScreenshotPlugin.initialize();
funkin.util.plugins.VolumePlugin.initialize();
funkin.util.plugins.WatchPlugin.initialize();
//
// GAME DATA PARSING
//
2024-02-07 18:45:13 -05:00
// NOTE: Registries must be imported and not referenced with fully qualified names,
// to ensure build macros work properly.
2024-02-07 18:45:13 -05:00
trace('Parsing game data...');
var perfStart = haxe.Timer.stamp();
SongEventRegistry.loadEventCache(); // SongEventRegistry is structured differently so it's not a BaseRegistry.
SongRegistry.instance.loadEntries();
LevelRegistry.instance.loadEntries();
NoteStyleRegistry.instance.loadEntries();
ConversationRegistry.instance.loadEntries();
DialogueBoxRegistry.instance.loadEntries();
SpeakerRegistry.instance.loadEntries();
StageRegistry.instance.loadEntries();
2024-02-07 18:45:13 -05:00
// TODO: CharacterDataParser doesn't use json2object, so it's way slower than the other parsers.
CharacterDataParser.loadCharacterCache(); // TODO: Migrate characters to BaseRegistry.
ModuleHandler.buildModuleCallbacks();
ModuleHandler.loadModuleCache();
ModuleHandler.callOnCreate();
2024-02-07 18:45:13 -05:00
var perfEnd = haxe.Timer.stamp();
2024-02-07 18:45:13 -05:00
trace('Parsing game data took ${Math.floor((perfEnd - perfStart) * 1000)}ms.');
}
2023-01-22 19:55:30 -05:00
/**
* Start the game.
*
* By default, moves to the `TitleState`.
* But based on compile defines, the game can start immediately on a specific song,
* or immediately in a specific debug menu.
*/
function startGame():Void
{
#if SONG // -DSONG=bopeebo
startSong(defineSong(), defineDifficulty());
#elseif LEVEL // -DLEVEL=week1 -DDIFFICULTY=hard
startLevel(defineLevel(), defineDifficulty());
#elseif FREEPLAY // -DFREEPLAY
FlxG.switchState(() -> new funkin.ui.freeplay.FreeplayState());
#elseif DIALOGUE // -DDIALOGUE
FlxG.switchState(() -> new funkin.ui.debug.dialogue.ConversationDebugState());
#elseif ANIMATE // -DANIMATE
FlxG.switchState(() -> new funkin.ui.debug.anim.FlxAnimateTest());
#elseif WAVEFORM // -DWAVEFORM
FlxG.switchState(() -> new funkin.ui.debug.WaveformTestState());
#elseif CHARTING // -DCHARTING
FlxG.switchState(() -> new funkin.ui.debug.charting.ChartEditorState());
#elseif STAGEBUILD // -DSTAGEBUILD
FlxG.switchState(() -> new funkin.ui.debug.stage.StageBuilderState());
#elseif ANIMDEBUG // -DANIMDEBUG
FlxG.switchState(() -> new funkin.ui.debug.anim.DebugBoundingState());
#elseif LATENCY // -DLATENCY
FlxG.switchState(() -> new funkin.LatencyState());
#else
startGameNormally();
#end
}
2023-01-22 19:55:30 -05:00
/**
* Start the game by moving to the title state and play the game as normal.
*/
function startGameNormally():Void
{
var params:CLIParams = CLIUtil.processArgs();
trace('Command line args: ${params}');
if (params.chart.shouldLoadChart)
{
FlxG.switchState(() -> new ChartEditorState(
{
fnfcTargetPath: params.chart.chartPath,
}));
}
else
{
FlxG.sound.cache(Paths.music('freakyMenu/freakyMenu'));
FlxG.switchState(() -> new TitleState());
}
}
2023-01-22 19:55:30 -05:00
/**
* Start the game by directly loading into a specific song.
* @param songId
* @param difficultyId
*/
function startSong(songId:String, difficultyId:String = 'normal'):Void
{
var songData:funkin.play.song.Song = funkin.data.song.SongRegistry.instance.fetchEntry(songId);
2023-01-22 19:55:30 -05:00
if (songData == null)
{
startGameNormally();
return;
}
2023-01-22 19:55:30 -05:00
LoadingState.loadPlayState(
{
targetSong: songData,
targetDifficulty: difficultyId,
});
}
2023-01-22 19:55:30 -05:00
/**
* Start the game by directly loading into a specific story mode level.
* @param levelId
* @param difficultyId
*/
function startLevel(levelId:String, difficultyId:String = 'normal'):Void
{
var currentLevel:funkin.ui.story.Level = funkin.data.level.LevelRegistry.instance.fetchEntry(levelId);
2023-01-22 19:55:30 -05:00
if (currentLevel == null)
2023-01-22 19:55:30 -05:00
{
startGameNormally();
return;
2023-01-22 19:55:30 -05:00
}
PlayStatePlaylist.playlistSongIds = currentLevel.getSongs();
PlayStatePlaylist.isStoryMode = true;
PlayStatePlaylist.campaignScore = 0;
2023-01-22 19:55:30 -05:00
var targetSongId:String = PlayStatePlaylist.playlistSongIds.shift();
var targetSong:funkin.play.song.Song = SongRegistry.instance.fetchEntry(targetSongId);
2023-01-22 19:55:30 -05:00
LoadingState.loadPlayState(
{
targetSong: targetSong,
targetDifficulty: difficultyId,
});
}
2023-01-22 19:55:30 -05:00
function defineSong():String
{
return MacroUtil.getDefine('SONG');
2023-01-22 19:55:30 -05:00
}
function defineLevel():String
2023-01-22 19:55:30 -05:00
{
return MacroUtil.getDefine('LEVEL');
}
2023-01-22 19:55:30 -05:00
function defineDifficulty():String
{
return MacroUtil.getDefine('DIFFICULTY');
2023-01-22 19:55:30 -05:00
}
2022-02-09 20:24:33 -05:00
}