mirror of
https://github.com/space-wizards/space-station-14.git
synced 2026-02-14 19:29:53 +01:00
Documents all the public APIs for Nubody & markings code (#42857)
This commit is contained in:
committed by
GitHub
parent
41dddf4d99
commit
9c23b4a6d8
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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!;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user