SharedBodySystem.PartAppearance.cs 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. // SPDX-FileCopyrightText: 2024 Piras314 <p1r4s@proton.me>
  2. // SPDX-FileCopyrightText: 2024 gluesniffler <159397573+gluesniffler@users.noreply.github.com>
  3. // SPDX-FileCopyrightText: 2025 Aiden <28298836+Aidenkrz@users.noreply.github.com>
  4. //
  5. // SPDX-License-Identifier: AGPL-3.0-or-later
  6. using System.Linq;
  7. using Content.Shared.Body.Components;
  8. using Content.Shared.Body.Part;
  9. using Content.Shared._Shitmed.Body.Part;
  10. using Content.Shared.Humanoid;
  11. using Content.Shared.Humanoid.Markings;
  12. using Content.Shared.Humanoid.Prototypes;
  13. using Robust.Shared.Prototypes;
  14. namespace Content.Shared.Body.Systems;
  15. public partial class SharedBodySystem
  16. {
  17. [Dependency] private readonly SharedHumanoidAppearanceSystem _humanoid = default!;
  18. [Dependency] private readonly MarkingManager _markingManager = default!;
  19. [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
  20. private void InitializePartAppearances()
  21. {
  22. base.Initialize();
  23. SubscribeLocalEvent<BodyPartAppearanceComponent, ComponentStartup>(OnPartAppearanceStartup);
  24. SubscribeLocalEvent<BodyPartAppearanceComponent, AfterAutoHandleStateEvent>(HandleState);
  25. SubscribeLocalEvent<BodyComponent, BodyPartAddedEvent>(OnPartAttachedToBody);
  26. SubscribeLocalEvent<BodyComponent, BodyPartRemovedEvent>(OnPartDroppedFromBody);
  27. }
  28. private void OnPartAppearanceStartup(EntityUid uid, BodyPartAppearanceComponent component, ComponentStartup args)
  29. {
  30. if (!TryComp(uid, out BodyPartComponent? part)
  31. || part.ToHumanoidLayers() is not { } relevantLayer)
  32. return;
  33. if (part.BaseLayerId != null)
  34. {
  35. component.ID = part.BaseLayerId;
  36. component.Type = relevantLayer;
  37. return;
  38. }
  39. if (part.Body is not { Valid: true } body
  40. || !TryComp(body, out HumanoidAppearanceComponent? bodyAppearance))
  41. return;
  42. var customLayers = bodyAppearance.CustomBaseLayers;
  43. var spriteLayers = bodyAppearance.BaseLayers;
  44. component.Type = relevantLayer;
  45. part.Species = bodyAppearance.Species;
  46. if (customLayers.ContainsKey(component.Type))
  47. {
  48. component.ID = customLayers[component.Type].Id;
  49. component.Color = customLayers[component.Type].Color;
  50. }
  51. else if (spriteLayers.ContainsKey(component.Type))
  52. {
  53. component.ID = spriteLayers[component.Type].ID;
  54. component.Color = bodyAppearance.SkinColor;
  55. }
  56. else
  57. {
  58. component.ID = CreateIdFromPart(bodyAppearance, relevantLayer);
  59. component.Color = bodyAppearance.SkinColor;
  60. }
  61. // I HATE HARDCODED CHECKS I HATE HARDCODED CHECKS I HATE HARDCODED CHECKS
  62. if (part.PartType == BodyPartType.Head)
  63. component.EyeColor = bodyAppearance.EyeColor;
  64. var markingsByLayer = new Dictionary<HumanoidVisualLayers, List<Marking>>();
  65. foreach (var layer in HumanoidVisualLayersExtension.Sublayers(relevantLayer))
  66. {
  67. var category = MarkingCategoriesConversion.FromHumanoidVisualLayers(layer);
  68. if (bodyAppearance.MarkingSet.Markings.TryGetValue(category, out var markingList))
  69. markingsByLayer[layer] = markingList.Select(m => new Marking(m.MarkingId, m.MarkingColors.ToList())).ToList();
  70. }
  71. component.Markings = markingsByLayer;
  72. }
  73. private string? CreateIdFromPart(HumanoidAppearanceComponent bodyAppearance, HumanoidVisualLayers part)
  74. {
  75. var speciesProto = _prototypeManager.Index(bodyAppearance.Species);
  76. var baseSprites = _prototypeManager.Index<HumanoidSpeciesBaseSpritesPrototype>(speciesProto.SpriteSet);
  77. if (!baseSprites.Sprites.ContainsKey(part))
  78. return null;
  79. return HumanoidVisualLayersExtension.GetSexMorph(part, bodyAppearance.Sex, baseSprites.Sprites[part]);
  80. }
  81. public void ModifyMarkings(EntityUid uid,
  82. Entity<BodyPartAppearanceComponent?> partAppearance,
  83. HumanoidAppearanceComponent bodyAppearance,
  84. HumanoidVisualLayers targetLayer,
  85. string markingId,
  86. bool remove = false)
  87. {
  88. if (!Resolve(partAppearance, ref partAppearance.Comp))
  89. return;
  90. if (!remove)
  91. {
  92. if (!_markingManager.Markings.TryGetValue(markingId, out var prototype))
  93. return;
  94. var markingColors = MarkingColoring.GetMarkingLayerColors(
  95. prototype,
  96. bodyAppearance.SkinColor,
  97. bodyAppearance.EyeColor,
  98. bodyAppearance.MarkingSet
  99. );
  100. var marking = new Marking(markingId, markingColors);
  101. _humanoid.SetLayerVisibility(uid, targetLayer, true, true, bodyAppearance);
  102. _humanoid.AddMarking(uid, markingId, markingColors, true, true, bodyAppearance);
  103. if (!partAppearance.Comp.Markings.ContainsKey(targetLayer))
  104. partAppearance.Comp.Markings[targetLayer] = new List<Marking>();
  105. partAppearance.Comp.Markings[targetLayer].Add(marking);
  106. }
  107. //else
  108. //RemovePartMarkings(uid, component, bodyAppearance);
  109. }
  110. private void HandleState(EntityUid uid, BodyPartAppearanceComponent component, ref AfterAutoHandleStateEvent args) =>
  111. ApplyPartMarkings(uid, component);
  112. private void OnPartAttachedToBody(EntityUid uid, BodyComponent component, ref BodyPartAddedEvent args)
  113. {
  114. if (!TryComp(args.Part, out BodyPartAppearanceComponent? partAppearance)
  115. || !TryComp(uid, out HumanoidAppearanceComponent? bodyAppearance))
  116. return;
  117. if (partAppearance.ID != null)
  118. _humanoid.SetBaseLayerId(uid, partAppearance.Type, partAppearance.ID, sync: true, bodyAppearance);
  119. UpdateAppearance(uid, partAppearance);
  120. }
  121. private void OnPartDroppedFromBody(EntityUid uid, BodyComponent component, ref BodyPartRemovedEvent args)
  122. {
  123. if (TerminatingOrDeleted(uid)
  124. || TerminatingOrDeleted(args.Part)
  125. || !TryComp(uid, out HumanoidAppearanceComponent? bodyAppearance))
  126. return;
  127. // We check for this conditional here since some entities may not have a profile... If they dont
  128. // have one, and their part is gibbed, the markings will not be removed or applied properly.
  129. if (!HasComp<BodyPartAppearanceComponent>(args.Part))
  130. EnsureComp<BodyPartAppearanceComponent>(args.Part);
  131. if (TryComp<BodyPartAppearanceComponent>(args.Part, out var partAppearance))
  132. RemoveAppearance(uid, partAppearance, args.Part);
  133. }
  134. protected void UpdateAppearance(EntityUid target,
  135. BodyPartAppearanceComponent component)
  136. {
  137. if (!TryComp(target, out HumanoidAppearanceComponent? bodyAppearance))
  138. return;
  139. if (component.EyeColor != null)
  140. {
  141. bodyAppearance.EyeColor = component.EyeColor.Value;
  142. _humanoid.SetLayerVisibility(target, HumanoidVisualLayers.Eyes, true, true, bodyAppearance);
  143. }
  144. if (component.Color != null)
  145. _humanoid.SetBaseLayerColor(target, component.Type, component.Color, true, bodyAppearance);
  146. _humanoid.SetLayerVisibility(target, component.Type, true, true, bodyAppearance);
  147. foreach (var (visualLayer, markingList) in component.Markings)
  148. {
  149. _humanoid.SetLayerVisibility(target, visualLayer, true, true, bodyAppearance);
  150. foreach (var marking in markingList)
  151. {
  152. _humanoid.AddMarking(target, marking.MarkingId, marking.MarkingColors, true, true, bodyAppearance);
  153. }
  154. }
  155. Dirty(target, bodyAppearance);
  156. }
  157. protected void RemoveAppearance(EntityUid entity, BodyPartAppearanceComponent component, EntityUid partEntity)
  158. {
  159. if (!TryComp(entity, out HumanoidAppearanceComponent? bodyAppearance))
  160. return;
  161. foreach (var (visualLayer, markingList) in component.Markings)
  162. {
  163. _humanoid.SetLayerVisibility(entity, visualLayer, false, true, bodyAppearance);
  164. }
  165. RemoveBodyMarkings(entity, component, bodyAppearance);
  166. }
  167. protected abstract void ApplyPartMarkings(EntityUid target, BodyPartAppearanceComponent component);
  168. protected abstract void RemoveBodyMarkings(EntityUid target, BodyPartAppearanceComponent partAppearance, HumanoidAppearanceComponent bodyAppearance);
  169. }