Client LuaCsForBarotrauma
SpreadsheetExport.cs
1 #nullable enable
2 using System;
3 using System.Collections.Generic;
4 using System.Globalization;
5 using System.Linq;
6 using System.Xml.Linq;
7 using Barotrauma.IO;
9 
10 namespace Barotrauma
11 {
12  public class SpreadsheetExport
13  {
14  private const char separator = ',';
15  private const string debugIdentifier = "_spreadsheet";
16 
17  public static void Export()
18  {
19  XDocument doc = new XDocument();
20  if (doc.Root == null)
21  {
22  doc.Add(new XElement("Content"));
23  }
24 
25  XElement root = doc.Root!;
26 
27  foreach (ItemPrefab prefab in ItemPrefab.Prefabs)
28  {
29  XElement itemElement = new XElement("Item",
30  new XAttribute("identifier", prefab.Identifier),
31  new XAttribute("name", prefab.Name),
32  new XAttribute("tags", FormatArray(prefab.Tags)),
33  new XAttribute("value", prefab.DefaultPrice?.Price ?? 0)
34  );
35 
36  itemElement.Add(ParseRecipe(prefab));
37  itemElement.Add(ParseDecon(prefab));
38  itemElement.Add(ParseMedical(prefab));
39  itemElement.Add(ParseWeapon(prefab));
40 
41  root.Add(itemElement);
42  }
43 
44  System.Xml.XmlWriterSettings settings = new System.Xml.XmlWriterSettings
45  {
46  Indent = false,
47  NewLineOnAttributes = false
48  };
49 
50  using XmlWriter? writer = XmlWriter.Create("spreadsheetdata.xml", settings);
51  doc.SaveSafe(writer);
52  }
53 
54  private static XElement ParseRecipe(ItemPrefab prefab)
55  {
56  FabricationRecipe? recipe = prefab.FabricationRecipes.Values.FirstOrDefault();
57 
58  List<ItemPrefab> ingredients = recipe?.RequiredItems.SelectMany(ri => ri.ItemPrefabs).Distinct().ToList() ?? new List<ItemPrefab>();
59  Skill? skill = recipe?.RequiredSkills.FirstOrDefault();
60 
61  return new XElement("Recipe",
62  new XAttribute("amount", recipe?.Amount ?? 0),
63  new XAttribute("time", recipe?.RequiredTime ?? 0),
64  new XAttribute("skillname", skill?.Identifier.Value ?? ""),
65  new XAttribute("skillamount", (int?) skill?.Level ?? 0),
66  new XAttribute("ingredients", FormatArray(ingredients.Select(ip => ip.Name))),
67  new XAttribute("values", FormatArray(ingredients.Select(ip => ip.DefaultPrice?.Price ?? 0)))
68  );
69  }
70 
71  private static XElement ParseDecon(ItemPrefab prefab)
72  {
73  List<ItemPrefab> deconOutput = prefab.DeconstructItems.Select(item => ItemPrefab.Find(null, item.ItemIdentifier)).Where(outputPrefab => outputPrefab != null).ToList();
74  return new XElement("Deconstruct",
75  new XAttribute("time", prefab.DeconstructTime),
76  new XAttribute("outputs", FormatArray(deconOutput.Select(ip => ip.Name))),
77  new XAttribute("values", FormatArray(deconOutput.Select(ip => ip.DefaultPrice?.Price ?? 0)))
78  );
79  }
80 
81  private static XElement ParseMedical(ItemPrefab prefab)
82  {
83  ContentXElement? itemMeleeWeapon = prefab.ConfigElement.GetChildElement(nameof(MeleeWeapon));
84  // affliction, amount, duration
85  List<(LocalizedString Name, float Amount, float Duration)> onSuccessAfflictions = new List<(LocalizedString Name, float Amount, float Duration)>();
86  List<(LocalizedString Name, float Amount, float Duration)> onFailureAfflictions = new List<(LocalizedString Name, float Amount, float Duration)>();
87  int medicalRequiredSkill = 0;
88  if (itemMeleeWeapon != null)
89  {
90  List<StatusEffect> statusEffects = new List<StatusEffect>();
91  foreach (var subElement in itemMeleeWeapon.Elements())
92  {
93  string name = subElement.Name.ToString();
94  if (name.Equals(nameof(StatusEffect), StringComparison.OrdinalIgnoreCase))
95  {
96  StatusEffect statusEffect = StatusEffect.Load(subElement, debugIdentifier);
97  if (statusEffect == null || !statusEffect.HasTag(Tags.MedicalItem)) { continue; }
98  statusEffects.Add(statusEffect);
99  }
100  else if (IsRequiredSkill(subElement, out Skill? skill) && skill != null)
101  {
102  medicalRequiredSkill = (int) skill.Level;
103  }
104  }
105 
106  List<StatusEffect> successEffects = statusEffects.Where(se => se.type == ActionType.OnSuccess).ToList();
107  List<StatusEffect> failureEffects = statusEffects.Where(se => se.type == ActionType.OnFailure).ToList();
108 
109  foreach (StatusEffect statusEffect in successEffects)
110  {
111  float duration = statusEffect.Duration;
112  onSuccessAfflictions.AddRange(statusEffect.ReduceAffliction.Select(ra => (GetAfflictionName(ra.AfflictionIdentifier), -ra.ReduceAmount, duration)));
113  onSuccessAfflictions.AddRange(statusEffect.Afflictions.Select(affliction => (affliction.Prefab.Name, affliction.NonClampedStrength, duration)));
114  }
115 
116  foreach (StatusEffect statusEffect in failureEffects)
117  {
118  float duration = statusEffect.Duration;
119  onFailureAfflictions.AddRange(statusEffect.ReduceAffliction.Select(ra => (GetAfflictionName(ra.AfflictionIdentifier), -ra.ReduceAmount, duration)));
120  onFailureAfflictions.AddRange(statusEffect.Afflictions.Select(affliction => (affliction.Prefab.Name, affliction.NonClampedStrength, duration)));
121  }
122  }
123 
124  return new XElement("Medical",
125  new XAttribute("skillamount", medicalRequiredSkill),
126  new XAttribute("successafflictions", FormatArray(onSuccessAfflictions.Select(tpl => tpl.Item1))),
127  new XAttribute("successamounts", FormatArray(onSuccessAfflictions.Select(tpl => FormatFloat(tpl.Item2)))),
128  new XAttribute("successdurations", FormatArray(onSuccessAfflictions.Select(tpl => FormatFloat(tpl.Item3)))),
129  new XAttribute("failureafflictions", FormatArray(onFailureAfflictions.Select(tpl => tpl.Item1))),
130  new XAttribute("failureamounts", FormatArray(onFailureAfflictions.Select(tpl => FormatFloat(tpl.Item2)))),
131  new XAttribute("failuredurations", FormatArray(onFailureAfflictions.Select(tpl => FormatFloat(tpl.Item3))))
132  );
133  }
134 
135  private static XElement ParseWeapon(ItemPrefab prefab)
136  {
137  float stun = 0;
138  bool isAoE = false;
139  float? structDamage = null;
140  int skillRequirement = 0;
141 
142  // affliction, amount
143  List<(LocalizedString Name, float Amount)> damages = new List<(LocalizedString Name, float Amount)>();
144 
145  string[] validNames = { nameof(Projectile), nameof(MeleeWeapon), nameof(RepairTool), nameof(ItemComponent), nameof(RangedWeapon) };
146  foreach (var icElement in prefab.ConfigElement.Elements())
147  {
148  string icName = icElement.Name.ToString();
149  if (!validNames.Any(name => icName.Equals(name, StringComparison.OrdinalIgnoreCase))) { continue; }
150 
151  foreach (var icChildElement in icElement.Elements())
152  {
153  string name = icChildElement.Name.ToString();
154  if (IsRequiredSkill(icChildElement, out Skill? skill) && skill != null)
155  {
156  skillRequirement = (int) skill.Level;
157  }
158  else if (name.Equals(nameof(Attack), StringComparison.OrdinalIgnoreCase))
159  {
160  ParseAttack(new Attack(icChildElement, debugIdentifier));
161  }
162  else if (name.Equals(nameof(Explosion), StringComparison.OrdinalIgnoreCase))
163  {
164  ParseExplosion(new[] { new Explosion(icChildElement, debugIdentifier) });
165  }
166  else if (name.Equals(nameof(StatusEffect), StringComparison.OrdinalIgnoreCase))
167  {
168  ParseStatusEffect(new[] { StatusEffect.Load(icChildElement, debugIdentifier) });
169  }
170 
171  void ParseStatusEffect(IEnumerable<StatusEffect> statusEffects)
172  {
173  foreach (StatusEffect effect in statusEffects)
174  {
175  if (effect.HasTargetType(StatusEffect.TargetType.Character)) { continue; }
176 
177  ParseAfflictions(effect.Afflictions);
178  ParseExplosion(effect.Explosions);
179  }
180  }
181 
182  void ParseExplosion(IEnumerable<Explosion> explosions)
183  {
184  foreach (Explosion explosion in explosions)
185  {
186  isAoE = true;
187  ParseAttack(explosion.Attack);
188  ParseStatusEffect(explosion.Attack.StatusEffects);
189  }
190  }
191 
192  void ParseAttack(Attack attack)
193  {
194  structDamage ??= attack.StructureDamage;
195  ParseAfflictions(attack.Afflictions.Keys);
196  ParseStatusEffect(attack.StatusEffects);
197  }
198 
199  void ParseAfflictions(IEnumerable<Affliction> afflictions)
200  {
201  foreach (Affliction affliction in afflictions)
202  {
203  // Exclude stuns
204  if (affliction.Prefab == AfflictionPrefab.Stun)
205  {
206  stun += affliction.NonClampedStrength;
207  continue;
208  }
209 
210  damages.Add((affliction.Prefab.Name, affliction.NonClampedStrength));
211  }
212  }
213  }
214  }
215 
216  return new XElement("Weapon",
217  new XAttribute("damagenames", FormatArray(damages.Select(tpl => tpl.Item1))),
218  new XAttribute("damageamounts", FormatArray(damages.Select(tpl => FormatFloat(tpl.Item2)))),
219  new XAttribute("isaoe", isAoE),
220  new XAttribute("structuredamage", structDamage ?? 0),
221  new XAttribute("stun", FormatFloat(stun)),
222  new XAttribute("skillrequirement", skillRequirement)
223  );
224  }
225 
226  private static LocalizedString GetAfflictionName(Identifier identifier)
227  {
228  return AfflictionPrefab.Prefabs.Find(prefab => prefab.Identifier == identifier)?.Name ?? CultureInfo.CurrentCulture.TextInfo.ToTitleCase(identifier.Value!.ToLower());
229  }
230 
231  private static string FormatFloat(float value)
232  {
233  return value.ToString("0.00", CultureInfo.InvariantCulture);
234  }
235 
236  private static string FormatArray<T>(IEnumerable<T> array)
237  {
238  return string.Join(separator, array);
239  }
240 
241  private static bool IsRequiredSkill(ContentXElement element, out Skill? skill)
242  {
243  string name = element.Name.ToString();
244  bool isSkill = name.Equals("RequiredSkill", StringComparison.OrdinalIgnoreCase) ||
245  name.Equals("RequiredSkills", StringComparison.OrdinalIgnoreCase);
246 
247  if (isSkill)
248  {
249  Identifier identifier = element.GetAttributeIdentifier(nameof(Skill.Identifier), Identifier.Empty);
250  float level = element.GetAttributeFloat(nameof(Skill.Level).ToLowerInvariant(), 0f);
251  skill = new Skill(identifier, level);
252  }
253  else
254  {
255  skill = null;
256  }
257 
258  return isSkill;
259  }
260  }
261 }
readonly ImmutableArray< RequiredItem > RequiredItems
static XmlWriter Create(string path, System.Xml.XmlWriterSettings settings)
Definition: SafeIO.cs:163
static readonly PrefabCollection< ItemPrefab > Prefabs
ImmutableDictionary< uint, FabricationRecipe > FabricationRecipes
override ImmutableHashSet< Identifier > Tags
The base class for components holding the different functionalities of the item
readonly Identifier Identifier
Definition: Prefab.cs:34
ActionType
ActionTypes define when a StatusEffect is executed.
Definition: Enums.cs:19