using System.Diagnostics.CodeAnalysis; using Content.Shared.Humanoid.Prototypes; using Robust.Shared.Prototypes; namespace Content.Shared.Humanoid.Markings { public sealed class MarkingManager { [Dependency] private readonly IPrototypeManager _prototypeManager = default!; private readonly List _index = new(); private readonly Dictionary> _markingDict = new(); private readonly Dictionary _markings = new(); public void Initialize() { _prototypeManager.PrototypesReloaded += OnPrototypeReload; foreach (var category in Enum.GetValues()) { _markingDict.Add(category, new Dictionary()); } foreach (var prototype in _prototypeManager.EnumeratePrototypes()) { _index.Add(prototype); _markingDict[prototype.MarkingCategory].Add(prototype.ID, prototype); _markings.Add(prototype.ID, prototype); } } public IReadOnlyDictionary Markings => _markings; public IReadOnlyDictionary> CategorizedMarkings => _markingDict; public IReadOnlyDictionary MarkingsByCategory(MarkingCategories category) { // all marking categories are guaranteed to have a dict entry return _markingDict[category]; } /// /// Markings by category and species. /// /// /// /// /// This is done per category, as enumerating over every single marking by species isn't useful. /// Please make a pull request if you find a use case for that behavior. /// /// public IReadOnlyDictionary MarkingsByCategoryAndSpecies(MarkingCategories category, string species) { var speciesProto = _prototypeManager.Index(species); var onlyWhitelisted = _prototypeManager.Index(speciesProto.MarkingPoints).OnlyWhitelisted; var res = new Dictionary(); foreach (var (key, marking) in MarkingsByCategory(category)) { if (onlyWhitelisted && marking.SpeciesRestrictions == null) { continue; } if (marking.SpeciesRestrictions != null && !marking.SpeciesRestrictions.Contains(species)) { continue; } res.Add(key, marking); } return res; } public bool TryGetMarking(Marking marking, [NotNullWhen(true)] out MarkingPrototype? markingResult) { return _markings.TryGetValue(marking.MarkingId, out markingResult); } /// /// Check if a marking is valid according to the category, species, and current data this marking has. /// /// /// /// /// public bool IsValidMarking(Marking marking, MarkingCategories category, string species) { if (!TryGetMarking(marking, out var proto)) { return false; } if (proto.MarkingCategory != category || proto.SpeciesRestrictions != null && !proto.SpeciesRestrictions.Contains(species)) { return false; } if (marking.MarkingColors.Count != proto.Sprites.Count) { return false; } return true; } private void OnPrototypeReload(PrototypesReloadedEventArgs args) { if(!args.ByType.TryGetValue(typeof(MarkingPrototype), out var set)) return; _index.RemoveAll(i => set.Modified.ContainsKey(i.ID)); foreach (var prototype in set.Modified.Values) { var markingPrototype = (MarkingPrototype) prototype; _index.Add(markingPrototype); } } public bool CanBeApplied(string species, Marking marking, IPrototypeManager? prototypeManager = null) { IoCManager.Resolve(ref prototypeManager); var speciesProto = prototypeManager.Index(species); var onlyWhitelisted = prototypeManager.Index(speciesProto.MarkingPoints).OnlyWhitelisted; if (!TryGetMarking(marking, out var prototype)) { return false; } if (onlyWhitelisted && prototype.SpeciesRestrictions == null) { return false; } if (prototype.SpeciesRestrictions != null && !prototype.SpeciesRestrictions.Contains(species)) { return false; } return true; } public bool CanBeApplied(string species, MarkingPrototype prototype, IPrototypeManager? prototypeManager = null) { IoCManager.Resolve(ref prototypeManager); var speciesProto = prototypeManager.Index(species); var onlyWhitelisted = prototypeManager.Index(speciesProto.MarkingPoints).OnlyWhitelisted; if (onlyWhitelisted && prototype.SpeciesRestrictions == null) { return false; } if (prototype.SpeciesRestrictions != null && !prototype.SpeciesRestrictions.Contains(species)) { return false; } return true; } public bool MustMatchSkin(string species, HumanoidVisualLayers layer, IPrototypeManager? prototypeManager = null) { IoCManager.Resolve(ref prototypeManager); var speciesProto = prototypeManager.Index(species); if ( !prototypeManager.TryIndex(speciesProto.SpriteSet, out HumanoidSpeciesBaseSpritesPrototype? baseSprites) || !baseSprites.Sprites.TryGetValue(layer, out var spriteName) || !prototypeManager.TryIndex(spriteName, out HumanoidSpeciesSpriteLayer? sprite) || sprite == null || !sprite.MarkingsMatchSkin ) { return false; } return true; } } }