using System; using System.Collections.Generic; using System.Linq; using Babushka.scripts.CSharp.GameEntity.Entities; using Godot; using Newtonsoft.Json.Linq; using Entity = Babushka.scripts.CSharp.GameEntity.Entities.Entity; using PositionalEntity = Babushka.scripts.CSharp.GameEntity.Entities.PositionalEntity; namespace Babushka.scripts.CSharp.GameEntity.Management; /// /// Manages the lifecycle and interactions of all entities within the game, including their creation, retrieval, /// and organization. The EntityManager serves as a centralized hub for managing both standard and positional entities. /// public partial class EntityManager : Node { public static EntityManager Instance; [Export] private EntityNodeCreator _nodeCreator = null!; [Export] private string saveDirectory = "user://save_data/"; private EntitySceneContainer? _currentEntitySceneContainer; private readonly List _allEntities = new(); public IEnumerable AllEntities => _allEntities; public IEnumerable AllPositionalEntities => _allEntities.OfType(); public EntitySceneContainer? CurrentEntitySceneContainer => _currentEntitySceneContainer; public EntityNodeCreator NodeCreator => _nodeCreator; public override void _EnterTree() { Instance = this; Load(); } public override void _Input(InputEvent @event) { // for debugging purposes if (@event.IsActionPressed("DebugEntities")) { GD.Print("Entities:"); foreach (var entity in AllEntities) { GD.Print(entity.EntityType + " " + entity.id); } } if(@event.IsActionPressed("SaveGame")) Save(); } public void Save() { JArray array = new JArray(); foreach (var entity in AllEntities) { JObject saveData = new JObject(); entity.SaveEntity(saveData); array.Add(saveData); } using var SaveFile = FileAccess.Open(saveDirectory + "save.json", FileAccess.ModeFlags.Write); SaveFile.StoreString(array.ToString()); } public void Load() { using var saveFile = FileAccess.Open(saveDirectory + "save.json", FileAccess.ModeFlags.Read); if (saveFile == null) return; JArray array = JArray.Parse(saveFile.GetAsText()); foreach (var token in array) { var jobj = (JObject)token; if (jobj == null) continue; if (jobj.TryGetValue("type", out var entityType)) { string entityTypeString = (string) entityType!; Entity entity = InitializeEntity(entityTypeString); entity.LoadEntity(jobj); AddEntity(entity); } } } private Entity InitializeEntity(string type) { Entity entity = type switch { TrashEntity.OWN_TYPE_NAME => new TrashEntity(), LoadedScenesEntity.OWN_TYPE_NAME => new LoadedScenesEntity(), _ => throw new Exception($"Trying to load unknown entity type: {type}") }; return entity; } #region ENTITY MANAGEMENT /// /// Adds an entity to the list of managed entities. If the entity is a positional entity /// and its scene matches the current scene container, it is also instantiated in the scene. /// /// The entity to be added to the manager. public void AddEntity(Entity entity) { if (!_allEntities.Contains(entity)) _allEntities.Add(entity); if(entity is PositionalEntity positionalEntity && positionalEntity.sceneName == _currentEntitySceneContainer?.sceneName) InstantiatePositionalEntityNode(positionalEntity); } private void InstantiatePositionalEntityNode(PositionalEntity entity) { if(_currentEntitySceneContainer == null) return; entity.InstantiateEntityNode(_currentEntitySceneContainer); } /// /// Retrieves the first entity of the specified type from the list of managed entities. /// If no such entity exists, creates a new instance of the specified type, adds it to the manager, and returns it. /// /// The type of entity to retrieve or create. Must inherit from the Entity class and have a parameterless constructor. /// The first entity of the specified type or a newly created entity of that type if none were found. public T GetUniqueEntity() where T : Entity, new() { var result = AllEntities.OfType().FirstOrDefault(); if (result == null) { var newEntity = new T(); AddEntity(newEntity); result = newEntity; } return result; } #endregion #region SCENE CONTAINER ACCESS public void SetSceneContainer(EntitySceneContainer sceneContainer) { _currentEntitySceneContainer = sceneContainer; } public void UnsetSceneContainer() { _currentEntitySceneContainer = null; } #endregion public void RemoveEntity(Entity entity) { _allEntities.Remove(entity); if (entity is PositionalEntity positionalEntity) { positionalEntity.RemoveEntityNode(); } } }