Files
tbd-station-14/Content.Shared/Humanoid/Markings/MarkingManager.cs
csqrb 8b3d7728d7 Marking default coloring (#13039)
* Marking coloring WIP

* EnsureDefault now supports coloring!

* Now markings have coloring when they get added

* Many things

* yml files

* cleanup

* Some requested changes

* Nullable type and WIP caching

* Time to resolve that thing with deprecated hair fields

* Latest reviews + im still trying to use these hair markings

* FirstOrDefault thing and Tattoo docs

* IDK

* It's now works a bit more properly in preferences GUI

* THEY SYNCING! However preferences GUI still broken and doesn't work properly

* Markings now updating when changing in GUI. However they still don't work properly with bald humanoids

* Forgor...

* Default hair-colored markings will not color to hair if there is no hair

* Fixed default colors for customizable markings

* Fixed bug in prefs GUI that set current hair to null

* Now markings that must match skin color because of limb (e.x. Slimes) - will match skin color

* final tweaks: if hair uses skin color then markings will use skin color as hair color (slimes)

* fix

* fixed dirty. no more funni invis bug

* Mirrors and client profile loading

* default colors soon TM

* review + better coloring

* Hardcode is gone

* diona markings

* oh my god

* fixed CategoryColoring

* cool fallback, clean up and some other tweaks

* code style

* more style

* a
2023-03-04 18:59:07 -08:00

189 lines
6.8 KiB
C#

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<MarkingPrototype> _index = new();
private readonly Dictionary<MarkingCategories, Dictionary<string, MarkingPrototype>> _markingDict = new();
private readonly Dictionary<string, MarkingPrototype> _markings = new();
public void Initialize()
{
_prototypeManager.PrototypesReloaded += OnPrototypeReload;
foreach (var category in Enum.GetValues<MarkingCategories>())
{
_markingDict.Add(category, new Dictionary<string, MarkingPrototype>());
}
foreach (var prototype in _prototypeManager.EnumeratePrototypes<MarkingPrototype>())
{
_index.Add(prototype);
_markingDict[prototype.MarkingCategory].Add(prototype.ID, prototype);
_markings.Add(prototype.ID, prototype);
}
}
public IReadOnlyDictionary<string, MarkingPrototype> Markings => _markings;
public IReadOnlyDictionary<MarkingCategories, Dictionary<string, MarkingPrototype>> CategorizedMarkings => _markingDict;
public IReadOnlyDictionary<string, MarkingPrototype> MarkingsByCategory(MarkingCategories category)
{
// all marking categories are guaranteed to have a dict entry
return _markingDict[category];
}
/// <summary>
/// Markings by category and species.
/// </summary>
/// <param name="category"></param>
/// <param name="species"></param>
/// <remarks>
/// 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.
/// </remarks>
/// <returns></returns>
public IReadOnlyDictionary<string, MarkingPrototype> MarkingsByCategoryAndSpecies(MarkingCategories category,
string species)
{
var speciesProto = _prototypeManager.Index<SpeciesPrototype>(species);
var onlyWhitelisted = _prototypeManager.Index<MarkingPointsPrototype>(speciesProto.MarkingPoints).OnlyWhitelisted;
var res = new Dictionary<string, MarkingPrototype>();
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);
}
/// <summary>
/// Check if a marking is valid according to the category, species, and current data this marking has.
/// </summary>
/// <param name="marking"></param>
/// <param name="category"></param>
/// <param name="species"></param>
/// <returns></returns>
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<SpeciesPrototype>(species);
var onlyWhitelisted = prototypeManager.Index<MarkingPointsPrototype>(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<SpeciesPrototype>(species);
var onlyWhitelisted = prototypeManager.Index<MarkingPointsPrototype>(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<SpeciesPrototype>(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;
}
}
}