Unstable 0.17.0.0
This commit is contained in:
@@ -2,14 +2,14 @@
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
interface ISerializableEntity
|
||||
public interface ISerializableEntity
|
||||
{
|
||||
string Name
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
Dictionary<string, SerializableProperty> SerializableProperties
|
||||
Dictionary<Identifier, SerializableProperty> SerializableProperties
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ using Barotrauma.Items.Components;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
@@ -10,6 +11,8 @@ using System.Reflection;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Networking;
|
||||
|
||||
//TODO: come back to this later, clever use of reflection would make this nicer >:)
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
@@ -77,7 +80,8 @@ namespace Barotrauma
|
||||
//I would love to see a better way to do this
|
||||
AllowLinkingWifiToChat,
|
||||
IsSwappableItem,
|
||||
AllowRotating
|
||||
AllowRotating,
|
||||
Attachable
|
||||
}
|
||||
|
||||
public bool IsEditable(ISerializableEntity entity)
|
||||
@@ -94,18 +98,27 @@ namespace Barotrauma
|
||||
{
|
||||
return entity is Item item && item.body == null && item.Prefab.AllowRotatingInEditor && Screen.Selected == GameMain.SubEditorScreen;
|
||||
}
|
||||
case ConditionType.Attachable:
|
||||
{
|
||||
return entity is Holdable holdable && holdable.Attachable;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public enum IsPropertySaveable
|
||||
{
|
||||
Yes,
|
||||
No
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public class Serialize : Attribute
|
||||
{
|
||||
public object defaultValue;
|
||||
public bool isSaveable;
|
||||
public string translationTextTag;
|
||||
public readonly object DefaultValue;
|
||||
public readonly IsPropertySaveable IsSaveable;
|
||||
public readonly Identifier TranslationTextTag;
|
||||
|
||||
/// <summary>
|
||||
/// If set to true, the instance values saved in a submarine file will always override the prefab values, even if using a mod that normally overrides instance values.
|
||||
@@ -122,48 +135,49 @@ namespace Barotrauma
|
||||
/// <param name="translationTextTag">If set to anything else than null, SerializableEntityEditors will show what the text gets translated to or warn if the text is not found in the language files.
|
||||
/// <param name="alwaysUseInstanceValues">If set to true, the instance values saved in a submarine file will always override the prefab values, even if using a mod that normally overrides instance values.
|
||||
/// Setting the value to a non-empty string will let the user select the text from one whose tag starts with the given string (e.g. RoomName. would show all texts with a RoomName.* tag)</param>
|
||||
public Serialize(object defaultValue, bool isSaveable, string description = "", string translationTextTag = null, bool alwaysUseInstanceValues = false)
|
||||
public Serialize(object defaultValue, IsPropertySaveable isSaveable, string description = "", string translationTextTag = "", bool alwaysUseInstanceValues = false)
|
||||
{
|
||||
this.defaultValue = defaultValue;
|
||||
this.isSaveable = isSaveable;
|
||||
this.translationTextTag = translationTextTag;
|
||||
this.DefaultValue = defaultValue;
|
||||
this.IsSaveable = isSaveable;
|
||||
this.TranslationTextTag = translationTextTag.ToIdentifier();
|
||||
Description = description;
|
||||
AlwaysUseInstanceValues = alwaysUseInstanceValues;
|
||||
}
|
||||
}
|
||||
|
||||
class SerializableProperty
|
||||
public class SerializableProperty
|
||||
{
|
||||
private static Dictionary<Type, string> supportedTypes = new Dictionary<Type, string>
|
||||
private readonly static ImmutableDictionary<Type, string> supportedTypes = new Dictionary<Type, string>
|
||||
{
|
||||
{ typeof(bool), "bool" },
|
||||
{ typeof(int), "int" },
|
||||
{ typeof(float), "float" },
|
||||
{ typeof(string), "string" },
|
||||
{ typeof(Identifier), "identifier" },
|
||||
{ typeof(LocalizedString), "localizedstring" },
|
||||
{ typeof(Point), "point" },
|
||||
{ typeof(Vector2), "vector2" },
|
||||
{ typeof(Vector3), "vector3" },
|
||||
{ typeof(Vector4), "vector4" },
|
||||
{ typeof(Rectangle), "rectangle" },
|
||||
{ typeof(Color), "color" },
|
||||
{ typeof(string[]), "stringarray" }
|
||||
};
|
||||
{ typeof(string[]), "stringarray" },
|
||||
{ typeof(Identifier[]), "identifierarray" }
|
||||
}.ToImmutableDictionary();
|
||||
|
||||
private static readonly Dictionary<Type, Dictionary<string, SerializableProperty>> cachedProperties =
|
||||
new Dictionary<Type, Dictionary<string, SerializableProperty>>();
|
||||
private static readonly Dictionary<Type, Dictionary<Identifier, SerializableProperty>> cachedProperties =
|
||||
new Dictionary<Type, Dictionary<Identifier, SerializableProperty>>();
|
||||
public readonly string Name;
|
||||
public readonly string NameToLowerInvariant;
|
||||
public readonly AttributeCollection Attributes;
|
||||
public readonly Type PropertyType;
|
||||
|
||||
public readonly bool OverridePrefabValues;
|
||||
|
||||
public PropertyInfo PropertyInfo { get; private set; }
|
||||
public readonly PropertyInfo PropertyInfo;
|
||||
|
||||
public SerializableProperty(PropertyDescriptor property)
|
||||
{
|
||||
Name = property.Name;
|
||||
NameToLowerInvariant = Name.ToLowerInvariant();
|
||||
PropertyInfo = property.ComponentType.GetProperty(property.Name);
|
||||
PropertyType = property.PropertyType;
|
||||
Attributes = property.Attributes;
|
||||
@@ -215,8 +229,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to set the value of the property \"" + Name + "\" of \"" + parentObject + "\" to " + value);
|
||||
DebugConsole.ThrowError("(Type not supported)");
|
||||
DebugConsole.ThrowError($"Failed to set the value of the property \"{Name}\" of \"{parentObject}\" to {value} (Type \"{PropertyType.Name}\" not supported)");
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -274,12 +287,20 @@ namespace Barotrauma
|
||||
case "rectangle":
|
||||
PropertyInfo.SetValue(parentObject, XMLExtensions.ParseRect(value, true));
|
||||
break;
|
||||
case "identifier":
|
||||
PropertyInfo.SetValue(parentObject, value.ToIdentifier());
|
||||
break;
|
||||
case "localizedstring":
|
||||
PropertyInfo.SetValue(parentObject, new RawLString(value));
|
||||
break;
|
||||
case "stringarray":
|
||||
PropertyInfo.SetValue(parentObject, XMLExtensions.ParseStringArray(value));
|
||||
break;
|
||||
case "identifierarray":
|
||||
PropertyInfo.SetValue(parentObject, XMLExtensions.ParseStringArray(value).ToIdentifiers().ToArray());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to set the value of the property \"" + Name + "\" of \"" + parentObject.ToString() + "\" to " + value.ToString(), e);
|
||||
@@ -307,7 +328,8 @@ namespace Barotrauma
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to set the value of the property \"" + Name + "\" of \"" + parentObject + "\" to " + value + " (not a valid " + PropertyInfo.PropertyType + ")", e);
|
||||
DebugConsole.ThrowError(
|
||||
$"Failed to set the value of the property \"{Name}\" of \"{parentObject}\" to {value} (not a valid {PropertyInfo.PropertyType})", e);
|
||||
return false;
|
||||
}
|
||||
PropertyInfo.SetValue(parentObject, enumVal);
|
||||
@@ -315,8 +337,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to set the value of the property \"" + Name + "\" of \"" + parentObject + "\" to " + value);
|
||||
DebugConsole.ThrowError("(Type not supported)");
|
||||
DebugConsole.ThrowError($"Failed to set the value of the property \"{Name}\" of \"{parentObject}\" to {value} (Type \"{PropertyType.Name}\" not supported)");
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -349,9 +370,18 @@ namespace Barotrauma
|
||||
case "rectangle":
|
||||
PropertyInfo.SetValue(parentObject, XMLExtensions.ParseRect((string)value, false));
|
||||
return true;
|
||||
case "identifier":
|
||||
PropertyInfo.SetValue(parentObject, new Identifier((string)value));
|
||||
return true;
|
||||
case "localizedstring":
|
||||
PropertyInfo.SetValue(parentObject, new RawLString((string)value));
|
||||
return true;
|
||||
case "stringarray":
|
||||
PropertyInfo.SetValue(parentObject, XMLExtensions.ParseStringArray((string)value));
|
||||
break;
|
||||
return true;
|
||||
case "identifierarray":
|
||||
PropertyInfo.SetValue(parentObject, XMLExtensions.ParseStringArray((string)value).ToIdentifiers().ToArray());
|
||||
return true;
|
||||
default:
|
||||
DebugConsole.ThrowError("Failed to set the value of the property \"" + Name + "\" of \"" + parentObject.ToString() + "\" to " + value.ToString());
|
||||
DebugConsole.ThrowError("(Cannot convert a string to a " + PropertyType.ToString() + ")");
|
||||
@@ -527,6 +557,35 @@ namespace Barotrauma
|
||||
return typeName;
|
||||
}
|
||||
|
||||
private readonly ImmutableDictionary<Identifier, Func<object, object>> valueGetters =
|
||||
new Dictionary<Identifier, Func<object, object>>()
|
||||
{
|
||||
{"Voltage".ToIdentifier(), (obj) => obj is Powered p ? p.Voltage : (object) null},
|
||||
{"Charge".ToIdentifier(), (obj) => obj is PowerContainer p ? p.Charge : (object) null},
|
||||
{"Overload".ToIdentifier(), (obj) => obj is PowerTransfer p ? p.Overload : (object) null},
|
||||
{"AvailableFuel".ToIdentifier(), (obj) => obj is Reactor r ? r.AvailableFuel : (object) null},
|
||||
{"FissionRate".ToIdentifier(), (obj) => obj is Reactor r ? r.FissionRate : (object) null},
|
||||
{"OxygenFlow".ToIdentifier(), (obj) => obj is Vent v ? v.OxygenFlow : (object) null},
|
||||
{
|
||||
"CurrFlow".ToIdentifier(),
|
||||
(obj) => obj is Pump p ? (object) p.CurrFlow :
|
||||
obj is OxygenGenerator o ? (object)o.CurrFlow :
|
||||
null
|
||||
},
|
||||
{"CurrentVolume".ToIdentifier(), (obj) => obj is Engine e ? e.CurrentVolume : (object)null},
|
||||
{"MotionDetected".ToIdentifier(), (obj) => obj is MotionSensor m ? m.MotionDetected : (object)null},
|
||||
{"Oxygen".ToIdentifier(), (obj) => obj is Character c ? c.Oxygen : (object)null},
|
||||
{"Health".ToIdentifier(), (obj) => obj is Character c ? c.Health : (object)null},
|
||||
{"OxygenAvailable".ToIdentifier(), (obj) => obj is Character c ? c.OxygenAvailable : (object)null},
|
||||
{"PressureProtection".ToIdentifier(), (obj) => obj is Character c ? c.PressureProtection : (object)null},
|
||||
{"IsDead".ToIdentifier(), (obj) => obj is Character c ? c.IsDead : (object)null},
|
||||
{"IsHuman".ToIdentifier(), (obj) => obj is Character c ? c.IsHuman : (object)null},
|
||||
{"IsOn".ToIdentifier(), (obj) => obj is LightComponent l ? l.IsOn : (object)null},
|
||||
{"Condition".ToIdentifier(), (obj) => obj is Item i ? i.Condition : (object)null},
|
||||
{"ContainerIdentifier".ToIdentifier(), (obj) => obj is Item i ? i.ContainerIdentifier : (object)null},
|
||||
{"PhysicsBodyActive".ToIdentifier(), (obj) => obj is Item i ? i.PhysicsBodyActive : (object)null},
|
||||
}.ToImmutableDictionary();
|
||||
|
||||
/// <summary>
|
||||
/// Try getting the values of some commonly used properties directly without reflection
|
||||
/// </summary>
|
||||
@@ -683,7 +742,7 @@ namespace Barotrauma
|
||||
{
|
||||
case nameof(Item.ContainerIdentifier):
|
||||
{
|
||||
if (parentObject is Item item) { value = item.ContainerIdentifier; return true; }
|
||||
if (parentObject is Item item) { value = item.ContainerIdentifier.Value; return true; }
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -761,7 +820,7 @@ namespace Barotrauma
|
||||
return editableProperties;
|
||||
}
|
||||
|
||||
public static Dictionary<string, SerializableProperty> GetProperties(object obj)
|
||||
public static Dictionary<Identifier, SerializableProperty> GetProperties(object obj)
|
||||
{
|
||||
Type objType = obj.GetType();
|
||||
if (cachedProperties.ContainsKey(objType))
|
||||
@@ -770,11 +829,11 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
var properties = TypeDescriptor.GetProperties(obj.GetType()).Cast<PropertyDescriptor>();
|
||||
Dictionary<string, SerializableProperty> dictionary = new Dictionary<string, SerializableProperty>();
|
||||
Dictionary<Identifier, SerializableProperty> dictionary = new Dictionary<Identifier, SerializableProperty>();
|
||||
foreach (var property in properties)
|
||||
{
|
||||
var serializableProperty = new SerializableProperty(property);
|
||||
dictionary.Add(serializableProperty.NameToLowerInvariant, serializableProperty);
|
||||
dictionary.Add(serializableProperty.Name.ToIdentifier(), serializableProperty);
|
||||
}
|
||||
|
||||
cachedProperties[objType] = dictionary;
|
||||
@@ -782,16 +841,16 @@ namespace Barotrauma
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
public static Dictionary<string, SerializableProperty> DeserializeProperties(object obj, XElement element = null)
|
||||
public static Dictionary<Identifier, SerializableProperty> DeserializeProperties(object obj, XElement element = null)
|
||||
{
|
||||
Dictionary<string, SerializableProperty> dictionary = GetProperties(obj);
|
||||
Dictionary<Identifier, SerializableProperty> dictionary = GetProperties(obj);
|
||||
|
||||
foreach (var property in dictionary.Values)
|
||||
{
|
||||
//set the value of the property to the default value if there is one
|
||||
foreach (var ini in property.Attributes.OfType<Serialize>())
|
||||
{
|
||||
property.TrySetValue(obj, ini.defaultValue);
|
||||
property.TrySetValue(obj, ini.DefaultValue);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -802,7 +861,7 @@ namespace Barotrauma
|
||||
//and set the value of the matching property if it is initializable
|
||||
foreach (XAttribute attribute in element.Attributes())
|
||||
{
|
||||
if (!dictionary.TryGetValue(attribute.Name.ToString().ToLowerInvariant(), out SerializableProperty property)) { continue; }
|
||||
if (!dictionary.TryGetValue(attribute.NameAsIdentifier(), out SerializableProperty property)) { continue; }
|
||||
if (!property.Attributes.OfType<Serialize>().Any()) { continue; }
|
||||
property.TrySetValue(obj, attribute.Value);
|
||||
}
|
||||
@@ -827,7 +886,7 @@ namespace Barotrauma
|
||||
bool save = false;
|
||||
foreach (var attribute in property.Attributes.OfType<Serialize>())
|
||||
{
|
||||
if ((attribute.isSaveable && !attribute.defaultValue.Equals(value)) ||
|
||||
if ((attribute.IsSaveable == IsPropertySaveable.Yes && !attribute.DefaultValue.Equals(value)) ||
|
||||
(!ignoreEditable && property.Attributes.OfType<Editable>().Any()))
|
||||
{
|
||||
save = true;
|
||||
@@ -881,6 +940,10 @@ namespace Barotrauma
|
||||
string[] stringArray = (string[])value;
|
||||
stringValue = stringArray != null ? string.Join(';', stringArray) : "";
|
||||
break;
|
||||
case "identifierarray":
|
||||
Identifier[] identifierArray = (Identifier[])value;
|
||||
stringValue = identifierArray != null ? string.Join(';', identifierArray) : "";
|
||||
break;
|
||||
default:
|
||||
stringValue = value.ToString();
|
||||
break;
|
||||
@@ -888,7 +951,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
element.Attribute(property.Name)?.Remove();
|
||||
element.SetAttributeValue(property.NameToLowerInvariant, stringValue);
|
||||
element.SetAttributeValue(property.Name, stringValue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -899,9 +962,9 @@ namespace Barotrauma
|
||||
/// <param name="entity">The entity to upgrade</param>
|
||||
/// <param name="configElement">The XML element to get the upgrade instructions from (e.g. the config of an item prefab)</param>
|
||||
/// <param name="savedVersion">The game version the entity was saved with</param>
|
||||
public static void UpgradeGameVersion(ISerializableEntity entity, XElement configElement, Version savedVersion)
|
||||
public static void UpgradeGameVersion(ISerializableEntity entity, ContentXElement configElement, Version savedVersion)
|
||||
{
|
||||
foreach (XElement subElement in configElement.Elements())
|
||||
foreach (var subElement in configElement.Elements())
|
||||
{
|
||||
if (!subElement.Name.ToString().Equals("upgrade", StringComparison.OrdinalIgnoreCase)) { continue; }
|
||||
var upgradeVersion = new Version(subElement.GetAttributeString("gameversion", "0.0.0.0"));
|
||||
@@ -909,7 +972,7 @@ namespace Barotrauma
|
||||
|
||||
foreach (XAttribute attribute in subElement.Attributes())
|
||||
{
|
||||
string attributeName = attribute.Name.ToString().ToLowerInvariant();
|
||||
var attributeName = attribute.NameAsIdentifier();
|
||||
if (attributeName == "gameversion") { continue; }
|
||||
|
||||
if (attributeName == "refreshrect")
|
||||
@@ -1024,19 +1087,19 @@ namespace Barotrauma
|
||||
|
||||
if (entity is Item item2)
|
||||
{
|
||||
XElement componentElement = subElement.FirstElement();
|
||||
var componentElement = subElement.FirstElement();
|
||||
if (componentElement == null) { continue; }
|
||||
ItemComponent itemComponent = item2.Components.First(c => c.Name == componentElement.Name.ToString());
|
||||
if (itemComponent == null) { continue; }
|
||||
foreach (XAttribute attribute in componentElement.Attributes())
|
||||
{
|
||||
string attributeName = attribute.Name.ToString().ToLowerInvariant();
|
||||
var attributeName = attribute.NameAsIdentifier();
|
||||
if (itemComponent.SerializableProperties.TryGetValue(attributeName, out SerializableProperty property))
|
||||
{
|
||||
FixValue(property, itemComponent, attribute);
|
||||
}
|
||||
}
|
||||
foreach (XElement element in componentElement.Elements())
|
||||
foreach (var element in componentElement.Elements())
|
||||
{
|
||||
switch (element.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
|
||||
@@ -0,0 +1,233 @@
|
||||
#nullable enable
|
||||
using Barotrauma.Extensions;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Immutable;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Xml.Linq;
|
||||
using System.Xml.Schema;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
public static class StructSerialization
|
||||
{
|
||||
private static readonly ImmutableDictionary<Type, MethodInfo> deserializeMethods;
|
||||
private static readonly ImmutableDictionary<Type, MethodInfo> serializeMethods;
|
||||
|
||||
public class SkipAttribute : Attribute { }
|
||||
|
||||
private static bool ShouldSkip(this FieldInfo field)
|
||||
=> field.GetCustomAttribute<SkipAttribute>() != null;
|
||||
|
||||
private static HandlerAttribute? ExtractHandler(this FieldInfo field)
|
||||
=> field.GetCustomAttribute<HandlerAttribute>();
|
||||
|
||||
public class HandlerAttribute : Attribute
|
||||
{
|
||||
public readonly Func<string?, object?> Read;
|
||||
public readonly Func<object?, string?> Write;
|
||||
|
||||
public HandlerAttribute(Type handlerType)
|
||||
{
|
||||
var readAction =
|
||||
handlerType.GetMethod(nameof(Read), BindingFlags.Public | BindingFlags.Static)
|
||||
?? throw new Exception($"Type {handlerType.Name} does not have a static {nameof(Read)} method");
|
||||
var writeAction =
|
||||
handlerType.GetMethod(nameof(Write), BindingFlags.Public | BindingFlags.Static)
|
||||
?? throw new Exception($"Type {handlerType.Name} does not have a static {nameof(Write)} method");
|
||||
var paramArray = new object?[1];
|
||||
Read = (s) =>
|
||||
{
|
||||
paramArray[0] = s;
|
||||
return readAction.Invoke(null, paramArray);
|
||||
};
|
||||
Write = (o) =>
|
||||
{
|
||||
paramArray[0] = o;
|
||||
return writeAction.Invoke(null, paramArray)?.ToString();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
static StructSerialization()
|
||||
{
|
||||
deserializeMethods =
|
||||
typeof(StructSerialization)
|
||||
.GetMethods(BindingFlags.Static | BindingFlags.Public)
|
||||
.Where(m =>
|
||||
{
|
||||
if (!m.Name.StartsWith("Deserialize")) { return false; }
|
||||
var parameters = m.GetParameters();
|
||||
if (parameters.Length < 1 || parameters.Length > 2 ||
|
||||
parameters[0].ParameterType != typeof(string))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.Select(m => (m.ReturnType, m))
|
||||
.ToImmutableDictionary();
|
||||
|
||||
serializeMethods =
|
||||
typeof(StructSerialization)
|
||||
.GetMethods(BindingFlags.Static | BindingFlags.Public)
|
||||
.Where(m =>
|
||||
{
|
||||
if (!m.Name.StartsWith("Serialize")) { return false; }
|
||||
var parameters = m.GetParameters();
|
||||
if (parameters.Length != 1 ||
|
||||
m.ReturnType != typeof(string))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.Select(m => (m.GetParameters()[0].ParameterType, m))
|
||||
.ToImmutableDictionary();
|
||||
}
|
||||
|
||||
public static void CopyPropertiesFrom<T>(this ref T self, in T other) where T : struct
|
||||
{
|
||||
var fields = self.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance).Where(f => !f.IsInitOnly).ToArray();
|
||||
foreach (var field in fields)
|
||||
{
|
||||
if (field.ShouldSkip()) { continue; }
|
||||
field.SetValue(self, field.GetValue(other));
|
||||
}
|
||||
}
|
||||
|
||||
public static void DeserializeElement<T>(this ref T self, XElement element) where T : struct
|
||||
{
|
||||
var fields = self.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance).ToArray();
|
||||
|
||||
//box the struct here so we don't end up
|
||||
//making a copy every time we feed this to reflection
|
||||
object boxedSelf = self;
|
||||
foreach (var field in fields)
|
||||
{
|
||||
if (field.ShouldSkip()) { continue; }
|
||||
boxedSelf.TryDeserialize(field, element);
|
||||
}
|
||||
//copy the boxed struct into the original
|
||||
self = (T)boxedSelf;
|
||||
}
|
||||
|
||||
public static void TryDeserialize(this object boxedSelf, FieldInfo field, XElement element)
|
||||
{
|
||||
string fieldName = field.Name.ToLowerInvariant();
|
||||
string valueStr = element.GetAttributeString(fieldName, field.GetValue(boxedSelf)?.ToString() ?? "");
|
||||
|
||||
var handler = field.ExtractHandler();
|
||||
|
||||
if (handler != null)
|
||||
{
|
||||
field.SetValue(boxedSelf, handler.Read(valueStr));
|
||||
}
|
||||
else if (deserializeMethods.TryGetValue(field.FieldType, out MethodInfo? deserializeMethod))
|
||||
{
|
||||
object?[] parameters = { valueStr };
|
||||
if (deserializeMethod.GetParameters().Length > 1)
|
||||
{
|
||||
Array.Resize(ref parameters, 2);
|
||||
parameters[1] = field.GetValue(boxedSelf);
|
||||
}
|
||||
field.SetValue(boxedSelf, deserializeMethod.Invoke(boxedSelf, parameters));
|
||||
}
|
||||
else if (field.FieldType.IsEnum)
|
||||
{
|
||||
field.SetValue(boxedSelf, DeserializeEnum(field.FieldType, valueStr, (Enum)field.GetValue(boxedSelf)!));
|
||||
}
|
||||
}
|
||||
|
||||
public static string DeserializeString(string str)
|
||||
{
|
||||
return str;
|
||||
}
|
||||
|
||||
public static bool DeserializeBool(string str, bool defaultValue)
|
||||
{
|
||||
if (bool.TryParse(str, out bool result)) { return result; }
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public static float DeserializeFloat(string str, float defaultValue)
|
||||
{
|
||||
if (float.TryParse(str, NumberStyles.Float, CultureInfo.InvariantCulture, out float result)) { return result; }
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public static Int32 DeserializeInt32(string str, Int32 defaultValue)
|
||||
{
|
||||
if (Int32.TryParse(str, NumberStyles.Any, CultureInfo.InvariantCulture, out Int32 result)) { return result; }
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public static Identifier DeserializeIdentifier(string str)
|
||||
{
|
||||
return str.ToIdentifier();
|
||||
}
|
||||
|
||||
public static LanguageIdentifier DeserializeLanguageIdentifier(string str)
|
||||
{
|
||||
return str.ToLanguageIdentifier();
|
||||
}
|
||||
|
||||
public static Color DeserializeColor(string str)
|
||||
{
|
||||
return XMLExtensions.ParseColor(str);
|
||||
}
|
||||
|
||||
public static Enum DeserializeEnum(Type enumType, string str, Enum defaultValue)
|
||||
{
|
||||
if (Enum.TryParse(enumType, str, out object? result)) { return (Enum)result!; }
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public static void SerializeElement<T>(this ref T self, XElement element) where T : struct
|
||||
{
|
||||
var fields = self.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance).ToArray();
|
||||
|
||||
foreach (var field in fields)
|
||||
{
|
||||
if (field.ShouldSkip()) { continue; }
|
||||
self.TrySerialize(field, element);
|
||||
}
|
||||
}
|
||||
|
||||
public static void TrySerialize<T>(this T self, FieldInfo field, XElement element) where T : struct
|
||||
{
|
||||
string fieldName = field.Name.ToLowerInvariant();
|
||||
object? fieldValue = field.GetValue(self);
|
||||
|
||||
string valueStr = fieldValue?.ToString() ?? "";
|
||||
|
||||
var handler = field.ExtractHandler();
|
||||
|
||||
if (handler != null)
|
||||
{
|
||||
valueStr = handler.Write(valueStr) ?? "";
|
||||
}
|
||||
else if (serializeMethods.TryGetValue(field.FieldType, out MethodInfo? method))
|
||||
{
|
||||
object?[] parameters = { fieldValue };
|
||||
valueStr = (string)method.Invoke(self, parameters)!;
|
||||
}
|
||||
|
||||
element.SetAttributeValue(fieldName, valueStr);
|
||||
}
|
||||
|
||||
public static string SerializeBool(bool val)
|
||||
=> val ? "true" : "false";
|
||||
|
||||
public static string SerializeInt32(Int32 val)
|
||||
=> val.ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
public static string SerializeFloat(float val)
|
||||
=> val.ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
public static string SerializeColor(Color val)
|
||||
=> val.ToStringHex();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@@ -69,8 +70,25 @@ namespace Barotrauma
|
||||
return doc;
|
||||
}
|
||||
|
||||
public static XDocument TryLoadXml(ContentPath path) => TryLoadXml(path.Value);
|
||||
|
||||
public static XDocument TryLoadXml(string filePath)
|
||||
{
|
||||
var doc = TryLoadXml(filePath, out var exception);
|
||||
if (exception != null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Couldn't load xml document \"{filePath}\"!", exception);
|
||||
}
|
||||
else if (doc is null)
|
||||
{
|
||||
DebugConsole.ThrowError($"File \"{filePath}\" could not be loaded: Document or the root element is invalid!");
|
||||
}
|
||||
return doc;
|
||||
}
|
||||
|
||||
public static XDocument TryLoadXml(string filePath, out Exception exception)
|
||||
{
|
||||
exception = null;
|
||||
XDocument doc;
|
||||
try
|
||||
{
|
||||
@@ -81,42 +99,16 @@ namespace Barotrauma
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Couldn't load xml document \"" + filePath + "\"!", e);
|
||||
exception = e;
|
||||
return null;
|
||||
}
|
||||
if (doc?.Root == null)
|
||||
{
|
||||
DebugConsole.ThrowError("File \"" + filePath + "\" could not be loaded: Document or the root element is invalid!");
|
||||
return null;
|
||||
}
|
||||
return doc;
|
||||
}
|
||||
|
||||
public static XDocument LoadXml(string filePath)
|
||||
{
|
||||
XDocument doc = null;
|
||||
|
||||
ToolBox.IsProperFilenameCase(filePath);
|
||||
|
||||
if (File.Exists(filePath))
|
||||
{
|
||||
try
|
||||
{
|
||||
using FileStream stream = File.Open(filePath, System.IO.FileMode.Open, System.IO.FileAccess.Read);
|
||||
using XmlReader reader = CreateReader(stream, Path.GetFullPath(filePath));
|
||||
doc = XDocument.Load(reader);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (doc.Root == null) { return null; }
|
||||
}
|
||||
|
||||
return doc;
|
||||
}
|
||||
|
||||
public static object GetAttributeObject(XAttribute attribute)
|
||||
{
|
||||
if (attribute == null) { return null; }
|
||||
@@ -151,8 +143,48 @@ namespace Barotrauma
|
||||
|
||||
public static string GetAttributeString(this XElement element, string name, string defaultValue)
|
||||
{
|
||||
if (element?.Attribute(name) == null) { return defaultValue; }
|
||||
return GetAttributeString(element.Attribute(name), defaultValue);
|
||||
if (element?.GetAttribute(name) == null) { return defaultValue; }
|
||||
string str = GetAttributeString(element.GetAttribute(name), defaultValue);
|
||||
#if DEBUG
|
||||
if (!str.IsNullOrEmpty() &&
|
||||
(str.Contains("%ModDir", StringComparison.OrdinalIgnoreCase)
|
||||
|| str.CleanUpPathCrossPlatform(correctFilenameCase: false).StartsWith("Content/", StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
DebugConsole.ThrowError($"Use {nameof(GetAttributeContentPath)} instead of {nameof(GetAttributeString)}\n{Environment.StackTrace.CleanupStackTrace()}");
|
||||
if (Debugger.IsAttached) { Debugger.Break(); }
|
||||
}
|
||||
#endif
|
||||
return str;
|
||||
}
|
||||
|
||||
public static string GetAttributeStringUnrestricted(this XElement element, string name, string defaultValue)
|
||||
{
|
||||
#warning TODO: remove?
|
||||
if (element?.GetAttribute(name) == null) { return defaultValue; }
|
||||
return GetAttributeString(element.GetAttribute(name), defaultValue);
|
||||
}
|
||||
|
||||
public static bool DoesAttributeReferenceFileNameAlone(this XElement element, string name)
|
||||
{
|
||||
string texName = element.GetAttributeStringUnrestricted(name, "");
|
||||
return !texName.IsNullOrEmpty() & !texName.Contains("/") && !texName.Contains("%ModDir", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public static ContentPath GetAttributeContentPath(this XElement element, string name,
|
||||
ContentPackage contentPackage)
|
||||
{
|
||||
if (element?.GetAttribute(name) == null) { return null; }
|
||||
return ContentPath.FromRaw(contentPackage, GetAttributeString(element.GetAttribute(name), null));
|
||||
}
|
||||
|
||||
public static Identifier GetAttributeIdentifier(this XElement element, string name, string defaultValue)
|
||||
{
|
||||
return element.GetAttributeString(name, defaultValue).ToIdentifier();
|
||||
}
|
||||
|
||||
public static Identifier GetAttributeIdentifier(this XElement element, string name, Identifier defaultValue)
|
||||
{
|
||||
return element.GetAttributeIdentifier(name, defaultValue.Value);
|
||||
}
|
||||
|
||||
private static string GetAttributeString(XAttribute attribute, string defaultValue)
|
||||
@@ -163,43 +195,41 @@ namespace Barotrauma
|
||||
|
||||
public static string[] GetAttributeStringArray(this XElement element, string name, string[] defaultValue, bool trim = true, bool convertToLowerInvariant = false)
|
||||
{
|
||||
if (element?.Attribute(name) == null) { return defaultValue; }
|
||||
if (element?.GetAttribute(name) == null) { return defaultValue; }
|
||||
|
||||
string stringValue = element.Attribute(name).Value;
|
||||
string stringValue = element.GetAttribute(name).Value;
|
||||
if (string.IsNullOrEmpty(stringValue)) { return defaultValue; }
|
||||
|
||||
string[] splitValue = stringValue.Split(',', ',');
|
||||
|
||||
if (convertToLowerInvariant)
|
||||
for (int i = 0; i < splitValue.Length; i++)
|
||||
{
|
||||
for (int i = 0; i < splitValue.Length; i++)
|
||||
{
|
||||
splitValue[i] = splitValue[i].ToLowerInvariant();
|
||||
}
|
||||
}
|
||||
if (trim)
|
||||
{
|
||||
for (int i = 0; i < splitValue.Length; i++)
|
||||
{
|
||||
splitValue[i] = splitValue[i].Trim();
|
||||
}
|
||||
if (convertToLowerInvariant) { splitValue[i] = splitValue[i].ToLowerInvariant(); }
|
||||
if (trim) { splitValue[i] = splitValue[i].Trim(); }
|
||||
}
|
||||
|
||||
return splitValue;
|
||||
}
|
||||
|
||||
public static Identifier[] GetAttributeIdentifierArray(this XElement element, string name, Identifier[] defaultValue, bool trim = true)
|
||||
{
|
||||
return element.GetAttributeStringArray(name, null, trim: trim, convertToLowerInvariant: false)
|
||||
?.ToIdentifiers()
|
||||
?? defaultValue;
|
||||
}
|
||||
|
||||
public static float GetAttributeFloat(this XElement element, float defaultValue, params string[] matchingAttributeName)
|
||||
{
|
||||
if (element == null) { return defaultValue; }
|
||||
|
||||
foreach (string name in matchingAttributeName)
|
||||
{
|
||||
if (element.Attribute(name) == null) { continue; }
|
||||
if (element.GetAttribute(name) == null) { continue; }
|
||||
|
||||
float val;
|
||||
try
|
||||
{
|
||||
string strVal = element.Attribute(name).Value;
|
||||
string strVal = element.GetAttribute(name).Value;
|
||||
if (strVal.LastOrDefault() == 'f')
|
||||
{
|
||||
strVal = strVal.Substring(0, strVal.Length - 1);
|
||||
@@ -219,12 +249,12 @@ namespace Barotrauma
|
||||
|
||||
public static float GetAttributeFloat(this XElement element, string name, float defaultValue)
|
||||
{
|
||||
if (element?.Attribute(name) == null) { return defaultValue; }
|
||||
if (element?.GetAttribute(name) == null) { return defaultValue; }
|
||||
|
||||
float val = defaultValue;
|
||||
try
|
||||
{
|
||||
string strVal = element.Attribute(name).Value;
|
||||
string strVal = element.GetAttribute(name).Value;
|
||||
if (strVal.LastOrDefault() == 'f')
|
||||
{
|
||||
strVal = strVal.Substring(0, strVal.Length - 1);
|
||||
@@ -286,9 +316,9 @@ namespace Barotrauma
|
||||
|
||||
public static float[] GetAttributeFloatArray(this XElement element, string name, float[] defaultValue)
|
||||
{
|
||||
if (element?.Attribute(name) == null) { return defaultValue; }
|
||||
if (element?.GetAttribute(name) == null) { return defaultValue; }
|
||||
|
||||
string stringValue = element.Attribute(name).Value;
|
||||
string stringValue = element.GetAttribute(name).Value;
|
||||
if (string.IsNullOrEmpty(stringValue)) { return defaultValue; }
|
||||
|
||||
string[] splitValue = stringValue.Split(',');
|
||||
@@ -315,15 +345,15 @@ namespace Barotrauma
|
||||
|
||||
public static int GetAttributeInt(this XElement element, string name, int defaultValue)
|
||||
{
|
||||
if (element?.Attribute(name) == null) { return defaultValue; }
|
||||
if (element?.GetAttribute(name) == null) { return defaultValue; }
|
||||
|
||||
int val = defaultValue;
|
||||
|
||||
try
|
||||
{
|
||||
if (!Int32.TryParse(element.Attribute(name).Value, NumberStyles.Any, CultureInfo.InvariantCulture, out val))
|
||||
if (!Int32.TryParse(element.GetAttribute(name).Value, NumberStyles.Any, CultureInfo.InvariantCulture, out val))
|
||||
{
|
||||
val = (int)float.Parse(element.Attribute(name).Value, CultureInfo.InvariantCulture);
|
||||
val = (int)float.Parse(element.GetAttribute(name).Value, CultureInfo.InvariantCulture);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -336,13 +366,13 @@ namespace Barotrauma
|
||||
|
||||
public static uint GetAttributeUInt(this XElement element, string name, uint defaultValue)
|
||||
{
|
||||
if (element?.Attribute(name) == null) { return defaultValue; }
|
||||
if (element?.GetAttribute(name) == null) { return defaultValue; }
|
||||
|
||||
uint val = defaultValue;
|
||||
|
||||
try
|
||||
{
|
||||
val = UInt32.Parse(element.Attribute(name).Value);
|
||||
val = UInt32.Parse(element.GetAttribute(name).Value);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -354,13 +384,31 @@ namespace Barotrauma
|
||||
|
||||
public static UInt64 GetAttributeUInt64(this XElement element, string name, UInt64 defaultValue)
|
||||
{
|
||||
if (element?.Attribute(name) == null) { return defaultValue; }
|
||||
if (element?.GetAttribute(name) == null) { return defaultValue; }
|
||||
|
||||
UInt64 val = defaultValue;
|
||||
|
||||
try
|
||||
{
|
||||
val = UInt64.Parse(element.Attribute(name).Value);
|
||||
val = UInt64.Parse(element.GetAttribute(name).Value, NumberStyles.Any, CultureInfo.InvariantCulture);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in " + element + "! ", e);
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
public static Version GetAttributeVersion(this XElement element, string name, Version defaultValue)
|
||||
{
|
||||
if (element?.GetAttribute(name) == null) return defaultValue;
|
||||
|
||||
Version val = defaultValue;
|
||||
|
||||
try
|
||||
{
|
||||
val = Version.Parse(element.GetAttribute(name).Value);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -372,13 +420,13 @@ namespace Barotrauma
|
||||
|
||||
public static UInt64 GetAttributeSteamID(this XElement element, string name, UInt64 defaultValue)
|
||||
{
|
||||
if (element?.Attribute(name) == null) { return defaultValue; }
|
||||
if (element?.GetAttribute(name) == null) { return defaultValue; }
|
||||
|
||||
UInt64 val = defaultValue;
|
||||
|
||||
try
|
||||
{
|
||||
val = Steam.SteamManager.SteamIDStringToUInt64(element.Attribute(name).Value);
|
||||
val = Steam.SteamManager.SteamIDStringToUInt64(element.GetAttribute(name).Value);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -390,9 +438,9 @@ namespace Barotrauma
|
||||
|
||||
public static int[] GetAttributeIntArray(this XElement element, string name, int[] defaultValue)
|
||||
{
|
||||
if (element?.Attribute(name) == null) { return defaultValue; }
|
||||
if (element?.GetAttribute(name) == null) { return defaultValue; }
|
||||
|
||||
string stringValue = element.Attribute(name).Value;
|
||||
string stringValue = element.GetAttribute(name).Value;
|
||||
if (string.IsNullOrEmpty(stringValue)) { return defaultValue; }
|
||||
|
||||
string[] splitValue = stringValue.Split(',');
|
||||
@@ -414,9 +462,9 @@ namespace Barotrauma
|
||||
}
|
||||
public static ushort[] GetAttributeUshortArray(this XElement element, string name, ushort[] defaultValue)
|
||||
{
|
||||
if (element?.Attribute(name) == null) { return defaultValue; }
|
||||
if (element?.GetAttribute(name) == null) { return defaultValue; }
|
||||
|
||||
string stringValue = element.Attribute(name).Value;
|
||||
string stringValue = element.GetAttribute(name).Value;
|
||||
if (string.IsNullOrEmpty(stringValue)) { return defaultValue; }
|
||||
|
||||
string[] splitValue = stringValue.Split(',');
|
||||
@@ -448,8 +496,8 @@ namespace Barotrauma
|
||||
|
||||
public static bool GetAttributeBool(this XElement element, string name, bool defaultValue)
|
||||
{
|
||||
if (element?.Attribute(name) == null) { return defaultValue; }
|
||||
return element.Attribute(name).GetAttributeBool(defaultValue);
|
||||
if (element?.GetAttribute(name) == null) { return defaultValue; }
|
||||
return element.GetAttribute(name).GetAttributeBool(defaultValue);
|
||||
}
|
||||
|
||||
public static bool GetAttributeBool(this XAttribute attribute, bool defaultValue)
|
||||
@@ -472,45 +520,45 @@ namespace Barotrauma
|
||||
|
||||
public static Point GetAttributePoint(this XElement element, string name, Point defaultValue)
|
||||
{
|
||||
if (element?.Attribute(name) == null) { return defaultValue; }
|
||||
return ParsePoint(element.Attribute(name).Value);
|
||||
if (element?.GetAttribute(name) == null) { return defaultValue; }
|
||||
return ParsePoint(element.GetAttribute(name).Value);
|
||||
}
|
||||
|
||||
public static Vector2 GetAttributeVector2(this XElement element, string name, Vector2 defaultValue)
|
||||
{
|
||||
if (element?.Attribute(name) == null) { return defaultValue; }
|
||||
return ParseVector2(element.Attribute(name).Value);
|
||||
if (element?.GetAttribute(name) == null) { return defaultValue; }
|
||||
return ParseVector2(element.GetAttribute(name).Value);
|
||||
}
|
||||
|
||||
public static Vector3 GetAttributeVector3(this XElement element, string name, Vector3 defaultValue)
|
||||
{
|
||||
if (element == null || element.Attribute(name) == null) { return defaultValue; }
|
||||
return ParseVector3(element.Attribute(name).Value);
|
||||
if (element == null || element.GetAttribute(name) == null) { return defaultValue; }
|
||||
return ParseVector3(element.GetAttribute(name).Value);
|
||||
}
|
||||
|
||||
public static Vector4 GetAttributeVector4(this XElement element, string name, Vector4 defaultValue)
|
||||
{
|
||||
if (element == null || element.Attribute(name) == null) { return defaultValue; }
|
||||
return ParseVector4(element.Attribute(name).Value);
|
||||
if (element == null || element.GetAttribute(name) == null) { return defaultValue; }
|
||||
return ParseVector4(element.GetAttribute(name).Value);
|
||||
}
|
||||
|
||||
public static Color GetAttributeColor(this XElement element, string name, Color defaultValue)
|
||||
{
|
||||
if (element == null || element.Attribute(name) == null) { return defaultValue; }
|
||||
return ParseColor(element.Attribute(name).Value);
|
||||
if (element == null || element.GetAttribute(name) == null) { return defaultValue; }
|
||||
return ParseColor(element.GetAttribute(name).Value);
|
||||
}
|
||||
|
||||
public static Color? GetAttributeColor(this XElement element, string name)
|
||||
{
|
||||
if (element == null || element.Attribute(name) == null) { return null; }
|
||||
return ParseColor(element.Attribute(name).Value);
|
||||
if (element == null || element.GetAttribute(name) == null) { return null; }
|
||||
return ParseColor(element.GetAttribute(name).Value);
|
||||
}
|
||||
|
||||
public static Color[] GetAttributeColorArray(this XElement element, string name, Color[] defaultValue)
|
||||
{
|
||||
if (element?.Attribute(name) == null) { return defaultValue; }
|
||||
if (element?.GetAttribute(name) == null) { return defaultValue; }
|
||||
|
||||
string stringValue = element.Attribute(name).Value;
|
||||
string stringValue = element.GetAttribute(name).Value;
|
||||
if (string.IsNullOrEmpty(stringValue)) { return defaultValue; }
|
||||
|
||||
string[] splitValue = stringValue.Split(';');
|
||||
@@ -531,10 +579,31 @@ namespace Barotrauma
|
||||
return colorValue;
|
||||
}
|
||||
|
||||
#if CLIENT
|
||||
public static KeyOrMouse GetAttributeKeyOrMouse(this XElement element, string name, KeyOrMouse defaultValue)
|
||||
{
|
||||
string strValue = element.GetAttributeString(name, defaultValue?.ToString() ?? "");
|
||||
if (Enum.TryParse(strValue, true, out Microsoft.Xna.Framework.Input.Keys key))
|
||||
{
|
||||
return key;
|
||||
}
|
||||
else if (Enum.TryParse(strValue, out MouseButton mouseButton))
|
||||
{
|
||||
return mouseButton;
|
||||
}
|
||||
else if (int.TryParse(strValue, NumberStyles.Any, CultureInfo.InvariantCulture, out int mouseButtonInt) &&
|
||||
(Enum.GetValues(typeof(MouseButton)) as MouseButton[]).Contains((MouseButton)mouseButtonInt))
|
||||
{
|
||||
return (MouseButton)mouseButtonInt;
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
#endif
|
||||
|
||||
public static Rectangle GetAttributeRect(this XElement element, string name, Rectangle defaultValue)
|
||||
{
|
||||
if (element == null || element.Attribute(name) == null) { return defaultValue; }
|
||||
return ParseRect(element.Attribute(name).Value, false);
|
||||
if (element == null || element.GetAttribute(name) == null) { return defaultValue; }
|
||||
return ParseRect(element.GetAttribute(name).Value, false);
|
||||
}
|
||||
|
||||
//TODO: nested tuples and and n-uples where n!=2 are unsupported
|
||||
@@ -703,16 +772,10 @@ namespace Barotrauma
|
||||
if (stringColor.StartsWith("gui.", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
#if CLIENT
|
||||
if (GUI.Style != null)
|
||||
Identifier colorName = stringColor.Substring(4).ToIdentifier();
|
||||
if (GUIStyle.Colors.TryGetValue(colorName, out GUIColor guiColor))
|
||||
{
|
||||
string colorName = stringColor.Substring(4);
|
||||
var property = GUI.Style.GetType().GetProperties().FirstOrDefault(
|
||||
p => p.PropertyType == typeof(Color) &&
|
||||
p.Name.Equals(colorName, StringComparison.OrdinalIgnoreCase));
|
||||
if (property != null)
|
||||
{
|
||||
return (Color)property?.GetValue(GUI.Style);
|
||||
}
|
||||
return guiColor.Value;
|
||||
}
|
||||
#endif
|
||||
return Color.White;
|
||||
@@ -727,7 +790,7 @@ namespace Barotrauma
|
||||
|
||||
if (strComponents.Length == 1)
|
||||
{
|
||||
bool hexFailed = true;
|
||||
bool altParseFailed = true;
|
||||
stringColor = stringColor.Trim();
|
||||
if (stringColor.Length > 0 && stringColor[0] == '#')
|
||||
{
|
||||
@@ -744,11 +807,40 @@ namespace Barotrauma
|
||||
components[2] = ((float)((colorInt & 0x0000ff00) >> 8)) / 255.0f;
|
||||
components[3] = ((float)(colorInt & 0x000000ff)) / 255.0f;
|
||||
|
||||
hexFailed = false;
|
||||
altParseFailed = false;
|
||||
}
|
||||
}
|
||||
else if (stringColor.Length > 0 && stringColor[0] == '{')
|
||||
{
|
||||
stringColor = stringColor.Substring(1, stringColor.Length-2);
|
||||
|
||||
string[] mgComponents = stringColor.Split(' ');
|
||||
if (mgComponents.Length == 4)
|
||||
{
|
||||
altParseFailed = false;
|
||||
|
||||
string[] expectedPrefixes = {"R:", "G:", "B:", "A:"};
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
if (mgComponents[i].StartsWith(expectedPrefixes[i], StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
string strToParse = mgComponents[i]
|
||||
.Remove(expectedPrefixes[i], StringComparison.OrdinalIgnoreCase)
|
||||
.Trim();
|
||||
int val = 0;
|
||||
altParseFailed |= !int.TryParse(strToParse, out val);
|
||||
components[i] = ((float) val) / 255f;
|
||||
}
|
||||
else
|
||||
{
|
||||
altParseFailed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hexFailed)
|
||||
if (altParseFailed)
|
||||
{
|
||||
if (errorMessages) { DebugConsole.ThrowError("Failed to parse the string \"" + stringColor + "\" to Color"); }
|
||||
return Color.White;
|
||||
@@ -807,18 +899,27 @@ namespace Barotrauma
|
||||
return floatArray;
|
||||
}
|
||||
|
||||
public static Identifier VariantOf(this XElement element) =>
|
||||
element.GetAttributeIdentifier("inherit", element.GetAttributeIdentifier("variantof", ""));
|
||||
|
||||
public static string[] ParseStringArray(string stringArrayValues)
|
||||
{
|
||||
return string.IsNullOrEmpty(stringArrayValues) ? new string[0] : stringArrayValues.Split(';');
|
||||
return string.IsNullOrEmpty(stringArrayValues) ? Array.Empty<string>() : stringArrayValues.Split(';');
|
||||
}
|
||||
|
||||
public static Identifier[] ParseIdentifierArray(string stringArrayValues)
|
||||
{
|
||||
return ParseStringArray(stringArrayValues).ToIdentifiers().ToArray();
|
||||
}
|
||||
|
||||
public static bool IsOverride(this XElement element) => element.Name.ToString().Equals("override", StringComparison.OrdinalIgnoreCase);
|
||||
public static bool IsCharacterVariant(this XElement element) => element.Name.ToString().Equals("charactervariant", StringComparison.OrdinalIgnoreCase);
|
||||
public static bool IsOverride(this XElement element) => element.NameAsIdentifier() == "override";
|
||||
|
||||
public static XElement FirstElement(this XElement element) => element.Elements().FirstOrDefault();
|
||||
|
||||
public static XAttribute GetAttribute(this XElement element, string name, StringComparison comparisonMethod = StringComparison.OrdinalIgnoreCase) => element.GetAttribute(a => a.Name.ToString().Equals(name, comparisonMethod));
|
||||
|
||||
public static XAttribute GetAttribute(this XElement element, Identifier name) => element.GetAttribute(name.Value, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
public static XAttribute GetAttribute(this XElement element, Func<XAttribute, bool> predicate) => element.Attributes().FirstOrDefault(predicate);
|
||||
|
||||
/// <summary>
|
||||
@@ -830,5 +931,31 @@ namespace Barotrauma
|
||||
/// Returns all child elements that match the name using the provided comparison method.
|
||||
/// </summary>
|
||||
public static IEnumerable<XElement> GetChildElements(this XContainer container, string name, StringComparison comparisonMethod = StringComparison.OrdinalIgnoreCase) => container.Elements().Where(e => e.Name.ToString().Equals(name, comparisonMethod));
|
||||
|
||||
public static IEnumerable<XElement> GetChildElements(this XContainer container, params string[] names)
|
||||
{
|
||||
return names.SelectMany(name => container.GetChildElements(name));
|
||||
}
|
||||
|
||||
public static bool ComesAfter(this XElement element, XElement other)
|
||||
{
|
||||
if (element.Parent != other.Parent) { return false; }
|
||||
foreach (var child in element.Parent.Elements())
|
||||
{
|
||||
if (child == element) { return false; }
|
||||
if (child == other) { return true; }
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Identifier NameAsIdentifier(this XElement elem)
|
||||
{
|
||||
return elem.Name.LocalName.ToIdentifier();
|
||||
}
|
||||
|
||||
public static Identifier NameAsIdentifier(this XAttribute attr)
|
||||
{
|
||||
return attr.Name.LocalName.ToIdentifier();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user