using Godot; using SupaLidlGame.Characters; using SupaLidlGame.Extensions; using SupaLidlGame.Scenes; using SupaLidlGame.State.Global; namespace SupaLidlGame.Utils; public partial class World : Node { public static World Instance { get; private set; } [Export] public Map CurrentMap { get; protected set; } [Export] public Player CurrentPlayer { get; set; } [Export] public Boss CurrentBoss { get; set; } [Export] public UI.UIController UIController { get; set; } [Export] public AudioStreamPlayer MusicPlayer { get; set; } [Export] public Dialogue.Balloon DialogueBalloon { get; set; } public State.Global.GlobalState GlobalState { get; set; } public Events.EventBus EventBus { get; set; } private CacheStore _maps = new(); private string _currentConnector; private string _currentMapResourcePath; //private Entities.Campfire _lastCampfire = null; public Vector2 SaveLocation { get; set; } public string SaveMapKey { get; set; } private const string PLAYER_PATH = "res://Characters/Player.tscn"; private PackedScene _playerScene; public World() { _playerScene = ResourceLoader.Load(PLAYER_PATH); if (Instance is null) { Instance = this; } else { throw new System.Exception("Another World instance is running."); } } public override void _Ready() { // check if world already exists GlobalState = this.GetGlobalState(); Godot.RenderingServer.SetDefaultClearColor(Godot.Colors.Black); // create a player (currently unparented) CreatePlayer(); EventBus = this.GetEventBus(); EventBus.RequestMoveToArea += (Events.RequestAreaArgs args) => { GD.Print("request move to area"); MoveToArea(args.Area, args.Connector); }; base._Ready(); } public void RegisterBoss(Boss boss) { CurrentBoss = boss; UIController.BossBar.Boss = boss; MusicPlayer.Stream = boss.Music; MusicPlayer.Play(); } public void DeregisterBoss(Boss boss) { CurrentBoss = null; UIController.BossBar.Boss = null; MusicPlayer.Stop(); } private void LoadMap(Map map) { GD.Print("Loading map " + map.Name); if (CurrentMap is not null) { CurrentMap.Entities.RemoveChild(CurrentPlayer); GetTree().Root.RemoveChild(CurrentMap); CurrentMap.Active = false; } GetTree().Root.AddChild(map); InitTilemap(map); CurrentMap = map; CurrentMap.Active = true; CurrentMap.Load(); if (CurrentPlayer is not null) { CurrentMap.Entities.AddChild(CurrentPlayer); } } public void LoadScene(Map map) { _maps.Update(map.SceneFilePath, map); LoadMap(map); } public void LoadScene(PackedScene scene) { if (CurrentMap is not null) { _maps.Update(CurrentMap.SceneFilePath); } Map map; string path = scene.ResourcePath; if (_maps.IsItemValid(path)) { GD.Print($"{path} is cached"); map = _maps.Retrieve(path); } else { if (_maps.IsItemStale(path)) { _maps[path].Value.QueueFree(); GD.Print("Freeing stale map " + path); } map = scene.Instantiate(); _maps.Update(path, map); } LoadMap(map); } public void LoadScene(string path) { Map map; if (_maps.IsItemValid(path)) { GD.Print($"{path} is cached"); map = _maps.Retrieve(path); } else { if (_maps.IsItemStale(path)) { _maps[path].Value.QueueFree(); GD.Print("Freeing stale map " + path); } var scene = ResourceLoader.Load(path); map = scene.Instantiate(); _maps.Update(scene.ResourcePath, map); } LoadMap(map); } public Player CreatePlayer() { CurrentPlayer = _playerScene.Instantiate(); CurrentPlayer.Death += (Events.HealthChangedArgs args) => { // TODO: respawn the player at the last campfire. GetTree().CreateTimer(3).Timeout += () => { SpawnPlayer(); }; }; CurrentPlayer.Hurt += (Events.HealthChangedArgs args) => { // TODO: move this to UI controller and add a setup method var bar = UIController.GetNode("Top/Margin/HealthBar"); bar.ProgressBar.Value = args.NewHealth; }; return CurrentPlayer; } private void InitTilemap(Map map) { // this is being replaced with interaction triggers } private void MovePlayerToConnector(string name) { // find the first connector with the specified name // TODO: replace this with event buses //var connector = CurrentMap.Areas.GetChildren().First((child) => //{ // if (child is BoundingBoxes.ConnectorBox connector) // { // return connector.Identifier == name; // } // return false; //}) as BoundingBoxes.ConnectorBox; //CurrentPlayer.GlobalPosition = connector.GlobalPosition; CurrentPlayer.GlobalPosition = Vector2.Zero; } public void MoveToArea(string path, string connector) { _currentConnector = connector; if (path != _currentMapResourcePath) { var scene = ResourceLoader.Load(path); LoadScene(path); _currentMapResourcePath = path; } // after finished loading, move our player to the connector MovePlayerToConnector(connector); } public void _on_area_2d_requested_enter( BoundingBoxes.ConnectorBox box, Player player) { GD.Print("Requesting to enter " + box.ToConnector); MoveToArea(box.ToArea, box.ToConnector); } public void SaveGame() { ResourceSaver.Save(GlobalState.Progression, "user://progression.save"); ResourceSaver.Save(GlobalState.MapState, "user://map-state.save"); throw new System.NotImplementedException(); } public void LoadGame() { var prog = ResourceLoader.Load("user://progression.save"); var mapState = ResourceLoader.Load("user://map-state.save"); GlobalState.Progression = prog; GlobalState.MapState = mapState; // load the player scene // TODO: implement throw new System.NotImplementedException(); } /// /// Sets the player's saved spawn position. /// /// The position to save and spawn the player in /// /// The map to spawn the player in. If , use the /// World's CurrentMap /// public void SetSpawn(Vector2 position, string mapKey = null) { GD.Print("Set spawn"); if (mapKey is null) { mapKey = CurrentMap.SceneFilePath; SaveLocation = position; SaveMapKey = mapKey; } } public void SpawnPlayer() { // TODO: add max health property //CurrentPlayer.Health = 100; //CurrentPlayer.Sprite.Visible = true; if (CurrentMap.SceneFilePath != SaveMapKey) { LoadScene(SaveMapKey); } CurrentPlayer.GlobalPosition = SaveLocation; CurrentPlayer.Spawn(); } public Node FindEntity(string name) => CurrentMap.Entities.GetNode(name); }