Documents all the public APIs for Nubody & markings code (#42857)

This commit is contained in:
pathetic meowmeow
2026-02-08 13:30:09 -05:00
committed by GitHub
parent 41dddf4d99
commit 9c23b4a6d8
15 changed files with 297 additions and 22 deletions

View File

@@ -7,6 +7,9 @@ using Robust.Shared.Utility;
namespace Content.Client.Humanoid;
/// <summary>
/// View model for UIs manipulating a set of markings, responsible for applying markings logic and keeping state synchronized.
/// </summary>
public sealed class MarkingsViewModel
{
[Dependency] private readonly MarkingManager _marking = default!;
@@ -14,6 +17,10 @@ public sealed class MarkingsViewModel
private bool _enforceLimits = true;
/// <summary>
/// Whether the markings view model will enforce limitations on how many markings an organ can have
/// </summary>
/// <seealso cref="EnforcementsChanged" />
public bool EnforceLimits
{
get => _enforceLimits;
@@ -29,6 +36,10 @@ public sealed class MarkingsViewModel
private bool _enforceGroupAndSexRestrictions = true;
/// <summary>
/// Whether the markings view model will enforce restrictions on the group and sex of markings for an organ
/// </summary>
/// <seealso cref="EnforcementsChanged" />
public bool EnforceGroupAndSexRestrictions
{
get => _enforceGroupAndSexRestrictions;
@@ -44,10 +55,18 @@ public sealed class MarkingsViewModel
private bool AnyEnforcementsLifted => !_enforceLimits || !_enforceGroupAndSexRestrictions;
/// <summary>
/// Raised whenever the view model is enforcing a different set of constraints on possible markings than before
/// </summary>
/// <seealso cref="EnforceLimits" />
/// <seealso cref="EnforceGroupAndSexRestrictions" />
public event Action? EnforcementsChanged;
private Dictionary<ProtoId<OrganCategoryPrototype>, OrganProfileData> _organProfileData = new();
/// <summary>
/// The organ profile data this view model is concerned with.
/// </summary>
public Dictionary<ProtoId<OrganCategoryPrototype>, OrganProfileData> OrganProfileData
{
get => _organProfileData;
@@ -58,6 +77,10 @@ public sealed class MarkingsViewModel
}
}
/// <summary>
/// Sets the sex of all organ profiles in the view model.
/// </summary>
/// <param name="sex">The new sex</param>
public void SetOrganSexes(Sex sex)
{
foreach (var (organ, data) in _organProfileData)
@@ -67,6 +90,10 @@ public sealed class MarkingsViewModel
OrganProfileDataChanged?.Invoke(true);
}
/// <summary>
/// Sets the skin color of all organ profiles in the view model.
/// </summary>
/// <param name="skinColor">The new skin color</param>
public void SetOrganSkinColor(Color skinColor)
{
foreach (var (organ, data) in _organProfileData)
@@ -76,6 +103,10 @@ public sealed class MarkingsViewModel
OrganProfileDataChanged?.Invoke(false);
}
/// <summary>
/// Sets the eye color of all organ profiles in the view model.
/// </summary>
/// <param name="eyeColor">The new eye color</param>
public void SetOrganEyeColor(Color eyeColor)
{
foreach (var (organ, data) in _organProfileData)
@@ -85,10 +116,21 @@ public sealed class MarkingsViewModel
OrganProfileDataChanged?.Invoke(false);
}
/// <summary>
/// Raised whenever the organ profile data changes.
/// The boolean value represents whether the set of possible markings may have changed.
/// </summary>
/// <seealso cref="OrganProfileData" />
/// <seealso cref="SetOrganSexes" />
/// <seealso cref="SetOrganSkinColor" />
/// <seealso cref="SetOrganEyeColor" />
public event Action<bool>? OrganProfileDataChanged;
private Dictionary<ProtoId<OrganCategoryPrototype>, Dictionary<HumanoidVisualLayers, List<Marking>>> _markings = new();
/// <summary>
/// The currently applied set of markings
/// </summary>
public Dictionary<ProtoId<OrganCategoryPrototype>, Dictionary<HumanoidVisualLayers, List<Marking>>> Markings
{
get => _markings;
@@ -104,12 +146,21 @@ public sealed class MarkingsViewModel
}
}
/// <summary>
/// Raised whenever the set of markings has fully changed and requires a UI reload
/// </summary>
public event Action? MarkingsReset;
/// <summary>
/// Raised whenever a specific layer's markings have changed
/// </summary>
public event Action<ProtoId<OrganCategoryPrototype>, HumanoidVisualLayers>? MarkingsChanged;
private Dictionary<ProtoId<OrganCategoryPrototype>, OrganMarkingData> _organData = new();
/// <summary>
/// The organ marking data the view model is concerned with.
/// </summary>
public Dictionary<ProtoId<OrganCategoryPrototype>, OrganMarkingData>
OrganData
{
@@ -125,15 +176,25 @@ public sealed class MarkingsViewModel
}
}
/// <summary>
/// Raised whenever the organ data within the view model is changed.
/// </summary>
public event Action? OrganDataChanged;
private Dictionary<ProtoId<MarkingPrototype>, List<Color>> _previousColors = new();
private readonly Dictionary<ProtoId<MarkingPrototype>, List<Color>> _previousColors = new();
public MarkingsViewModel()
{
IoCManager.InjectDependencies(this);
}
/// <summary>
/// Returns whether the given marking is currently selected in the model
/// </summary>
/// <param name="organ">The organ to check for the marking within</param>
/// <param name="layer">The layer within the organ to check for the marking within</param>
/// <param name="markingId">The marking ID to check for</param>
/// <returns>Whether the marking is currently present within the set of selected markings</returns>
public bool IsMarkingSelected(ProtoId<OrganCategoryPrototype> organ,
HumanoidVisualLayers layer,
ProtoId<MarkingPrototype> markingId)
@@ -141,6 +202,11 @@ public sealed class MarkingsViewModel
return GetMarking(organ, layer, markingId) is not null;
}
/// <summary>
/// Returns whether the marking at the given location can have its color customized by the user
/// </summary>
/// <inheritdoc cref="IsMarkingSelected" path="param" />
/// <returns>Whether the marking is capable of having its color customized by the user</returns>
public bool IsMarkingColorCustomizable(ProtoId<OrganCategoryPrototype> organ,
HumanoidVisualLayers layer,
ProtoId<MarkingPrototype> markingId)
@@ -163,6 +229,11 @@ public sealed class MarkingsViewModel
return !appearance.MatchSkin;
}
/// <summary>
/// Returns the currently applied marking by its ID, if it exists
/// </summary>
/// <inheritdoc cref="IsMarkingSelected" path="param" />
/// <returns>The marking currently applied if it exists, otherwise null</returns>
public Marking? GetMarking(ProtoId<OrganCategoryPrototype> organ,
HumanoidVisualLayers layer,
ProtoId<MarkingPrototype> markingId)
@@ -176,6 +247,11 @@ public sealed class MarkingsViewModel
return markings.FirstOrNull(it => it.MarkingId == markingId);
}
/// <summary>
/// Attempts to add a marking to the current set of markings
/// </summary>
/// <inheritdoc cref="IsMarkingSelected" path="param" />
/// <returns>Whether the marking was successfully added to the set of markings</returns>
public bool TrySelectMarking(ProtoId<OrganCategoryPrototype> organ,
HumanoidVisualLayers layer,
ProtoId<MarkingPrototype> markingId)
@@ -230,6 +306,12 @@ public sealed class MarkingsViewModel
return false;
}
/// <summary>
/// Returns the list of currently selected markings for the layer on the given organ
/// </summary>
/// <param name="organ">The organ to look up the layer within</param>
/// <param name="layer">The layer within the organ to look up</param>
/// <returns>The set of markings for the provided organ if it has any, or null</returns>
public List<Marking>? SelectedMarkings(ProtoId<OrganCategoryPrototype> organ,
HumanoidVisualLayers layer)
{
@@ -238,6 +320,11 @@ public sealed class MarkingsViewModel
: organMarkings.GetValueOrDefault(layer);
}
/// <summary>
/// Attempts to remove a marking from the current set of markings
/// </summary>
/// <inheritdoc cref="IsMarkingSelected" path="param" />
/// <returns>Whether the marking was successfully removed from the set of markings</returns>
public bool TryDeselectMarking(ProtoId<OrganCategoryPrototype> organ,
HumanoidVisualLayers layer,
ProtoId<MarkingPrototype> markingId)
@@ -275,6 +362,12 @@ public sealed class MarkingsViewModel
return true;
}
/// <summary>
/// Attempts to set the color of the specified marking at the given index
/// </summary>
/// <inheritdoc cref="IsMarkingSelected" path="param" />
/// <param name="colorIndex">The index within the marking's color array to set</param>
/// <param name="color">The new color to set</param>
public void TrySetMarkingColor(ProtoId<OrganCategoryPrototype> organ,
HumanoidVisualLayers layer,
ProtoId<MarkingPrototype> markingId,
@@ -295,6 +388,9 @@ public sealed class MarkingsViewModel
MarkingsChanged?.Invoke(organ, layer);
}
/// <summary>
/// Ensures the markings within the model are valid.
/// </summary>
public void ValidateMarkings()
{
foreach (var (organ, organData) in _organData)
@@ -318,6 +414,14 @@ public sealed class MarkingsViewModel
MarkingsReset?.Invoke();
}
/// <summary>
/// Gets the count data for an organ layer.
/// </summary>
/// <param name="organ">The organ to look up count data for</param>
/// <param name="layer">The layer within the organ to look up count data for</param>
/// <param name="isRequired">Whether this layer requires at least one marking to be selected</param>
/// <param name="count">The maximum amount of markings that can be selected for this layer</param>
/// <param name="selected">The currently selected amount of markings</param>
public void GetMarkingCounts(ProtoId<OrganCategoryPrototype> organ, HumanoidVisualLayers layer, out bool isRequired, out int count, out int selected)
{
isRequired = false;
@@ -348,6 +452,14 @@ public sealed class MarkingsViewModel
selected = layerMarkings.Count;
}
/// <summary>
/// Reorders the specified marking ID to the index and position relative to its index
/// </summary>
/// <param name="organ">The organ to reorder the markings of</param>
/// <param name="layer">The layer to reorder the markings of</param>
/// <param name="markingId">The marking to reorder</param>
/// <param name="position">Whether the marking should be moved to before or after the given index</param>
/// <param name="positionIndex">The new position index of the marking</param>
public void ChangeMarkingOrder(ProtoId<OrganCategoryPrototype> organ,
HumanoidVisualLayers layer,
ProtoId<MarkingPrototype> markingId,
@@ -381,6 +493,9 @@ public sealed class MarkingsViewModel
}
}
/// <summary>
/// Specifies whether an item in a list will be moved to before or after a corresponding index
/// </summary>
public enum CandidatePosition
{
Before,

View File

@@ -3,6 +3,11 @@ using Robust.Shared.GameStates;
namespace Content.Shared.Body;
/// <summary>
/// Component on the entity that "has" a body, and that oversees entities with the <see cref="OrganComponent"/> inside it.
/// </summary>
/// <seealso cref="BodySystem" />
/// <seealso cref="SharedVisualBodySystem" />
[RegisterComponent, NetworkedComponent]
[Access(typeof(BodySystem))]
public sealed partial class BodyComponent : Component

View File

@@ -1,8 +1,19 @@
using JetBrains.Annotations;
namespace Content.Shared.Body;
public sealed partial class BodySystem
{
/// <summary>
/// Returns a list of organs with a given component in the body.
/// This is only provided to ease migration from the older BodySystem and should not be used in new code.
/// </summary>
/// <param name="ent">The body to query.</param>
/// <param name="organs">The set of organs with the given component.</param>
/// <typeparam name="TComp">The component to test for.</typeparam>
/// <returns>Whether any organs were returned.</returns>
[Obsolete("Use an event-relay based approach instead")]
[PublicAPI]
public bool TryGetOrgansWithComponent<TComp>(Entity<BodyComponent?> ent, out List<Entity<TComp>> organs) where TComp : Component
{
organs = new();

View File

@@ -2,11 +2,13 @@ using Content.Shared.Body.Events;
using Content.Shared.Gibbing;
using Content.Shared.Humanoid;
using Content.Shared.Medical;
using JetBrains.Annotations;
namespace Content.Shared.Body;
public sealed partial class BodySystem
{
// Refrain from adding an infinite block of relays here - consuming systems can use RelayEvent
private void InitializeRelay()
{
SubscribeLocalEvent<BodyComponent, ApplyMetabolicMultiplierEvent>(RefRelayBodyEvent);
@@ -28,6 +30,13 @@ public sealed partial class BodySystem
RelayEvent((uid, component), args);
}
/// <summary>
/// Relays the given event to organs within a body.
/// </summary>
/// <param name="ent">The body to relay the event within</param>
/// <param name="args">The event to relay</param>
/// <typeparam name="T">The type of the event</typeparam>
[PublicAPI]
public void RelayEvent<T>(Entity<BodyComponent> ent, ref T args) where T : struct
{
var ev = new BodyRelayedEvent<T>(ent, args);
@@ -38,6 +47,13 @@ public sealed partial class BodySystem
args = ev.Args;
}
/// <summary>
/// Relays the given event to organs within a body.
/// </summary>
/// <param name="ent">The body to relay the event within</param>
/// <param name="args">The event to relay</param>
/// <typeparam name="T">The type of the event</typeparam>
[PublicAPI]
public void RelayEvent<T>(Entity<BodyComponent> ent, T args) where T : class
{
var ev = new BodyRelayedEvent<T>(ent, args);
@@ -49,7 +65,7 @@ public sealed partial class BodySystem
}
/// <summary>
/// Event wrapper for relayed events.
/// Event wrapper for events being relayed to organs within a body.
/// </summary>
[ByRefEvent]
public record struct BodyRelayedEvent<TEvent>(Entity<BodyComponent> Body, TEvent Args);

View File

@@ -3,6 +3,17 @@ using Robust.Shared.Containers;
namespace Content.Shared.Body;
/// <summary>
/// System responsible for coordinating entities with <see cref="BodyComponent" /> and their entities with <see cref="OrganComponent" />.
/// This system is primarily responsible for event relaying and the relationships between a body and its organs.
/// It is not responsible for player-facing body features, e.g. "blood" or "breathing."
/// Such features should be implemented in systems relying on the various events raised by this class.
/// </summary>
/// <seealso cref="OrganGotInsertedEvent" />
/// <seealso cref="OrganGotRemovedEvent" />
/// <seealso cref="OrganInsertedIntoEvent" />
/// <seealso cref="OrganRemovedFromEvent" />
/// <seealso cref="BodyRelayedEvent{TEvent}" />
public sealed partial class BodySystem : EntitySystem
{
[Dependency] private readonly SharedContainerSystem _container = default!;

View File

@@ -1,7 +1,12 @@
using Content.Shared.Gibbing;
using Robust.Shared.GameStates;
namespace Content.Shared.Body;
/// <summary>
/// Component that causes this entity to become gibs when the body it's in is gibbed
/// </summary>
/// <seealso cref="GibbingSystem" />
[RegisterComponent, NetworkedComponent]
[Access(typeof(GibbableOrganSystem))]
public sealed partial class GibbableOrganComponent : Component;

View File

@@ -3,13 +3,22 @@ using Robust.Shared.GameStates;
namespace Content.Shared.Body;
/// <summary>
/// Organs with this component provide a hand with the given ID and data to the body when inserted.
/// </summary>
[RegisterComponent, NetworkedComponent]
[Access(typeof(HandOrganSystem))]
public sealed partial class HandOrganComponent : Component
{
/// <summary>
/// The hand ID used by <seealso cref="HandsComponent" /> on the body
/// </summary>
[DataField(required: true)]
public string HandID;
/// <summary>
/// The data used to create the hand
/// </summary>
[DataField(required: true)]
public Hand Data;
}

View File

@@ -4,11 +4,15 @@ namespace Content.Shared.Body;
/// <summary>
/// On map initialization, spawns the given organs into the body.
/// Liable to change as the body becomes more complex.
/// </summary>
[RegisterComponent]
[Access(typeof(InitialBodySystem))]
public sealed partial class InitialBodyComponent : Component
{
/// <summary>
/// The organs to spawn based on their category.
/// </summary>
[DataField(required: true)]
public Dictionary<ProtoId<OrganCategoryPrototype>, EntProtoId<OrganComponent>> Organs;
}

View File

@@ -3,6 +3,10 @@ using Robust.Shared.Prototypes;
namespace Content.Shared.Body;
/// <summary>
/// Marks an entity as being able to be inserted into an entity with <seealso cref="BodyComponent" />.
/// </summary>
/// <seealso cref="BodySystem" />
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
[Access(typeof(BodySystem))]
public sealed partial class OrganComponent : Component

View File

@@ -6,6 +6,7 @@ using Content.Shared.Humanoid;
using Content.Shared.Humanoid.Markings;
using Content.Shared.Preferences;
using Content.Shared.Verbs;
using JetBrains.Annotations;
using Robust.Shared.Containers;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
@@ -47,6 +48,26 @@ public abstract partial class SharedVisualBodySystem
});
}
/// <summary>
/// Copies the appearance of organs from one body to another
/// </summary>
/// <param name="source">The body whose organs to copy the appearance from</param>
/// <param name="target">The body whose organs to copy the appearance to</param>
[PublicAPI]
public void CopyAppearanceFrom(Entity<BodyComponent?> source, Entity<BodyComponent?> target)
{
if (!Resolve(source, ref source.Comp) || !Resolve(target, ref target.Comp))
return;
var sourceOrgans = _container.EnsureContainer<Container>(source, BodyComponent.ContainerID);
foreach (var sourceOrgan in sourceOrgans.ContainedEntities)
{
var evt = new OrganCopyAppearanceEvent(sourceOrgan);
RaiseLocalEvent(target, ref evt);
}
}
/// <summary>
/// Gathers all the markings-relevant data from this entity
/// </summary>
@@ -111,6 +132,12 @@ public abstract partial class SharedVisualBodySystem
RaiseLocalEvent(ent, ref markingsEvt);
}
/// <summary>
/// Applies the given set of markings to the body.
/// </summary>
/// <param name="ent">The body whose organs to apply markings to.</param>
/// <param name="markings">A dictionary of organ categories to markings information. Organs not included in this dictionary will remain unaffected.</param>
[PublicAPI]
public void ApplyMarkings(EntityUid ent, Dictionary<ProtoId<OrganCategoryPrototype>, Dictionary<HumanoidVisualLayers, List<Marking>>> markings)
{
var markingsEvt = new ApplyOrganMarkingsEvent(markings);
@@ -134,17 +161,37 @@ public abstract partial class SharedVisualBodySystem
RaiseLocalEvent(ent, ref markingsEvt);
}
/// <summary>
/// Applies the information contained with a <see cref="HumanoidCharacterProfile"/> to a visual body's appearance.
/// This sets the profile data and markings of all organs contained within the profile.
/// </summary>
/// <param name="ent">The body to apply the profile to</param>
/// <param name="profile">The profile to apply</param>
[PublicAPI]
public void ApplyProfileTo(Entity<VisualBodyComponent?> ent, HumanoidCharacterProfile profile)
{
ApplyAppearanceTo(ent, profile.Appearance, profile.Sex);
}
/// <summary>
/// Applies profile data to all visual organs within the body.
/// </summary>
/// <param name="ent">The body to apply the organ profile to</param>
/// <param name="profile">The profile to apply</param>
[PublicAPI]
public void ApplyProfile(EntityUid ent, OrganProfileData profile)
{
var profileEvt = new ApplyOrganProfileDataEvent(profile, null);
RaiseLocalEvent(ent, ref profileEvt);
}
/// <summary>
/// Applies profile data to the specified visual organs within the body.
/// Organs not specified are left unchanged.
/// </summary>
/// <param name="ent">The body to apply the organ profiles to.</param>
/// <param name="profiles">The profiles to apply.</param>
[PublicAPI]
public void ApplyProfiles(EntityUid ent, Dictionary<ProtoId<OrganCategoryPrototype>, OrganProfileData> profiles)
{
var profileEvt = new ApplyOrganProfileDataEvent(null, profiles);

View File

@@ -7,6 +7,9 @@ using Robust.Shared.Utility;
namespace Content.Shared.Body;
/// <summary>
/// Class responsible for managing the appearance of an entity with <see cref="VisualBodyComponent" /> via its organs with <see cref="VisualOrganComponent" />
/// </summary>
public abstract partial class SharedVisualBodySystem : EntitySystem
{
[Dependency] private readonly IPrototypeManager _prototype = default!;
@@ -84,20 +87,6 @@ public abstract partial class SharedVisualBodySystem : EntitySystem
Dirty(ent);
}
public void CopyAppearanceFrom(Entity<BodyComponent?> source, Entity<BodyComponent?> target)
{
if (!Resolve(source, ref source.Comp) || !Resolve(target, ref target.Comp))
return;
var sourceOrgans = _container.EnsureContainer<Container>(source, BodyComponent.ContainerID);
foreach (var sourceOrgan in sourceOrgans.ContainedEntities)
{
var evt = new OrganCopyAppearanceEvent(sourceOrgan);
RaiseLocalEvent(target, ref evt);
}
}
private void OnVisualOrganCopyAppearance(Entity<VisualOrganComponent> ent, ref BodyRelayedEvent<OrganCopyAppearanceEvent> args)
{
if (!TryComp<VisualOrganComponent>(args.Args.Organ, out var other))

View File

@@ -2,6 +2,9 @@ using Robust.Shared.GameStates;
namespace Content.Shared.Body;
/// <summary>
/// Component on an entity with <see cref="BodyComponent" /> that modifies its appearance based on contained organs with <see cref="VisualOrganComponent" />
/// </summary>
[RegisterComponent, NetworkedComponent]
[Access(typeof(SharedVisualBodySystem))]
public sealed partial class VisualBodyComponent : Component;

View File

@@ -4,28 +4,35 @@ using Robust.Shared.Serialization;
namespace Content.Shared.Body;
/// <summary>
/// Defines an organ that applies a sprite to the specified <see cref="Layer" /> within the body
/// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)]
[Access(typeof(SharedVisualBodySystem))]
public sealed partial class VisualOrganComponent : Component
{
/// <summary>
/// The layer on the entity that this contributes to
/// The sprite layer on the entity that this contributes to
/// </summary>
[DataField(required: true)]
public Enum Layer;
/// <summary>
/// The data for the layer
/// The sprite data for the layer
/// </summary>
[DataField(required: true), AutoNetworkedField, AlwaysPushInheritance]
public PrototypeLayerData Data;
/// <summary>
/// When applying a profile, if the sex is present in this dictionary, overrides the state of the data.
/// When applying a profile, if the sex is present in this dictionary, overrides the state of the sprite data.
/// Used for e.g. male vs female torsoes.
/// </summary>
[DataField]
public Dictionary<Sex, string>? SexStateOverrides;
/// <summary>
/// The current profile data of this organ, used for alternate sprite selection and colouration.
/// </summary>
[DataField, AutoNetworkedField]
public OrganProfileData Profile = new();
}

View File

@@ -6,18 +6,21 @@ using Robust.Shared.Serialization;
namespace Content.Shared.Body;
/// <summary>
/// Defines an organ that applies markings on top of the layer specified in <see cref="VisualOrganComponent" />
/// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(raiseAfterAutoHandleState: true, fieldDeltas: true)]
[Access(typeof(SharedVisualBodySystem))]
public sealed partial class VisualOrganMarkingsComponent : Component
{
/// <summary>
/// What markings this organ can take
/// Defines the type of markings this organ can take
/// </summary>
[DataField(required: true), AlwaysPushInheritance]
public OrganMarkingData MarkingData = default!;
/// <summary>
/// The list of markings to apply to the entity
/// The list of markings this organ is currently providing to the entity
/// </summary>
[DataField, AutoNetworkedField]
public Dictionary<HumanoidVisualLayers, List<Marking>> Markings = new();

View File

@@ -7,6 +7,9 @@ using Robust.Shared.Prototypes;
namespace Content.Shared.Humanoid.Markings;
/// <summary>
/// Manager responsible for sharing the logic of markings between in-simulation bodies and out-of-simulation profile editing
/// </summary>
public sealed class MarkingManager
{
[Dependency] private readonly IComponentFactory _component = default!;
@@ -81,6 +84,12 @@ public sealed class MarkingManager
return res;
}
/// <summary>
/// Gets the marking prototype associated with the marking.
/// </summary>
/// <param name="marking">The marking to look up</param>
/// <param name="markingResult">When this method returns; will contain the marking prototype corresponding to the one specified by the marking if it exists.</param>
/// <returns>Whether a marking prototype exists for the given marking</returns>
public bool TryGetMarking(Marking marking, [NotNullWhen(true)] out MarkingPrototype? markingResult)
{
return _markings.TryGetValue(marking.MarkingId, out markingResult);
@@ -92,7 +101,13 @@ public sealed class MarkingManager
CachePrototypes();
}
/// <summary>
/// Determines if a marking prototype can be applied to something with the given markings group and sex.
/// </summary>
/// <param name="group">The markings group to test</param>
/// <param name="sex">The sex to test</param>
/// <param name="prototype">The prototype to reference against</param>
/// <returns>True if a marking with the prototype could be applied</returns>
public bool CanBeApplied(ProtoId<MarkingsGroupPrototype> group, Sex sex, MarkingPrototype prototype)
{
var groupProto = _prototype.Index(group);
@@ -223,6 +238,11 @@ public sealed class MarkingManager
}
}
/// <summary>
/// Returns the expected set of organs for a species to have.
/// </summary>
/// <param name="species">The species to look up.</param>
/// <returns>A dictionary of organ categories to their usual organs within a species.</returns>
public Dictionary<ProtoId<OrganCategoryPrototype>, EntProtoId<OrganComponent>> GetOrgans(ProtoId<SpeciesPrototype> species)
{
var speciesPrototype = _prototype.Index(species);
@@ -234,6 +254,11 @@ public sealed class MarkingManager
return initialBody.Organs;
}
/// <summary>
/// Looks up the expected set of <see cref="OrganMarkingData" /> for the species to have
/// </summary>
/// <param name="species">The species to look up the usual organs of.</param>
/// <returns>A dictionary of organ categories to their usual organ marking data within a species.</returns>
public Dictionary<ProtoId<OrganCategoryPrototype>, OrganMarkingData> GetMarkingData(ProtoId<SpeciesPrototype> species)
{
var ret = new Dictionary<ProtoId<OrganCategoryPrototype>, OrganMarkingData>();
@@ -249,6 +274,15 @@ public sealed class MarkingManager
return ret;
}
/// <summary>
/// Expands the provided profile data into all the categories for a species.
/// </summary>
/// <param name="species">The species the returned dictionary should be comprehensive for.</param>
/// <param name="sex">The sex to apply to all organs</param>
/// <param name="skinColor">The skin color to apply to all organs</param>
/// <param name="eyeColor">The eye color to apply to all organs</param>
/// <returns></returns>
/// <seealso cref="OrganProfileData" />
public Dictionary<ProtoId<OrganCategoryPrototype>, OrganProfileData> GetProfileData(ProtoId<SpeciesPrototype> species,
Sex sex,
Color skinColor,
@@ -269,6 +303,12 @@ public sealed class MarkingManager
return ret;
}
/// <summary>
/// Gets the <see cref="OrganMarkingData" /> for the entity prototype corresponding to an organ
/// </summary>
/// <param name="organ">The ID of the organ entity prototype to look up</param>
/// <param name="organData">The marking data for the organ if it exists</param>
/// <returns>Whether the provided entity prototype ID corresponded to organ marking data that could be returned</returns>
public bool TryGetMarkingData(EntProtoId organ, [NotNullWhen(true)] out OrganMarkingData? organData)
{
organData = null;
@@ -284,6 +324,12 @@ public sealed class MarkingManager
return true;
}
/// <summary>
/// Converts a legacy flat list of markings to a structured markings dictionary for a given species
/// </summary>
/// <param name="markings">A flat list of markings</param>
/// <param name="species">The species to convert the markings for</param>
/// <returns>A dictionary with the provided markings categorized appropriately for the species</returns>
public Dictionary<ProtoId<OrganCategoryPrototype>, Dictionary<HumanoidVisualLayers, List<Marking>>> ConvertMarkings(List<Marking> markings,
ProtoId<SpeciesPrototype> species)
{