using System.Collections.Generic; using System.Linq; using Unity.Plastic.Newtonsoft.Json; using UnityEngine; using UnityEngine.Tilemaps; using GatherAndDefend.LevelEditor; using Unity.VisualScripting.YamlDotNet.Core.Tokens; using System.Text; using System.IO; #region [custom inspector] #if UNITY_EDITOR using UnityEditor; [CustomEditor(typeof(LevelManagerScript))] public class LevelManagerEditor : Editor { public override void OnInspectorGUI() { DrawDefaultInspector(); if (GUILayout.Button("Save")) { LevelManager.Instance.SaveFile(); } if (GUILayout.Button("Load")) { LevelManager.Instance.LoadFile(); } } } #endif #endregion #region [singleton level manager] public class LevelManager : Singleton { string SavePath => Application.dataPath + "/save.txt"; public delegate void LevelAction(ILevelObject levelObject); public delegate bool LevelPredicate(T levelObject) where T : ILevelObject; public event LevelAction Added; public event LevelAction Removed; private readonly List _toAdd; private readonly List _toRemove; private readonly List _levelObjects; public Level _currentLevel; public Transform LevelTransform { get;private set; } public LevelManager() { _toAdd = new List(); _toRemove = new List(); _levelObjects = new List(); var mgrScript = Object.FindObjectOfType(); if (!mgrScript) mgrScript = new GameObject(nameof(LevelManager)).AddComponent(); LevelTransform = mgrScript.transform; } #region [~CRUD~] public void Add(ILevelObject levelObject) { _toAdd.Add(levelObject); } public void Remove(ILevelObject levelObject) { _toRemove.Add(levelObject); } public void Clear() { _toAdd.Clear(); _toRemove.Clear(); _levelObjects.Clear(); } public T Get(LevelPredicate predicate = null) where T : ILevelObject { if (predicate == null) predicate = (generic) => true; return (T)_levelObjects.Find(levelObject => levelObject is T generic && predicate(generic)); } public List GetAll(LevelPredicate predicate = null) where T : ILevelObject { if (predicate == null) predicate = (generic) => true; List ret = new(); foreach (var levelObject in _levelObjects) if (levelObject is T generic && predicate(generic)) ret.Add(generic); return ret; } public int Count(LevelPredicate predicate = null) where T : ILevelObject { return GetAll(predicate).Count; } public bool Has(LevelPredicate predicate = null) where T : ILevelObject { if (predicate == null) predicate = (generic) => true; return _levelObjects.Exists(levelObject => levelObject is T generic && predicate(generic)); } #endregion #region [Level management] public void UpdateLevel() { _levelObjects.ForEach(levelObject => levelObject.LevelUpdate()); var toAdd = new List(this._toAdd); toAdd.ForEach(addedObject => { this._toAdd.Remove(addedObject); _levelObjects.Add(addedObject); Added?.Invoke(addedObject); addedObject.LevelStart(); }); var toRemove = new List(this._toRemove); toRemove.ForEach(removedObject => { this._toRemove.Remove(removedObject); _levelObjects.Remove(removedObject); Removed?.Invoke(removedObject); removedObject.LevelDestroy(); }); toRemove.Clear(); } public void ClearLevel() { foreach (var obj in _levelObjects) { obj.RemoveFromLevel(); } Clear(); } /// /// permet de loader un scriptable object de niveau /// /// le niveau à loader /// est ce qu'on veut effacer ce qui est déjà là? public void LoadLevel(Level level, bool shouldClear = false) { if (shouldClear) { ClearLevel(); } _currentLevel = level; Grid grid = Object.FindObjectOfType(); //create new grid if there is none if (!grid) { grid = new GameObject("Grid").AddComponent(); } //remove all tilemaps if there is a grid else { foreach (Transform child in grid.transform) { Object.Destroy(child.gameObject); } } //generate all tilemaps foreach (TilemapData tilemapData in _currentLevel) { var tilemap = new GameObject(tilemapData.Key).AddComponent(); tilemap.gameObject.AddComponent(); tilemapData.LoadToTilemap(tilemap); tilemap.transform.SetParent(grid.transform); } Debug.Log("level loaded successfully"); } /// /// permet de loader un scriptable object de niveau /// /// le nom du niveau à loader /// est ce qu'on veut effacer ce qui est déjà là? public void LoadLevel(string levelName, bool shouldClear = false) { if (shouldClear) { ClearLevel(); } //fetch level from database _currentLevel = Database.Instance.ScriptableObjects[levelName] as Level; LoadLevel(_currentLevel, shouldClear); } public void SaveFile() { var list = _levelObjects.Select(obj => obj.ToDictionary()).ToList(); string saved = JsonConvert.SerializeObject(list); File.WriteAllText(SavePath, saved, Encoding.UTF8); Debug.Log("game saved successfully"); } public void LoadFile() { string saved = File.ReadAllText(SavePath, Encoding.UTF8); var dicts = JsonConvert.DeserializeObject>>(saved); ClearLevel(); var prefabDicts = dicts.FindAll(x => x[nameof(ILevelObject.ObjectType)].ToString() == nameof(ILevelObject.ObjectType.Prefab)); foreach (var prefabDict in prefabDicts) CreatePrefab(prefabDict); var tileDicts = dicts.FindAll(x => x[nameof(ILevelObject.ObjectType)].ToString() == nameof(ILevelObject.ObjectType.Tile)); foreach (var tileDict in tileDicts) CreateTile(tileDict); Debug.Log("game loaded successfully"); } private void CreatePrefab(Dictionary dict) { var name = dict["Name"].ToString(); var prefab = Database.Instance.Prefabs[name]; var instance = prefab.Create(Vector3.zero, parent: LevelTransform); var comp = instance.GetComponent(); comp.LoadDictionary(dict); } private void CreateTile(Dictionary dict) { var name = dict["Name"].ToString(); var tile = Object.Instantiate(Database.Instance.ScriptableObjects[name]) as LevelTile; tile.Instantiated = true; tile.LoadDictionary(dict); tile.AddToLevel(); } #endregion } #endregion #region [mono behaviour] public class LevelManagerScript : MonoBehaviour { public Level firstLevel; private static LevelManagerScript _instance; void Awake() { //we don't want to ever have two LevelManagerScript at the same time in the game. //We prevent that by erasing any instances that are not registered as our main instance. if (!_instance) { _instance = this; } else { Destroy(gameObject); return; } DontDestroyOnLoad(gameObject); if (!firstLevel) throw new System.Exception("there is no first level set in the level manager script"); LevelManager.Instance.LoadLevel(firstLevel, true); } void Update() { LevelManager.Instance.UpdateLevel(); } } #endregion