diff --git a/Content.Client/Body/Components/BrainComponent.cs b/Content.Client/Body/Components/BrainComponent.cs new file mode 100644 index 00000000000..826cdbacd42 --- /dev/null +++ b/Content.Client/Body/Components/BrainComponent.cs @@ -0,0 +1,9 @@ +using Robust.Shared.GameStates; + +namespace Content.Client.Body.Components +{ + [RegisterComponent] + public sealed partial class BrainComponent : Component + { + } +} diff --git a/Content.Client/Body/Components/LungComponent.cs b/Content.Client/Body/Components/LungComponent.cs new file mode 100644 index 00000000000..0c16b43b63c --- /dev/null +++ b/Content.Client/Body/Components/LungComponent.cs @@ -0,0 +1,9 @@ +using Robust.Shared.GameStates; + +namespace Content.Client.Body.Components +{ + [RegisterComponent] + public sealed partial class LungComponent : Component + { + } +} diff --git a/Content.Client/Body/Components/StomachComponent.cs b/Content.Client/Body/Components/StomachComponent.cs new file mode 100644 index 00000000000..3022d4b4aa2 --- /dev/null +++ b/Content.Client/Body/Components/StomachComponent.cs @@ -0,0 +1,9 @@ +using Robust.Shared.GameStates; + +namespace Content.Client.Body.Components +{ + [RegisterComponent] + public sealed partial class StomachComponent : Component + { + } +} diff --git a/Content.Client/Body/Systems/BodySystem.cs b/Content.Client/Body/Systems/BodySystem.cs index bab785525b0..41117d7fdc2 100644 --- a/Content.Client/Body/Systems/BodySystem.cs +++ b/Content.Client/Body/Systems/BodySystem.cs @@ -1,7 +1,83 @@ using Content.Shared.Body.Systems; +using Content.Shared.Body.Part; +using Content.Shared.Humanoid; +using Content.Shared.Humanoid.Markings; +using Robust.Client.GameObjects; +using Robust.Shared.Utility; +using Content.Shared.Body.Components; namespace Content.Client.Body.Systems; public sealed class BodySystem : SharedBodySystem { + [Dependency] private readonly MarkingManager _markingManager = default!; + + private void ApplyMarkingToPart(MarkingPrototype markingPrototype, + IReadOnlyList? colors, + bool visible, + SpriteComponent sprite) + { + for (var j = 0; j < markingPrototype.Sprites.Count; j++) + { + var markingSprite = markingPrototype.Sprites[j]; + + if (markingSprite is not SpriteSpecifier.Rsi rsi) + { + continue; + } + + var layerId = $"{markingPrototype.ID}-{rsi.RsiState}"; + + if (!sprite.LayerMapTryGet(layerId, out _)) + { + var layer = sprite.AddLayer(markingSprite, j + 1); + sprite.LayerMapSet(layerId, layer); + sprite.LayerSetSprite(layerId, rsi); + } + + sprite.LayerSetVisible(layerId, visible); + + if (!visible) + { + continue; + } + + // Okay so if the marking prototype is modified but we load old marking data this may no longer be valid + // and we need to check the index is correct. + // So if that happens just default to white? + if (colors != null && j < colors.Count) + { + sprite.LayerSetColor(layerId, colors[j]); + } + else + { + sprite.LayerSetColor(layerId, Color.White); + } + } + } + + protected override void ApplyPartMarkings(EntityUid target, BodyPartAppearanceComponent component) + { + if (!TryComp(target, out SpriteComponent? sprite)) + return; + + if (component.Color != null) + sprite.Color = component.Color.Value; + + foreach (var (visualLayer, markingList) in component.Markings) + { + foreach (var marking in markingList) + { + if (!_markingManager.TryGetMarking(marking, out var markingPrototype)) + continue; + + ApplyMarkingToPart(markingPrototype, marking.MarkingColors, marking.Visible, sprite); + } + } + } + + protected override void RemoveBodyMarkings(EntityUid target, BodyPartAppearanceComponent partAppearance, HumanoidAppearanceComponent bodyAppearance) + { + return; + } } diff --git a/Content.Client/Hands/Systems/HandsSystem.cs b/Content.Client/Hands/Systems/HandsSystem.cs index 7319b97b42b..1706e102f2f 100644 --- a/Content.Client/Hands/Systems/HandsSystem.cs +++ b/Content.Client/Hands/Systems/HandsSystem.cs @@ -3,6 +3,7 @@ using Content.Client.Examine; using Content.Client.Strip; using Content.Client.Verbs.UI; +using Content.Shared.Body.Part; using Content.Shared.Hands; using Content.Shared.Hands.Components; using Content.Shared.Hands.EntitySystems; @@ -38,7 +39,6 @@ public sealed class HandsSystem : SharedHandsSystem public event Action? OnPlayerItemRemoved; public event Action? OnPlayerHandBlocked; public event Action? OnPlayerHandUnblocked; - public override void Initialize() { base.Initialize(); @@ -49,6 +49,8 @@ public override void Initialize() SubscribeLocalEvent(OnHandsShutdown); SubscribeLocalEvent(HandleComponentState); SubscribeLocalEvent(OnVisualsChanged); + SubscribeLocalEvent(HandleBodyPartRemoved); + SubscribeLocalEvent(HandleBodyPartDisabled); OnHandSetActive += OnHandActivated; } @@ -236,8 +238,46 @@ public void UIHandAltActivateItem(string handName) RaisePredictiveEvent(new RequestHandAltInteractEvent(handName)); } + #region pulling + + #endregion + #region visuals + private void HideLayers(EntityUid uid, HandsComponent component, Entity part, SpriteComponent? sprite = null) + { + if (part.Comp.PartType != BodyPartType.Hand || !Resolve(uid, ref sprite, logMissing: false)) + return; + + var location = part.Comp.Symmetry switch + { + BodyPartSymmetry.None => HandLocation.Middle, + BodyPartSymmetry.Left => HandLocation.Left, + BodyPartSymmetry.Right => HandLocation.Right, + _ => throw new ArgumentOutOfRangeException(nameof(part.Comp.Symmetry)) + }; + + if (component.RevealedLayers.TryGetValue(location, out var revealedLayers)) + { + foreach (var key in revealedLayers) + { + sprite.RemoveLayer(key); + } + + revealedLayers.Clear(); + } + } + + private void HandleBodyPartRemoved(EntityUid uid, HandsComponent component, ref BodyPartRemovedEvent args) + { + HideLayers(uid, component, args.Part); + } + + private void HandleBodyPartDisabled(EntityUid uid, HandsComponent component, ref BodyPartDisabledEvent args) + { + HideLayers(uid, component, args.Part); + } + protected override void HandleEntityInserted(EntityUid uid, HandsComponent hands, EntInsertedIntoContainerMessage args) { base.HandleEntityInserted(uid, hands, args); @@ -262,6 +302,7 @@ protected override void HandleEntityRemoved(EntityUid uid, HandsComponent hands, if (!hands.Hands.TryGetValue(args.Container.ID, out var hand)) return; + UpdateHandVisuals(uid, args.Entity, hand); _stripSys.UpdateUi(uid); diff --git a/Content.Client/HealthAnalyzer/UI/HealthAnalyzerBoundUserInterface.cs b/Content.Client/HealthAnalyzer/UI/HealthAnalyzerBoundUserInterface.cs index dc0a3e9fccd..0010c281a20 100644 --- a/Content.Client/HealthAnalyzer/UI/HealthAnalyzerBoundUserInterface.cs +++ b/Content.Client/HealthAnalyzer/UI/HealthAnalyzerBoundUserInterface.cs @@ -1,4 +1,5 @@ using Content.Shared.MedicalScanner; +using Content.Shared.Targeting; using JetBrains.Annotations; using Robust.Client.GameObjects; @@ -22,6 +23,7 @@ protected override void Open() Title = EntMan.GetComponent(Owner).EntityName, }; _window.OnClose += Close; + _window.OnBodyPartSelected += SendBodyPartMessage; _window.OpenCentered(); } @@ -36,6 +38,14 @@ protected override void ReceiveMessage(BoundUserInterfaceMessage message) _window.Populate(cast); } + private void SendBodyPartMessage(TargetBodyPart? part, EntityUid target) + { + if (part == null) + SendMessage(new HealthAnalyzerPartMessage(EntMan.GetNetEntity(target), null)); + else + SendMessage(new HealthAnalyzerPartMessage(EntMan.GetNetEntity(target), part.Value)); + } + protected override void Dispose(bool disposing) { base.Dispose(disposing); @@ -43,7 +53,10 @@ protected override void Dispose(bool disposing) return; if (_window != null) + { _window.OnClose -= Close; + _window.OnBodyPartSelected -= SendBodyPartMessage; + } _window?.Dispose(); } diff --git a/Content.Client/HealthAnalyzer/UI/HealthAnalyzerWindow.xaml b/Content.Client/HealthAnalyzer/UI/HealthAnalyzerWindow.xaml index e070af95d82..0a0b5ac89e7 100644 --- a/Content.Client/HealthAnalyzer/UI/HealthAnalyzerWindow.xaml +++ b/Content.Client/HealthAnalyzer/UI/HealthAnalyzerWindow.xaml @@ -2,7 +2,7 @@ xmlns="https://spacestation14.io" xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls" MaxHeight="525" - MinWidth="300"> + MinWidth="350">