cirnogodot/Scripts/GlobalState.cs

312 lines
9.4 KiB
C#
Raw Permalink Normal View History

2025-03-25 16:48:11 +01:00
using System;
2025-04-04 10:40:02 +02:00
using System.Linq;
2025-02-24 13:47:38 +01:00
using System.Threading.Tasks;
2025-04-08 15:17:21 +02:00
using Cirno.Scripts.Enums;
2025-02-21 11:39:22 +01:00
using Cirno.Scripts.Resources;
2025-02-27 08:37:55 +01:00
using Cirno.Scripts.Utils;
2025-02-20 21:26:51 +01:00
using Godot;
2025-04-04 10:40:02 +02:00
using Godot.Collections;
2025-02-24 13:47:38 +01:00
using GTweens.Builders;
using GTweensGodot.Extensions;
2025-02-20 21:26:51 +01:00
public partial class GlobalState : Node
{
public static GlobalState Instance { get; private set; }
2025-03-24 16:56:35 +01:00
public static SessionSettings Session => GlobalState.Instance.SessionSettings;
2025-02-20 21:26:51 +01:00
public Node CurrentScene { get; set; }
2025-02-24 13:47:38 +01:00
private ColorRect _fader { get; set; }
2025-02-20 21:26:51 +01:00
2025-03-10 14:23:28 +01:00
public SessionSettings SessionSettings { get; set; } = new();
2025-02-27 08:37:55 +01:00
2025-03-06 15:45:22 +01:00
private PackedScene _plaqueTemplate;
private Control _loadingPlaque;
2025-03-25 16:48:11 +01:00
private readonly StringName _menuMouseTexturePath = "uid://d3oxwik1uoe4j";
2025-05-08 12:32:16 +02:00
private readonly StringName _reticuleMouseTexturePath = "uid://b76bhqv247nft";
2025-03-25 16:48:11 +01:00
2025-04-02 17:42:55 +02:00
private readonly StringName _mapsDatabaseResource = "uid://blf2ii0j3fqil";
private MapsDatabase _mapsDatabase;
2025-09-10 16:16:05 +02:00
public MapsDatabase MapsDatabase => _mapsDatabase;
2025-04-02 17:42:55 +02:00
2025-03-25 16:48:11 +01:00
private Texture2D _menuMouseTexture;
private Image _menuMouseImage;
2025-05-08 12:32:16 +02:00
private Texture2D _reticuleMouseTexture;
private Image _reticuleMouseImage;
2025-03-25 16:48:11 +01:00
2025-05-08 12:32:16 +02:00
public bool UseMenuCursor { get; set; } = true;
2025-09-10 16:16:05 +02:00
2025-02-20 21:26:51 +01:00
public override void _Ready()
{
Instance = this;
2025-02-24 10:37:27 +01:00
this.ProcessMode = ProcessModeEnum.Always;
2025-04-02 17:42:55 +02:00
_mapsDatabase = ResourceLoader.Load<MapsDatabase>(_mapsDatabaseResource);
2025-04-10 19:04:06 +02:00
2025-02-20 21:26:51 +01:00
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);
2025-02-24 13:47:38 +01:00
_fader = CreateFader();
2025-04-10 19:04:06 +02:00
_menuMouseTexture = ResourceLoader.Load<Texture2D>(_menuMouseTexturePath);
2025-03-25 16:48:11 +01:00
_menuMouseImage = _menuMouseTexture.GetImage();
2025-09-10 16:16:05 +02:00
2025-05-08 12:32:16 +02:00
_reticuleMouseTexture = ResourceLoader.Load<Texture2D>(_reticuleMouseTexturePath);
_reticuleMouseImage = _reticuleMouseTexture.GetImage();
2025-03-25 16:48:11 +01:00
GetTree().GetRoot().SizeChanged += OnSizeChanged;
//_mouseTexture =
OnSizeChanged();
2025-03-06 15:45:22 +01:00
//LoadPlaque();
2025-02-20 21:26:51 +01:00
}
2025-03-25 16:48:11 +01:00
private void OnSizeChanged()
2025-04-04 09:53:29 +02:00
{
ResizeCursor();
}
2025-05-08 12:32:16 +02:00
public void ChangeCursor(bool useMenu)
{
UseMenuCursor = useMenu;
ResizeCursor();
}
2025-04-04 09:53:29 +02:00
public void ResizeCursor()
2025-03-25 16:48:11 +01:00
{
var root = GetTree().GetRoot();
var baseSize = root.ContentScaleSize;
var newSize = root.Size;
2025-04-10 19:04:06 +02:00
2025-03-25 16:48:11 +01:00
int scaleX = newSize.X / baseSize.X;
int scaleY = newSize.Y / baseSize.Y;
int scale = Math.Min(scaleX, scaleY); // Ensure pixel-perfect scaling
2025-05-08 12:32:16 +02:00
ResizeCursor(UseMenuCursor ? scale / 2 : scale, UseMenuCursor);
2025-03-25 16:48:11 +01:00
}
2025-02-20 21:26:51 +01:00
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:
2025-04-10 19:04:06 +02:00
2025-02-24 13:47:38 +01:00
//CallDeferred(MethodName.DeferredGotoScene, path, new MapStartDataResource());
2025-04-10 19:04:06 +02:00
2025-02-24 13:47:38 +01:00
GoToScene(path, new MapStartDataResource());
2025-02-20 21:26:51 +01:00
}
2025-02-21 11:39:22 +01:00
public void GoToScene(string path, MapStartDataResource startData)
{
2025-02-24 13:47:38 +01:00
GTweenSequenceBuilder.New()
2025-04-10 19:04:06 +02:00
.AppendCallback(() => { _loadingPlaque?.Show(); })
2025-02-24 13:47:38 +01:00
//.Append(_fader.TweenModulateAlpha(0, 0f))
.Append(_fader.TweenModulateAlpha(1, 0.5f))
2025-04-10 19:04:06 +02:00
.AppendCallback(() => { CallDeferred(MethodName.DeferredGotoScene, path, startData); })
2025-02-24 13:47:38 +01:00
.Build()
.PlayUnpausable();
2025-04-10 19:04:06 +02:00
2025-02-24 13:47:38 +01:00
//CallDeferred(MethodName.DeferredGotoScene, path, startData);
2025-02-21 11:39:22 +01:00
}
2025-04-10 19:04:06 +02:00
2025-04-02 17:42:55 +02:00
public void GotoScene(MapResource map)
{
this.SessionSettings.LevelNumber = map.LevelId;
GoToScene(map.ScenePath.ToString(), map.StartData);
}
2025-02-21 11:39:22 +01:00
private void DeferredGotoScene(string path, MapStartDataResource startData = null)
2025-02-20 21:26:51 +01:00
{
// It is now safe to remove the current scene.
CurrentScene.Free();
2025-03-28 14:02:33 +01:00
//var sceneParent = CurrentScene.GetParent();
//sceneParent.RemoveChild(CurrentScene);
//sceneParent.QueueFree();
2025-04-10 19:04:06 +02:00
2025-02-20 21:26:51 +01:00
// Load a new scene.
var nextScene = GD.Load<PackedScene>(path);
2025-04-10 19:04:06 +02:00
2025-02-20 21:26:51 +01:00
// Instance the new scene.
CurrentScene = nextScene.Instantiate();
// Add it to the active scene, as child of root.
GetTree().Root.AddChild(CurrentScene);
2025-04-10 19:04:06 +02:00
2025-02-20 21:26:51 +01:00
// Optionally, to make it compatible with the SceneTree.change_scene_to_file() API.
GetTree().CurrentScene = CurrentScene;
2025-04-10 19:04:06 +02:00
// // Update current scene here too
// CurrentScene = GetTree().Root.GetChild(-1);
2025-04-10 19:04:06 +02:00
2025-02-24 21:54:20 +01:00
if (startData is not null && GameManager.Instance is not null)
2025-02-21 11:39:22 +01:00
{
// Call deferred if it gives issues
DeferredAddStartDataToGameManager(startData);
}
2025-02-27 08:37:55 +01:00
2025-03-28 14:02:33 +01:00
// Do not do this here because it might get the old gamemanager instead
// if (GameManager.Instance is not null) {
// GameManager.Instance.ApplySessionState(SessionSettings);
// }
2025-04-10 19:04:06 +02:00
2025-02-24 13:47:38 +01:00
FadeIn();
2025-02-20 21:26:51 +01:00
}
2025-02-21 11:39:22 +01:00
private void DeferredAddStartDataToGameManager(MapStartDataResource resource)
{
2025-02-24 21:54:20 +01:00
GameManager.Instance?.ApplyMapStartData(resource);
2025-02-21 11:39:22 +01:00
}
2025-02-24 13:47:38 +01:00
2025-03-06 15:45:22 +01:00
private Control LoadPlaque()
{
_plaqueTemplate = GD.Load<PackedScene>("res://Scenes/HUD/LoadingPlaque.tscn");
return _plaqueTemplate.Instantiate<Control>();
}
2025-04-10 19:04:06 +02:00
2025-02-24 13:47:38 +01:00
private ColorRect CreateFader()
{
var canvas = new CanvasLayer();
canvas.ProcessMode = ProcessModeEnum.Always;
2025-03-06 15:45:22 +01:00
2025-02-24 13:47:38 +01:00
var rect = new ColorRect();
2025-03-06 15:56:58 +01:00
rect.ZAsRelative = false;
rect.ZIndex = 100;
2025-02-24 13:47:38 +01:00
rect.SetAnchorsPreset(Control.LayoutPreset.FullRect);
rect.Color = new Color(Colors.Black, 1f);
rect.ProcessMode = ProcessModeEnum.Always;
2025-04-10 19:04:06 +02:00
rect.Modulate = new Color(0, 0, 0, 0);
2025-02-24 13:47:38 +01:00
rect.MouseFilter = Control.MouseFilterEnum.Ignore;
canvas.CallDeferred("add_child", rect);
2025-04-10 19:04:06 +02:00
2025-02-24 13:47:38 +01:00
this.CallDeferred("add_child", canvas);
2025-04-10 19:04:06 +02:00
2025-03-06 15:45:22 +01:00
_loadingPlaque = LoadPlaque();
canvas.CallDeferred("add_child", _loadingPlaque);
_loadingPlaque.Hide();
2025-04-10 19:04:06 +02:00
2025-02-24 13:47:38 +01:00
return rect;
}
public void FadeOut()
{
_fader.TweenModulateAlpha(1, 0.5f).PlayUnpausable();
2025-03-06 15:45:22 +01:00
_loadingPlaque?.Show();
2025-02-24 13:47:38 +01:00
}
public void FadeIn()
{
2025-03-06 15:45:22 +01:00
_loadingPlaque?.Hide();
2025-02-24 13:47:38 +01:00
_fader.TweenModulateAlpha(0, 1f).PlayUnpausable();
}
2025-03-24 16:56:35 +01:00
2025-04-04 10:40:02 +02:00
private readonly string SaveNameFile = "user://savegame.save";
2025-04-10 19:04:06 +02:00
2025-03-24 16:56:35 +01:00
public void SaveGame()
{
var items = InventoryManager.Instance.Save();
2025-04-10 19:04:06 +02:00
2025-04-01 13:44:23 +02:00
SessionSettings.Items = items;
2025-04-10 19:04:06 +02:00
2025-03-24 16:56:35 +01:00
var serializedSavedata = new Godot.Collections.Dictionary<string, Variant>()
{
{ "Items", items },
2025-04-08 15:17:21 +02:00
{ "Level", SessionSettings.LevelNumber },
2025-09-10 16:16:05 +02:00
{ "MapId", SessionSettings.MapId },
2025-04-10 19:04:06 +02:00
{ "Difficulty", (int)SessionSettings.Difficulty }
2025-03-24 16:56:35 +01:00
};
2025-04-04 10:40:02 +02:00
var saveFile = FileAccess.Open(SaveNameFile, FileAccess.ModeFlags.Write);
2025-03-24 16:56:35 +01:00
var jsonString = Json.Stringify(serializedSavedata);
saveFile.StoreLine(jsonString);
2025-04-10 19:04:06 +02:00
2025-04-04 10:40:02 +02:00
GD.Print("Saved data successfully");
2025-03-24 16:56:35 +01:00
}
2025-04-10 19:04:06 +02:00
2025-04-04 10:40:02 +02:00
public bool LoadGame()
2025-03-24 16:56:35 +01:00
{
2025-04-04 10:40:02 +02:00
if (!FileAccess.FileExists(SaveNameFile))
{
return false; // Error! We don't have a save to load.
}
2025-04-10 19:04:06 +02:00
2025-04-04 10:40:02 +02:00
using var saveFile = FileAccess.Open(SaveNameFile, FileAccess.ModeFlags.Read);
var jsonString = saveFile.GetLine();
var json = new Json();
var parseResult = json.Parse(jsonString);
if (parseResult != Error.Ok)
{
GD.Print($"JSON Parse Error: {json.GetErrorMessage()} in {jsonString} at line {json.GetErrorLine()}");
return false;
}
2025-04-10 19:04:06 +02:00
2025-04-04 10:40:02 +02:00
var deserializedSaveData = new Dictionary<string, Variant>((Dictionary)json.Data);
Dictionary<string, int> items = (Dictionary<string, int>)deserializedSaveData["Items"];
2025-04-08 15:17:21 +02:00
DifficultyLevel difficulty = (DifficultyLevel)deserializedSaveData["Difficulty"].AsInt32();
2025-04-10 19:04:06 +02:00
2025-09-10 16:16:05 +02:00
int levelNumber = deserializedSaveData["Level"].AsInt32();
StringName mapId = deserializedSaveData["MapId"].AsStringName();
2025-04-04 10:40:02 +02:00
2025-09-10 16:16:05 +02:00
var levelData = _mapsDatabase.FindMap(mapId);
2025-04-04 10:40:02 +02:00
if (levelData is null)
{
return false;
}
2025-04-10 19:04:06 +02:00
2025-04-04 10:40:02 +02:00
this.SessionSettings.NewSession();
SessionSettings.LevelNumber = levelNumber;
2025-09-10 16:16:05 +02:00
SessionSettings.MapId = mapId;
2025-04-04 10:40:02 +02:00
SessionSettings.Items = items;
2025-04-08 15:17:21 +02:00
SessionSettings.Difficulty = difficulty;
2025-04-10 19:04:06 +02:00
2025-04-04 10:40:02 +02:00
this.GotoScene(levelData);
2025-04-10 19:04:06 +02:00
2025-04-04 10:40:02 +02:00
return true;
2025-03-24 16:56:35 +01:00
}
2025-04-10 19:04:06 +02:00
2025-05-08 12:32:16 +02:00
private void ResizeCursor(float scale, bool useMenuCursor)
2025-03-25 16:48:11 +01:00
{
2025-05-08 12:32:16 +02:00
Image scaled;
Vector2I size;
if (useMenuCursor)
{
2025-09-10 16:16:05 +02:00
scaled = (Image)_menuMouseImage.Duplicate();
size = (Vector2I)(scale * _menuMouseTexture.GetSize());
2025-05-08 12:32:16 +02:00
}
else
{
scaled = (Image)_reticuleMouseImage.Duplicate();
size = (Vector2I)(scale * _reticuleMouseTexture.GetSize());
}
2025-04-10 19:04:06 +02:00
2025-03-25 16:48:11 +01:00
scaled.Resize(size.X, size.Y, Image.Interpolation.Nearest);
2025-04-10 19:04:06 +02:00
2025-09-10 16:16:05 +02:00
Input.SetCustomMouseCursor(scaled, Input.CursorShape.Arrow,
useMenuCursor ? Vector2.Zero : new Vector2(size.X / 2, size.Y / 2));
2025-05-08 12:32:16 +02:00
//DisplayServer.CursorSetCustomImage(scaled);
2025-03-25 16:48:11 +01:00
}
2025-04-01 11:23:37 +02:00
public void RestartLevel()
{
GotoScene(CurrentScene.GetSceneFilePath());
}
2025-02-20 21:26:51 +01:00
}