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 the identifier. /// /// Prefab identifier /// Active prefab with the identifier public T this[string identifier] { get { return prefabs[identifier].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 the identifier exists, false otherwise. /// /// Prefab identifier /// Whether a prefab with the identifier exists or not public bool ContainsKey(string identifier) { return prefabs.ContainsKey(identifier); } /// /// 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!"); } bool basePrefabExists = prefabs.TryGetValue(prefab.Identifier, out List list); //Handle bad overrides and duplicates if (basePrefabExists && !isOverride) { DebugConsole.ThrowError($"Failed to add the prefab \"{prefab.OriginalName}\", \"{prefab.Identifier}\" ({typeof(T)}): a prefab with the same identifier already exists; try overriding\n{Environment.StackTrace}"); return; } //Add to list if (!basePrefabExists) { list = new List(); } list.Add(prefab); Sort(list); if (!basePrefabExists) { prefabs.Add(prefab.Identifier, list); } } /// /// 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.EnabledRegularPackages.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(); } } }