Client LuaCsForBarotrauma
IImplementsVariants.cs
1 #nullable enable
2 
3 using System;
4 using System.Collections.Generic;
5 using System.Globalization;
6 using System.Linq;
7 using System.Xml.Linq;
8 
9 namespace Barotrauma
10 {
11  public interface IImplementsVariants<T> where T : Prefab
12  {
13  public Identifier VariantOf { get; }
14 
15  public T? ParentPrefab { get; set; }
16 
17  public void InheritFrom(T parent);
18  }
19 
20  public static class VariantExtensions
21  {
22  public static ContentXElement CreateVariantXML(this ContentXElement variantElement, ContentXElement baseElement)
23  {
24  #warning TODO: fix %ModDir% instances in the base element such that they become %ModDir:BaseMod% if necessary
25  return variantElement.Element.CreateVariantXML(baseElement.Element).FromPackage(variantElement.ContentPackage);
26  }
27 
28  public delegate void VariantXMLChecker(XElement originalElement, XElement? variantElement, XElement result);
29 
30  public static XElement CreateVariantXML(this XElement variantElement, XElement baseElement, VariantXMLChecker? checker = null)
31  {
32  XElement newElement = new XElement(variantElement.Name);
33  newElement.Add(baseElement.Attributes());
34  newElement.Add(baseElement.Elements());
35 
36  ReplaceElement(newElement, variantElement);
37 
38  void ReplaceElement(XElement element, XElement replacement)
39  {
40  XElement originalElement = new XElement(element);
41 
42  List<XElement> newElementsFromBase = new List<XElement>(element.Elements());
43  List<XElement> elementsToRemove = new List<XElement>();
44  foreach (XAttribute attribute in replacement.Attributes())
45  {
46  ReplaceAttribute(element, attribute);
47  }
48  foreach (XElement replacementSubElement in replacement.Elements())
49  {
50  int index = replacement.Elements().ToList().FindAll(e => e.Name.ToString().Equals(replacementSubElement.Name.ToString(), StringComparison.OrdinalIgnoreCase)).IndexOf(replacementSubElement);
51  System.Diagnostics.Debug.Assert(index > -1);
52 
53  int i = 0;
54  bool matchingElementFound = false;
55  foreach (var subElement in element.Elements())
56  {
57  if (replacementSubElement.Name.ToString().Equals("clear", StringComparison.OrdinalIgnoreCase))
58  {
59  matchingElementFound = true;
60  newElementsFromBase.Clear();
61  elementsToRemove.AddRange(element.Elements());
62  break;
63  }
64  if (!subElement.Name.ToString().Equals(replacementSubElement.Name.ToString(), StringComparison.OrdinalIgnoreCase)) { continue; }
65  if (i == index)
66  {
67  if (!replacementSubElement.HasAttributes && !replacementSubElement.HasElements)
68  {
69  //if the replacement is empty (no attributes or child elements)
70  //remove the element from the variant
71  elementsToRemove.Add(subElement);
72  }
73  else
74  {
75  ReplaceElement(subElement, replacementSubElement);
76  }
77  matchingElementFound = true;
78  newElementsFromBase.Remove(subElement);
79  break;
80  }
81  i++;
82  }
83  if (!matchingElementFound)
84  {
85  element.Add(replacementSubElement);
86  }
87  }
88  elementsToRemove.ForEach(e => e.Remove());
89  checker?.Invoke(originalElement, replacement, element);
90  foreach (XElement newElement in newElementsFromBase)
91  {
92  checker?.Invoke(newElement, null, newElement);
93  }
94  }
95 
96  void ReplaceAttribute(XElement element, XAttribute newAttribute)
97  {
98  XAttribute? existingAttribute = element.Attributes().FirstOrDefault(a => a.Name.ToString().Equals(newAttribute.Name.ToString(), StringComparison.OrdinalIgnoreCase));
99  if (existingAttribute == null)
100  {
101  element.Add(newAttribute);
102  return;
103  }
104  float.TryParse(existingAttribute.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out float value);
105  if (newAttribute.Value.StartsWith('*'))
106  {
107  string multiplierStr = newAttribute.Value.Substring(1, newAttribute.Value.Length - 1);
108  float.TryParse(multiplierStr, NumberStyles.Any, CultureInfo.InvariantCulture, out float multiplier);
109  if (multiplierStr.Contains('.') || existingAttribute.Value.Contains('.'))
110  {
111  existingAttribute.Value = (value * multiplier).ToString("G", CultureInfo.InvariantCulture);
112  }
113  else
114  {
115  existingAttribute.Value = ((int)(value * multiplier)).ToString();
116  }
117  }
118  else if (newAttribute.Value.StartsWith('+'))
119  {
120  string additionStr = newAttribute.Value.Substring(1, newAttribute.Value.Length - 1);
121  float.TryParse(additionStr, NumberStyles.Any, CultureInfo.InvariantCulture, out float addition);
122  if (additionStr.Contains('.') || existingAttribute.Value.Contains('.'))
123  {
124  existingAttribute.Value = (value + addition).ToString("G", CultureInfo.InvariantCulture);
125  }
126  else
127  {
128  existingAttribute.Value = ((int)(value + addition)).ToString();
129  }
130  }
131  else
132  {
133  existingAttribute.Value = newAttribute.Value;
134  }
135  }
136 
137  return newElement;
138  }
139 
140  }
141 }
ContentPackage? ContentPackage
readonly XElement Element