DamageVisualsSystem.cs 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701
  1. using System.Linq;
  2. using Content.Shared.Damage;
  3. using Content.Shared.Damage.Prototypes;
  4. using Content.Shared.FixedPoint;
  5. using Robust.Client.GameObjects;
  6. using Robust.Shared.Prototypes;
  7. using Robust.Shared.Utility;
  8. namespace Content.Client.Damage;
  9. /// <summary>
  10. /// A simple visualizer for any entity with a DamageableComponent
  11. /// to display the status of how damaged it is.
  12. ///
  13. /// Can either be an overlay for an entity, or target multiple
  14. /// layers on the same entity.
  15. ///
  16. /// This can be disabled dynamically by passing into SetData,
  17. /// key DamageVisualizerKeys.Disabled, value bool
  18. /// (DamageVisualizerKeys lives in Content.Shared.Damage)
  19. ///
  20. /// Damage layers, if targeting layers, can also be dynamically
  21. /// disabled if needed by passing into SetData, the name/enum
  22. /// of the sprite layer, and then passing in a bool value
  23. /// (true to enable, false to disable).
  24. /// </summary>
  25. public sealed class DamageVisualsSystem : VisualizerSystem<DamageVisualsComponent>
  26. {
  27. [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
  28. public override void Initialize()
  29. {
  30. base.Initialize();
  31. SubscribeLocalEvent<DamageVisualsComponent, ComponentInit>(InitializeEntity);
  32. }
  33. private void InitializeEntity(EntityUid entity, DamageVisualsComponent comp, ComponentInit args)
  34. {
  35. VerifyVisualizerSetup(entity, comp);
  36. if (!comp.Valid)
  37. {
  38. RemCompDeferred<DamageVisualsComponent>(entity);
  39. return;
  40. }
  41. InitializeVisualizer(entity, comp);
  42. }
  43. private void VerifyVisualizerSetup(EntityUid entity, DamageVisualsComponent damageVisComp)
  44. {
  45. if (damageVisComp.Thresholds.Count < 1)
  46. {
  47. Log.Error($"ThresholdsLookup were invalid for entity {entity}. ThresholdsLookup: {damageVisComp.Thresholds}");
  48. damageVisComp.Valid = false;
  49. return;
  50. }
  51. if (damageVisComp.Divisor == 0)
  52. {
  53. Log.Error($"Divisor for {entity} is set to zero.");
  54. damageVisComp.Valid = false;
  55. return;
  56. }
  57. if (damageVisComp.Overlay)
  58. {
  59. if (damageVisComp.DamageOverlayGroups == null && damageVisComp.DamageOverlay == null)
  60. {
  61. Log.Error($"Enabled overlay without defined damage overlay sprites on {entity}.");
  62. damageVisComp.Valid = false;
  63. return;
  64. }
  65. if (damageVisComp.TrackAllDamage && damageVisComp.DamageOverlay == null)
  66. {
  67. Log.Error($"Enabled all damage tracking without a damage overlay sprite on {entity}.");
  68. damageVisComp.Valid = false;
  69. return;
  70. }
  71. if (!damageVisComp.TrackAllDamage && damageVisComp.DamageOverlay != null)
  72. {
  73. Log.Warning($"Disabled all damage tracking with a damage overlay sprite on {entity}.");
  74. damageVisComp.Valid = false;
  75. return;
  76. }
  77. if (damageVisComp.TrackAllDamage && damageVisComp.DamageOverlayGroups != null)
  78. {
  79. Log.Warning($"Enabled all damage tracking with damage overlay groups on {entity}.");
  80. damageVisComp.Valid = false;
  81. return;
  82. }
  83. }
  84. else if (!damageVisComp.Overlay)
  85. {
  86. if (damageVisComp.TargetLayers == null)
  87. {
  88. Log.Error($"Disabled overlay without target layers on {entity}.");
  89. damageVisComp.Valid = false;
  90. return;
  91. }
  92. if (damageVisComp.DamageOverlayGroups != null || damageVisComp.DamageOverlay != null)
  93. {
  94. Log.Error($"Disabled overlay with defined damage overlay sprites on {entity}.");
  95. damageVisComp.Valid = false;
  96. return;
  97. }
  98. if (damageVisComp.DamageGroup == null)
  99. {
  100. Log.Error($"Disabled overlay without defined damage group on {entity}.");
  101. damageVisComp.Valid = false;
  102. return;
  103. }
  104. }
  105. if (damageVisComp.DamageOverlayGroups != null && damageVisComp.DamageGroup != null)
  106. {
  107. Log.Warning($"Damage overlay sprites and damage group are both defined on {entity}.");
  108. }
  109. if (damageVisComp.DamageOverlay != null && damageVisComp.DamageGroup != null)
  110. {
  111. Log.Warning($"Damage overlay sprites and damage group are both defined on {entity}.");
  112. }
  113. }
  114. private void InitializeVisualizer(EntityUid entity, DamageVisualsComponent damageVisComp)
  115. {
  116. if (!TryComp(entity, out SpriteComponent? spriteComponent)
  117. || !TryComp<DamageableComponent>(entity, out var damageComponent)
  118. || !HasComp<AppearanceComponent>(entity))
  119. return;
  120. damageVisComp.Thresholds.Add(FixedPoint2.Zero);
  121. damageVisComp.Thresholds.Sort();
  122. if (damageVisComp.Thresholds[0] != 0)
  123. {
  124. Log.Error($"ThresholdsLookup were invalid for entity {entity}. ThresholdsLookup: {damageVisComp.Thresholds}");
  125. damageVisComp.Valid = false;
  126. return;
  127. }
  128. // If the damage container on our entity's DamageableComponent
  129. // is not null, we can try to check through its groups.
  130. if (damageComponent.DamageContainerID != null
  131. && _prototypeManager.TryIndex<DamageContainerPrototype>(damageComponent.DamageContainerID, out var damageContainer))
  132. {
  133. // Are we using damage overlay sprites by group?
  134. // Check if the container matches the supported groups,
  135. // and start caching the last threshold.
  136. if (damageVisComp.DamageOverlayGroups != null)
  137. {
  138. foreach (var damageType in damageVisComp.DamageOverlayGroups.Keys)
  139. {
  140. if (!damageContainer.SupportedGroups.Contains(damageType))
  141. {
  142. Log.Error($"Damage key {damageType} was invalid for entity {entity}.");
  143. damageVisComp.Valid = false;
  144. return;
  145. }
  146. damageVisComp.LastThresholdPerGroup.Add(damageType, FixedPoint2.Zero);
  147. }
  148. }
  149. // Are we tracking a single damage group without overlay instead?
  150. // See if that group is in our entity's damage container.
  151. else if (!damageVisComp.Overlay && damageVisComp.DamageGroup != null)
  152. {
  153. if (!damageContainer.SupportedGroups.Contains(damageVisComp.DamageGroup))
  154. {
  155. Log.Error($"Damage keys were invalid for entity {entity}.");
  156. damageVisComp.Valid = false;
  157. return;
  158. }
  159. damageVisComp.LastThresholdPerGroup.Add(damageVisComp.DamageGroup, FixedPoint2.Zero);
  160. }
  161. }
  162. // Ditto above, but instead we go through every group.
  163. else // oh boy! time to enumerate through every single group!
  164. {
  165. var damagePrototypeIdList = _prototypeManager.EnumeratePrototypes<DamageGroupPrototype>()
  166. .Select((p, _) => p.ID)
  167. .ToList();
  168. if (damageVisComp.DamageOverlayGroups != null)
  169. {
  170. foreach (var damageType in damageVisComp.DamageOverlayGroups.Keys)
  171. {
  172. if (!damagePrototypeIdList.Contains(damageType))
  173. {
  174. Log.Error($"Damage keys were invalid for entity {entity}.");
  175. damageVisComp.Valid = false;
  176. return;
  177. }
  178. damageVisComp.LastThresholdPerGroup.Add(damageType, FixedPoint2.Zero);
  179. }
  180. }
  181. else if (damageVisComp.DamageGroup != null)
  182. {
  183. if (!damagePrototypeIdList.Contains(damageVisComp.DamageGroup))
  184. {
  185. Log.Error($"Damage keys were invalid for entity {entity}.");
  186. damageVisComp.Valid = false;
  187. return;
  188. }
  189. damageVisComp.LastThresholdPerGroup.Add(damageVisComp.DamageGroup, FixedPoint2.Zero);
  190. }
  191. }
  192. // If we're targeting any layers, and the amount of
  193. // layers is greater than zero, we start reserving
  194. // all the layers needed to track damage groups
  195. // on the entity.
  196. if (damageVisComp.TargetLayers is { Count: > 0 })
  197. {
  198. // This should ensure that the layers we're targeting
  199. // are valid for the visualizer's use.
  200. //
  201. // If the layer doesn't have a base state, or
  202. // the layer key just doesn't exist, we skip it.
  203. foreach (var key in damageVisComp.TargetLayers)
  204. {
  205. if (!spriteComponent.LayerMapTryGet(key, out var index))
  206. {
  207. Log.Warning($"Layer at key {key} was invalid for entity {entity}.");
  208. continue;
  209. }
  210. damageVisComp.TargetLayerMapKeys.Add(key);
  211. }
  212. // Similar to damage overlay groups, if none of the targeted
  213. // sprite layers could be used, we display an error and
  214. // invalidate the visualizer without crashing.
  215. if (damageVisComp.TargetLayerMapKeys.Count == 0)
  216. {
  217. Log.Error($"Target layers were invalid for entity {entity}.");
  218. damageVisComp.Valid = false;
  219. return;
  220. }
  221. // Otherwise, we start reserving layers. Since the filtering
  222. // loop above ensures that all of these layers are not null,
  223. // and have valid state IDs, there should be no issues.
  224. foreach (var layer in damageVisComp.TargetLayerMapKeys)
  225. {
  226. var layerCount = spriteComponent.AllLayers.Count();
  227. var index = spriteComponent.LayerMapGet(layer);
  228. // var layerState = spriteComponent.LayerGetState(index).ToString()!;
  229. if (index + 1 != layerCount)
  230. {
  231. index += 1;
  232. }
  233. damageVisComp.LayerMapKeyStates.Add(layer, layer.ToString());
  234. // If we're an overlay, and we're targeting groups,
  235. // we reserve layers per damage group.
  236. if (damageVisComp.Overlay && damageVisComp.DamageOverlayGroups != null)
  237. {
  238. foreach (var (group, sprite) in damageVisComp.DamageOverlayGroups)
  239. {
  240. AddDamageLayerToSprite(spriteComponent,
  241. sprite,
  242. $"{layer}_{group}_{damageVisComp.Thresholds[1]}",
  243. $"{layer}{group}",
  244. index);
  245. }
  246. damageVisComp.DisabledLayers.Add(layer, false);
  247. }
  248. // If we're not targeting groups, and we're still
  249. // using an overlay, we instead just add a general
  250. // overlay that reflects on how much damage
  251. // was taken.
  252. else if (damageVisComp.DamageOverlay != null)
  253. {
  254. AddDamageLayerToSprite(spriteComponent,
  255. damageVisComp.DamageOverlay,
  256. $"{layer}_{damageVisComp.Thresholds[1]}",
  257. $"{layer}trackDamage",
  258. index);
  259. damageVisComp.DisabledLayers.Add(layer, false);
  260. }
  261. }
  262. }
  263. // If we're not targeting layers, however,
  264. // we should ensure that we instead
  265. // reserve it as an overlay.
  266. else
  267. {
  268. if (damageVisComp.DamageOverlayGroups != null)
  269. {
  270. foreach (var (group, sprite) in damageVisComp.DamageOverlayGroups)
  271. {
  272. AddDamageLayerToSprite(spriteComponent,
  273. sprite,
  274. $"DamageOverlay_{group}_{damageVisComp.Thresholds[1]}",
  275. $"DamageOverlay{group}");
  276. damageVisComp.TopMostLayerKey = $"DamageOverlay{group}";
  277. }
  278. }
  279. else if (damageVisComp.DamageOverlay != null)
  280. {
  281. AddDamageLayerToSprite(spriteComponent,
  282. damageVisComp.DamageOverlay,
  283. $"DamageOverlay_{damageVisComp.Thresholds[1]}",
  284. "DamageOverlay");
  285. damageVisComp.TopMostLayerKey = $"DamageOverlay";
  286. }
  287. }
  288. }
  289. /// <summary>
  290. /// Adds a damage tracking layer to a given sprite component.
  291. /// </summary>
  292. private void AddDamageLayerToSprite(SpriteComponent spriteComponent, DamageVisualizerSprite sprite, string state, string mapKey, int? index = null)
  293. {
  294. var newLayer = spriteComponent.AddLayer(
  295. new SpriteSpecifier.Rsi(
  296. new (sprite.Sprite), state
  297. ), index);
  298. spriteComponent.LayerMapSet(mapKey, newLayer);
  299. if (sprite.Color != null)
  300. spriteComponent.LayerSetColor(newLayer, Color.FromHex(sprite.Color));
  301. spriteComponent.LayerSetVisible(newLayer, false);
  302. }
  303. protected override void OnAppearanceChange(EntityUid uid, DamageVisualsComponent damageVisComp, ref AppearanceChangeEvent args)
  304. {
  305. // how is this still here?
  306. if (!damageVisComp.Valid)
  307. return;
  308. // If this was passed into the component, we update
  309. // the data to ensure that the current disabled
  310. // bool matches.
  311. if (AppearanceSystem.TryGetData<bool>(uid, DamageVisualizerKeys.Disabled, out var disabledStatus, args.Component))
  312. damageVisComp.Disabled = disabledStatus;
  313. if (damageVisComp.Disabled)
  314. return;
  315. HandleDamage(uid, args.Component, damageVisComp);
  316. }
  317. private void HandleDamage(EntityUid uid, AppearanceComponent component, DamageVisualsComponent damageVisComp)
  318. {
  319. if (!TryComp(uid, out SpriteComponent? spriteComponent)
  320. || !TryComp(uid, out DamageableComponent? damageComponent))
  321. return;
  322. if (damageVisComp.TargetLayers != null && damageVisComp.DamageOverlayGroups != null)
  323. UpdateDisabledLayers(uid, spriteComponent, component, damageVisComp);
  324. if (damageVisComp.Overlay && damageVisComp.DamageOverlayGroups != null && damageVisComp.TargetLayers == null)
  325. CheckOverlayOrdering(spriteComponent, damageVisComp);
  326. if (AppearanceSystem.TryGetData<bool>(uid, DamageVisualizerKeys.ForceUpdate, out var update, component)
  327. && update)
  328. {
  329. ForceUpdateLayers(damageComponent, spriteComponent, damageVisComp);
  330. return;
  331. }
  332. if (damageVisComp.TrackAllDamage)
  333. {
  334. UpdateDamageVisuals(damageComponent, spriteComponent, damageVisComp);
  335. return;
  336. }
  337. if (!AppearanceSystem.TryGetData<DamageVisualizerGroupData>(uid, DamageVisualizerKeys.DamageUpdateGroups,
  338. out var data, component))
  339. {
  340. data = new DamageVisualizerGroupData(Comp<DamageableComponent>(uid).DamagePerGroup.Keys.ToList());
  341. }
  342. UpdateDamageVisuals(data.GroupList, damageComponent, spriteComponent, damageVisComp);
  343. }
  344. /// <summary>
  345. /// Checks if any layers were disabled in the last
  346. /// data update. Disabled layers mean that the
  347. /// layer will no longer be visible, or obtain
  348. /// any damage updates.
  349. /// </summary>
  350. private void UpdateDisabledLayers(EntityUid uid, SpriteComponent spriteComponent, AppearanceComponent component, DamageVisualsComponent damageVisComp)
  351. {
  352. foreach (var layer in damageVisComp.TargetLayerMapKeys)
  353. {
  354. // I assume this gets set by something like body system if limbs are missing???
  355. // TODO is this actually used by anything anywhere?
  356. AppearanceSystem.TryGetData(uid, layer, out bool disabled, component);
  357. if (damageVisComp.DisabledLayers[layer] == disabled)
  358. continue;
  359. damageVisComp.DisabledLayers[layer] = disabled;
  360. if (damageVisComp.TrackAllDamage)
  361. {
  362. spriteComponent.LayerSetVisible($"{layer}trackDamage", !disabled);
  363. continue;
  364. }
  365. if (damageVisComp.DamageOverlayGroups == null)
  366. continue;
  367. foreach (var damageGroup in damageVisComp.DamageOverlayGroups.Keys)
  368. {
  369. spriteComponent.LayerSetVisible($"{layer}{damageGroup}", !disabled);
  370. }
  371. }
  372. }
  373. /// <summary>
  374. /// Checks the overlay ordering on the current
  375. /// sprite component, compared to the
  376. /// data for the visualizer. If the top
  377. /// most layer doesn't match, the sprite
  378. /// layers are recreated and placed on top.
  379. /// </summary>
  380. private void CheckOverlayOrdering(SpriteComponent spriteComponent, DamageVisualsComponent damageVisComp)
  381. {
  382. if (spriteComponent[damageVisComp.TopMostLayerKey] != spriteComponent[spriteComponent.AllLayers.Count() - 1])
  383. {
  384. if (!damageVisComp.TrackAllDamage && damageVisComp.DamageOverlayGroups != null)
  385. {
  386. foreach (var (damageGroup, sprite) in damageVisComp.DamageOverlayGroups)
  387. {
  388. var threshold = damageVisComp.LastThresholdPerGroup[damageGroup];
  389. ReorderOverlaySprite(spriteComponent,
  390. damageVisComp,
  391. sprite,
  392. $"DamageOverlay{damageGroup}",
  393. $"DamageOverlay_{damageGroup}",
  394. threshold);
  395. }
  396. }
  397. else if (damageVisComp.TrackAllDamage && damageVisComp.DamageOverlay != null)
  398. {
  399. ReorderOverlaySprite(spriteComponent,
  400. damageVisComp,
  401. damageVisComp.DamageOverlay,
  402. $"DamageOverlay",
  403. $"DamageOverlay",
  404. damageVisComp.LastDamageThreshold);
  405. }
  406. }
  407. }
  408. private void ReorderOverlaySprite(SpriteComponent spriteComponent, DamageVisualsComponent damageVisComp, DamageVisualizerSprite sprite, string key, string statePrefix, FixedPoint2 threshold)
  409. {
  410. spriteComponent.LayerMapTryGet(key, out var spriteLayer);
  411. var visibility = spriteComponent[spriteLayer].Visible;
  412. spriteComponent.RemoveLayer(spriteLayer);
  413. if (threshold == FixedPoint2.Zero) // these should automatically be invisible
  414. threshold = damageVisComp.Thresholds[1];
  415. spriteLayer = spriteComponent.AddLayer(
  416. new SpriteSpecifier.Rsi(
  417. new (sprite.Sprite),
  418. $"{statePrefix}_{threshold}"
  419. ),
  420. spriteLayer);
  421. spriteComponent.LayerMapSet(key, spriteLayer);
  422. spriteComponent.LayerSetVisible(spriteLayer, visibility);
  423. // this is somewhat iffy since it constantly reallocates
  424. damageVisComp.TopMostLayerKey = key;
  425. }
  426. /// <summary>
  427. /// Updates damage visuals without tracking
  428. /// any damage groups.
  429. /// </summary>
  430. private void UpdateDamageVisuals(DamageableComponent damageComponent, SpriteComponent spriteComponent, DamageVisualsComponent damageVisComp)
  431. {
  432. if (!CheckThresholdBoundary(damageComponent.TotalDamage, damageVisComp.LastDamageThreshold, damageVisComp, out var threshold))
  433. return;
  434. damageVisComp.LastDamageThreshold = threshold;
  435. if (damageVisComp.TargetLayers != null)
  436. {
  437. foreach (var layerMapKey in damageVisComp.TargetLayerMapKeys)
  438. {
  439. UpdateTargetLayer(spriteComponent, damageVisComp, layerMapKey, threshold);
  440. }
  441. }
  442. else
  443. {
  444. UpdateOverlay(spriteComponent, threshold);
  445. }
  446. }
  447. /// <summary>
  448. /// Updates damage visuals by damage group,
  449. /// according to the list of damage groups
  450. /// passed into it.
  451. /// </summary>
  452. private void UpdateDamageVisuals(List<string> delta, DamageableComponent damageComponent, SpriteComponent spriteComponent, DamageVisualsComponent damageVisComp)
  453. {
  454. foreach (var damageGroup in delta)
  455. {
  456. if (!damageVisComp.Overlay && damageGroup != damageVisComp.DamageGroup)
  457. continue;
  458. if (!_prototypeManager.TryIndex<DamageGroupPrototype>(damageGroup, out var damageGroupPrototype)
  459. || !damageComponent.Damage.TryGetDamageInGroup(damageGroupPrototype, out var damageTotal))
  460. continue;
  461. if (!damageVisComp.LastThresholdPerGroup.TryGetValue(damageGroup, out var lastThreshold)
  462. || !CheckThresholdBoundary(damageTotal, lastThreshold, damageVisComp, out var threshold))
  463. continue;
  464. damageVisComp.LastThresholdPerGroup[damageGroup] = threshold;
  465. if (damageVisComp.TargetLayers != null)
  466. {
  467. foreach (var layerMapKey in damageVisComp.TargetLayerMapKeys)
  468. {
  469. UpdateTargetLayer(spriteComponent, damageVisComp, layerMapKey, damageGroup, threshold);
  470. }
  471. }
  472. else
  473. {
  474. UpdateOverlay(spriteComponent, damageVisComp, damageGroup, threshold);
  475. }
  476. }
  477. }
  478. /// <summary>
  479. /// Checks if a threshold boundary was passed.
  480. /// </summary>
  481. private bool CheckThresholdBoundary(FixedPoint2 damageTotal, FixedPoint2 lastThreshold, DamageVisualsComponent damageVisComp, out FixedPoint2 threshold)
  482. {
  483. threshold = FixedPoint2.Zero;
  484. damageTotal = damageTotal / damageVisComp.Divisor;
  485. var thresholdIndex = damageVisComp.Thresholds.BinarySearch(damageTotal);
  486. if (thresholdIndex < 0)
  487. {
  488. thresholdIndex = ~thresholdIndex;
  489. threshold = damageVisComp.Thresholds[thresholdIndex - 1];
  490. }
  491. else
  492. {
  493. threshold = damageVisComp.Thresholds[thresholdIndex];
  494. }
  495. if (threshold == lastThreshold)
  496. return false;
  497. return true;
  498. }
  499. /// <summary>
  500. /// This is the entry point for
  501. /// forcing an update on all damage layers.
  502. /// Does different things depending on
  503. /// the configuration of the visualizer.
  504. /// </summary>
  505. private void ForceUpdateLayers(DamageableComponent damageComponent, SpriteComponent spriteComponent, DamageVisualsComponent damageVisComp)
  506. {
  507. if (damageVisComp.DamageOverlayGroups != null)
  508. {
  509. UpdateDamageVisuals(damageVisComp.DamageOverlayGroups.Keys.ToList(), damageComponent, spriteComponent, damageVisComp);
  510. }
  511. else if (damageVisComp.DamageGroup != null)
  512. {
  513. UpdateDamageVisuals(new List<string>(){ damageVisComp.DamageGroup }, damageComponent, spriteComponent, damageVisComp);
  514. }
  515. else if (damageVisComp.DamageOverlay != null)
  516. {
  517. UpdateDamageVisuals(damageComponent, spriteComponent, damageVisComp);
  518. }
  519. }
  520. /// <summary>
  521. /// Updates a target layer. Without a damage group passed in,
  522. /// it assumes you're updating a layer that is tracking all
  523. /// damage.
  524. /// </summary>
  525. private void UpdateTargetLayer(SpriteComponent spriteComponent, DamageVisualsComponent damageVisComp, object layerMapKey, FixedPoint2 threshold)
  526. {
  527. if (damageVisComp.Overlay && damageVisComp.DamageOverlayGroups != null)
  528. {
  529. if (!damageVisComp.DisabledLayers[layerMapKey])
  530. {
  531. var layerState = damageVisComp.LayerMapKeyStates[layerMapKey];
  532. spriteComponent.LayerMapTryGet($"{layerMapKey}trackDamage", out var spriteLayer);
  533. UpdateDamageLayerState(spriteComponent,
  534. spriteLayer,
  535. $"{layerState}",
  536. threshold);
  537. }
  538. }
  539. else if (!damageVisComp.Overlay)
  540. {
  541. var layerState = damageVisComp.LayerMapKeyStates[layerMapKey];
  542. spriteComponent.LayerMapTryGet(layerMapKey, out var spriteLayer);
  543. UpdateDamageLayerState(spriteComponent,
  544. spriteLayer,
  545. $"{layerState}",
  546. threshold);
  547. }
  548. }
  549. /// <summary>
  550. /// Updates a target layer by damage group.
  551. /// </summary>
  552. private void UpdateTargetLayer(SpriteComponent spriteComponent, DamageVisualsComponent damageVisComp, object layerMapKey, string damageGroup, FixedPoint2 threshold)
  553. {
  554. if (damageVisComp.Overlay && damageVisComp.DamageOverlayGroups != null)
  555. {
  556. if (damageVisComp.DamageOverlayGroups.ContainsKey(damageGroup) && !damageVisComp.DisabledLayers[layerMapKey])
  557. {
  558. var layerState = damageVisComp.LayerMapKeyStates[layerMapKey];
  559. spriteComponent.LayerMapTryGet($"{layerMapKey}{damageGroup}", out var spriteLayer);
  560. UpdateDamageLayerState(spriteComponent,
  561. spriteLayer,
  562. $"{layerState}_{damageGroup}",
  563. threshold);
  564. }
  565. }
  566. else if (!damageVisComp.Overlay)
  567. {
  568. var layerState = damageVisComp.LayerMapKeyStates[layerMapKey];
  569. spriteComponent.LayerMapTryGet(layerMapKey, out var spriteLayer);
  570. UpdateDamageLayerState(spriteComponent,
  571. spriteLayer,
  572. $"{layerState}_{damageGroup}",
  573. threshold);
  574. }
  575. }
  576. /// <summary>
  577. /// Updates an overlay that is tracking all damage.
  578. /// </summary>
  579. private void UpdateOverlay(SpriteComponent spriteComponent, FixedPoint2 threshold)
  580. {
  581. spriteComponent.LayerMapTryGet($"DamageOverlay", out var spriteLayer);
  582. UpdateDamageLayerState(spriteComponent,
  583. spriteLayer,
  584. $"DamageOverlay",
  585. threshold);
  586. }
  587. /// <summary>
  588. /// Updates an overlay based on damage group.
  589. /// </summary>
  590. private void UpdateOverlay(SpriteComponent spriteComponent, DamageVisualsComponent damageVisComp, string damageGroup, FixedPoint2 threshold)
  591. {
  592. if (damageVisComp.DamageOverlayGroups != null)
  593. {
  594. if (damageVisComp.DamageOverlayGroups.ContainsKey(damageGroup))
  595. {
  596. spriteComponent.LayerMapTryGet($"DamageOverlay{damageGroup}", out var spriteLayer);
  597. UpdateDamageLayerState(spriteComponent,
  598. spriteLayer,
  599. $"DamageOverlay_{damageGroup}",
  600. threshold);
  601. }
  602. }
  603. }
  604. /// <summary>
  605. /// Updates a layer on the sprite by what
  606. /// prefix it has (calculated by whatever
  607. /// function calls it), and what threshold
  608. /// was passed into it.
  609. /// </summary>
  610. private void UpdateDamageLayerState(SpriteComponent spriteComponent, int spriteLayer, string statePrefix, FixedPoint2 threshold)
  611. {
  612. if (threshold == 0)
  613. {
  614. spriteComponent.LayerSetVisible(spriteLayer, false);
  615. }
  616. else
  617. {
  618. if (!spriteComponent[spriteLayer].Visible)
  619. {
  620. spriteComponent.LayerSetVisible(spriteLayer, true);
  621. }
  622. spriteComponent.LayerSetState(spriteLayer, $"{statePrefix}_{threshold}");
  623. }
  624. }
  625. }