HealthChange.cs 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. using Content.Shared.Damage;
  2. using Content.Shared.Damage.Prototypes;
  3. using Content.Shared.EntityEffects;
  4. using Content.Shared.FixedPoint;
  5. using Content.Shared.Localizations;
  6. using JetBrains.Annotations;
  7. using Robust.Shared.Prototypes;
  8. using System.Linq;
  9. using System.Text.Json.Serialization;
  10. namespace Content.Server.EntityEffects.Effects
  11. {
  12. /// <summary>
  13. /// Default metabolism used for medicine reagents.
  14. /// </summary>
  15. [UsedImplicitly]
  16. public sealed partial class HealthChange : EntityEffect
  17. {
  18. /// <summary>
  19. /// Damage to apply every cycle. Damage Ignores resistances.
  20. /// </summary>
  21. [DataField(required: true)]
  22. [JsonPropertyName("damage")]
  23. public DamageSpecifier Damage = default!;
  24. /// <summary>
  25. /// Should this effect scale the damage by the amount of chemical in the solution?
  26. /// Useful for touch reactions, like styptic powder or acid.
  27. /// Only usable if the EntityEffectBaseArgs is an EntityEffectReagentArgs.
  28. /// </summary>
  29. [DataField]
  30. [JsonPropertyName("scaleByQuantity")]
  31. public bool ScaleByQuantity;
  32. [DataField]
  33. [JsonPropertyName("ignoreResistances")]
  34. public bool IgnoreResistances = true;
  35. protected override string ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
  36. {
  37. var damages = new List<string>();
  38. var heals = false;
  39. var deals = false;
  40. var damageSpec = new DamageSpecifier(Damage);
  41. var universalReagentDamageModifier = entSys.GetEntitySystem<DamageableSystem>().UniversalReagentDamageModifier;
  42. var universalReagentHealModifier = entSys.GetEntitySystem<DamageableSystem>().UniversalReagentHealModifier;
  43. if (universalReagentDamageModifier != 1 || universalReagentHealModifier != 1)
  44. {
  45. foreach (var (type, val) in damageSpec.DamageDict)
  46. {
  47. if (val < 0f)
  48. {
  49. damageSpec.DamageDict[type] = val * universalReagentHealModifier;
  50. }
  51. if (val > 0f)
  52. {
  53. damageSpec.DamageDict[type] = val * universalReagentDamageModifier;
  54. }
  55. }
  56. }
  57. damageSpec = entSys.GetEntitySystem<DamageableSystem>().ApplyUniversalAllModifiers(damageSpec);
  58. foreach (var group in prototype.EnumeratePrototypes<DamageGroupPrototype>())
  59. {
  60. if (!damageSpec.TryGetDamageInGroup(group, out var amount))
  61. continue;
  62. var relevantTypes = damageSpec.DamageDict
  63. .Where(x => x.Value != FixedPoint2.Zero && group.DamageTypes.Contains(x.Key)).ToList();
  64. if (relevantTypes.Count != group.DamageTypes.Count)
  65. continue;
  66. var sum = FixedPoint2.Zero;
  67. foreach (var type in group.DamageTypes)
  68. {
  69. sum += damageSpec.DamageDict.GetValueOrDefault(type);
  70. }
  71. // if the total sum of all the types equal the damage amount,
  72. // assume that they're evenly distributed.
  73. if (sum != amount)
  74. continue;
  75. var sign = FixedPoint2.Sign(amount);
  76. if (sign < 0)
  77. heals = true;
  78. if (sign > 0)
  79. deals = true;
  80. damages.Add(
  81. Loc.GetString("health-change-display",
  82. ("kind", group.LocalizedName),
  83. ("amount", MathF.Abs(amount.Float())),
  84. ("deltasign", sign)
  85. ));
  86. foreach (var type in group.DamageTypes)
  87. {
  88. damageSpec.DamageDict.Remove(type);
  89. }
  90. }
  91. foreach (var (kind, amount) in damageSpec.DamageDict)
  92. {
  93. var sign = FixedPoint2.Sign(amount);
  94. if (sign < 0)
  95. heals = true;
  96. if (sign > 0)
  97. deals = true;
  98. damages.Add(
  99. Loc.GetString("health-change-display",
  100. ("kind", prototype.Index<DamageTypePrototype>(kind).LocalizedName),
  101. ("amount", MathF.Abs(amount.Float())),
  102. ("deltasign", sign)
  103. ));
  104. }
  105. var healsordeals = heals ? (deals ? "both" : "heals") : (deals ? "deals" : "none");
  106. return Loc.GetString("reagent-effect-guidebook-health-change",
  107. ("chance", Probability),
  108. ("changes", ContentLocalizationManager.FormatList(damages)),
  109. ("healsordeals", healsordeals));
  110. }
  111. public override void Effect(EntityEffectBaseArgs args)
  112. {
  113. var scale = FixedPoint2.New(1);
  114. var damageSpec = new DamageSpecifier(Damage);
  115. if (args is EntityEffectReagentArgs reagentArgs)
  116. {
  117. scale = ScaleByQuantity ? reagentArgs.Quantity * reagentArgs.Scale : reagentArgs.Scale;
  118. }
  119. var universalReagentDamageModifier = args.EntityManager.System<DamageableSystem>().UniversalReagentDamageModifier;
  120. var universalReagentHealModifier = args.EntityManager.System<DamageableSystem>().UniversalReagentHealModifier;
  121. if (universalReagentDamageModifier != 1 || universalReagentHealModifier != 1)
  122. {
  123. foreach (var (type, val) in damageSpec.DamageDict)
  124. {
  125. if (val < 0f)
  126. {
  127. damageSpec.DamageDict[type] = val * universalReagentHealModifier;
  128. }
  129. if (val > 0f)
  130. {
  131. damageSpec.DamageDict[type] = val * universalReagentDamageModifier;
  132. }
  133. }
  134. }
  135. args.EntityManager.System<DamageableSystem>()
  136. .TryChangeDamage(
  137. args.TargetEntity,
  138. damageSpec * scale,
  139. IgnoreResistances,
  140. interruptsDoAfters: false);
  141. }
  142. }
  143. }