Client LuaCsForBarotrauma
ItemStatManager.cs
1 #nullable enable
2 
3 using System;
4 using System.Collections.Generic;
5 using System.Xml.Linq;
6 
7 namespace Barotrauma
8 {
9  [NetworkSerialize]
10  internal readonly record struct TalentStatIdentifier(ItemTalentStats Stat, Identifier TalentIdentifier, Option<UInt32> UniqueCharacterId, bool Save) : INetSerializableStruct
11  {
15  public static TalentStatIdentifier CreateStackable(ItemTalentStats stat, Identifier talentIdentifier, UInt32 characterId)
16  => new(stat, talentIdentifier, Option<UInt32>.Some(characterId), Save: false);
17 
22  public static TalentStatIdentifier CreateUnstackable(ItemTalentStats stat, Identifier talentIdentifier, bool Save)
23  => new(stat, talentIdentifier, Option.None, Save);
24 
25  public XElement Serialize()
26  => new XElement("Stat",
27  new XAttribute("type", Stat),
28  new XAttribute("talent", TalentIdentifier));
29 
30  public static Option<TalentStatIdentifier> TryLoadFromXML(XElement element)
31  {
32  var stat = element.GetAttributeEnum("type", ItemTalentStats.None);
33  var talentIdentifier = element.GetAttributeIdentifier("talent", Identifier.Empty);
34 
35  if (stat == ItemTalentStats.None || talentIdentifier == Identifier.Empty)
36  {
37  var error = $"Failed to load talent stat identifier from XML {element}";
38  DebugConsole.ThrowError(error);
39  GameAnalyticsManager.AddErrorEventOnce("ItemStatManager.TryLoadFromXML:Invalid", GameAnalyticsManager.ErrorSeverity.Error, error);
40  return Option.None;
41  }
42 
43  return Option.Some(CreateUnstackable(stat, talentIdentifier, true));
44  }
45  }
46 
47  internal sealed class ItemStatManager
48  {
49  private readonly Dictionary<TalentStatIdentifier, float> talentStats = new();
50  private readonly Item item;
51 
52  public ItemStatManager(Item item) => this.item = item;
53 
54  public void ApplyStat(ItemTalentStats stat, bool stackable, bool save, float value, CharacterTalent talent)
55  {
56  if (talent.Character?.ID is not { } characterId ||
57  talent.Prefab?.Identifier is not { } talentIdentifier) { return; }
58 
59  var identifier = stackable
60  ? TalentStatIdentifier.CreateStackable(stat, talentIdentifier, characterId)
61  : TalentStatIdentifier.CreateUnstackable(stat, talentIdentifier, save);
62 
63  if (!stackable)
64  {
65  if (talentStats.TryGetValue(identifier, out float existingValue))
66  {
67  // Always use the highest value for non-stackable stats
68  if (existingValue > value) { return; }
69  }
70  }
71 
72  talentStats[identifier] = value;
73 
74 #if SERVER
75  if (GameMain.NetworkMember is { IsServer: true } server)
76  {
77  server.CreateEntityEvent(item, new Item.SetItemStatEventData(talentStats));
78  }
79 #endif
80  }
81 
82  public void Save(XElement parent)
83  {
84  var element = new XElement("itemstats");
85 
86  foreach (var (key, value) in talentStats)
87  {
88  if (!key.Save) { continue; }
89 
90  var statElement = key.Serialize();
91  statElement.Add(new XAttribute("value", value));
92 
93  element.Add(statElement);
94  }
95 
96  parent.Add(element);
97  }
98 
99  public void Load(XElement element)
100  {
101  foreach (XElement statElement in element.Elements())
102  {
103  if (!TalentStatIdentifier.TryLoadFromXML(statElement).TryUnwrap(out var identifier)) { continue; }
104 
105  var value = statElement.GetAttributeFloat("value", 0f);
106 
107  ApplyStatDirect(identifier, value);
108  }
109  }
110 
114  public void ApplyStatDirect(TalentStatIdentifier identifier, float value)
115  => talentStats[identifier] = value;
116 
120  public float GetAdjustedValueMultiplicative(ItemTalentStats stat, float originalValue)
121  {
122  float total = originalValue;
123 
124  foreach (var (key, value) in talentStats)
125  {
126  if (key.Stat != stat) { continue; }
127  total *= value;
128  }
129 
130  return total;
131  }
132 
136  public float GetAdjustedValueAdditive(ItemTalentStats stat, float originalValue)
137  {
138  float total = originalValue;
139 
140  foreach (var (key, value) in talentStats)
141  {
142  if (key.Stat != stat) { continue; }
143  total += value;
144  }
145 
146  return total;
147  }
148  }
149 }