SupaLidlGame/Utils/World.cs

366 lines
9.2 KiB
C#
Raw Normal View History

2023-03-22 21:22:51 -07:00
using Godot;
using SupaLidlGame.Audio;
2023-03-22 21:22:51 -07:00
using SupaLidlGame.Characters;
2023-07-25 03:47:31 -07:00
using SupaLidlGame.Extensions;
2023-03-26 10:53:45 -07:00
using SupaLidlGame.Scenes;
2023-08-05 23:50:08 -07:00
using SupaLidlGame.State.Global;
2023-08-17 00:13:24 -07:00
using SupaLidlGame.Events;
2023-03-22 21:22:51 -07:00
2023-06-03 18:21:46 -07:00
namespace SupaLidlGame.Utils;
2023-08-03 16:17:34 -07:00
public partial class World : Node
2023-03-22 21:22:51 -07:00
{
2023-08-05 23:50:08 -07:00
public static World Instance { get; private set; }
2023-03-22 21:22:51 -07:00
2023-06-03 18:21:46 -07:00
[Export]
public Map CurrentMap { get; protected set; }
2023-03-22 21:22:51 -07:00
2023-06-03 18:21:46 -07:00
[Export]
public Player CurrentPlayer { get; set; }
2023-03-22 21:22:51 -07:00
2023-07-24 12:40:28 -07:00
[Export]
public Boss CurrentBoss { get; set; }
2023-07-23 11:05:01 -07:00
public UI.UIController UIController { get; set; }
2023-07-22 20:23:48 -07:00
2023-07-24 00:43:21 -07:00
[Export]
public AudioStreamPlayer MusicPlayer { get; set; }
2023-07-25 03:47:31 -07:00
[Export]
public Dialogue.Balloon DialogueBalloon
{
get
{
2023-08-13 16:49:18 -07:00
if (!IsDialogueOpen)
{
var scene = GD.Load<PackedScene>("res://Dialogue/balloon.tscn");
_dialogueBalloon = scene.Instantiate<Dialogue.Balloon>();
_uiViewport.AddChild(_dialogueBalloon);
}
return _dialogueBalloon;
}
set
{
if (_dialogueBalloon != value && _dialogueBalloon is not null)
{
_dialogueBalloon.QueueFree();
}
_dialogueBalloon = value;
}
}
2023-08-13 16:49:18 -07:00
public bool IsDialogueOpen
{
get => _dialogueBalloon is not null && IsInstanceValid(_dialogueBalloon);
}
private Dialogue.Balloon _dialogueBalloon;
private SubViewport _uiViewport;
2023-07-25 03:47:31 -07:00
2023-08-01 02:10:55 -07:00
public State.Global.GlobalState GlobalState { get; set; }
2023-08-17 00:13:24 -07:00
public EventBus EventBus { get; set; }
2023-08-01 23:49:48 -07:00
2023-08-03 16:17:34 -07:00
private CacheStore<string, Map> _maps = new();
2023-03-22 21:22:51 -07:00
2023-06-03 18:21:46 -07:00
private string _currentConnector;
2023-03-22 21:22:51 -07:00
2023-06-03 18:21:46 -07:00
private string _currentMapResourcePath;
2023-03-26 10:53:45 -07:00
2024-10-18 11:24:24 -07:00
internal DebugCommands Debug { get; }
2023-06-10 22:15:28 -07:00
//private Entities.Campfire _lastCampfire = null;
2023-06-06 18:39:23 -07:00
2023-06-03 18:21:46 -07:00
private const string PLAYER_PATH = "res://Characters/Player.tscn";
private PackedScene _playerScene;
2023-03-26 10:53:45 -07:00
2023-06-03 18:21:46 -07:00
public World()
{
_playerScene = ResourceLoader.Load<PackedScene>(PLAYER_PATH);
2023-08-05 23:50:08 -07:00
if (Instance is null)
{
Instance = this;
}
else
{
throw new System.Exception("Another World instance is running.");
}
2024-10-18 11:24:24 -07:00
Debug = new(this);
}
~World()
{
Debug.Free();
2023-06-03 18:21:46 -07:00
}
public override void _Ready()
{
2023-07-24 12:40:28 -07:00
// check if world already exists
2023-08-01 23:49:48 -07:00
GlobalState = this.GetGlobalState();
2023-07-24 12:40:28 -07:00
2023-07-19 01:25:02 -07:00
Godot.RenderingServer.SetDefaultClearColor(Godot.Colors.Black);
2023-07-24 12:40:28 -07:00
2023-08-13 16:49:18 -07:00
UIController = this.GetMainUI();
EventBus = this.GetEventBus();
2023-08-17 00:13:24 -07:00
EventBus.RequestMoveToArea += (RequestAreaArgs args) =>
2023-08-13 16:49:18 -07:00
{
2023-08-17 00:13:24 -07:00
//CallDeferred(nameof(MoveToArea), args.Area, args.Connector);
MoveToArea(args.Area, args.Connector);
2023-08-13 16:49:18 -07:00
};
EventBus.RegisteredBoss += RegisterBoss;
EventBus.DeregisteredBoss += DeregisterBoss;
2023-08-13 16:49:18 -07:00
_uiViewport = GetNode<SubViewport>("CanvasLayer/SubViewportContainer/UIViewport");
2023-08-03 16:17:34 -07:00
// create a player (currently unparented)
2023-06-03 18:21:46 -07:00
CreatePlayer();
2023-03-26 10:53:45 -07:00
2023-08-07 02:38:51 -07:00
// TODO: create start menu and load game from there
LoadGame();
2023-06-03 18:21:46 -07:00
base._Ready();
}
2023-03-26 10:53:45 -07:00
private void RegisterBoss(Boss boss)
2023-07-23 11:05:01 -07:00
{
2023-09-09 01:00:13 -07:00
if (boss is null)
2023-08-07 02:38:51 -07:00
{
2023-09-09 01:00:13 -07:00
DeregisterBoss(CurrentBoss);
return;
2023-08-07 02:38:51 -07:00
}
2023-09-09 01:00:13 -07:00
CurrentBoss = boss;
GetNode<AudioManager>("/root/AudioManager").PlayBackground(boss.Music, 2);
2023-07-23 11:05:01 -07:00
}
private void DeregisterBoss(Boss boss)
2023-07-24 12:40:28 -07:00
{
CurrentBoss = null;
GetNode<AudioManager>("/root/AudioManager").StopBackground(2);
2023-07-24 12:40:28 -07:00
}
2023-06-10 22:15:28 -07:00
private void LoadMap(Map map)
2023-06-03 18:21:46 -07:00
{
2023-06-10 22:15:28 -07:00
GD.Print("Loading map " + map.Name);
2023-03-26 10:53:45 -07:00
var root = GetTree().Root;
2023-06-03 18:21:46 -07:00
if (CurrentMap is not null)
{
_maps.Update(CurrentMap.SceneFilePath);
2023-06-03 18:21:46 -07:00
CurrentMap.Entities.RemoveChild(CurrentPlayer);
root.RemoveChild(CurrentMap);
2023-06-03 18:21:46 -07:00
CurrentMap.Active = false;
}
2023-03-26 13:03:45 -07:00
root.AddChild(map);
2023-03-26 10:53:45 -07:00
2023-06-03 18:21:46 -07:00
CurrentMap = map;
CurrentMap.Active = true;
2023-07-31 01:12:47 -07:00
CurrentMap.Load();
2023-03-26 10:53:45 -07:00
2023-08-17 00:13:24 -07:00
EventBus.EmitSignal(EventBus.SignalName.AreaChanged, map);
2023-08-13 20:11:40 -07:00
2023-06-03 18:21:46 -07:00
if (CurrentPlayer is not null)
2023-03-22 21:22:51 -07:00
{
2023-03-26 10:53:45 -07:00
CurrentMap.Entities.AddChild(CurrentPlayer);
2023-03-22 21:22:51 -07:00
}
2023-06-03 18:21:46 -07:00
}
2023-03-22 21:22:51 -07:00
2023-08-03 16:17:34 -07:00
public void LoadScene(Map map)
{
_maps.Update(map.SceneFilePath, map);
LoadMap(map);
}
2023-06-10 22:15:28 -07:00
public void LoadScene(PackedScene scene)
{
Map map;
2023-08-03 16:17:34 -07:00
string path = scene.ResourcePath;
if (_maps.IsItemValid(path))
2023-06-10 22:15:28 -07:00
{
2023-08-03 16:17:34 -07:00
GD.Print($"{path} is cached");
map = _maps.Retrieve(path);
2023-06-10 22:15:28 -07:00
}
else
{
2023-08-03 16:17:34 -07:00
if (_maps.IsItemStale(path))
{
_maps[path].Value.QueueFree();
GD.Print("Freeing stale map " + path);
}
2023-06-10 22:15:28 -07:00
map = scene.Instantiate<Map>();
2023-08-03 16:17:34 -07:00
_maps.Update(path, map);
2023-06-10 22:15:28 -07:00
}
LoadMap(map);
}
public void LoadScene(string path)
{
Map map;
2023-08-03 16:17:34 -07:00
if (_maps.IsItemValid(path))
2023-06-10 22:15:28 -07:00
{
2023-08-03 16:17:34 -07:00
GD.Print($"{path} is cached");
map = _maps.Retrieve(path);
2023-06-10 22:15:28 -07:00
}
else
{
2023-08-03 16:17:34 -07:00
if (_maps.IsItemStale(path))
{
_maps[path].Value.QueueFree();
GD.Print("Freeing stale map " + path);
}
2023-06-10 22:15:28 -07:00
var scene = ResourceLoader.Load<PackedScene>(path);
map = scene.Instantiate<Map>();
2023-08-03 16:17:34 -07:00
_maps.Update(scene.ResourcePath, map);
2023-06-10 22:15:28 -07:00
}
LoadMap(map);
}
2023-08-03 16:17:34 -07:00
public Player CreatePlayer()
2023-06-03 18:21:46 -07:00
{
CurrentPlayer = _playerScene.Instantiate<Player>();
2023-08-03 16:17:34 -07:00
2023-08-17 00:13:24 -07:00
CurrentPlayer.Death += (HurtArgs args) =>
2023-08-03 16:17:34 -07:00
{
// TODO: respawn the player at the last campfire.
GetTree().CreateTimer(3).Timeout += () =>
{
SpawnPlayer();
};
2023-08-15 14:00:03 -07:00
GlobalState.Stats.DeathCount++;
2023-09-09 01:00:13 -07:00
//EventBus.EmitSignal(EventBus.SignalName.DeregisteredBoss,
// CurrentBoss);
2023-08-03 16:17:34 -07:00
};
return CurrentPlayer;
2023-06-03 18:21:46 -07:00
}
2023-03-22 21:22:51 -07:00
2023-06-03 18:21:46 -07:00
private void MovePlayerToConnector(string name)
{
var marker = CurrentMap.Markers.GetNode<Marker2D>(name);
CurrentPlayer.GlobalPosition = marker?.GlobalPosition ?? Vector2.Zero;
2023-06-03 18:21:46 -07:00
}
2023-03-26 10:53:45 -07:00
2023-08-17 00:13:24 -07:00
public async void MoveToArea(string path, string connector)
2023-06-03 18:21:46 -07:00
{
_currentConnector = connector;
2023-08-07 02:38:51 -07:00
GD.Print($"Moving to area {path} - {connector}");
2023-08-17 00:13:24 -07:00
EventBus.EmitSignal(EventBus.SignalName.EnterTransition);
GetTree().Paused = true;
await ToSignal(EventBus, EventBus.SignalName.TransitionFinished);
2023-08-07 02:38:51 -07:00
if (path != CurrentMap.SceneFilePath)
2023-03-25 10:21:24 -07:00
{
2023-08-01 23:49:48 -07:00
LoadScene(path);
2023-03-26 10:53:45 -07:00
}
2023-08-17 00:13:24 -07:00
GetTree().Paused = false;
2023-08-17 01:14:12 -07:00
EventBus.EmitSignal(EventBus.SignalName.ExitTransition);
2023-08-17 00:13:24 -07:00
2023-06-03 18:21:46 -07:00
// after finished loading, move our player to the connector
MovePlayerToConnector(connector);
}
public void SaveGame()
{
2023-08-07 02:38:51 -07:00
SetSpawn(CurrentPlayer.GlobalPosition);
2023-08-12 15:01:22 -07:00
var save = GetSave();
GlobalState.ExportToSave(save);
ResourceSaver.Save(save, "user://save.tres");
2023-06-03 18:21:46 -07:00
}
2023-08-12 15:01:22 -07:00
private Save GetSave()
2023-06-03 18:21:46 -07:00
{
2023-08-12 15:01:22 -07:00
if (ResourceLoader.Exists("user://save.tres"))
{
return ResourceLoader.Load<Save>("user://save.tres");
}
else
{
return ResourceLoader.Load("res://Assets/default-save.tres")
.Duplicate(true) as Save;
2023-08-12 15:01:22 -07:00
}
}
2023-08-07 02:38:51 -07:00
2023-08-12 15:01:22 -07:00
public void LoadGame()
{
GlobalState.ImportFromSave(GetSave());
2023-08-05 23:50:08 -07:00
// load the player scene
// TODO: implement
2023-03-22 21:22:51 -07:00
}
2023-06-10 22:15:28 -07:00
/// <summary>
/// Sets the player's saved spawn position.
/// </summary>
/// <param name="position">The position to save and spawn the player in</param>
/// <param name="mapKey">
/// The map to spawn the player in. If <see langword="null" />, use the
/// <c>World</c>'s <c>CurrentMap</c>
/// </param>
public void SetSpawn(Vector2 position, string mapKey = null)
{
GD.Print("Set spawn");
if (mapKey is null)
{
mapKey = CurrentMap.SceneFilePath;
}
2023-08-07 02:38:51 -07:00
GlobalState.Stats.SaveLocation = position;
GlobalState.Stats.SaveMapKey = mapKey;
2023-06-10 22:15:28 -07:00
}
public void SpawnPlayer()
{
// TODO: add max health property
2023-08-07 02:38:51 -07:00
if (CurrentMap.SceneFilePath != GlobalState.Stats.SaveMapKey)
2023-06-10 22:15:28 -07:00
{
2023-08-07 02:38:51 -07:00
LoadScene(GlobalState.Stats.SaveMapKey);
2023-06-10 22:15:28 -07:00
}
2023-08-07 02:38:51 -07:00
CurrentPlayer.GlobalPosition = GlobalState.Stats.SaveLocation;
2023-06-13 02:55:30 -07:00
CurrentPlayer.Spawn();
2023-06-10 22:15:28 -07:00
}
2023-07-25 03:47:31 -07:00
2023-08-17 01:05:21 -07:00
public void StaleCache()
{
foreach (var kv in _maps)
{
var map = kv.Value?.Value;
if (map is not null && IsInstanceValid(map))
{
GD.Print($"Staling {kv.Key}");
kv.Value.Stale();
}
}
}
2023-07-25 03:47:31 -07:00
public Node FindEntity(string name) => CurrentMap.Entities.GetNode(name);
2023-03-22 21:22:51 -07:00
}
2024-10-18 11:24:24 -07:00
internal partial class DebugCommands : Godot.GodotObject
{
private World _world;
internal DebugCommands(World world)
{
_world = world;
}
internal Items.ItemMetadata GiveItem(string item)
{
var itemMetadata = ResourceLoader
.Load<Items.ItemMetadata>($"res://Items/{item}.tres");
_world.CurrentPlayer.Inventory.Add(itemMetadata);
return itemMetadata;
}
}