mirror of
https://github.com/FunkinCrew/Funkin.git
synced 2024-11-27 10:05:41 -05:00
1175 lines
37 KiB
Haxe
1175 lines
37 KiB
Haxe
package funkin.ui.transition.preload;
|
|
|
|
import openfl.filters.GlowFilter;
|
|
import openfl.display.SpreadMethod;
|
|
import openfl.display.GradientType;
|
|
import openfl.geom.Matrix;
|
|
import openfl.filters.BlurFilter;
|
|
import openfl.events.MouseEvent;
|
|
import flash.display.Bitmap;
|
|
import flash.display.BitmapData;
|
|
import flash.display.BlendMode;
|
|
import flash.display.Sprite;
|
|
import flash.Lib;
|
|
import flixel.system.FlxBasePreloader;
|
|
import funkin.modding.PolymodHandler;
|
|
import funkin.play.character.CharacterData.CharacterDataParser;
|
|
import funkin.util.MathUtil;
|
|
import lime.app.Future;
|
|
import lime.math.Rectangle;
|
|
import openfl.display.Sprite;
|
|
import openfl.text.TextField;
|
|
import openfl.text.TextFormat;
|
|
import openfl.text.TextFormatAlign;
|
|
|
|
using StringTools;
|
|
|
|
// Annotation embeds the asset in the executable for faster loading.
|
|
// Polymod can't override this, so we can't use this technique elsewhere.
|
|
|
|
@:bitmap("art/preloaderArt.png")
|
|
class LogoImage extends BitmapData {}
|
|
|
|
#if TOUCH_HERE_TO_PLAY
|
|
@:bitmap('art/touchHereToPlay.png')
|
|
class TouchHereToPlayImage extends BitmapData {}
|
|
#end
|
|
|
|
/**
|
|
* This preloader displays a logo while the game downloads assets.
|
|
*/
|
|
class FunkinPreloader extends FlxBasePreloader
|
|
{
|
|
/**
|
|
* The logo image width at the base resolution.
|
|
* Scaled up/down appropriately as needed.
|
|
*/
|
|
static final BASE_WIDTH:Float = 1280;
|
|
|
|
/**
|
|
* Margin at the sides and bottom, around the loading bar.
|
|
*/
|
|
static final BAR_PADDING:Float = 20;
|
|
|
|
static final BAR_HEIGHT:Int = 12;
|
|
|
|
/**
|
|
* Logo takes this long (in seconds) to fade in.
|
|
*/
|
|
static final LOGO_FADE_TIME:Float = 2.5;
|
|
|
|
// Ratio between window size and BASE_WIDTH
|
|
var ratio:Float = 0;
|
|
|
|
var currentState:FunkinPreloaderState = FunkinPreloaderState.NotStarted;
|
|
|
|
// private var downloadingAssetsStartTime:Float = -1;
|
|
private var downloadingAssetsPercent:Float = -1;
|
|
private var downloadingAssetsComplete:Bool = false;
|
|
|
|
private var preloadingPlayAssetsPercent:Float = -1;
|
|
private var preloadingPlayAssetsStartTime:Float = -1;
|
|
private var preloadingPlayAssetsComplete:Bool = false;
|
|
|
|
private var cachingGraphicsPercent:Float = -1;
|
|
private var cachingGraphicsStartTime:Float = -1;
|
|
private var cachingGraphicsComplete:Bool = false;
|
|
|
|
private var cachingAudioPercent:Float = -1;
|
|
private var cachingAudioStartTime:Float = -1;
|
|
private var cachingAudioComplete:Bool = false;
|
|
|
|
private var cachingDataPercent:Float = -1;
|
|
private var cachingDataStartTime:Float = -1;
|
|
private var cachingDataComplete:Bool = false;
|
|
|
|
private var parsingSpritesheetsPercent:Float = -1;
|
|
private var parsingSpritesheetsStartTime:Float = -1;
|
|
private var parsingSpritesheetsComplete:Bool = false;
|
|
|
|
private var parsingStagesPercent:Float = -1;
|
|
private var parsingStagesStartTime:Float = -1;
|
|
private var parsingStagesComplete:Bool = false;
|
|
|
|
private var parsingCharactersPercent:Float = -1;
|
|
private var parsingCharactersStartTime:Float = -1;
|
|
private var parsingCharactersComplete:Bool = false;
|
|
|
|
private var parsingSongsPercent:Float = -1;
|
|
private var parsingSongsStartTime:Float = -1;
|
|
private var parsingSongsComplete:Bool = false;
|
|
|
|
private var initializingScriptsPercent:Float = -1;
|
|
|
|
private var cachingCoreAssetsPercent:Float = -1;
|
|
|
|
/**
|
|
* The timestamp when the other steps completed and the `Finishing up` step started.
|
|
*/
|
|
private var completeTime:Float = -1;
|
|
|
|
// Graphics
|
|
var logo:Bitmap;
|
|
#if TOUCH_HERE_TO_PLAY
|
|
var touchHereToPlay:Bitmap;
|
|
var touchHereSprite:Sprite;
|
|
#end
|
|
var progressBarPieces:Array<Sprite>;
|
|
var progressBar:Bitmap;
|
|
var progressLeftText:TextField;
|
|
var progressRightText:TextField;
|
|
|
|
var dspText:TextField;
|
|
var fnfText:TextField;
|
|
var enhancedText:TextField;
|
|
var stereoText:TextField;
|
|
|
|
var vfdShader:VFDOverlay;
|
|
var vfdBitmap:Bitmap;
|
|
var box:Sprite;
|
|
var progressLines:Sprite;
|
|
|
|
public function new()
|
|
{
|
|
super(Constants.PRELOADER_MIN_STAGE_TIME);
|
|
|
|
// We can't even call trace() yet, until Flixel loads.
|
|
trace('Initializing custom preloader...');
|
|
|
|
this.siteLockTitleText = Constants.SITE_LOCK_TITLE;
|
|
this.siteLockBodyText = Constants.SITE_LOCK_DESC;
|
|
}
|
|
|
|
override function create():Void
|
|
{
|
|
// Nothing happens in the base preloader.
|
|
super.create();
|
|
|
|
// Background color.
|
|
Lib.current.stage.color = Constants.COLOR_PRELOADER_BG;
|
|
|
|
// Width and height of the preloader.
|
|
this._width = Lib.current.stage.stageWidth;
|
|
this._height = Lib.current.stage.stageHeight;
|
|
|
|
// Scale assets to the screen size.
|
|
ratio = this._width / BASE_WIDTH / 2.0;
|
|
|
|
// Create the logo.
|
|
logo = createBitmap(LogoImage, function(bmp:Bitmap) {
|
|
// Scale and center the logo.
|
|
// We have to do this inside the async call, after the image size is known.
|
|
bmp.scaleX = bmp.scaleY = ratio;
|
|
bmp.x = (this._width - bmp.width) / 2;
|
|
bmp.y = (this._height - bmp.height) / 2;
|
|
});
|
|
// addChild(logo);
|
|
|
|
var amountOfPieces:Int = 16;
|
|
progressBarPieces = [];
|
|
var maxBarWidth = this._width - BAR_PADDING * 2;
|
|
var pieceWidth = maxBarWidth / amountOfPieces;
|
|
var pieceGap:Int = 8;
|
|
|
|
progressLines = new openfl.display.Sprite();
|
|
progressLines.graphics.lineStyle(2, Constants.COLOR_PRELOADER_BAR);
|
|
progressLines.graphics.drawRect(-2, 480, this._width + 4, 30);
|
|
addChild(progressLines);
|
|
|
|
var progressBarPiece = new Sprite();
|
|
progressBarPiece.graphics.beginFill(Constants.COLOR_PRELOADER_BAR);
|
|
progressBarPiece.graphics.drawRoundRect(0, 0, pieceWidth - pieceGap, BAR_HEIGHT, 4, 4);
|
|
progressBarPiece.graphics.endFill();
|
|
|
|
for (i in 0...amountOfPieces)
|
|
{
|
|
var piece = new Sprite();
|
|
piece.graphics.beginFill(Constants.COLOR_PRELOADER_BAR);
|
|
piece.graphics.drawRoundRect(0, 0, pieceWidth - pieceGap, BAR_HEIGHT, 4, 4);
|
|
piece.graphics.endFill();
|
|
|
|
piece.x = i * (piece.width + pieceGap);
|
|
piece.y = this._height - BAR_PADDING - BAR_HEIGHT - 200;
|
|
addChild(piece);
|
|
progressBarPieces.push(piece);
|
|
}
|
|
|
|
// Create the progress bar.
|
|
// progressBar = new Bitmap(new BitmapData(1, BAR_HEIGHT, true, Constants.COLOR_PRELOADER_BAR));
|
|
// progressBar.x = BAR_PADDING;
|
|
// progressBar.y = this._height - BAR_PADDING - BAR_HEIGHT;
|
|
// addChild(progressBar);
|
|
|
|
// Create the progress message.
|
|
progressLeftText = new TextField();
|
|
dspText = new TextField();
|
|
fnfText = new TextField();
|
|
enhancedText = new TextField();
|
|
stereoText = new TextField();
|
|
|
|
var progressLeftTextFormat = new TextFormat("DS-Digital", 32, Constants.COLOR_PRELOADER_BAR, true);
|
|
progressLeftTextFormat.align = TextFormatAlign.LEFT;
|
|
progressLeftText.defaultTextFormat = progressLeftTextFormat;
|
|
|
|
progressLeftText.selectable = false;
|
|
progressLeftText.width = this._width - BAR_PADDING * 2;
|
|
progressLeftText.text = 'Downloading assets...';
|
|
progressLeftText.x = BAR_PADDING;
|
|
progressLeftText.y = this._height - BAR_PADDING - BAR_HEIGHT - 290;
|
|
// progressLeftText.shader = new VFDOverlay();
|
|
addChild(progressLeftText);
|
|
|
|
// Create the progress %.
|
|
progressRightText = new TextField();
|
|
|
|
var progressRightTextFormat = new TextFormat("DS-Digital", 16, Constants.COLOR_PRELOADER_BAR, true);
|
|
progressRightTextFormat.align = TextFormatAlign.RIGHT;
|
|
progressRightText.defaultTextFormat = progressRightTextFormat;
|
|
|
|
progressRightText.selectable = false;
|
|
progressRightText.width = this._width - BAR_PADDING * 2;
|
|
progressRightText.text = '0%';
|
|
progressRightText.x = BAR_PADDING;
|
|
progressRightText.y = this._height - BAR_PADDING - BAR_HEIGHT - 16 - 4;
|
|
addChild(progressRightText);
|
|
|
|
box = new Sprite();
|
|
box.graphics.beginFill(Constants.COLOR_PRELOADER_BAR, 1);
|
|
box.graphics.drawRoundRect(0, 0, 64, 20, 5, 5);
|
|
box.graphics.drawRoundRect(70, 0, 58, 20, 5, 5);
|
|
box.graphics.endFill();
|
|
box.graphics.beginFill(Constants.COLOR_PRELOADER_BAR, 0.1);
|
|
box.graphics.drawRoundRect(0, 0, 128, 20, 5, 5);
|
|
box.graphics.endFill();
|
|
box.x = 880;
|
|
box.y = 440;
|
|
addChild(box);
|
|
|
|
dspText.selectable = false;
|
|
dspText.textColor = 0x000000;
|
|
dspText.width = this._width;
|
|
dspText.height = 20;
|
|
dspText.text = 'DSP';
|
|
dspText.x = 10;
|
|
dspText.y = -5;
|
|
box.addChild(dspText);
|
|
|
|
fnfText.selectable = false;
|
|
fnfText.textColor = 0x000000;
|
|
fnfText.width = this._width;
|
|
fnfText.height = 20;
|
|
fnfText.x = 75;
|
|
fnfText.y = -5;
|
|
fnfText.text = 'FNF';
|
|
box.addChild(fnfText);
|
|
|
|
enhancedText.selectable = false;
|
|
enhancedText.textColor = Constants.COLOR_PRELOADER_BAR;
|
|
enhancedText.width = this._width;
|
|
enhancedText.height = 100;
|
|
enhancedText.text = 'ENHANCED';
|
|
enhancedText.x = -100;
|
|
enhancedText.y = 0;
|
|
box.addChild(enhancedText);
|
|
|
|
stereoText.selectable = false;
|
|
stereoText.textColor = Constants.COLOR_PRELOADER_BAR;
|
|
stereoText.width = this._width;
|
|
stereoText.height = 100;
|
|
stereoText.text = 'STEREO';
|
|
stereoText.x = 0;
|
|
stereoText.y = -40;
|
|
box.addChild(stereoText);
|
|
|
|
// var dummyMatrix:openfl.geom.Matrix = new Matrix();
|
|
// dummyMatrix.createGradientBox(this._width, this._height * 0.1, 90 * Math.PI / 180);
|
|
|
|
// var gradient:Sprite = new Sprite();
|
|
// gradient.graphics.beginGradientFill(GradientType.LINEAR, [0xFFFFFF, 0x000000], [1, 1], [0, 255], dummyMatrix, SpreadMethod.REFLECT);
|
|
// gradient.graphics.drawRect(0, 0, this._width, this._height);
|
|
// gradient.graphics.endFill();
|
|
// addChild(gradient);
|
|
|
|
vfdBitmap = new Bitmap(new BitmapData(this._width, this._height, true, 0xFFFFFFFF));
|
|
addChild(vfdBitmap);
|
|
|
|
vfdShader = new VFDOverlay();
|
|
vfdBitmap.shader = vfdShader;
|
|
|
|
#if TOUCH_HERE_TO_PLAY
|
|
touchHereToPlay = createBitmap(TouchHereToPlayImage, function(bmp:Bitmap) {
|
|
// Scale and center the touch to start image.
|
|
// We have to do this inside the async call, after the image size is known.
|
|
bmp.scaleX = bmp.scaleY = ratio;
|
|
bmp.x = (this._width - bmp.width) / 2;
|
|
bmp.y = (this._height - bmp.height) / 2;
|
|
});
|
|
touchHereToPlay.alpha = 0.0;
|
|
|
|
touchHereSprite = new Sprite();
|
|
touchHereSprite.buttonMode = false;
|
|
touchHereSprite.addChild(touchHereToPlay);
|
|
addChild(touchHereSprite);
|
|
#end
|
|
}
|
|
|
|
var lastElapsed:Float = 0.0;
|
|
|
|
override function update(percent:Float):Void
|
|
{
|
|
var elapsed:Float = (Date.now().getTime() - this._startTime) / 1000.0;
|
|
|
|
vfdShader.update(elapsed * 100);
|
|
// trace('Time since last frame: ' + (lastElapsed - elapsed));
|
|
|
|
downloadingAssetsPercent = percent;
|
|
var loadPercent:Float = updateState(percent, elapsed);
|
|
updateGraphics(loadPercent, elapsed);
|
|
|
|
lastElapsed = elapsed;
|
|
}
|
|
|
|
function updateState(percent:Float, elapsed:Float):Float
|
|
{
|
|
switch (currentState)
|
|
{
|
|
case FunkinPreloaderState.NotStarted:
|
|
if (downloadingAssetsPercent > 0.0) currentState = FunkinPreloaderState.DownloadingAssets;
|
|
|
|
return percent;
|
|
|
|
case FunkinPreloaderState.DownloadingAssets:
|
|
// Sometimes percent doesn't go to 100%, it's a floating point error.
|
|
if (downloadingAssetsPercent >= 1.0
|
|
|| (elapsed > Constants.PRELOADER_MIN_STAGE_TIME
|
|
&& downloadingAssetsComplete)) currentState = FunkinPreloaderState.PreloadingPlayAssets;
|
|
|
|
return percent;
|
|
|
|
case FunkinPreloaderState.PreloadingPlayAssets:
|
|
if (preloadingPlayAssetsPercent < 0.0)
|
|
{
|
|
preloadingPlayAssetsStartTime = elapsed;
|
|
preloadingPlayAssetsPercent = 0.0;
|
|
|
|
// This is quick enough to do synchronously.
|
|
// Assets.initialize();
|
|
|
|
/*
|
|
// Make a future to retrieve the manifest
|
|
var future:Future<lime.utils.AssetLibrary> = Assets.preloadLibrary('gameplay');
|
|
|
|
future.onProgress((loaded:Int, total:Int) -> {
|
|
preloadingPlayAssetsPercent = loaded / total;
|
|
});
|
|
future.onComplete((library:lime.utils.AssetLibrary) -> {
|
|
});
|
|
*/
|
|
|
|
// TODO: Reimplement this.
|
|
preloadingPlayAssetsPercent = 1.0;
|
|
preloadingPlayAssetsComplete = true;
|
|
return 0.0;
|
|
}
|
|
else if (Constants.PRELOADER_MIN_STAGE_TIME > 0)
|
|
{
|
|
var elapsedPreloadingPlayAssets:Float = elapsed - preloadingPlayAssetsStartTime;
|
|
if (preloadingPlayAssetsComplete && elapsedPreloadingPlayAssets >= Constants.PRELOADER_MIN_STAGE_TIME)
|
|
{
|
|
currentState = FunkinPreloaderState.InitializingScripts;
|
|
return 0.0;
|
|
}
|
|
else
|
|
{
|
|
// We need to return SIMULATED progress here.
|
|
if (preloadingPlayAssetsPercent < (elapsedPreloadingPlayAssets / Constants.PRELOADER_MIN_STAGE_TIME)) return preloadingPlayAssetsPercent;
|
|
else
|
|
return elapsedPreloadingPlayAssets / Constants.PRELOADER_MIN_STAGE_TIME;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (preloadingPlayAssetsComplete) currentState = FunkinPreloaderState.InitializingScripts;
|
|
}
|
|
|
|
return preloadingPlayAssetsPercent;
|
|
|
|
case FunkinPreloaderState.InitializingScripts:
|
|
if (initializingScriptsPercent < 0.0)
|
|
{
|
|
initializingScriptsPercent = 0.0;
|
|
|
|
/*
|
|
var future:Future<Array<String>> = []; // PolymodHandler.loadNoModsAsync();
|
|
|
|
future.onProgress((loaded:Int, total:Int) -> {
|
|
trace('PolymodHandler.loadNoModsAsync() progress: ' + loaded + '/' + total);
|
|
initializingScriptsPercent = loaded / total;
|
|
});
|
|
future.onComplete((result:Array<String>) -> {
|
|
trace('Completed initializing scripts: ' + result);
|
|
});
|
|
*/
|
|
|
|
initializingScriptsPercent = 1.0;
|
|
currentState = FunkinPreloaderState.CachingGraphics;
|
|
return 0.0;
|
|
}
|
|
|
|
return initializingScriptsPercent;
|
|
|
|
case CachingGraphics:
|
|
if (cachingGraphicsPercent < 0)
|
|
{
|
|
cachingGraphicsPercent = 0.0;
|
|
cachingGraphicsStartTime = elapsed;
|
|
|
|
/*
|
|
var assetsToCache:Array<String> = []; // Assets.listGraphics('core');
|
|
|
|
var future:Future<Array<String>> = []; // Assets.cacheAssets(assetsToCache);
|
|
future.onProgress((loaded:Int, total:Int) -> {
|
|
cachingGraphicsPercent = loaded / total;
|
|
});
|
|
future.onComplete((_result) -> {
|
|
trace('Completed caching graphics.');
|
|
});
|
|
*/
|
|
|
|
// TODO: Reimplement this.
|
|
cachingGraphicsPercent = 1.0;
|
|
cachingGraphicsComplete = true;
|
|
return 0.0;
|
|
}
|
|
else if (Constants.PRELOADER_MIN_STAGE_TIME > 0)
|
|
{
|
|
var elapsedCachingGraphics:Float = elapsed - cachingGraphicsStartTime;
|
|
if (cachingGraphicsComplete && elapsedCachingGraphics >= Constants.PRELOADER_MIN_STAGE_TIME)
|
|
{
|
|
currentState = FunkinPreloaderState.CachingAudio;
|
|
return 0.0;
|
|
}
|
|
else
|
|
{
|
|
if (cachingGraphicsPercent < (elapsedCachingGraphics / Constants.PRELOADER_MIN_STAGE_TIME))
|
|
{
|
|
// Return real progress if it's lower.
|
|
return cachingGraphicsPercent;
|
|
}
|
|
else
|
|
{
|
|
// Return simulated progress if it's higher.
|
|
return elapsedCachingGraphics / Constants.PRELOADER_MIN_STAGE_TIME;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (cachingGraphicsComplete)
|
|
{
|
|
currentState = FunkinPreloaderState.CachingAudio;
|
|
return 0.0;
|
|
}
|
|
else
|
|
{
|
|
return cachingGraphicsPercent;
|
|
}
|
|
}
|
|
|
|
case CachingAudio:
|
|
if (cachingAudioPercent < 0)
|
|
{
|
|
cachingAudioPercent = 0.0;
|
|
cachingAudioStartTime = elapsed;
|
|
|
|
var assetsToCache:Array<String> = []; // Assets.listSound('core');
|
|
|
|
/*
|
|
var future:Future<Array<String>> = []; // Assets.cacheAssets(assetsToCache);
|
|
|
|
future.onProgress((loaded:Int, total:Int) -> {
|
|
cachingAudioPercent = loaded / total;
|
|
});
|
|
future.onComplete((_result) -> {
|
|
trace('Completed caching audio.');
|
|
});
|
|
*/
|
|
|
|
// TODO: Reimplement this.
|
|
cachingAudioPercent = 1.0;
|
|
cachingAudioComplete = true;
|
|
return 0.0;
|
|
}
|
|
else if (Constants.PRELOADER_MIN_STAGE_TIME > 0)
|
|
{
|
|
var elapsedCachingAudio:Float = elapsed - cachingAudioStartTime;
|
|
if (cachingAudioComplete && elapsedCachingAudio >= Constants.PRELOADER_MIN_STAGE_TIME)
|
|
{
|
|
currentState = FunkinPreloaderState.CachingData;
|
|
return 0.0;
|
|
}
|
|
else
|
|
{
|
|
// We need to return SIMULATED progress here.
|
|
if (cachingAudioPercent < (elapsedCachingAudio / Constants.PRELOADER_MIN_STAGE_TIME))
|
|
{
|
|
return cachingAudioPercent;
|
|
}
|
|
else
|
|
{
|
|
return elapsedCachingAudio / Constants.PRELOADER_MIN_STAGE_TIME;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (cachingAudioComplete)
|
|
{
|
|
currentState = FunkinPreloaderState.CachingData;
|
|
return 0.0;
|
|
}
|
|
else
|
|
{
|
|
return cachingAudioPercent;
|
|
}
|
|
}
|
|
|
|
case CachingData:
|
|
if (cachingDataPercent < 0)
|
|
{
|
|
cachingDataPercent = 0.0;
|
|
cachingDataStartTime = elapsed;
|
|
|
|
var assetsToCache:Array<String> = [];
|
|
var sparrowFramesToCache:Array<String> = [];
|
|
|
|
// Core files
|
|
// assetsToCache = assetsToCache.concat(Assets.listText('core'));
|
|
// assetsToCache = assetsToCache.concat(Assets.listJSON('core'));
|
|
// Core spritesheets
|
|
// assetsToCache = assetsToCache.concat(Assets.listXML('core'));
|
|
|
|
// Gameplay files
|
|
// assetsToCache = assetsToCache.concat(Assets.listText('gameplay'));
|
|
// assetsToCache = assetsToCache.concat(Assets.listJSON('gameplay'));
|
|
// We're not caching gameplay spritesheets here because they're fetched on demand.
|
|
|
|
/*
|
|
var future:Future<Array<String>> = [];
|
|
// Assets.cacheAssets(assetsToCache, true);
|
|
future.onProgress((loaded:Int, total:Int) -> {
|
|
cachingDataPercent = loaded / total;
|
|
});
|
|
future.onComplete((_result) -> {
|
|
trace('Completed caching data.');
|
|
});
|
|
*/
|
|
cachingDataPercent = 1.0;
|
|
cachingDataComplete = true;
|
|
return 0.0;
|
|
}
|
|
else if (Constants.PRELOADER_MIN_STAGE_TIME > 0)
|
|
{
|
|
var elapsedCachingData:Float = elapsed - cachingDataStartTime;
|
|
if (cachingDataComplete && elapsedCachingData >= Constants.PRELOADER_MIN_STAGE_TIME)
|
|
{
|
|
currentState = FunkinPreloaderState.ParsingSpritesheets;
|
|
return 0.0;
|
|
}
|
|
else
|
|
{
|
|
// We need to return SIMULATED progress here.
|
|
if (cachingDataPercent < (elapsedCachingData / Constants.PRELOADER_MIN_STAGE_TIME)) return cachingDataPercent;
|
|
else
|
|
return elapsedCachingData / Constants.PRELOADER_MIN_STAGE_TIME;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (cachingDataComplete)
|
|
{
|
|
currentState = FunkinPreloaderState.ParsingSpritesheets;
|
|
return 0.0;
|
|
}
|
|
}
|
|
|
|
return cachingDataPercent;
|
|
|
|
case ParsingSpritesheets:
|
|
if (parsingSpritesheetsPercent < 0)
|
|
{
|
|
parsingSpritesheetsPercent = 0.0;
|
|
parsingSpritesheetsStartTime = elapsed;
|
|
|
|
// Core spritesheets
|
|
var sparrowFramesToCache = []; // Assets.listXML('core').map((xml:String) -> xml.replace('.xml', '').replace('core:assets/core/', ''));
|
|
// We're not caching gameplay spritesheets here because they're fetched on demand.
|
|
|
|
/*
|
|
var future:Future<Array<String>> = []; // Assets.cacheSparrowFrames(sparrowFramesToCache, true);
|
|
future.onProgress((loaded:Int, total:Int) -> {
|
|
parsingSpritesheetsPercent = loaded / total;
|
|
});
|
|
future.onComplete((_result) -> {
|
|
trace('Completed parsing spritesheets.');
|
|
});
|
|
*/
|
|
parsingSpritesheetsPercent = 1.0;
|
|
parsingSpritesheetsComplete = true;
|
|
return 0.0;
|
|
}
|
|
else if (Constants.PRELOADER_MIN_STAGE_TIME > 0)
|
|
{
|
|
var elapsedParsingSpritesheets:Float = elapsed - parsingSpritesheetsStartTime;
|
|
if (parsingSpritesheetsComplete && elapsedParsingSpritesheets >= Constants.PRELOADER_MIN_STAGE_TIME)
|
|
{
|
|
currentState = FunkinPreloaderState.ParsingStages;
|
|
return 0.0;
|
|
}
|
|
else
|
|
{
|
|
// We need to return SIMULATED progress here.
|
|
if (parsingSpritesheetsPercent < (elapsedParsingSpritesheets / Constants.PRELOADER_MIN_STAGE_TIME)) return parsingSpritesheetsPercent;
|
|
else
|
|
return elapsedParsingSpritesheets / Constants.PRELOADER_MIN_STAGE_TIME;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (parsingSpritesheetsComplete)
|
|
{
|
|
currentState = FunkinPreloaderState.ParsingStages;
|
|
return 0.0;
|
|
}
|
|
}
|
|
|
|
return parsingSpritesheetsPercent;
|
|
|
|
case ParsingStages:
|
|
if (parsingStagesPercent < 0)
|
|
{
|
|
parsingStagesPercent = 0.0;
|
|
parsingStagesStartTime = elapsed;
|
|
|
|
/*
|
|
// TODO: Reimplement this.
|
|
var future:Future<Array<String>> = []; // StageDataParser.loadStageCacheAsync();
|
|
|
|
future.onProgress((loaded:Int, total:Int) -> {
|
|
parsingStagesPercent = loaded / total;
|
|
});
|
|
|
|
future.onComplete((_result) -> {
|
|
trace('Completed parsing stages.');
|
|
});
|
|
*/
|
|
|
|
parsingStagesPercent = 1.0;
|
|
parsingStagesComplete = true;
|
|
return 0.0;
|
|
}
|
|
else if (Constants.PRELOADER_MIN_STAGE_TIME > 0)
|
|
{
|
|
var elapsedParsingStages:Float = elapsed - parsingStagesStartTime;
|
|
if (parsingStagesComplete && elapsedParsingStages >= Constants.PRELOADER_MIN_STAGE_TIME)
|
|
{
|
|
currentState = FunkinPreloaderState.ParsingCharacters;
|
|
return 0.0;
|
|
}
|
|
else
|
|
{
|
|
// We need to return SIMULATED progress here.
|
|
if (parsingStagesPercent < (elapsedParsingStages / Constants.PRELOADER_MIN_STAGE_TIME)) return parsingStagesPercent;
|
|
else
|
|
return elapsedParsingStages / Constants.PRELOADER_MIN_STAGE_TIME;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (parsingStagesComplete)
|
|
{
|
|
currentState = FunkinPreloaderState.ParsingCharacters;
|
|
return 0.0;
|
|
}
|
|
}
|
|
|
|
return parsingStagesPercent;
|
|
|
|
case ParsingCharacters:
|
|
if (parsingCharactersPercent < 0)
|
|
{
|
|
parsingCharactersPercent = 0.0;
|
|
parsingCharactersStartTime = elapsed;
|
|
|
|
/*
|
|
// TODO: Reimplement this.
|
|
var future:Future<Array<String>> = []; // CharacterDataParser.loadCharacterCacheAsync();
|
|
|
|
future.onProgress((loaded:Int, total:Int) -> {
|
|
parsingCharactersPercent = loaded / total;
|
|
});
|
|
|
|
future.onComplete((_result) -> {
|
|
trace('Completed parsing characters.');
|
|
});
|
|
*/
|
|
|
|
parsingCharactersPercent = 1.0;
|
|
parsingCharactersComplete = true;
|
|
return 0.0;
|
|
}
|
|
else if (Constants.PRELOADER_MIN_STAGE_TIME > 0)
|
|
{
|
|
var elapsedParsingCharacters:Float = elapsed - parsingCharactersStartTime;
|
|
if (parsingCharactersComplete && elapsedParsingCharacters >= Constants.PRELOADER_MIN_STAGE_TIME)
|
|
{
|
|
currentState = FunkinPreloaderState.ParsingSongs;
|
|
return 0.0;
|
|
}
|
|
else
|
|
{
|
|
// We need to return SIMULATED progress here.
|
|
if (parsingCharactersPercent < (elapsedParsingCharacters / Constants.PRELOADER_MIN_STAGE_TIME)) return parsingCharactersPercent;
|
|
else
|
|
return elapsedParsingCharacters / Constants.PRELOADER_MIN_STAGE_TIME;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (parsingStagesComplete)
|
|
{
|
|
currentState = FunkinPreloaderState.ParsingSongs;
|
|
return 0.0;
|
|
}
|
|
}
|
|
|
|
return parsingCharactersPercent;
|
|
|
|
case ParsingSongs:
|
|
if (parsingSongsPercent < 0)
|
|
{
|
|
parsingSongsPercent = 0.0;
|
|
parsingSongsStartTime = elapsed;
|
|
|
|
/*
|
|
// TODO: Reimplement this.
|
|
var future:Future<Array<String>> = ;
|
|
// SongDataParser.loadSongCacheAsync();
|
|
|
|
future.onProgress((loaded:Int, total:Int) -> {
|
|
parsingSongsPercent = loaded / total;
|
|
});
|
|
|
|
future.onComplete((_result) -> {
|
|
trace('Completed parsing songs.');
|
|
});
|
|
*/
|
|
|
|
parsingSongsPercent = 1.0;
|
|
parsingSongsComplete = true;
|
|
|
|
return 0.0;
|
|
}
|
|
else if (Constants.PRELOADER_MIN_STAGE_TIME > 0)
|
|
{
|
|
var elapsedParsingSongs:Float = elapsed - parsingSongsStartTime;
|
|
if (parsingSongsComplete && elapsedParsingSongs >= Constants.PRELOADER_MIN_STAGE_TIME)
|
|
{
|
|
currentState = FunkinPreloaderState.Complete;
|
|
return 0.0;
|
|
}
|
|
else
|
|
{
|
|
// We need to return SIMULATED progress here.
|
|
if (parsingSongsPercent < (elapsedParsingSongs / Constants.PRELOADER_MIN_STAGE_TIME))
|
|
{
|
|
return parsingSongsPercent;
|
|
}
|
|
else
|
|
{
|
|
return elapsedParsingSongs / Constants.PRELOADER_MIN_STAGE_TIME;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (parsingSongsComplete)
|
|
{
|
|
currentState = FunkinPreloaderState.Complete;
|
|
return 0.0;
|
|
}
|
|
else
|
|
{
|
|
return parsingSongsPercent;
|
|
}
|
|
}
|
|
case FunkinPreloaderState.Complete:
|
|
if (completeTime < 0)
|
|
{
|
|
completeTime = elapsed;
|
|
}
|
|
|
|
return 1.0;
|
|
#if TOUCH_HERE_TO_PLAY
|
|
case FunkinPreloaderState.TouchHereToPlay:
|
|
if (completeTime < 0)
|
|
{
|
|
completeTime = elapsed;
|
|
}
|
|
|
|
if (touchHereToPlay.alpha < 1.0)
|
|
{
|
|
touchHereSprite.buttonMode = true;
|
|
touchHereToPlay.alpha = 1.0;
|
|
removeChild(vfdBitmap);
|
|
|
|
addEventListener(MouseEvent.CLICK, onTouchHereToPlay);
|
|
touchHereSprite.addEventListener(MouseEvent.MOUSE_OVER, overTouchHereToPlay);
|
|
touchHereSprite.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownTouchHereToPlay);
|
|
touchHereSprite.addEventListener(MouseEvent.MOUSE_OUT, outTouchHereToPlay);
|
|
}
|
|
|
|
return 1.0;
|
|
#end
|
|
|
|
default:
|
|
// Do nothing.
|
|
}
|
|
|
|
return 0.0;
|
|
}
|
|
|
|
#if TOUCH_HERE_TO_PLAY
|
|
function overTouchHereToPlay(e:MouseEvent):Void
|
|
{
|
|
touchHereToPlay.scaleX = touchHereToPlay.scaleY = ratio * 1.1;
|
|
touchHereToPlay.x = (this._width - touchHereToPlay.width) / 2;
|
|
touchHereToPlay.y = (this._height - touchHereToPlay.height) / 2;
|
|
}
|
|
|
|
function outTouchHereToPlay(e:MouseEvent):Void
|
|
{
|
|
touchHereToPlay.scaleX = touchHereToPlay.scaleY = ratio * 1;
|
|
touchHereToPlay.x = (this._width - touchHereToPlay.width) / 2;
|
|
touchHereToPlay.y = (this._height - touchHereToPlay.height) / 2;
|
|
}
|
|
|
|
function mouseDownTouchHereToPlay(e:MouseEvent):Void
|
|
{
|
|
touchHereToPlay.y += 10;
|
|
}
|
|
|
|
function onTouchHereToPlay(e:MouseEvent):Void
|
|
{
|
|
touchHereToPlay.x = (this._width - touchHereToPlay.width) / 2;
|
|
touchHereToPlay.y = (this._height - touchHereToPlay.height) / 2;
|
|
|
|
removeEventListener(MouseEvent.CLICK, onTouchHereToPlay);
|
|
touchHereSprite.removeEventListener(MouseEvent.MOUSE_OVER, overTouchHereToPlay);
|
|
touchHereSprite.removeEventListener(MouseEvent.MOUSE_OUT, outTouchHereToPlay);
|
|
touchHereSprite.removeEventListener(MouseEvent.MOUSE_DOWN, mouseDownTouchHereToPlay);
|
|
|
|
// This is the actual thing that makes the game load.
|
|
immediatelyStartGame();
|
|
}
|
|
#end
|
|
|
|
static final TOTAL_STEPS:Int = 11;
|
|
static final ELLIPSIS_TIME:Float = 0.5;
|
|
|
|
function updateGraphics(percent:Float, elapsed:Float):Void
|
|
{
|
|
// Render logo (including transitions)
|
|
if (completeTime > 0.0)
|
|
{
|
|
var elapsedFinished:Float = renderLogoFadeOut(elapsed);
|
|
// trace('Fading out logo... (' + elapsedFinished + 's)');
|
|
if (elapsedFinished > LOGO_FADE_TIME)
|
|
{
|
|
#if TOUCH_HERE_TO_PLAY
|
|
// The logo has faded out, but we're not quite done yet.
|
|
// In order to prevent autoplay issues, we need the user to click after the loading finishes.
|
|
currentState = FunkinPreloaderState.TouchHereToPlay;
|
|
#else
|
|
immediatelyStartGame();
|
|
#end
|
|
}
|
|
}
|
|
else
|
|
{
|
|
renderLogoFadeIn(elapsed);
|
|
|
|
// Render progress bar
|
|
var maxWidth = this._width - BAR_PADDING * 2;
|
|
var barWidth = maxWidth * percent;
|
|
var piecesToRender:Int = Std.int(percent * progressBarPieces.length);
|
|
|
|
for (i => piece in progressBarPieces)
|
|
{
|
|
piece.alpha = i <= piecesToRender ? 0.9 : 0.1;
|
|
}
|
|
}
|
|
|
|
// progressBar.width = barWidth;
|
|
|
|
// Cycle ellipsis count to show loading
|
|
var ellipsisCount:Int = Std.int(elapsed / ELLIPSIS_TIME) % 3 + 1;
|
|
var ellipsis:String = '';
|
|
for (i in 0...ellipsisCount)
|
|
ellipsis += '.';
|
|
|
|
// Render status text
|
|
switch (currentState)
|
|
{
|
|
// case FunkinPreloaderState.NotStarted:
|
|
default:
|
|
updateProgressLeftText('Loading \n0/$TOTAL_STEPS $ellipsis');
|
|
case FunkinPreloaderState.DownloadingAssets:
|
|
updateProgressLeftText('Downloading assets \n1/$TOTAL_STEPS $ellipsis');
|
|
case FunkinPreloaderState.PreloadingPlayAssets:
|
|
updateProgressLeftText('Preloading assets \n2/$TOTAL_STEPS $ellipsis');
|
|
case FunkinPreloaderState.InitializingScripts:
|
|
updateProgressLeftText('Initializing scripts \n3/$TOTAL_STEPS $ellipsis');
|
|
case FunkinPreloaderState.CachingGraphics:
|
|
updateProgressLeftText('Caching graphics \n4/$TOTAL_STEPS $ellipsis');
|
|
case FunkinPreloaderState.CachingAudio:
|
|
updateProgressLeftText('Caching audio \n5/$TOTAL_STEPS $ellipsis');
|
|
case FunkinPreloaderState.CachingData:
|
|
updateProgressLeftText('Caching data \n6/$TOTAL_STEPS $ellipsis');
|
|
case FunkinPreloaderState.ParsingSpritesheets:
|
|
updateProgressLeftText('Parsing spritesheets \n7/$TOTAL_STEPS $ellipsis');
|
|
case FunkinPreloaderState.ParsingStages:
|
|
updateProgressLeftText('Parsing stages \n8/$TOTAL_STEPS $ellipsis');
|
|
case FunkinPreloaderState.ParsingCharacters:
|
|
updateProgressLeftText('Parsing characters \n9/$TOTAL_STEPS $ellipsis');
|
|
case FunkinPreloaderState.ParsingSongs:
|
|
updateProgressLeftText('Parsing songs \n10/$TOTAL_STEPS $ellipsis');
|
|
case FunkinPreloaderState.Complete:
|
|
updateProgressLeftText('Finishing up \n$TOTAL_STEPS/$TOTAL_STEPS $ellipsis');
|
|
#if TOUCH_HERE_TO_PLAY
|
|
case FunkinPreloaderState.TouchHereToPlay:
|
|
updateProgressLeftText(null);
|
|
#end
|
|
}
|
|
|
|
var percentage:Int = Math.floor(percent * 100);
|
|
trace('Preloader state: ' + currentState + ' (' + percentage + '%, ' + elapsed + 's)');
|
|
|
|
// Render percent text
|
|
progressRightText.text = '$percentage%';
|
|
|
|
super.update(percent);
|
|
}
|
|
|
|
function updateProgressLeftText(text:Null<String>):Void
|
|
{
|
|
if (progressLeftText != null)
|
|
{
|
|
if (text == null)
|
|
{
|
|
progressLeftText.alpha = 0.0;
|
|
}
|
|
else if (progressLeftText.text != text)
|
|
{
|
|
// We have to keep updating the text format, because the font can take a frame or two to load.
|
|
var progressLeftTextFormat = new TextFormat("DS-Digital", 32, Constants.COLOR_PRELOADER_BAR, true);
|
|
progressLeftTextFormat.align = TextFormatAlign.LEFT;
|
|
progressLeftText.defaultTextFormat = progressLeftTextFormat;
|
|
progressLeftText.text = text;
|
|
|
|
dspText.defaultTextFormat = new TextFormat("Quantico", 20, 0x000000, false);
|
|
dspText.text = 'DSP'; // fukin dum....
|
|
dspText.textColor = 0x000000;
|
|
|
|
fnfText.defaultTextFormat = new TextFormat("Quantico", 20, 0x000000, false);
|
|
fnfText.text = 'FNF';
|
|
fnfText.textColor = 0x000000;
|
|
|
|
enhancedText.defaultTextFormat = new TextFormat("Inconsolata Black", 16, Constants.COLOR_PRELOADER_BAR, false);
|
|
enhancedText.text = 'ENHANCED';
|
|
enhancedText.textColor = Constants.COLOR_PRELOADER_BAR;
|
|
|
|
stereoText.defaultTextFormat = new TextFormat("Inconsolata Bold", 36, Constants.COLOR_PRELOADER_BAR, false);
|
|
stereoText.text = 'NATURAL STEREO';
|
|
}
|
|
}
|
|
}
|
|
|
|
function immediatelyStartGame():Void
|
|
{
|
|
_loaded = true;
|
|
}
|
|
|
|
/**
|
|
* Fade out the logo.
|
|
* @param elapsed Elapsed time since the preloader started.
|
|
* @return Elapsed time since the logo started fading out.
|
|
*/
|
|
function renderLogoFadeOut(elapsed:Float):Float
|
|
{
|
|
// Fade-out takes LOGO_FADE_TIME seconds.
|
|
var elapsedFinished = elapsed - completeTime;
|
|
|
|
logo.alpha = 1.0 - MathUtil.easeInOutCirc(elapsedFinished / LOGO_FADE_TIME);
|
|
logo.scaleX = (1.0 - MathUtil.easeInBack(elapsedFinished / LOGO_FADE_TIME)) * ratio;
|
|
logo.scaleY = (1.0 - MathUtil.easeInBack(elapsedFinished / LOGO_FADE_TIME)) * ratio;
|
|
logo.x = (this._width - logo.width) / 2;
|
|
logo.y = (this._height - logo.height) / 2;
|
|
|
|
// Fade out progress bar too.
|
|
// progressBar.alpha = logo.alpha;
|
|
progressLeftText.alpha = logo.alpha;
|
|
progressRightText.alpha = logo.alpha;
|
|
box.alpha = logo.alpha;
|
|
dspText.alpha = logo.alpha;
|
|
fnfText.alpha = logo.alpha;
|
|
enhancedText.alpha = logo.alpha;
|
|
stereoText.alpha = logo.alpha;
|
|
progressLines.alpha = logo.alpha;
|
|
|
|
for (piece in progressBarPieces)
|
|
piece.alpha = logo.alpha;
|
|
|
|
return elapsedFinished;
|
|
}
|
|
|
|
function renderLogoFadeIn(elapsed:Float):Void
|
|
{
|
|
// Fade-in takes LOGO_FADE_TIME seconds.
|
|
logo.alpha = MathUtil.easeInOutCirc(elapsed / LOGO_FADE_TIME);
|
|
logo.scaleX = MathUtil.easeOutBack(elapsed / LOGO_FADE_TIME) * ratio;
|
|
logo.scaleY = MathUtil.easeOutBack(elapsed / LOGO_FADE_TIME) * ratio;
|
|
logo.x = (this._width - logo.width) / 2;
|
|
logo.y = (this._height - logo.height) / 2;
|
|
}
|
|
|
|
#if html5
|
|
// These fields only exist on Web builds.
|
|
|
|
/**
|
|
* Format the layout of the site lock screen.
|
|
*/
|
|
override function createSiteLockFailureScreen():Void
|
|
{
|
|
// addChild(createSiteLockFailureBackground(Constants.COLOR_PRELOADER_LOCK_BG, Constants.COLOR_PRELOADER_LOCK_BG));
|
|
// addChild(createSiteLockFailureIcon(Constants.COLOR_PRELOADER_LOCK_FG, 0.9));
|
|
// addChild(createSiteLockFailureText(30));
|
|
}
|
|
|
|
/**
|
|
* Format the text of the site lock screen.
|
|
*/
|
|
override function adjustSiteLockTextFields(titleText:TextField, bodyText:TextField, hyperlinkText:TextField):Void
|
|
{
|
|
var titleFormat = titleText.defaultTextFormat;
|
|
titleFormat.align = TextFormatAlign.CENTER;
|
|
titleFormat.color = Constants.COLOR_PRELOADER_LOCK_FONT;
|
|
titleText.setTextFormat(titleFormat);
|
|
|
|
var bodyFormat = bodyText.defaultTextFormat;
|
|
bodyFormat.align = TextFormatAlign.CENTER;
|
|
bodyFormat.color = Constants.COLOR_PRELOADER_LOCK_FONT;
|
|
bodyText.setTextFormat(bodyFormat);
|
|
|
|
var hyperlinkFormat = hyperlinkText.defaultTextFormat;
|
|
hyperlinkFormat.align = TextFormatAlign.CENTER;
|
|
hyperlinkFormat.color = Constants.COLOR_PRELOADER_LOCK_LINK;
|
|
hyperlinkText.setTextFormat(hyperlinkFormat);
|
|
}
|
|
#end
|
|
|
|
override function destroy():Void
|
|
{
|
|
// Ensure the graphics are properly destroyed and GC'd.
|
|
removeChild(logo);
|
|
// removeChild(progressBar);
|
|
logo = null;
|
|
super.destroy();
|
|
}
|
|
|
|
override function onLoaded():Void
|
|
{
|
|
super.onLoaded();
|
|
// We're not ACTUALLY finished.
|
|
// This function gets called when the DownloadingAssets step is done.
|
|
// We need to wait for the other steps, then the logo to fade out.
|
|
_loaded = false;
|
|
downloadingAssetsComplete = true;
|
|
}
|
|
}
|
|
|
|
enum FunkinPreloaderState
|
|
{
|
|
/**
|
|
* The state before downloading has begun.
|
|
* Moves to either `DownloadingAssets` or `CachingGraphics` based on platform.
|
|
*/
|
|
NotStarted;
|
|
|
|
/**
|
|
* Downloading assets.
|
|
* On HTML5, Lime will do this for us, before calling `onLoaded`.
|
|
* On Desktop, this step will be completed immediately, and we'll go straight to `CachingGraphics`.
|
|
*/
|
|
DownloadingAssets;
|
|
|
|
/**
|
|
* Preloading play assets.
|
|
* Loads the `manifest.json` for the `gameplay` library.
|
|
* If we make the base preloader do this, it will download all the assets as well,
|
|
* so we have to do it ourselves.
|
|
*/
|
|
PreloadingPlayAssets;
|
|
|
|
/**
|
|
* Loading FireTongue, loading Polymod, parsing and instantiating module scripts.
|
|
*/
|
|
InitializingScripts;
|
|
|
|
/**
|
|
* Loading all graphics from the `core` library to the cache.
|
|
*/
|
|
CachingGraphics;
|
|
|
|
/**
|
|
* Loading all audio from the `core` library to the cache.
|
|
*/
|
|
CachingAudio;
|
|
|
|
/**
|
|
* Loading all data files from the `core` library to the cache.
|
|
*/
|
|
CachingData;
|
|
|
|
/**
|
|
* Parsing all XML files from the `core` library into FlxFramesCollections and caching them.
|
|
*/
|
|
ParsingSpritesheets;
|
|
|
|
/**
|
|
* Parsing stage data and scripts.
|
|
*/
|
|
ParsingStages;
|
|
|
|
/**
|
|
* Parsing character data and scripts.
|
|
*/
|
|
ParsingCharacters;
|
|
|
|
/**
|
|
* Parsing song data and scripts.
|
|
*/
|
|
ParsingSongs;
|
|
|
|
/**
|
|
* Finishing up.
|
|
*/
|
|
Complete;
|
|
|
|
#if TOUCH_HERE_TO_PLAY
|
|
/**
|
|
* Touch Here to Play is displayed.
|
|
*/
|
|
TouchHereToPlay;
|
|
#end
|
|
}
|