Client LuaCsForBarotrauma
BarotraumaShared/SharedSource/Items/Components/GeneticMaterial.cs
3 using Microsoft.Xna.Framework;
4 using System;
5 using System.Linq;
6 using System.Xml.Linq;
7 
9 {
11  {
12  private readonly LocalizedString materialName;
13 
14  private Character targetCharacter;
15  private AfflictionPrefab selectedEffect, selectedTaintedEffect;
16 
17  [Serialize("", IsPropertySaveable.Yes)]
18  public string Effect
19  {
20  get;
21  set;
22  }
23 
24  [Serialize("geneticmaterialdebuff", IsPropertySaveable.Yes)]
25  public Identifier TaintedEffect
26  {
27  get;
28  set;
29  }
30 
31  private bool tainted;
32  [Serialize(false, IsPropertySaveable.Yes)]
33  public bool Tainted
34  {
35  get { return tainted; }
36  private set
37  {
38  if (!value) { return; }
39  tainted = true;
40  item.AllowDeconstruct = false;
41  if (!TaintedEffect.IsEmpty)
42  {
43  selectedTaintedEffect = AfflictionPrefab.Prefabs.Where(a =>
44  a.Identifier == TaintedEffect ||
45  a.AfflictionType == TaintedEffect).GetRandomUnsynced();
46  }
47  }
48  }
49 
50  //only for saving the selected tainted effect
51  [Serialize("", IsPropertySaveable.Yes)]
52  public Identifier SelectedTaintedEffect
53  {
54  get { return selectedTaintedEffect?.Identifier ?? Identifier.Empty; }
55  private set
56  {
57  selectedTaintedEffect = !value.IsEmpty ? AfflictionPrefab.Prefabs.Find(a => a.Identifier == value) : null;
58  }
59  }
60 
62  : base(item, element)
63  {
64  string nameId = element.GetAttributeString("nameidentifier", "");
65  if (!string.IsNullOrEmpty(nameId))
66  {
67  materialName = TextManager.Get(nameId);
68  }
69  if (!string.IsNullOrEmpty(Effect))
70  {
71  selectedEffect = AfflictionPrefab.Prefabs.Where(a =>
72  a.Identifier == Effect ||
73  a.AfflictionType == Effect).GetRandomUnsynced();
74  }
75  }
76 
77  [Serialize(3.0f, IsPropertySaveable.No)]
78  public float ConditionIncreaseOnCombineMin { get; set; }
79 
80  [Serialize(8.0f, IsPropertySaveable.No)]
81  public float ConditionIncreaseOnCombineMax { get; set; }
82 
83  public bool CanBeCombinedWith(GeneticMaterial otherGeneticMaterial)
84  {
85  return !tainted && otherGeneticMaterial != null && !otherGeneticMaterial.tainted && item.AllowDeconstruct && otherGeneticMaterial.item.AllowDeconstruct;
86  }
87 
88  public override void Equip(Character character)
89  {
90  if (character == null) { return; }
91  IsActive = true;
92 
93  if (targetCharacter != null) { return; }
94 
95  if (selectedEffect != null)
96  {
97  targetCharacter = character;
98  ApplyStatusEffects(ActionType.OnWearing, 1.0f, targetCharacter);
99  float selectedEffectStrength = GetCombinedEffectStrength();
100  character.CharacterHealth.ApplyAffliction(null, selectedEffect.Instantiate(selectedEffectStrength));
101  var affliction = character.CharacterHealth.GetAllAfflictions().FirstOrDefault(a => a.Prefab == selectedEffect);
102  if (affliction != null)
103  {
104  affliction.Strength = selectedEffectStrength;
105  //force strength to the correct value to bypass any clamping e.g. AfflictionHusk might be doing
106  affliction.SetStrength(selectedEffectStrength);
107  }
108 #if SERVER
109  item.CreateServerEvent(this);
110 #endif
111  }
112  if (tainted && selectedTaintedEffect != null)
113  {
114  float selectedTaintedEffectStrength = GetCombinedTaintedEffectStrength();
115  character.CharacterHealth.ApplyAffliction(null, selectedTaintedEffect.Instantiate(selectedTaintedEffectStrength));
116  var affliction = character.CharacterHealth.GetAllAfflictions().FirstOrDefault(a => a.Prefab == selectedTaintedEffect);
117  if (affliction != null)
118  {
119  affliction.Strength = selectedTaintedEffectStrength;
120  //force strength to the correct value to bypass any clamping e.g. AfflictionHusk might be doing
121  affliction.SetStrength(selectedTaintedEffectStrength);
122  }
123  targetCharacter = character;
124 #if SERVER
125  item.CreateServerEvent(this);
126 #endif
127  }
128  foreach (Item containedItem in item.ContainedItems)
129  {
130  containedItem.GetComponent<GeneticMaterial>()?.Equip(character);
131  }
132  }
133 
134  public override void Update(float deltaTime, Camera cam)
135  {
136  base.Update(deltaTime, cam);
137  if (targetCharacter != null)
138  {
139  var rootContainer = item.RootContainer;
140  if (!targetCharacter.HasEquippedItem(item) &&
141  (rootContainer == null || !targetCharacter.HasEquippedItem(rootContainer) || !targetCharacter.Inventory.IsInLimbSlot(rootContainer, InvSlotType.HealthInterface)))
142  {
143  item.ApplyStatusEffects(ActionType.OnSevered, 1.0f, targetCharacter);
144  IsActive = false;
145 
146  var affliction = targetCharacter.CharacterHealth.GetAllAfflictions().FirstOrDefault(a => a.Prefab == selectedEffect);
147  if (affliction != null) { affliction.Strength = GetCombinedEffectStrength(); }
148  var taintedAffliction = targetCharacter.CharacterHealth.GetAllAfflictions().FirstOrDefault(a => a.Prefab == selectedTaintedEffect);
149  if (taintedAffliction != null) { taintedAffliction.Strength = GetCombinedTaintedEffectStrength(); }
150 
151  targetCharacter = null;
152  }
153  }
154  }
155 
156  public enum CombineResult
157  {
158  None,
159  Refined,
160  Combined
161  }
162 
163  public CombineResult Combine(GeneticMaterial otherGeneticMaterial, Character user)
164  {
165  if (!CanBeCombinedWith(otherGeneticMaterial)) { return CombineResult.None; }
166 
167  float conditionIncrease = Rand.Range(ConditionIncreaseOnCombineMin, ConditionIncreaseOnCombineMax);
168  conditionIncrease += user?.GetStatValue(StatTypes.GeneticMaterialRefineBonus) ?? 0.0f;
169  if (item.Prefab == otherGeneticMaterial.item.Prefab)
170  {
171  float taintedProbability = GetTaintedProbabilityOnRefine(otherGeneticMaterial, user);
172  item.Condition = Math.Max(item.Condition, otherGeneticMaterial.item.Condition) + conditionIncrease;
173  if (taintedProbability >= Rand.Range(0.0f, 1.0f))
174  {
175  MakeTainted();
176  }
177  return CombineResult.Refined;
178  }
179  else
180  {
181  item.Condition = otherGeneticMaterial.Item.Condition =
182  (item.Condition + otherGeneticMaterial.Item.Condition) / 2.0f + conditionIncrease;
183  item.OwnInventory?.TryPutItem(otherGeneticMaterial.Item, user: null);
184  item.AllowDeconstruct = false;
185  otherGeneticMaterial.Item.AllowDeconstruct = false;
186  if (GetTaintedProbabilityOnCombine(user) >= Rand.Range(0.0f, 1.0f))
187  {
188  MakeTainted();
189  }
190  return CombineResult.Combined;
191  }
192  }
193 
194  private float GetCombinedEffectStrength()
195  {
196  float effectStrength = 0.0f;
197  foreach (Item otherItem in targetCharacter.Inventory.FindAllItems(recursive: true))
198  {
199  var geneticMaterial = otherItem.GetComponent<GeneticMaterial>();
200  if (geneticMaterial == null || !geneticMaterial.IsActive) { continue; }
201  if (geneticMaterial.selectedEffect == selectedEffect)
202  {
203  effectStrength += otherItem.ConditionPercentage / 100.0f * selectedEffect.MaxStrength;
204  }
205  }
206  return effectStrength;
207  }
208 
209  private float GetCombinedTaintedEffectStrength()
210  {
211  float taintedEffectStrength = 0.0f;
212  foreach (Item otherItem in targetCharacter.Inventory.FindAllItems(recursive: true))
213  {
214  var geneticMaterial = otherItem.GetComponent<GeneticMaterial>();
215  if (geneticMaterial == null || !geneticMaterial.IsActive) { continue; }
216  if (selectedTaintedEffect != null && geneticMaterial.selectedTaintedEffect == selectedTaintedEffect)
217  {
218  taintedEffectStrength += otherItem.ConditionPercentage / 100.0f * selectedTaintedEffect.MaxStrength;
219  }
220  }
221  return taintedEffectStrength;
222  }
223 
224  private float GetTaintedProbabilityOnRefine(GeneticMaterial otherGeneticMaterial, Character user)
225  {
226  if (user == null) { return 1.0f; }
227  float probability = MathHelper.Lerp(0.0f, 0.99f, Math.Max(item.Condition, otherGeneticMaterial.Item.Condition) / 100.0f);
228  probability *= MathHelper.Lerp(1.0f, 0.25f, DegreeOfSuccess(user));
229  return MathHelper.Clamp(probability, 0.0f, 1.0f);
230  }
231 
232  private static float GetTaintedProbabilityOnCombine(Character user)
233  {
234  if (user == null) { return 1.0f; }
235  float probability = 1.0f - user.GetStatValue(StatTypes.GeneticMaterialTaintedProbabilityReductionOnCombine);
236  return MathHelper.Clamp(probability, 0.0f, 1.0f);
237  }
238 
239  private void MakeTainted()
240  {
241  if (GameMain.NetworkMember?.IsClient ?? false) { return; }
242  Tainted = true;
243 #if SERVER
244  item.CreateServerEvent(this);
245 #endif
246  }
247 
248  public static LocalizedString TryCreateName(ItemPrefab prefab, XElement element)
249  {
250  foreach (XElement subElement in element.Elements())
251  {
252  if (subElement.NameAsIdentifier() == nameof(GeneticMaterial))
253  {
254  Identifier nameId = subElement.GetAttributeIdentifier("nameidentifier", "");
255  if (!nameId.IsEmpty)
256  {
257  return prefab.Name.Replace("[type]", TextManager.Get(nameId).Fallback(nameId.Value));
258  }
259  }
260  }
261  return prefab.Name;
262  }
263  }
264 }
AfflictionPrefab is a prefab that defines a type of affliction that can be applied to a character....
Affliction Instantiate(float strength, Character source=null)
readonly float MaxStrength
The maximum strength this affliction can have.
static readonly PrefabCollection< AfflictionPrefab > Prefabs
void ApplyAffliction(Limb targetLimb, Affliction affliction, bool allowStacking=true, bool ignoreUnkillability=false)
bool HasEquippedItem(Item item, InvSlotType? slotType=null, Func< InvSlotType, bool > predicate=null)
float GetStatValue(StatTypes statType, bool includeSaved=true)
string? GetAttributeString(string key, string? def)
List< Item > FindAllItems(Func< Item, bool > predicate=null, bool recursive=false, List< Item > list=null)
void ApplyStatusEffects(ActionType type, float deltaTime, Character character=null, Limb limb=null, Entity useTarget=null, bool isNetworkEvent=false, Vector2? worldPosition=null)
Executes all StatusEffects of the specified type. Note that condition checks are ignored here: that s...
override bool TryPutItem(Item item, Character user, IEnumerable< InvSlotType > allowedSlots=null, bool createNetworkEvent=true, bool ignoreCondition=false)
If there is room, puts the item in the inventory and returns true, otherwise returns false
CombineResult Combine(GeneticMaterial otherGeneticMaterial, Character user)
static LocalizedString TryCreateName(ItemPrefab prefab, XElement element)
The base class for components holding the different functionalities of the item
void ApplyStatusEffects(ActionType type, float deltaTime, Character character=null, Limb targetLimb=null, Entity useTarget=null, Character user=null, Vector2? worldPosition=null, float afflictionMultiplier=1.0f)
float DegreeOfSuccess(Character character)
Returns 0.0f-1.0f based on how well the Character can use the itemcomponent
LocalizedString Replace(Identifier find, LocalizedString replace, StringComparison stringComparison=StringComparison.Ordinal)
readonly Identifier Identifier
Definition: Prefab.cs:34
Interface for entities that the server can send events to the clients
ActionType
ActionTypes define when a StatusEffect is executed.
Definition: Enums.cs:19
StatTypes
StatTypes are used to alter several traits of a character. They are mostly used by talents.
Definition: Enums.cs:180