cirnogodot/Scripts/GlobalState.cs
2025-03-28 14:02:33 +01:00

222 lines
No EOL
6.6 KiB
C#

using System;
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 static SessionSettings Session => GlobalState.Instance.SessionSettings;
public Node CurrentScene { get; set; }
public string CurrentSceneId { get; private set; }
private ColorRect _fader { get; set; }
public SessionSettings SessionSettings { get; set; } = new();
private PackedScene _plaqueTemplate;
private Control _loadingPlaque;
private readonly StringName _menuMouseTexturePath = "uid://d3oxwik1uoe4j";
private readonly StringName _reticuleMouseTexturePath = "uid://8nns6w0skiy3";
private Texture2D _menuMouseTexture;
private Image _menuMouseImage;
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();
_menuMouseTexture =ResourceLoader.Load<Texture2D>(_menuMouseTexturePath);
_menuMouseImage = _menuMouseTexture.GetImage();
GetTree().GetRoot().SizeChanged += OnSizeChanged;
//_mouseTexture =
OnSizeChanged();
//LoadPlaque();
}
private void OnSizeChanged()
{
var root = GetTree().GetRoot();
var baseSize = root.ContentScaleSize;
var newSize = root.Size;
int scaleX = newSize.X / baseSize.X;
int scaleY = newSize.Y / baseSize.Y;
int scale = Math.Min(scaleX, scaleY); // Ensure pixel-perfect scaling
ResizeCursor(scale / 2);
}
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();
//var sceneParent = CurrentScene.GetParent();
//sceneParent.RemoveChild(CurrentScene);
//sceneParent.QueueFree();
// Load a new scene.
var nextScene = GD.Load<PackedScene>(path);
CurrentSceneId = nextScene.GetSceneUniqueId();
// 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);
}
// Do not do this here because it might get the old gamemanager instead
// 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<PackedScene>("res://Scenes/HUD/LoadingPlaque.tscn");
return _plaqueTemplate.Instantiate<Control>();
}
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();
}
public void SaveGame()
{
var items = InventoryManager.Instance.Save();
var serializedSavedata = new Godot.Collections.Dictionary<string, Variant>()
{
{ "Items", items },
{ "Level", CurrentSceneId }
};
var saveFile = FileAccess.Open("user://savegame.save", FileAccess.ModeFlags.Write);
var jsonString = Json.Stringify(serializedSavedata);
saveFile.StoreLine(jsonString);
}
public void LoadGame()
{
}
public void ResizeCursor(float scale)
{
var scaled = (Image)_menuMouseImage.Duplicate();
var size = (Vector2I)(scale * _menuMouseTexture.GetSize());
scaled.Resize(size.X, size.Y, Image.Interpolation.Nearest);
DisplayServer.CursorSetCustomImage(scaled);
}
}