1
0

SolutionContainerVisualsSystem.cs 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. using Content.Client.Items.Systems;
  2. using Content.Shared.Chemistry;
  3. using Content.Shared.Chemistry.Components;
  4. using Content.Shared.Chemistry.Reagent;
  5. using Content.Shared.Clothing;
  6. using Content.Shared.Clothing.Components;
  7. using Content.Shared.Hands;
  8. using Content.Shared.Item;
  9. using Content.Shared.Rounding;
  10. using Robust.Client.GameObjects;
  11. using Robust.Shared.Prototypes;
  12. namespace Content.Client.Chemistry.Visualizers;
  13. public sealed class SolutionContainerVisualsSystem : VisualizerSystem<SolutionContainerVisualsComponent>
  14. {
  15. [Dependency] private readonly IPrototypeManager _prototype = default!;
  16. [Dependency] private readonly ItemSystem _itemSystem = default!;
  17. public override void Initialize()
  18. {
  19. base.Initialize();
  20. SubscribeLocalEvent<SolutionContainerVisualsComponent, MapInitEvent>(OnMapInit);
  21. SubscribeLocalEvent<SolutionContainerVisualsComponent, GetInhandVisualsEvent>(OnGetHeldVisuals);
  22. SubscribeLocalEvent<SolutionContainerVisualsComponent, GetEquipmentVisualsEvent>(OnGetClothingVisuals);
  23. }
  24. private void OnMapInit(EntityUid uid, SolutionContainerVisualsComponent component, MapInitEvent args)
  25. {
  26. var meta = MetaData(uid);
  27. component.InitialDescription = meta.EntityDescription;
  28. }
  29. protected override void OnAppearanceChange(EntityUid uid, SolutionContainerVisualsComponent component, ref AppearanceChangeEvent args)
  30. {
  31. // Check if the solution that was updated is the one set as represented
  32. if (!string.IsNullOrEmpty(component.SolutionName))
  33. {
  34. if (AppearanceSystem.TryGetData<string>(uid, SolutionContainerVisuals.SolutionName, out var name,
  35. args.Component) && name != component.SolutionName)
  36. {
  37. return;
  38. }
  39. }
  40. if (!AppearanceSystem.TryGetData<float>(uid, SolutionContainerVisuals.FillFraction, out var fraction, args.Component))
  41. return;
  42. if (args.Sprite == null)
  43. return;
  44. if (!args.Sprite.LayerMapTryGet(component.Layer, out var fillLayer))
  45. return;
  46. var maxFillLevels = component.MaxFillLevels;
  47. var fillBaseName = component.FillBaseName;
  48. var changeColor = component.ChangeColor;
  49. var fillSprite = component.MetamorphicDefaultSprite;
  50. // Currently some solution methods such as overflowing will try to update appearance with a
  51. // volume greater than the max volume. We'll clamp it so players don't see
  52. // a giant error sign and error for debug.
  53. if (fraction > 1f)
  54. {
  55. Log.Error("Attempted to set solution container visuals volume ratio on " + ToPrettyString(uid) + " to a value greater than 1. Volume should never be greater than max volume!");
  56. fraction = 1f;
  57. }
  58. if (component.Metamorphic)
  59. {
  60. if (args.Sprite.LayerMapTryGet(component.BaseLayer, out var baseLayer))
  61. {
  62. var hasOverlay = args.Sprite.LayerMapTryGet(component.OverlayLayer, out var overlayLayer);
  63. if (AppearanceSystem.TryGetData<string>(uid, SolutionContainerVisuals.BaseOverride,
  64. out var baseOverride,
  65. args.Component))
  66. {
  67. _prototype.TryIndex<ReagentPrototype>(baseOverride, out var reagentProto);
  68. if (reagentProto?.MetamorphicSprite is { } sprite)
  69. {
  70. args.Sprite.LayerSetSprite(baseLayer, sprite);
  71. if (reagentProto.MetamorphicMaxFillLevels > 0)
  72. {
  73. args.Sprite.LayerSetVisible(fillLayer, true);
  74. maxFillLevels = reagentProto.MetamorphicMaxFillLevels;
  75. fillBaseName = reagentProto.MetamorphicFillBaseName;
  76. changeColor = reagentProto.MetamorphicChangeColor;
  77. fillSprite = sprite;
  78. }
  79. else
  80. args.Sprite.LayerSetVisible(fillLayer, false);
  81. if (hasOverlay)
  82. args.Sprite.LayerSetVisible(overlayLayer, false);
  83. }
  84. else
  85. {
  86. args.Sprite.LayerSetVisible(fillLayer, true);
  87. if (hasOverlay)
  88. args.Sprite.LayerSetVisible(overlayLayer, true);
  89. if (component.MetamorphicDefaultSprite != null)
  90. args.Sprite.LayerSetSprite(baseLayer, component.MetamorphicDefaultSprite);
  91. }
  92. }
  93. }
  94. }
  95. else
  96. {
  97. args.Sprite.LayerSetVisible(fillLayer, true);
  98. }
  99. var closestFillSprite = ContentHelpers.RoundToLevels(fraction, 1, maxFillLevels + 1);
  100. if (closestFillSprite > 0)
  101. {
  102. if (fillBaseName == null)
  103. return;
  104. var stateName = fillBaseName + closestFillSprite;
  105. if (fillSprite != null)
  106. args.Sprite.LayerSetSprite(fillLayer, fillSprite);
  107. args.Sprite.LayerSetState(fillLayer, stateName);
  108. if (changeColor && AppearanceSystem.TryGetData<Color>(uid, SolutionContainerVisuals.Color, out var color, args.Component))
  109. args.Sprite.LayerSetColor(fillLayer, color);
  110. else
  111. args.Sprite.LayerSetColor(fillLayer, Color.White);
  112. }
  113. else
  114. {
  115. if (component.EmptySpriteName == null)
  116. args.Sprite.LayerSetVisible(fillLayer, false);
  117. else
  118. {
  119. args.Sprite.LayerSetState(fillLayer, component.EmptySpriteName);
  120. if (changeColor)
  121. args.Sprite.LayerSetColor(fillLayer, component.EmptySpriteColor);
  122. else
  123. args.Sprite.LayerSetColor(fillLayer, Color.White);
  124. }
  125. }
  126. // in-hand visuals
  127. _itemSystem.VisualsChanged(uid);
  128. }
  129. private void OnGetHeldVisuals(EntityUid uid, SolutionContainerVisualsComponent component, GetInhandVisualsEvent args)
  130. {
  131. if (component.InHandsFillBaseName == null)
  132. return;
  133. if (!TryComp(uid, out AppearanceComponent? appearance))
  134. return;
  135. if (!TryComp<ItemComponent>(uid, out var item))
  136. return;
  137. if (!AppearanceSystem.TryGetData<float>(uid, SolutionContainerVisuals.FillFraction, out var fraction, appearance))
  138. return;
  139. var closestFillSprite = ContentHelpers.RoundToLevels(fraction, 1, component.InHandsMaxFillLevels + 1);
  140. if (closestFillSprite > 0)
  141. {
  142. var layer = new PrototypeLayerData();
  143. var heldPrefix = item.HeldPrefix == null ? "inhand-" : $"{item.HeldPrefix}-inhand-";
  144. var key = heldPrefix + args.Location.ToString().ToLowerInvariant() + component.InHandsFillBaseName + closestFillSprite;
  145. layer.State = key;
  146. if (component.ChangeColor && AppearanceSystem.TryGetData<Color>(uid, SolutionContainerVisuals.Color, out var color, appearance))
  147. layer.Color = color;
  148. args.Layers.Add((key, layer));
  149. }
  150. }
  151. private void OnGetClothingVisuals(Entity<SolutionContainerVisualsComponent> ent, ref GetEquipmentVisualsEvent args)
  152. {
  153. if (ent.Comp.EquippedFillBaseName == null)
  154. return;
  155. if (!TryComp<AppearanceComponent>(ent, out var appearance))
  156. return;
  157. if (!TryComp<ClothingComponent>(ent, out var clothing))
  158. return;
  159. if (!AppearanceSystem.TryGetData<float>(ent, SolutionContainerVisuals.FillFraction, out var fraction, appearance))
  160. return;
  161. var closestFillSprite = ContentHelpers.RoundToLevels(fraction, 1, ent.Comp.EquippedMaxFillLevels + 1);
  162. if (closestFillSprite > 0)
  163. {
  164. var layer = new PrototypeLayerData();
  165. var equippedPrefix = clothing.EquippedPrefix == null ? $"equipped-{args.Slot}" : $" {clothing.EquippedPrefix}-equipped-{args.Slot}";
  166. var key = equippedPrefix + ent.Comp.EquippedFillBaseName + closestFillSprite;
  167. // Make sure the sprite state is valid so we don't show a big red error message
  168. // This saves us from having to make fill level sprites for every possible slot the item could be in (including pockets).
  169. if (!TryComp<SpriteComponent>(ent, out var sprite) || sprite.BaseRSI == null || !sprite.BaseRSI.TryGetState(key, out _))
  170. return;
  171. layer.State = key;
  172. if (ent.Comp.ChangeColor && AppearanceSystem.TryGetData<Color>(ent, SolutionContainerVisuals.Color, out var color, appearance))
  173. layer.Color = color;
  174. args.Layers.Add((key, layer));
  175. }
  176. }
  177. }