2023-08-08 19:41:48 +00:00
|
|
|
package funkin.ui.title;
|
|
|
|
|
2023-08-08 20:37:17 +00:00
|
|
|
#if html5
|
|
|
|
import funkin.graphics.video.FlxVideo;
|
2024-01-13 02:52:28 +00:00
|
|
|
#end
|
2024-12-17 14:43:16 +00:00
|
|
|
#if hxvlc
|
2024-09-21 00:11:12 +00:00
|
|
|
import funkin.graphics.video.FunkinVideoSprite;
|
2023-08-08 20:37:17 +00:00
|
|
|
#end
|
2025-04-30 04:31:31 +00:00
|
|
|
#if FEATURE_TOUCH_CONTROLS
|
2025-01-02 13:49:51 +00:00
|
|
|
import funkin.util.TouchUtil;
|
|
|
|
#end
|
2023-11-07 09:04:22 +00:00
|
|
|
import funkin.ui.MusicBeatState;
|
2025-06-02 23:50:23 +00:00
|
|
|
import funkin.ui.FullScreenScaleMode;
|
2025-07-09 01:24:32 +00:00
|
|
|
import flixel.FlxG;
|
2025-06-02 23:50:23 +00:00
|
|
|
import flixel.math.FlxMath;
|
|
|
|
import flixel.util.FlxColor;
|
|
|
|
import flixel.addons.display.FlxPieDial;
|
2023-08-08 20:37:17 +00:00
|
|
|
|
2023-08-08 19:41:48 +00:00
|
|
|
/**
|
2025-08-13 16:14:49 +00:00
|
|
|
* After 40 seconds of inactivity on the title screen,
|
2023-08-08 19:41:48 +00:00
|
|
|
* the game will enter the Attract state, as a reference to physical arcade machines.
|
|
|
|
*
|
2025-08-13 16:14:49 +00:00
|
|
|
* In the current version, this just plays generic game/merch trailers,
|
|
|
|
* but this can be updated to include gameplay footage, or something more elaborate.
|
2023-08-08 19:41:48 +00:00
|
|
|
*/
|
|
|
|
class AttractState extends MusicBeatState
|
|
|
|
{
|
2025-07-09 01:24:32 +00:00
|
|
|
/**
|
|
|
|
* The videos that can be played by the Attract state.
|
|
|
|
* @param path The path to the video to play.
|
2025-08-13 16:14:49 +00:00
|
|
|
* This used
|
2025-07-09 01:24:32 +00:00
|
|
|
*/
|
2025-08-13 16:14:49 +00:00
|
|
|
static final VIDEO_PATHS:Array<{path:String}> = [
|
|
|
|
{path: Paths.videos('mobileRelease')},
|
|
|
|
{path: Paths.videos('boyfriendEverywhere')},
|
2025-07-09 01:24:32 +00:00
|
|
|
];
|
2024-12-17 14:43:16 +00:00
|
|
|
|
2025-08-13 16:14:49 +00:00
|
|
|
static var nextVideoToPlay:Int = 0;
|
|
|
|
|
2025-07-09 01:24:32 +00:00
|
|
|
/**
|
|
|
|
* Duration you need to touch for to skip the video.
|
|
|
|
*/
|
|
|
|
static final HOLD_TIME:Float = 1.5;
|
2023-08-08 20:37:17 +00:00
|
|
|
|
2025-06-02 23:50:23 +00:00
|
|
|
var pie:FlxPieDial;
|
|
|
|
var holdDelta:Float = 0;
|
|
|
|
|
2023-08-08 20:37:17 +00:00
|
|
|
public override function create():Void
|
|
|
|
{
|
|
|
|
// Pause existing music.
|
2024-03-23 21:50:48 +00:00
|
|
|
if (FlxG.sound.music != null)
|
|
|
|
{
|
|
|
|
FlxG.sound.music.destroy();
|
|
|
|
FlxG.sound.music = null;
|
|
|
|
}
|
2023-08-08 20:37:17 +00:00
|
|
|
|
|
|
|
#if html5
|
2025-07-09 01:24:32 +00:00
|
|
|
var videoPath:String = getVideoPath();
|
|
|
|
trace('Playing web video ${videoPath}');
|
|
|
|
playVideoHTML5(videoPath);
|
2024-01-13 02:52:28 +00:00
|
|
|
#end
|
|
|
|
|
2024-12-17 14:43:16 +00:00
|
|
|
#if hxvlc
|
2025-07-09 01:24:32 +00:00
|
|
|
var videoPath:String = getVideoPath();
|
|
|
|
trace('Playing native video ${videoPath}');
|
|
|
|
playVideoNative(videoPath);
|
2023-08-08 20:37:17 +00:00
|
|
|
#end
|
2025-06-02 23:50:23 +00:00
|
|
|
|
|
|
|
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);
|
2023-08-08 20:37:17 +00:00
|
|
|
}
|
|
|
|
|
2025-07-09 01:24:32 +00:00
|
|
|
/**
|
|
|
|
* Get the path of a random video to display to the user.
|
|
|
|
* @return The video path to play.
|
|
|
|
*/
|
|
|
|
function getVideoPath():String
|
|
|
|
{
|
2025-08-13 16:14:49 +00:00
|
|
|
var result:String = VIDEO_PATHS[nextVideoToPlay].path;
|
2025-07-09 01:24:32 +00:00
|
|
|
|
2025-08-13 16:14:49 +00:00
|
|
|
nextVideoToPlay = (nextVideoToPlay + 1) % VIDEO_PATHS.length;
|
2025-07-09 01:24:32 +00:00
|
|
|
|
|
|
|
#if html5
|
|
|
|
result = Paths.stripLibrary(result);
|
|
|
|
#end
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2023-08-08 20:37:17 +00:00
|
|
|
#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!');
|
|
|
|
}
|
|
|
|
}
|
2024-01-13 02:52:28 +00:00
|
|
|
#end
|
|
|
|
|
2024-12-17 14:43:16 +00:00
|
|
|
#if hxvlc
|
2024-09-21 00:11:12 +00:00
|
|
|
var vid:FunkinVideoSprite;
|
2023-08-08 20:37:17 +00:00
|
|
|
|
|
|
|
function playVideoNative(filePath:String):Void
|
|
|
|
{
|
|
|
|
// Video displays OVER the FlxState.
|
2024-09-21 00:11:12 +00:00
|
|
|
vid = new FunkinVideoSprite(0, 0);
|
2023-08-08 20:37:17 +00:00
|
|
|
|
|
|
|
if (vid != null)
|
|
|
|
{
|
|
|
|
vid.zIndex = 0;
|
2025-01-03 20:29:31 +00:00
|
|
|
vid.active = false;
|
2025-06-12 14:56:26 +00:00
|
|
|
vid.bitmap.onEncounteredError.add(function(msg:String):Void {
|
|
|
|
trace('[VLC] Encountered an error: $msg');
|
|
|
|
|
|
|
|
onAttractEnd();
|
|
|
|
});
|
2023-08-08 20:37:17 +00:00
|
|
|
vid.bitmap.onEndReached.add(onAttractEnd);
|
2025-04-14 18:53:16 +00:00
|
|
|
vid.bitmap.onFormatSetup.add(() -> {
|
|
|
|
vid.setGraphicSize(FlxG.initialWidth, FlxG.initialHeight);
|
|
|
|
vid.updateHitbox();
|
|
|
|
vid.screenCenter();
|
|
|
|
});
|
2023-08-08 20:37:17 +00:00
|
|
|
|
|
|
|
add(vid);
|
2024-12-22 22:45:54 +00:00
|
|
|
|
|
|
|
if (vid.load(filePath)) vid.play();
|
2023-08-08 20:37:17 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
trace('ALERT: Video is null! Could not play cutscene!');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#end
|
2023-08-08 19:41:48 +00:00
|
|
|
|
2023-08-08 20:37:17 +00:00
|
|
|
public override function update(elapsed:Float):Void
|
|
|
|
{
|
|
|
|
super.update(elapsed);
|
|
|
|
|
2024-12-22 22:45:54 +00:00
|
|
|
// If the user presses any button or hold their screen for 1.5 seconds, skip the video.
|
2025-06-02 23:50:23 +00:00
|
|
|
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)
|
2023-08-08 20:37:17 +00:00
|
|
|
{
|
2025-07-09 01:24:32 +00:00
|
|
|
holdDelta += elapsed;
|
2025-08-13 17:21:24 +00:00
|
|
|
holdDelta = holdDelta.clamp(0, HOLD_TIME);
|
2025-07-09 01:24:32 +00:00
|
|
|
|
2025-06-02 23:50:23 +00:00
|
|
|
pie.scale.x = pie.scale.y = FlxMath.lerp(pie.scale.x, 1.3, Math.exp(-elapsed * 140.0));
|
2023-08-08 20:37:17 +00:00
|
|
|
}
|
2025-06-02 23:50:23 +00:00
|
|
|
else
|
|
|
|
{
|
2025-07-09 01:24:32 +00:00
|
|
|
holdDelta = FlxMath.lerp(holdDelta, -0.1, (elapsed * 3).clamp(0, 1));
|
2025-08-13 17:21:24 +00:00
|
|
|
holdDelta = holdDelta.clamp(0, HOLD_TIME);
|
2025-06-02 23:50:23 +00:00
|
|
|
pie.scale.x = pie.scale.y = FlxMath.lerp(pie.scale.x, 1, Math.exp(-elapsed * 160.0));
|
|
|
|
}
|
|
|
|
|
2025-07-09 01:24:32 +00:00
|
|
|
pie.amount = Math.min(1, Math.max(0, (holdDelta / HOLD_TIME) * 1.025));
|
2025-06-02 23:50:23 +00:00
|
|
|
pie.alpha = FlxMath.remapToRange(pie.amount, 0.025, 1, 0, 1);
|
|
|
|
|
2025-07-09 01:24:32 +00:00
|
|
|
// If the dial is full, skip the video.
|
2025-06-02 23:50:23 +00:00
|
|
|
if (pie.amount >= 1) onAttractEnd();
|
2023-08-08 20:37:17 +00:00
|
|
|
}
|
2023-08-08 19:41:48 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* When the attraction state ends (after the video ends or the user presses any button),
|
|
|
|
* switch immediately to the title screen.
|
|
|
|
*/
|
|
|
|
function onAttractEnd():Void
|
|
|
|
{
|
2023-08-08 20:37:17 +00:00
|
|
|
#if html5
|
|
|
|
if (vid != null)
|
|
|
|
{
|
|
|
|
remove(vid);
|
|
|
|
}
|
2024-01-13 02:52:28 +00:00
|
|
|
#end
|
|
|
|
|
2024-12-17 14:43:16 +00:00
|
|
|
#if hxvlc
|
2023-08-08 20:37:17 +00:00
|
|
|
if (vid != null)
|
|
|
|
{
|
|
|
|
vid.stop();
|
|
|
|
remove(vid);
|
|
|
|
}
|
|
|
|
#end
|
2024-01-13 02:52:28 +00:00
|
|
|
|
2024-12-17 14:43:16 +00:00
|
|
|
#if (html5 || hxvlc)
|
2023-08-08 20:37:17 +00:00
|
|
|
vid.destroy();
|
|
|
|
vid = null;
|
2024-01-13 02:52:28 +00:00
|
|
|
#end
|
2023-08-08 20:37:17 +00:00
|
|
|
|
2024-02-06 00:46:11 +00:00
|
|
|
FlxG.switchState(() -> new TitleState());
|
2023-08-08 19:41:48 +00:00
|
|
|
}
|
|
|
|
}
|