using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; namespace Barotrauma { public class PrefabCollection : IEnumerable where T : class, IPrefab, IDisposable { /// /// Dictionary containing all prefabs of the same type. /// Key is the identifier. /// Value is a list of prefabs that share the same identifier, /// where the first element is the "base" prefab, /// i.e. the only prefab that's loaded when override tags are not defined. /// This first element can be null, if only overrides are defined. /// The last element of the list is the prefab that is effectively used /// (hereby called "active prefab") /// private readonly Dictionary> prefabs = new Dictionary>(); /// /// AllPrefabs exposes all prefabs instead of just the active ones. /// public IEnumerable>> AllPrefabs { get { foreach (var prefab in prefabs) { yield return prefab; } } } /// /// Returns the active prefab with identifier k. /// /// Prefab identifier /// Active prefab with identifier k public T this[string k] { get { return prefabs[k].Last(); } } /// /// Finds the first active prefab that returns true given the predicate, /// or null if no such prefab is found. /// /// Predicate to perform the search with. /// public T Find(Predicate predicate) { foreach (var kpv in prefabs) { if (predicate(kpv.Value.Last())) { return kpv.Value.Last(); } } return null; } /// /// Returns true if a prefab with identifier k exists, false otherwise. /// /// Prefab identifier /// Whether a prefab with identifier k exists or not public bool ContainsKey(string k) { return prefabs.ContainsKey(k); } /// /// Add a prefab to the collection. /// If not marked as an override, fail if a prefab with the same /// identifier already exists. /// Otherwise, add to the corresponding list, /// without making any changes to the base prefab. /// /// Prefab /// Is marked as override public void Add(T prefab, bool isOverride) { if (string.IsNullOrWhiteSpace(prefab.Identifier)) { DebugConsole.ThrowError($"Prefab \"{prefab.OriginalName}\" has no identifier!"); } List list = null; List newList = null; if (!prefabs.TryGetValue(prefab.Identifier, out list)) { newList = new List(); newList.Add(null); list = newList; } if (isOverride) { /*if (list[0] == null) { DebugConsole.ThrowError($"Error registering \"{prefab.OriginalName}\", \"{prefab.Identifier}\" ({typeof(T).ToString()}): overriding when base doesn't exist"); return; }*/ list.Add(prefab); } else { if (list[0] != null) { DebugConsole.ThrowError($"Error registering \"{prefab.OriginalName}\", \"{prefab.Identifier}\" ({typeof(T).ToString()}): base already exists; try overriding"); return; } list[0] = prefab; } Sort(list); if (newList != null) { prefabs.Add(prefab.Identifier, newList); } } /// /// Removes a prefab from the collection. /// /// Prefab public void Remove(T prefab) { if (!ContainsKey(prefab.Identifier)) { return; } if (!prefabs[prefab.Identifier].Contains(prefab)) { return; } if (prefabs[prefab.Identifier].IndexOf(prefab)==0) { prefabs[prefab.Identifier][0] = null; } else { prefabs[prefab.Identifier].Remove(prefab); } prefab.Dispose(); if (prefabs[prefab.Identifier].Count <= 0 || (prefabs[prefab.Identifier].Count == 1 && prefabs[prefab.Identifier][0] == null)) { prefabs.Remove(prefab.Identifier); } } /// /// Removes all prefabs that were loaded from a certain file. /// /// File path public void RemoveByFile(string filePath) { List prefabsToRemove = new List(); foreach (var kpv in prefabs) { foreach (var prefab in kpv.Value) { if (prefab != null && prefab.FilePath == filePath) { prefabsToRemove.Add(prefab); } } } foreach (var prefab in prefabsToRemove) { Remove(prefab); } } /// /// Sorts a list of prefabs based on the content package load order. /// /// List of prefabs private void Sort(List list) { if (list.Count <= 1) { return; } var newList = list.Skip(1).OrderByDescending(p => GameMain.Config.SelectedContentPackages.IndexOf(p.ContentPackage)).ToList(); list.RemoveRange(1, list.Count - 1); list.AddRange(newList); } /// /// Sorts all prefabs in the collection based on the content package load order. /// public void SortAll() { foreach (var kvp in prefabs) { Sort(kvp.Value); } } /// /// GetEnumerator implementation to enable foreach /// /// IEnumerator public IEnumerator GetEnumerator() { foreach (var kpv in prefabs) { yield return kpv.Value.Last(); } } /// /// GetEnumerator implementation to enable foreach /// /// IEnumerator IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } }