using System.Threading.Tasks; using Cirno.Scripts.Resources; using Cirno.Scripts.Utils; using Godot; using GTweens.Builders; using GTweensGodot.Extensions; public partial class GlobalState : Node { public static GlobalState Instance { get; private set; } public Node CurrentScene { get; set; } private ColorRect _fader { get; set; } public SessionSettings SessionSettings { get; set; } = new(); private PackedScene _plaqueTemplate; private Control _loadingPlaque; public override void _Ready() { Instance = this; this.ProcessMode = ProcessModeEnum.Always; Viewport root = GetTree().Root; // Using a negative index counts from the end, so this gets the last child node of `root`. CurrentScene = root.GetChild(-1); _fader = CreateFader(); //LoadPlaque(); } public void GotoScene(string path) { // This function will usually be called from a signal callback, // or some other function from the current scene. // Deleting the current scene at this point is // a bad idea, because it may still be executing code. // This will result in a crash or unexpected behavior. // The solution is to defer the load to a later time, when // we can be sure that no code from the current scene is running: //CallDeferred(MethodName.DeferredGotoScene, path, new MapStartDataResource()); GoToScene(path, new MapStartDataResource()); } public void GoToScene(string path, MapStartDataResource startData) { GTweenSequenceBuilder.New() .AppendCallback(() => { _loadingPlaque?.Show(); }) //.Append(_fader.TweenModulateAlpha(0, 0f)) .Append(_fader.TweenModulateAlpha(1, 0.5f)) .AppendCallback(() => { CallDeferred(MethodName.DeferredGotoScene, path, startData); }) .Build() .PlayUnpausable(); //CallDeferred(MethodName.DeferredGotoScene, path, startData); } private void DeferredGotoScene(string path, MapStartDataResource startData = null) { // It is now safe to remove the current scene. CurrentScene.Free(); // Load a new scene. var nextScene = GD.Load(path); // Instance the new scene. CurrentScene = nextScene.Instantiate(); // Add it to the active scene, as child of root. GetTree().Root.AddChild(CurrentScene); // Optionally, to make it compatible with the SceneTree.change_scene_to_file() API. GetTree().CurrentScene = CurrentScene; // // Update current scene here too // CurrentScene = GetTree().Root.GetChild(-1); if (startData is not null && GameManager.Instance is not null) { // Call deferred if it gives issues DeferredAddStartDataToGameManager(startData); } if (GameManager.Instance is not null) { GameManager.Instance.ApplySessionState(SessionSettings); } FadeIn(); } private void DeferredAddStartDataToGameManager(MapStartDataResource resource) { GameManager.Instance?.ApplyMapStartData(resource); } private Control LoadPlaque() { _plaqueTemplate = GD.Load("res://Scenes/HUD/LoadingPlaque.tscn"); return _plaqueTemplate.Instantiate(); } private ColorRect CreateFader() { var canvas = new CanvasLayer(); canvas.ProcessMode = ProcessModeEnum.Always; var rect = new ColorRect(); rect.ZAsRelative = false; rect.ZIndex = 100; rect.SetAnchorsPreset(Control.LayoutPreset.FullRect); rect.Color = new Color(Colors.Black, 1f); rect.ProcessMode = ProcessModeEnum.Always; rect.Modulate = new Color(0,0,0,0); rect.MouseFilter = Control.MouseFilterEnum.Ignore; canvas.CallDeferred("add_child", rect); this.CallDeferred("add_child", canvas); _loadingPlaque = LoadPlaque(); canvas.CallDeferred("add_child", _loadingPlaque); _loadingPlaque.Hide(); return rect; } public void FadeOut() { _fader.TweenModulateAlpha(1, 0.5f).PlayUnpausable(); _loadingPlaque?.Show(); } public void FadeIn() { _loadingPlaque?.Hide(); _fader.TweenModulateAlpha(0, 1f).PlayUnpausable(); } }