4 using System.Collections.Generic;
10 internal readonly record
struct TalentStatIdentifier(ItemTalentStats Stat, Identifier TalentIdentifier, Option<UInt32> UniqueCharacterId, bool Save) : INetSerializableStruct
15 public static TalentStatIdentifier CreateStackable(ItemTalentStats stat, Identifier talentIdentifier, UInt32 characterId)
16 =>
new(stat, talentIdentifier, Option<UInt32>.Some(characterId), Save:
false);
22 public static TalentStatIdentifier CreateUnstackable(ItemTalentStats stat, Identifier talentIdentifier,
bool Save)
23 =>
new(stat, talentIdentifier, Option.None, Save);
25 public XElement Serialize()
26 =>
new XElement(
"Stat",
27 new XAttribute(
"type", Stat),
28 new XAttribute(
"talent", TalentIdentifier));
30 public static Option<TalentStatIdentifier> TryLoadFromXML(XElement element)
32 var stat = element.GetAttributeEnum(
"type", ItemTalentStats.None);
33 var talentIdentifier = element.GetAttributeIdentifier(
"talent", Identifier.Empty);
35 if (stat == ItemTalentStats.None || talentIdentifier == Identifier.Empty)
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);
43 return Option.Some(CreateUnstackable(stat, talentIdentifier,
true));
47 internal sealed
class ItemStatManager
49 private readonly Dictionary<TalentStatIdentifier, float> talentStats =
new();
50 private readonly
Item item;
52 public ItemStatManager(Item item) => this.item = item;
54 public void ApplyStat(ItemTalentStats stat,
bool stackable,
bool save,
float value, CharacterTalent talent)
56 if (talent.Character?.ID is not { } characterId ||
57 talent.Prefab?.Identifier is not { } talentIdentifier) {
return; }
59 var identifier = stackable
60 ? TalentStatIdentifier.CreateStackable(stat, talentIdentifier, characterId)
61 : TalentStatIdentifier.CreateUnstackable(stat, talentIdentifier, save);
65 if (talentStats.TryGetValue(identifier, out
float existingValue))
68 if (existingValue > value) {
return; }
72 talentStats[identifier] = value;
75 if (GameMain.NetworkMember is { IsServer: true } server)
77 server.CreateEntityEvent(item,
new Item.SetItemStatEventData(talentStats));
82 public void Save(XElement parent)
84 var element =
new XElement(
"itemstats");
86 foreach (var (key, value) in talentStats)
88 if (!key.Save) {
continue; }
90 var statElement = key.Serialize();
91 statElement.Add(
new XAttribute(
"value", value));
93 element.Add(statElement);
99 public void Load(XElement element)
101 foreach (XElement statElement
in element.Elements())
103 if (!TalentStatIdentifier.TryLoadFromXML(statElement).TryUnwrap(out var identifier)) {
continue; }
105 var value = statElement.GetAttributeFloat(
"value", 0f);
107 ApplyStatDirect(identifier, value);
114 public void ApplyStatDirect(TalentStatIdentifier identifier,
float value)
115 => talentStats[identifier] = value;
120 public float GetAdjustedValueMultiplicative(ItemTalentStats stat,
float originalValue)
122 float total = originalValue;
124 foreach (var (key, value) in talentStats)
126 if (key.Stat != stat) {
continue; }
136 public float GetAdjustedValueAdditive(ItemTalentStats stat,
float originalValue)
138 float total = originalValue;
140 foreach (var (key, value) in talentStats)
142 if (key.Stat != stat) {
continue; }