mirror of
https://github.com/ninjamuffin99/Funkin.git
synced 2025-09-01 03:15:53 +00:00
207 lines
4.9 KiB
Haxe
207 lines
4.9 KiB
Haxe
package funkin.ui.title;
|
|
|
|
#if html5
|
|
import funkin.graphics.video.FlxVideo;
|
|
#end
|
|
#if hxvlc
|
|
import funkin.graphics.video.FunkinVideoSprite;
|
|
#end
|
|
#if FEATURE_TOUCH_CONTROLS
|
|
import funkin.util.TouchUtil;
|
|
#end
|
|
import funkin.ui.MusicBeatState;
|
|
import funkin.ui.FullScreenScaleMode;
|
|
import flixel.FlxG;
|
|
import flixel.math.FlxMath;
|
|
import flixel.util.FlxColor;
|
|
import flixel.addons.display.FlxPieDial;
|
|
|
|
/**
|
|
* After 40 seconds of inactivity on the title screen,
|
|
* the game will enter the Attract state, as a reference to physical arcade machines.
|
|
*
|
|
* In the current version, this just plays generic game/merch trailers,
|
|
* but this can be updated to include gameplay footage, or something more elaborate.
|
|
*/
|
|
class AttractState extends MusicBeatState
|
|
{
|
|
/**
|
|
* The videos that can be played by the Attract state.
|
|
* @param path The path to the video to play.
|
|
* This used
|
|
*/
|
|
static final VIDEO_PATHS:Array<{path:String}> = [
|
|
{path: Paths.videos('mobileRelease')},
|
|
{path: Paths.videos('boyfriendEverywhere')},
|
|
];
|
|
|
|
static var nextVideoToPlay:Int = 0;
|
|
|
|
/**
|
|
* Duration you need to touch for to skip the video.
|
|
*/
|
|
static final HOLD_TIME:Float = 1.5;
|
|
|
|
var pie:FlxPieDial;
|
|
var holdDelta:Float = 0;
|
|
|
|
public override function create():Void
|
|
{
|
|
// Pause existing music.
|
|
if (FlxG.sound.music != null)
|
|
{
|
|
FlxG.sound.music.destroy();
|
|
FlxG.sound.music = null;
|
|
}
|
|
|
|
#if html5
|
|
var videoPath:String = getVideoPath();
|
|
trace('Playing web video ${videoPath}');
|
|
playVideoHTML5(videoPath);
|
|
#end
|
|
|
|
#if hxvlc
|
|
var videoPath:String = getVideoPath();
|
|
trace('Playing native video ${videoPath}');
|
|
playVideoNative(videoPath);
|
|
#end
|
|
|
|
pie = new FlxPieDial(0, 0, 40, FlxColor.WHITE, 45, CIRCLE, true, 20);
|
|
pie.x = FlxG.width - ((pie.width * 1.5) + FullScreenScaleMode.gameNotchSize.x);
|
|
pie.y = FlxG.height - (pie.height * 1.5);
|
|
pie.amount = 0;
|
|
pie.replaceColor(FlxColor.BLACK, 0x8AC5C4C4);
|
|
add(pie);
|
|
}
|
|
|
|
/**
|
|
* Get the path of a random video to display to the user.
|
|
* @return The video path to play.
|
|
*/
|
|
function getVideoPath():String
|
|
{
|
|
var result:String = VIDEO_PATHS[nextVideoToPlay].path;
|
|
|
|
nextVideoToPlay = (nextVideoToPlay + 1) % VIDEO_PATHS.length;
|
|
|
|
#if html5
|
|
result = Paths.stripLibrary(result);
|
|
#end
|
|
|
|
return result;
|
|
}
|
|
|
|
#if html5
|
|
var vid:FlxVideo;
|
|
|
|
function playVideoHTML5(filePath:String):Void
|
|
{
|
|
// Video displays OVER the FlxState.
|
|
vid = new FlxVideo(filePath);
|
|
if (vid != null)
|
|
{
|
|
vid.zIndex = 0;
|
|
|
|
vid.finishCallback = onAttractEnd;
|
|
|
|
add(vid);
|
|
}
|
|
else
|
|
{
|
|
trace('ALERT: Video is null! Could not play cutscene!');
|
|
}
|
|
}
|
|
#end
|
|
|
|
#if hxvlc
|
|
var vid:FunkinVideoSprite;
|
|
|
|
function playVideoNative(filePath:String):Void
|
|
{
|
|
// Video displays OVER the FlxState.
|
|
vid = new FunkinVideoSprite(0, 0);
|
|
|
|
if (vid != null)
|
|
{
|
|
vid.zIndex = 0;
|
|
vid.active = false;
|
|
vid.bitmap.onEncounteredError.add(function(msg:String):Void {
|
|
trace('[VLC] Encountered an error: $msg');
|
|
|
|
onAttractEnd();
|
|
});
|
|
vid.bitmap.onEndReached.add(onAttractEnd);
|
|
vid.bitmap.onFormatSetup.add(() -> {
|
|
vid.setGraphicSize(FlxG.initialWidth, FlxG.initialHeight);
|
|
vid.updateHitbox();
|
|
vid.screenCenter();
|
|
});
|
|
|
|
add(vid);
|
|
|
|
if (vid.load(filePath)) vid.play();
|
|
}
|
|
else
|
|
{
|
|
trace('ALERT: Video is null! Could not play cutscene!');
|
|
}
|
|
}
|
|
#end
|
|
|
|
public override function update(elapsed:Float):Void
|
|
{
|
|
super.update(elapsed);
|
|
|
|
// If the user presses any button or hold their screen for 1.5 seconds, skip the video.
|
|
if ((FlxG.keys.pressed.ANY && !controls.VOLUME_MUTE && !controls.VOLUME_UP && !controls.VOLUME_DOWN) #if FEATURE_TOUCH_CONTROLS
|
|
|| TouchUtil.touch != null && TouchUtil.touch.pressed #end)
|
|
{
|
|
holdDelta += elapsed;
|
|
holdDelta = holdDelta.clamp(0, HOLD_TIME);
|
|
|
|
pie.scale.x = pie.scale.y = FlxMath.lerp(pie.scale.x, 1.3, Math.exp(-elapsed * 140.0));
|
|
}
|
|
else
|
|
{
|
|
holdDelta = FlxMath.lerp(holdDelta, -0.1, (elapsed * 3).clamp(0, 1));
|
|
holdDelta = holdDelta.clamp(0, HOLD_TIME);
|
|
pie.scale.x = pie.scale.y = FlxMath.lerp(pie.scale.x, 1, Math.exp(-elapsed * 160.0));
|
|
}
|
|
|
|
pie.amount = Math.min(1, Math.max(0, (holdDelta / HOLD_TIME) * 1.025));
|
|
pie.alpha = FlxMath.remapToRange(pie.amount, 0.025, 1, 0, 1);
|
|
|
|
// If the dial is full, skip the video.
|
|
if (pie.amount >= 1) onAttractEnd();
|
|
}
|
|
|
|
/**
|
|
* When the attraction state ends (after the video ends or the user presses any button),
|
|
* switch immediately to the title screen.
|
|
*/
|
|
function onAttractEnd():Void
|
|
{
|
|
#if html5
|
|
if (vid != null)
|
|
{
|
|
remove(vid);
|
|
}
|
|
#end
|
|
|
|
#if hxvlc
|
|
if (vid != null)
|
|
{
|
|
vid.stop();
|
|
remove(vid);
|
|
}
|
|
#end
|
|
|
|
#if (html5 || hxvlc)
|
|
vid.destroy();
|
|
vid = null;
|
|
#end
|
|
|
|
FlxG.switchState(() -> new TitleState());
|
|
}
|
|
}
|