3 using System.Collections.Generic;
4 using System.Globalization;
14 private const char separator =
',';
15 private const string debugIdentifier =
"_spreadsheet";
19 XDocument doc =
new XDocument();
22 doc.Add(
new XElement(
"Content"));
25 XElement root = doc.Root!;
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)),
36 itemElement.Add(ParseRecipe(prefab));
37 itemElement.Add(ParseDecon(prefab));
38 itemElement.Add(ParseMedical(prefab));
39 itemElement.Add(ParseWeapon(prefab));
41 root.Add(itemElement);
44 System.Xml.XmlWriterSettings settings =
new System.Xml.XmlWriterSettings
47 NewLineOnAttributes =
false
54 private static XElement ParseRecipe(
ItemPrefab prefab)
58 List<ItemPrefab> ingredients = recipe?.
RequiredItems.SelectMany(ri => ri.ItemPrefabs).Distinct().ToList() ??
new List<ItemPrefab>();
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)))
71 private static XElement ParseDecon(ItemPrefab prefab)
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)))
81 private static XElement ParseMedical(ItemPrefab prefab)
83 ContentXElement? itemMeleeWeapon = prefab.ConfigElement.GetChildElement(nameof(
MeleeWeapon));
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)
90 List<StatusEffect> statusEffects =
new List<StatusEffect>();
91 foreach (var subElement
in itemMeleeWeapon.Elements())
93 string name = subElement.Name.ToString();
94 if (name.Equals(nameof(StatusEffect), StringComparison.OrdinalIgnoreCase))
96 StatusEffect statusEffect = StatusEffect.Load(subElement, debugIdentifier);
97 if (statusEffect ==
null || !statusEffect.HasTag(Tags.MedicalItem)) {
continue; }
98 statusEffects.Add(statusEffect);
100 else if (IsRequiredSkill(subElement, out Skill? skill) && skill !=
null)
102 medicalRequiredSkill = (int) skill.Level;
106 List<StatusEffect> successEffects = statusEffects.Where(se => se.type ==
ActionType.OnSuccess).ToList();
107 List<StatusEffect> failureEffects = statusEffects.Where(se => se.type ==
ActionType.OnFailure).ToList();
109 foreach (StatusEffect statusEffect
in successEffects)
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)));
116 foreach (StatusEffect statusEffect
in failureEffects)
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)));
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))))
135 private static XElement ParseWeapon(ItemPrefab prefab)
139 float? structDamage =
null;
140 int skillRequirement = 0;
143 List<(LocalizedString Name,
float Amount)> damages =
new List<(LocalizedString Name,
float Amount)>();
146 foreach (var icElement
in prefab.ConfigElement.Elements())
148 string icName = icElement.Name.ToString();
149 if (!validNames.Any(name => icName.Equals(name, StringComparison.OrdinalIgnoreCase))) {
continue; }
151 foreach (var icChildElement
in icElement.Elements())
153 string name = icChildElement.Name.ToString();
154 if (IsRequiredSkill(icChildElement, out Skill? skill) && skill !=
null)
156 skillRequirement = (int) skill.Level;
158 else if (name.Equals(nameof(Attack), StringComparison.OrdinalIgnoreCase))
160 ParseAttack(
new Attack(icChildElement, debugIdentifier));
162 else if (name.Equals(nameof(Explosion), StringComparison.OrdinalIgnoreCase))
164 ParseExplosion(
new[] {
new Explosion(icChildElement, debugIdentifier) });
166 else if (name.Equals(nameof(StatusEffect), StringComparison.OrdinalIgnoreCase))
168 ParseStatusEffect(
new[] { StatusEffect.Load(icChildElement, debugIdentifier) });
171 void ParseStatusEffect(IEnumerable<StatusEffect> statusEffects)
173 foreach (StatusEffect effect
in statusEffects)
175 if (effect.HasTargetType(StatusEffect.TargetType.Character)) {
continue; }
177 ParseAfflictions(effect.Afflictions);
178 ParseExplosion(effect.Explosions);
182 void ParseExplosion(IEnumerable<Explosion> explosions)
184 foreach (Explosion explosion
in explosions)
187 ParseAttack(explosion.Attack);
188 ParseStatusEffect(explosion.Attack.StatusEffects);
192 void ParseAttack(Attack attack)
194 structDamage ??= attack.StructureDamage;
195 ParseAfflictions(attack.Afflictions.Keys);
196 ParseStatusEffect(attack.StatusEffects);
199 void ParseAfflictions(IEnumerable<Affliction> afflictions)
201 foreach (Affliction affliction
in afflictions)
204 if (affliction.Prefab == AfflictionPrefab.Stun)
206 stun += affliction.NonClampedStrength;
210 damages.Add((affliction.Prefab.Name, affliction.NonClampedStrength));
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)
226 private static LocalizedString GetAfflictionName(Identifier identifier)
228 return AfflictionPrefab.Prefabs.Find(prefab => prefab.Identifier == identifier)?.Name ?? CultureInfo.CurrentCulture.TextInfo.ToTitleCase(identifier.Value!.ToLower());
231 private static string FormatFloat(
float value)
233 return value.ToString(
"0.00", CultureInfo.InvariantCulture);
236 private static string FormatArray<T>(IEnumerable<T> array)
238 return string.Join(separator, array);
241 private static bool IsRequiredSkill(ContentXElement element, out Skill? skill)
243 string name = element.Name.ToString();
244 bool isSkill = name.Equals(
"RequiredSkill", StringComparison.OrdinalIgnoreCase) ||
245 name.Equals(
"RequiredSkills", StringComparison.OrdinalIgnoreCase);
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);
readonly ImmutableArray< RequiredItem > RequiredItems
readonly ImmutableArray< Skill > RequiredSkills
static XmlWriter Create(string path, System.Xml.XmlWriterSettings settings)
static readonly PrefabCollection< ItemPrefab > Prefabs
ImmutableDictionary< uint, FabricationRecipe > FabricationRecipes
override LocalizedString Name
override ImmutableHashSet< Identifier > Tags
The base class for components holding the different functionalities of the item
readonly Identifier Identifier
ActionType
ActionTypes define when a StatusEffect is executed.