Unstable 0.17.0.0
This commit is contained in:
+55
-54
@@ -21,71 +21,72 @@ namespace Barotrauma
|
||||
|
||||
abstract class GroundedMovementParams : AnimationParams
|
||||
{
|
||||
[Serialize("1.0, 1.0", true, description: "How big steps the character takes."), Editable(DecimalCount = 2, ValueStep = 0.01f)]
|
||||
[Serialize("1.0, 1.0", IsPropertySaveable.Yes, description: "How big steps the character takes."), Editable(DecimalCount = 2, ValueStep = 0.01f)]
|
||||
public Vector2 StepSize
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(0f, true, description: "How high above the ground the character's head is positioned."), Editable(DecimalCount = 2, ValueStep = 0.1f)]
|
||||
[Serialize(0f, IsPropertySaveable.Yes, description: "How high above the ground the character's head is positioned."), Editable(DecimalCount = 2, ValueStep = 0.1f)]
|
||||
public float HeadPosition { get; set; }
|
||||
|
||||
[Serialize(0f, true, description: "How high above the ground the character's torso is positioned."), Editable(DecimalCount = 2, ValueStep = 0.1f)]
|
||||
[Serialize(0f, IsPropertySaveable.Yes, description: "How high above the ground the character's torso is positioned."), Editable(DecimalCount = 2, ValueStep = 0.1f)]
|
||||
public float TorsoPosition { get; set; }
|
||||
|
||||
[Serialize(1f, true, description: "Separate multiplier for the head lift"), Editable(MinValueFloat = 0, MaxValueFloat = 2, ValueStep = 0.1f)]
|
||||
[Serialize(1f, IsPropertySaveable.Yes, description: "Separate multiplier for the head lift"), Editable(MinValueFloat = 0, MaxValueFloat = 2, ValueStep = 0.1f)]
|
||||
public float StepLiftHeadMultiplier { get; set; }
|
||||
|
||||
[Serialize(0f, true, description: "How much the body raises when taking a step."), Editable(MinValueFloat = 0, MaxValueFloat = 100, ValueStep = 0.1f)]
|
||||
[Serialize(0f, IsPropertySaveable.Yes, description: "How much the body raises when taking a step."), Editable(MinValueFloat = 0, MaxValueFloat = 100, ValueStep = 0.1f)]
|
||||
public float StepLiftAmount { get; set; }
|
||||
|
||||
[Serialize(true, true), Editable]
|
||||
[Serialize(true, IsPropertySaveable.Yes), Editable]
|
||||
public bool MultiplyByDir { get; set; }
|
||||
|
||||
[Serialize(0.5f, true, description: "When does the body raise when taking a step. The default (0.5) is in the middle of the step."), Editable(MinValueFloat = -1, MaxValueFloat = 1, DecimalCount = 2, ValueStep = 0.1f)]
|
||||
[Serialize(0.5f, IsPropertySaveable.Yes, description: "When does the body raise when taking a step. The default (0.5) is in the middle of the step."), Editable(MinValueFloat = -1, MaxValueFloat = 1, DecimalCount = 2, ValueStep = 0.1f)]
|
||||
public float StepLiftOffset { get; set; }
|
||||
|
||||
[Serialize(2f, true, description: "How frequently the body raises when taking a step. The default is 2 (after every step)."), Editable(MinValueFloat = 0, MaxValueFloat = 10, ValueStep = 0.1f)]
|
||||
[Serialize(2f, IsPropertySaveable.Yes, description: "How frequently the body raises when taking a step. The default is 2 (after every step)."), Editable(MinValueFloat = 0, MaxValueFloat = 10, ValueStep = 0.1f)]
|
||||
public float StepLiftFrequency { get; set; }
|
||||
|
||||
[Serialize(0.75f, true, description: "The character's movement speed is multiplied with this value when moving backwards."), Editable(MinValueFloat = 0.1f, MaxValueFloat = 0.99f, DecimalCount = 2)]
|
||||
[Serialize(0.75f, IsPropertySaveable.Yes, description: "The character's movement speed is multiplied with this value when moving backwards."), Editable(MinValueFloat = 0.1f, MaxValueFloat = 0.99f, DecimalCount = 2)]
|
||||
public float BackwardsMovementMultiplier { get; set; }
|
||||
}
|
||||
|
||||
abstract class SwimParams : AnimationParams
|
||||
{
|
||||
[Serialize(25.0f, true, description: "Turning speed (or rather a force applied on the main collider to make it turn). Note that you can set a limb-specific steering forces too (additional)."), Editable(MinValueFloat = 0, MaxValueFloat = 1000, ValueStep = 1)]
|
||||
[Serialize(25.0f, IsPropertySaveable.Yes, description: "Turning speed (or rather a force applied on the main collider to make it turn). Note that you can set a limb-specific steering forces too (additional)."), Editable(MinValueFloat = 0, MaxValueFloat = 1000, ValueStep = 1)]
|
||||
public float SteerTorque { get; set; }
|
||||
|
||||
[Serialize(25.0f, true, description: "How much torque is used to move the legs."), Editable(MinValueFloat = 0, MaxValueFloat = 1000, ValueStep = 1)]
|
||||
[Serialize(25.0f, IsPropertySaveable.Yes, description: "How much torque is used to move the legs."), Editable(MinValueFloat = 0, MaxValueFloat = 1000, ValueStep = 1)]
|
||||
public float LegTorque { get; set; }
|
||||
}
|
||||
|
||||
abstract class AnimationParams : EditableParams, IMemorizable<AnimationParams>
|
||||
{
|
||||
public string SpeciesName { get; private set; }
|
||||
public Identifier SpeciesName { get; private set; }
|
||||
public bool IsGroundedAnimation => AnimationType == AnimationType.Walk || AnimationType == AnimationType.Run || AnimationType == AnimationType.Crouch;
|
||||
public bool IsSwimAnimation => AnimationType == AnimationType.SwimSlow || AnimationType == AnimationType.SwimFast;
|
||||
|
||||
protected static Dictionary<string, Dictionary<string, AnimationParams>> allAnimations = new Dictionary<string, Dictionary<string, AnimationParams>>();
|
||||
protected static Dictionary<Identifier, Dictionary<string, AnimationParams>> allAnimations = new Dictionary<Identifier, Dictionary<string, AnimationParams>>();
|
||||
/// allAnimations[speciesName][fileName]
|
||||
|
||||
private float _movementSpeed;
|
||||
[Serialize(1.0f, true), Editable(DecimalCount = 2, MinValueFloat = 0, MaxValueFloat = Ragdoll.MAX_SPEED, ValueStep = 0.1f)]
|
||||
[Serialize(1.0f, IsPropertySaveable.Yes), Editable(DecimalCount = 2, MinValueFloat = 0, MaxValueFloat = Ragdoll.MAX_SPEED, ValueStep = 0.1f)]
|
||||
public float MovementSpeed
|
||||
{
|
||||
get => _movementSpeed;
|
||||
set => _movementSpeed = value;
|
||||
}
|
||||
|
||||
[Serialize(1.0f, true, description: "The speed of the \"animation cycle\", i.e. how fast the character takes steps or moves the tail/legs/arms (the outcome depends what the clip is about)"),
|
||||
[Serialize(1.0f, IsPropertySaveable.Yes, description: "The speed of the \"animation cycle\", i.e. how fast the character takes steps or moves the tail/legs/arms (the outcome depends what the clip is about)"),
|
||||
Editable(MinValueFloat = 0, MaxValueFloat = 10, DecimalCount = 2, ValueStep = 0.01f)]
|
||||
public float CycleSpeed { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// In degrees.
|
||||
/// </summary>
|
||||
[Serialize(float.NaN, true), Editable(-360f, 360f)]
|
||||
[Serialize(float.NaN, IsPropertySaveable.Yes), Editable(-360f, 360f)]
|
||||
public float HeadAngle
|
||||
{
|
||||
get => float.IsNaN(HeadAngleInRadians) ? float.NaN : MathHelper.ToDegrees(HeadAngleInRadians);
|
||||
@@ -102,7 +103,7 @@ namespace Barotrauma
|
||||
/// <summary>
|
||||
/// In degrees.
|
||||
/// </summary>
|
||||
[Serialize(float.NaN, true), Editable(-360f, 360f)]
|
||||
[Serialize(float.NaN, IsPropertySaveable.Yes), Editable(-360f, 360f)]
|
||||
public float TorsoAngle
|
||||
{
|
||||
get => float.IsNaN(TorsoAngleInRadians) ? float.NaN : MathHelper.ToDegrees(TorsoAngleInRadians);
|
||||
@@ -117,49 +118,44 @@ namespace Barotrauma
|
||||
|
||||
public float TorsoAngleInRadians { get; private set; } = float.NaN;
|
||||
|
||||
[Serialize(50.0f, true, description: "How much torque is used to rotate the head to the correct orientation."), Editable(MinValueFloat = 0, MaxValueFloat = 1000, ValueStep = 1)]
|
||||
[Serialize(50.0f, IsPropertySaveable.Yes, description: "How much torque is used to rotate the head to the correct orientation."), Editable(MinValueFloat = 0, MaxValueFloat = 1000, ValueStep = 1)]
|
||||
public float HeadTorque { get; set; }
|
||||
|
||||
[Serialize(50.0f, true, description: "How much torque is used to rotate the torso to the correct orientation."), Editable(MinValueFloat = 0, MaxValueFloat = 1000, ValueStep = 1)]
|
||||
[Serialize(50.0f, IsPropertySaveable.Yes, description: "How much torque is used to rotate the torso to the correct orientation."), Editable(MinValueFloat = 0, MaxValueFloat = 1000, ValueStep = 1)]
|
||||
public float TorsoTorque { get; set; }
|
||||
|
||||
[Serialize(25.0f, true, description: "How much torque is used to rotate the feet to the correct orientation."), Editable(MinValueFloat = 0, MaxValueFloat = 1000, ValueStep = 1)]
|
||||
[Serialize(25.0f, IsPropertySaveable.Yes, description: "How much torque is used to rotate the feet to the correct orientation."), Editable(MinValueFloat = 0, MaxValueFloat = 1000, ValueStep = 1)]
|
||||
public float FootTorque { get; set; }
|
||||
|
||||
[Serialize(AnimationType.NotDefined, true), Editable]
|
||||
[Serialize(AnimationType.NotDefined, IsPropertySaveable.Yes), Editable]
|
||||
public virtual AnimationType AnimationType { get; protected set; }
|
||||
|
||||
[Serialize(1f, true, description: "How much force is used to rotate the arms to the IK position."), Editable(MinValueFloat = 0, MaxValueFloat = 10, DecimalCount = 2)]
|
||||
[Serialize(1f, IsPropertySaveable.Yes, description: "How much force is used to rotate the arms to the IK position."), Editable(MinValueFloat = 0, MaxValueFloat = 10, DecimalCount = 2)]
|
||||
public float ArmIKStrength { get; set; }
|
||||
|
||||
[Serialize(1f, true, description: "How much force is used to rotate the hands to the IK position."), Editable(MinValueFloat = 0, MaxValueFloat = 10, DecimalCount = 2)]
|
||||
[Serialize(1f, IsPropertySaveable.Yes, description: "How much force is used to rotate the hands to the IK position."), Editable(MinValueFloat = 0, MaxValueFloat = 10, DecimalCount = 2)]
|
||||
public float HandIKStrength { get; set; }
|
||||
|
||||
public static string GetDefaultFileName(string speciesName, AnimationType animType) => $"{speciesName.CapitaliseFirstInvariant()}{animType}";
|
||||
public static string GetDefaultFile(string speciesName, AnimationType animType) => Path.Combine(GetFolder(speciesName), $"{GetDefaultFileName(speciesName, animType)}.xml");
|
||||
public static string GetDefaultFileName(Identifier speciesName, AnimationType animType) => $"{speciesName.Value.CapitaliseFirstInvariant()}{animType}";
|
||||
public static string GetDefaultFile(Identifier speciesName, AnimationType animType) => Barotrauma.IO.Path.Combine(GetFolder(speciesName), $"{GetDefaultFileName(speciesName, animType)}.xml");
|
||||
|
||||
public static string GetFolder(string speciesName)
|
||||
public static string GetFolder(Identifier speciesName)
|
||||
{
|
||||
CharacterPrefab prefab = CharacterPrefab.FindBySpeciesName(speciesName);
|
||||
if (prefab?.XDocument == null)
|
||||
if (prefab?.ConfigElement == null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Failed to find config file for '{speciesName}'");
|
||||
return string.Empty;
|
||||
}
|
||||
return GetFolder(prefab.XDocument, prefab.FilePath);
|
||||
return GetFolder(prefab.ConfigElement, prefab.FilePath.Value);
|
||||
}
|
||||
|
||||
public static string GetFolder(XDocument doc, string filePath)
|
||||
private static string GetFolder(ContentXElement root, string filePath)
|
||||
{
|
||||
var root = doc.Root;
|
||||
if (root?.IsOverride() ?? false)
|
||||
{
|
||||
root = root.FirstElement();
|
||||
}
|
||||
var folder = root?.Element("animations")?.GetAttributeString("folder", string.Empty);
|
||||
var folder = root?.GetChildElement("animations")?.GetAttributeContentPath("folder")?.Value;
|
||||
if (string.IsNullOrEmpty(folder) || folder.Equals("default", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
folder = Path.Combine(Path.GetDirectoryName(filePath), "Animations");
|
||||
folder = IO.Path.Combine(IO.Path.GetDirectoryName(filePath), "Animations");
|
||||
}
|
||||
return folder.CleanUpPathCrossPlatform(true);
|
||||
}
|
||||
@@ -167,9 +163,9 @@ namespace Barotrauma
|
||||
/// <summary>
|
||||
/// Selects a random filepath from multiple paths, matching the specified animation type.
|
||||
/// </summary>
|
||||
public static string GetRandomFilePath(IEnumerable<string> filePaths, AnimationType type)
|
||||
public static string GetRandomFilePath(IReadOnlyList<string> filePaths, AnimationType type)
|
||||
{
|
||||
return filePaths.GetRandom(f => AnimationPredicate(f, type), Rand.RandSync.Server);
|
||||
return filePaths.GetRandom(f => AnimationPredicate(f, type), Rand.RandSync.ServerAndClient);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -194,11 +190,12 @@ namespace Barotrauma
|
||||
|
||||
public static T GetDefaultAnimParams<T>(Character character, AnimationType animType) where T : AnimationParams, new()
|
||||
{
|
||||
string speciesName = character.VariantOf ?? character.SpeciesName;
|
||||
if (character.VariantOf != null && character.Params.VariantFile?.Root?.GetChildElement("animations")?.GetAttributeString("folder", null) != null)
|
||||
Identifier speciesName = character.SpeciesName;
|
||||
if (!character.VariantOf.IsEmpty
|
||||
&& (character.Params.VariantFile?.Root?.GetChildElement("animations")?.GetAttributeStringUnrestricted("folder", null)).IsNullOrEmpty())
|
||||
{
|
||||
// Use the overridden animations defined in the variant definition file.
|
||||
speciesName = character.SpeciesName;
|
||||
// Use the base animations defined in the base definition file.
|
||||
speciesName = character.VariantOf;
|
||||
}
|
||||
return GetAnimParams<T>(speciesName, animType, GetDefaultFileName(speciesName, animType));
|
||||
}
|
||||
@@ -207,7 +204,7 @@ namespace Barotrauma
|
||||
/// If the file name is left null, default file is selected. If fails, will select the default file. Note: Use the filename without the extensions, don't use the full path!
|
||||
/// If a custom folder is used, it's defined in the character info file.
|
||||
/// </summary>
|
||||
public static T GetAnimParams<T>(string speciesName, AnimationType animType, string fileName = null) where T : AnimationParams, new()
|
||||
public static T GetAnimParams<T>(Identifier speciesName, AnimationType animType, string fileName = null) where T : AnimationParams, new()
|
||||
{
|
||||
if (!allAnimations.TryGetValue(speciesName, out Dictionary<string, AnimationParams> anims))
|
||||
{
|
||||
@@ -239,7 +236,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
selectedFile = filteredFiles.FirstOrDefault(f => Path.GetFileNameWithoutExtension(f).Equals(fileName, StringComparison.OrdinalIgnoreCase));
|
||||
selectedFile = filteredFiles.FirstOrDefault(f => IO.Path.GetFileNameWithoutExtension(f).Equals(fileName, StringComparison.OrdinalIgnoreCase));
|
||||
if (selectedFile == null)
|
||||
{
|
||||
DebugConsole.ThrowError($"[AnimationParams] Could not find an animation file that matches the name {fileName} and the animation type {animType}. Using the default animations.");
|
||||
@@ -257,10 +254,11 @@ namespace Barotrauma
|
||||
throw new Exception("[AnimationParams] Selected file null!");
|
||||
}
|
||||
DebugConsole.Log($"[AnimationParams] Loading animations from {selectedFile}.");
|
||||
var characterPrefab = CharacterPrefab.Prefabs[speciesName];
|
||||
T a = new T();
|
||||
if (a.Load(selectedFile, speciesName))
|
||||
if (a.Load(ContentPath.FromRaw(characterPrefab.ContentPackage, selectedFile), speciesName))
|
||||
{
|
||||
fileName = Path.GetFileNameWithoutExtension(selectedFile);
|
||||
fileName = IO.Path.GetFileNameWithoutExtension(selectedFile);
|
||||
if (!anims.ContainsKey(fileName))
|
||||
{
|
||||
anims.Add(fileName, a);
|
||||
@@ -277,7 +275,7 @@ namespace Barotrauma
|
||||
|
||||
public static void ClearCache() => allAnimations.Clear();
|
||||
|
||||
public static AnimationParams Create(string fullPath, string speciesName, AnimationType animationType, Type type)
|
||||
public static AnimationParams Create(string fullPath, Identifier speciesName, AnimationType animationType, Type type)
|
||||
{
|
||||
if (type == typeof(HumanWalkParams))
|
||||
{
|
||||
@@ -317,7 +315,7 @@ namespace Barotrauma
|
||||
/// <summary>
|
||||
/// Note: Overrides old animations, if found!
|
||||
/// </summary>
|
||||
public static T Create<T>(string fullPath, string speciesName, AnimationType animationType) where T : AnimationParams, new()
|
||||
public static T Create<T>(string fullPath, Identifier speciesName, AnimationType animationType) where T : AnimationParams, new()
|
||||
{
|
||||
if (animationType == AnimationType.NotDefined)
|
||||
{
|
||||
@@ -328,7 +326,7 @@ namespace Barotrauma
|
||||
anims = new Dictionary<string, AnimationParams>();
|
||||
allAnimations.Add(speciesName, anims);
|
||||
}
|
||||
var fileName = Path.GetFileNameWithoutExtension(fullPath);
|
||||
var fileName = IO.Path.GetFileNameWithoutExtension(fullPath);
|
||||
if (anims.ContainsKey(fileName))
|
||||
{
|
||||
DebugConsole.NewMessage($"[AnimationParams] Removing the old animation of type {animationType}.", Color.Red);
|
||||
@@ -337,10 +335,12 @@ namespace Barotrauma
|
||||
var instance = new T();
|
||||
XElement animationElement = new XElement(GetDefaultFileName(speciesName, animationType), new XAttribute("animationtype", animationType.ToString()));
|
||||
instance.doc = new XDocument(animationElement);
|
||||
instance.UpdatePath(fullPath);
|
||||
var characterPrefab = CharacterPrefab.Prefabs[speciesName];
|
||||
var contentPath = ContentPath.FromRaw(characterPrefab.ContentPackage, fullPath);
|
||||
instance.UpdatePath(contentPath);
|
||||
instance.IsLoaded = instance.Deserialize(animationElement);
|
||||
instance.Save();
|
||||
instance.Load(fullPath, speciesName);
|
||||
instance.Load(contentPath, speciesName);
|
||||
anims.Add(fileName, instance);
|
||||
DebugConsole.NewMessage($"[AnimationParams] New animation file of type {animationType} created.", Color.GhostWhite);
|
||||
return instance;
|
||||
@@ -349,7 +349,7 @@ namespace Barotrauma
|
||||
public bool Serialize() => base.Serialize();
|
||||
public bool Deserialize() => base.Deserialize();
|
||||
|
||||
protected bool Load(string file, string speciesName)
|
||||
protected bool Load(ContentPath file, Identifier speciesName)
|
||||
{
|
||||
if (Load(file))
|
||||
{
|
||||
@@ -359,7 +359,7 @@ namespace Barotrauma
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override void UpdatePath(string newPath)
|
||||
protected override void UpdatePath(ContentPath newPath)
|
||||
{
|
||||
if (SpeciesName == null)
|
||||
{
|
||||
@@ -464,7 +464,8 @@ namespace Barotrauma
|
||||
var copy = new T
|
||||
{
|
||||
IsLoaded = true,
|
||||
doc = new XDocument(doc)
|
||||
doc = new XDocument(doc),
|
||||
Path = Path
|
||||
};
|
||||
copy.Deserialize();
|
||||
copy.Serialize();
|
||||
|
||||
+28
-29
@@ -11,7 +11,7 @@ namespace Barotrauma
|
||||
}
|
||||
public static FishWalkParams GetAnimParams(Character character, string fileName = null)
|
||||
{
|
||||
return Check(character) ? GetAnimParams<FishWalkParams>(character.VariantOf ?? character.SpeciesName, AnimationType.Walk, fileName) : Empty;
|
||||
return Check(character) ? GetAnimParams<FishWalkParams>(character.SpeciesName, AnimationType.Walk, fileName) : Empty;
|
||||
}
|
||||
|
||||
protected static FishWalkParams Empty = new FishWalkParams();
|
||||
@@ -27,7 +27,7 @@ namespace Barotrauma
|
||||
}
|
||||
public static FishRunParams GetAnimParams(Character character, string fileName = null)
|
||||
{
|
||||
return Check(character) ? GetAnimParams<FishRunParams>(character.VariantOf ?? character.SpeciesName, AnimationType.Run, fileName) : Empty;
|
||||
return Check(character) ? GetAnimParams<FishRunParams>(character.SpeciesName, AnimationType.Run, fileName) : Empty;
|
||||
}
|
||||
|
||||
protected static FishRunParams Empty = new FishRunParams();
|
||||
@@ -40,7 +40,7 @@ namespace Barotrauma
|
||||
public static FishSwimFastParams GetDefaultAnimParams(Character character) => GetDefaultAnimParams<FishSwimFastParams>(character, AnimationType.SwimFast);
|
||||
public static FishSwimFastParams GetAnimParams(Character character, string fileName = null)
|
||||
{
|
||||
return GetAnimParams<FishSwimFastParams>(character.VariantOf ?? character.SpeciesName, AnimationType.SwimFast, fileName);
|
||||
return GetAnimParams<FishSwimFastParams>(character.SpeciesName, AnimationType.SwimFast, fileName);
|
||||
}
|
||||
|
||||
public override void StoreSnapshot() => StoreSnapshot<FishSwimFastParams>();
|
||||
@@ -51,7 +51,7 @@ namespace Barotrauma
|
||||
public static FishSwimSlowParams GetDefaultAnimParams(Character character) => GetDefaultAnimParams<FishSwimSlowParams>(character, AnimationType.SwimSlow);
|
||||
public static FishSwimSlowParams GetAnimParams(Character character, string fileName = null)
|
||||
{
|
||||
return GetAnimParams<FishSwimSlowParams>(character.VariantOf ?? character.SpeciesName, AnimationType.SwimSlow, fileName);
|
||||
return GetAnimParams<FishSwimSlowParams>(character.SpeciesName, AnimationType.SwimSlow, fileName);
|
||||
}
|
||||
|
||||
public override void StoreSnapshot() => StoreSnapshot<FishSwimSlowParams>();
|
||||
@@ -69,35 +69,35 @@ namespace Barotrauma
|
||||
return true;
|
||||
}
|
||||
|
||||
[Editable, Serialize(true, true, description: "Should the character be flipped depending on which direction it faces. Should usually be enabled on all characters that have distinctive upper and lower sides.")]
|
||||
[Editable, Serialize(true, IsPropertySaveable.Yes, description: "Should the character be flipped depending on which direction it faces. Should usually be enabled on all characters that have distinctive upper and lower sides.")]
|
||||
public bool Flip { get; set; }
|
||||
|
||||
[Serialize(1f, true, description: "Reduces continuous flipping when the character abruptly changes direction."), Editable]
|
||||
[Serialize(1f, IsPropertySaveable.Yes, description: "Reduces continuous flipping when the character abruptly changes direction."), Editable]
|
||||
public float FlipCooldown { get; set; }
|
||||
|
||||
[Serialize(0.5f, true, description: "How much it takes before the character flips. The timer starts when the character starts to move in the different direction."), Editable]
|
||||
[Serialize(0.5f, IsPropertySaveable.Yes, description: "How much it takes before the character flips. The timer starts when the character starts to move in the different direction."), Editable]
|
||||
public float FlipDelay { get; set; }
|
||||
|
||||
[Serialize(10.0f, true, description: "How much force is used to move the head to the correct position."), Editable(MinValueFloat = 0, MaxValueFloat = 100)]
|
||||
[Serialize(10.0f, IsPropertySaveable.Yes, description: "How much force is used to move the head to the correct position."), Editable(MinValueFloat = 0, MaxValueFloat = 100)]
|
||||
public float HeadMoveForce { get; set; }
|
||||
|
||||
[Serialize(10.0f, true, description: "How much force is used to move the torso to the correct position."), Editable(MinValueFloat = 0, MaxValueFloat = 100)]
|
||||
[Serialize(10.0f, IsPropertySaveable.Yes, description: "How much force is used to move the torso to the correct position."), Editable(MinValueFloat = 0, MaxValueFloat = 100)]
|
||||
public float TorsoMoveForce { get; set; }
|
||||
|
||||
[Serialize(8.0f, true, description: "How much force is used to move the feet to the correct position."), Editable(MinValueFloat = 0, MaxValueFloat = 100)]
|
||||
[Serialize(8.0f, IsPropertySaveable.Yes, description: "How much force is used to move the feet to the correct position."), Editable(MinValueFloat = 0, MaxValueFloat = 100)]
|
||||
public float FootMoveForce { get; set; }
|
||||
|
||||
[Serialize(50.0f, true, description: "How much torque is used to rotate the tail to the correct orientation."), Editable(MinValueFloat = 0, MaxValueFloat = 1000, ValueStep = 1)]
|
||||
[Serialize(50.0f, IsPropertySaveable.Yes, description: "How much torque is used to rotate the tail to the correct orientation."), Editable(MinValueFloat = 0, MaxValueFloat = 1000, ValueStep = 1)]
|
||||
public float TailTorque { get; set; }
|
||||
|
||||
[Serialize(0.0f, true, description: "Optional torque that's constantly applied to legs."), Editable(MinValueFloat = 0, MaxValueFloat = 1000)]
|
||||
[Serialize(0.0f, IsPropertySaveable.Yes, description: "Optional torque that's constantly applied to legs."), Editable(MinValueFloat = 0, MaxValueFloat = 1000)]
|
||||
public float LegTorque { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The angle of the collider when standing (i.e. out of water).
|
||||
/// In degrees.
|
||||
/// </summary>
|
||||
[Serialize(0f, true, description: "The angle of the character's collider when standing."), Editable(MinValueFloat = -360, MaxValueFloat = 360)]
|
||||
[Serialize(0f, IsPropertySaveable.Yes, description: "The angle of the character's collider when standing."), Editable(MinValueFloat = -360, MaxValueFloat = 360)]
|
||||
public float ColliderStandAngle
|
||||
{
|
||||
get => MathHelper.ToDegrees(ColliderStandAngleInRadians);
|
||||
@@ -105,7 +105,7 @@ namespace Barotrauma
|
||||
}
|
||||
public float ColliderStandAngleInRadians { get; private set; }
|
||||
|
||||
[Serialize(null, true), Editable]
|
||||
[Serialize(null, IsPropertySaveable.Yes), Editable]
|
||||
public string FootAngles
|
||||
{
|
||||
get => ParseFootAngles(FootAnglesInRadians);
|
||||
@@ -120,7 +120,7 @@ namespace Barotrauma
|
||||
/// <summary>
|
||||
/// In degrees.
|
||||
/// </summary>
|
||||
[Serialize(float.NaN, true), Editable(-360f, 360f)]
|
||||
[Serialize(float.NaN, IsPropertySaveable.Yes), Editable(-360f, 360f)]
|
||||
public float TailAngle
|
||||
{
|
||||
get => float.IsNaN(TailAngleInRadians) ? float.NaN : MathHelper.ToDegrees(TailAngleInRadians);
|
||||
@@ -137,41 +137,40 @@ namespace Barotrauma
|
||||
|
||||
abstract class FishSwimParams : SwimParams, IFishAnimation
|
||||
{
|
||||
[Serialize(false, true, description: "Instead of linear movement (default), use a wave-like movement. Note: WaveAmplitude and WaveLength don't have any effect on this. It's synced with the movement speed."), Editable]
|
||||
[Serialize(false, IsPropertySaveable.Yes, description: "Instead of linear movement (default), use a wave-like movement. Note: WaveAmplitude and WaveLength don't have any effect on this. It's synced with the movement speed."), Editable]
|
||||
public bool UseSineMovement { get; set; }
|
||||
|
||||
[Editable, Serialize(true, true, description: "Should the character be flipped depending on which direction it faces. Should usually be enabled on all characters that have distinctive upper and lower sides.")]
|
||||
[Editable, Serialize(true, IsPropertySaveable.Yes, description: "Should the character be flipped depending on which direction it faces. Should usually be enabled on all characters that have distinctive upper and lower sides.")]
|
||||
public bool Flip { get; set; }
|
||||
|
||||
[Serialize(1f, true, description: "Reduces continuous flipping when the character abruptly changes direction."), Editable]
|
||||
[Serialize(1f, IsPropertySaveable.Yes, description: "Reduces continuous flipping when the character abruptly changes direction."), Editable]
|
||||
public float FlipCooldown { get; set; }
|
||||
|
||||
[Serialize(0.5f, true, description: "How much it takes before the character flips. The timer starts when the character starts to move in the different direction."), Editable]
|
||||
[Serialize(0.5f, IsPropertySaveable.Yes, description: "How much it takes before the character flips. The timer starts when the character starts to move in the different direction."), Editable]
|
||||
public float FlipDelay { get; set; }
|
||||
|
||||
[Editable, Serialize(true, true, description: "If enabled, the character will simply be mirrored horizontally when it wants to turn around. If disabled, it will rotate itself to face the other direction.")]
|
||||
[Editable, Serialize(true, IsPropertySaveable.Yes, description: "If enabled, the character will simply be mirrored horizontally when it wants to turn around. If disabled, it will rotate itself to face the other direction.")]
|
||||
public bool Mirror { get; set; }
|
||||
|
||||
[Editable, Serialize(true, true, description: "Disabling this will make mirroring instantaneous.")]
|
||||
[Editable, Serialize(true, IsPropertySaveable.Yes, description: "Disabling this will make mirroring instantaneous.")]
|
||||
public bool MirrorLerp { get; set; }
|
||||
|
||||
[Serialize(5f, true), Editable]
|
||||
[Serialize(5f, IsPropertySaveable.Yes), Editable]
|
||||
public float WaveAmplitude { get; set; }
|
||||
|
||||
[Serialize(10.0f, true), Editable]
|
||||
[Serialize(10.0f, IsPropertySaveable.Yes), Editable]
|
||||
public float WaveLength { get; set; }
|
||||
|
||||
[Editable, Serialize(true, true, description: "Should the character face towards the direction it's heading.")]
|
||||
[Editable, Serialize(true, IsPropertySaveable.Yes, description: "Should the character face towards the direction it's heading.")]
|
||||
public bool RotateTowardsMovement { get; set; }
|
||||
|
||||
[Serialize(50.0f, true, description: "How much torque is used to rotate the tail to the correct orientation."), Editable(MinValueFloat = 0, MaxValueFloat = 2000, ValueStep = 1)]
|
||||
[Serialize(50.0f, IsPropertySaveable.Yes, description: "How much torque is used to rotate the tail to the correct orientation."), Editable(MinValueFloat = 0, MaxValueFloat = 2000, ValueStep = 1)]
|
||||
public float TailTorque { get; set; }
|
||||
|
||||
[Serialize(1f, true, description: "Multiplier applied based on the angle difference between the tail and the main limb. Increasing the value prevents snake-like characters from getting tangled on themselves. Default = 1 (no boost)"), Editable(MinValueFloat = 1, MaxValueFloat = 100)]
|
||||
[Serialize(1f, IsPropertySaveable.Yes, description: "Multiplier applied based on the angle difference between the tail and the main limb. Increasing the value prevents snake-like characters from getting tangled on themselves. Default = 1 (no boost)"), Editable(MinValueFloat = 1, MaxValueFloat = 100)]
|
||||
public float TailTorqueMultiplier { get; set; }
|
||||
|
||||
|
||||
[Serialize(null, true), Editable]
|
||||
[Serialize(null, IsPropertySaveable.Yes), Editable]
|
||||
public string FootAngles
|
||||
{
|
||||
get => ParseFootAngles(FootAnglesInRadians);
|
||||
@@ -186,7 +185,7 @@ namespace Barotrauma
|
||||
/// <summary>
|
||||
/// In degrees.
|
||||
/// </summary>
|
||||
[Serialize(float.NaN, true), Editable(-360f, 360f)]
|
||||
[Serialize(float.NaN, IsPropertySaveable.Yes), Editable(-360f, 360f)]
|
||||
public float TailAngle
|
||||
{
|
||||
get => float.IsNaN(TailAngleInRadians) ? float.NaN : MathHelper.ToDegrees(TailAngleInRadians);
|
||||
|
||||
+24
-24
@@ -26,13 +26,13 @@ namespace Barotrauma
|
||||
|
||||
class HumanCrouchParams : HumanGroundedParams
|
||||
{
|
||||
[Serialize(0.0f, true, description: "How much lower the character's head and torso move when stationary."), Editable(MinValueFloat = 0, MaxValueFloat = 2, DecimalCount = 2)]
|
||||
[Serialize(0.0f, IsPropertySaveable.Yes, description: "How much lower the character's head and torso move when stationary."), Editable(MinValueFloat = 0, MaxValueFloat = 2, DecimalCount = 2)]
|
||||
public float MoveDownAmountWhenStationary { get; set; }
|
||||
|
||||
[Serialize(0.0f, true), Editable(-360f, 360f)]
|
||||
[Serialize(0.0f, IsPropertySaveable.Yes), Editable(-360f, 360f)]
|
||||
public float ExtraHeadAngleWhenStationary { get; set; }
|
||||
|
||||
[Serialize(0.0f, true), Editable(-360f, 360f)]
|
||||
[Serialize(0.0f, IsPropertySaveable.Yes), Editable(-360f, 360f)]
|
||||
public float ExtraTorsoAngleWhenStationary { get; set; }
|
||||
|
||||
public static HumanCrouchParams GetDefaultAnimParams(Character character) => GetDefaultAnimParams<HumanCrouchParams>(character, AnimationType.Crouch);
|
||||
@@ -69,25 +69,25 @@ namespace Barotrauma
|
||||
|
||||
abstract class HumanSwimParams : SwimParams, IHumanAnimation
|
||||
{
|
||||
[Serialize(0.5f, true), Editable(DecimalCount = 2)]
|
||||
[Serialize(0.5f, IsPropertySaveable.Yes), Editable(DecimalCount = 2)]
|
||||
public float LegMoveAmount { get; set; }
|
||||
|
||||
[Serialize(5.0f, true), Editable]
|
||||
[Serialize(5.0f, IsPropertySaveable.Yes), Editable]
|
||||
public float LegCycleLength { get; set; }
|
||||
|
||||
[Serialize("0.5, 0.1", true), Editable(DecimalCount = 2)]
|
||||
[Serialize("0.5, 0.1", IsPropertySaveable.Yes), Editable(DecimalCount = 2)]
|
||||
public Vector2 HandMoveAmount { get; set; }
|
||||
|
||||
[Serialize(5.0f, true), Editable]
|
||||
[Serialize(5.0f, IsPropertySaveable.Yes), Editable]
|
||||
public float HandCycleSpeed { get; set; }
|
||||
|
||||
[Serialize("0.0, 0.0", true), Editable(DecimalCount = 2)]
|
||||
[Serialize("0.0, 0.0", IsPropertySaveable.Yes), Editable(DecimalCount = 2)]
|
||||
public Vector2 HandMoveOffset { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// In degrees.
|
||||
/// </summary>
|
||||
[Serialize(0.0f, true), Editable(-360f, 360f)]
|
||||
[Serialize(0.0f, IsPropertySaveable.Yes), Editable(-360f, 360f)]
|
||||
public float FootAngle
|
||||
{
|
||||
get => MathHelper.ToDegrees(FootAngleInRadians);
|
||||
@@ -98,34 +98,34 @@ namespace Barotrauma
|
||||
}
|
||||
public float FootAngleInRadians { get; private set; }
|
||||
|
||||
[Serialize(1f, true, description: "How much force is used to move the arms."), Editable(MinValueFloat = 0, MaxValueFloat = 20, DecimalCount = 2)]
|
||||
[Serialize(1f, IsPropertySaveable.Yes, description: "How much force is used to move the arms."), Editable(MinValueFloat = 0, MaxValueFloat = 20, DecimalCount = 2)]
|
||||
public float ArmMoveStrength { get; set; }
|
||||
|
||||
[Serialize(1f, true, description: "How much force is used to move the hands."), Editable(MinValueFloat = 0, MaxValueFloat = 10, DecimalCount = 2)]
|
||||
[Serialize(1f, IsPropertySaveable.Yes, description: "How much force is used to move the hands."), Editable(MinValueFloat = 0, MaxValueFloat = 10, DecimalCount = 2)]
|
||||
public float HandMoveStrength { get; set; }
|
||||
}
|
||||
|
||||
abstract class HumanGroundedParams : GroundedMovementParams, IHumanAnimation
|
||||
{
|
||||
[Serialize(0.3f, true, description: "How much force is used to force the character upright."), Editable(MinValueFloat = 0, MaxValueFloat = 1, DecimalCount = 2)]
|
||||
[Serialize(0.3f, IsPropertySaveable.Yes, description: "How much force is used to force the character upright."), Editable(MinValueFloat = 0, MaxValueFloat = 1, DecimalCount = 2)]
|
||||
public float GetUpForce { get; set; }
|
||||
|
||||
[Serialize(0.25f, true, description: "How much the character's head leans forwards when moving."), Editable(DecimalCount = 2)]
|
||||
[Serialize(0.25f, IsPropertySaveable.Yes, description: "How much the character's head leans forwards when moving."), Editable(DecimalCount = 2)]
|
||||
public float HeadLeanAmount { get; set; }
|
||||
|
||||
[Serialize(0.25f, true, description: "How much the character's torso leans forwards when moving."), Editable(DecimalCount = 2)]
|
||||
[Serialize(0.25f, IsPropertySaveable.Yes, description: "How much the character's torso leans forwards when moving."), Editable(DecimalCount = 2)]
|
||||
public float TorsoLeanAmount { get; set; }
|
||||
|
||||
[Serialize(15.0f, true, description: "How much force is used to move the feet to the correct position."), Editable(MinValueFloat = 0, MaxValueFloat = 100)]
|
||||
[Serialize(15.0f, IsPropertySaveable.Yes, description: "How much force is used to move the feet to the correct position."), Editable(MinValueFloat = 0, MaxValueFloat = 100)]
|
||||
public float FootMoveStrength { get; set; }
|
||||
|
||||
[Serialize(0f, true, description: "How much the horizontal difference of waist and the foot positions has an effect to lifting the foot."), Editable(DecimalCount = 2, ValueStep = 0.1f, MinValueFloat = 0f, MaxValueFloat = 1f)]
|
||||
[Serialize(0f, IsPropertySaveable.Yes, description: "How much the horizontal difference of waist and the foot positions has an effect to lifting the foot."), Editable(DecimalCount = 2, ValueStep = 0.1f, MinValueFloat = 0f, MaxValueFloat = 1f)]
|
||||
public float FootLiftHorizontalFactor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// In degrees.
|
||||
/// </summary>
|
||||
[Serialize(0.0f, true), Editable(-360f, 360f)]
|
||||
[Serialize(0.0f, IsPropertySaveable.Yes), Editable(-360f, 360f)]
|
||||
public float FootAngle
|
||||
{
|
||||
get => MathHelper.ToDegrees(FootAngleInRadians);
|
||||
@@ -136,25 +136,25 @@ namespace Barotrauma
|
||||
}
|
||||
public float FootAngleInRadians { get; private set; }
|
||||
|
||||
[Serialize("0.0, 0.0", true, description: "Added to the calculated foot positions, e.g. a value of {-1.0, 0.0f} would make the character \"drag\" their feet one unit behind them."), Editable(DecimalCount = 2)]
|
||||
[Serialize("0.0, 0.0", IsPropertySaveable.Yes, description: "Added to the calculated foot positions, e.g. a value of {-1.0, 0.0f} would make the character \"drag\" their feet one unit behind them."), Editable(DecimalCount = 2)]
|
||||
public Vector2 FootMoveOffset { get; set; }
|
||||
|
||||
[Serialize(10.0f, true, description: "How much torque is used to bend the characters legs when taking a step."), Editable(MinValueFloat = 0, MaxValueFloat = 100)]
|
||||
[Serialize(10.0f, IsPropertySaveable.Yes, description: "How much torque is used to bend the characters legs when taking a step."), Editable(MinValueFloat = 0, MaxValueFloat = 100)]
|
||||
public float LegBendTorque { get; set; }
|
||||
|
||||
[Serialize("0.4, 0.15", true, description: "How much the hands move along each axis."), Editable(DecimalCount = 2)]
|
||||
[Serialize("0.4, 0.15", IsPropertySaveable.Yes, description: "How much the hands move along each axis."), Editable(DecimalCount = 2)]
|
||||
public Vector2 HandMoveAmount { get; set; }
|
||||
|
||||
[Serialize("-0.15, 0.0", true, description: "Added to the calculated hand positions, e.g. a value of {-1.0, 0.0f} would make the character \"drag\" their hands one unit behind them."), Editable(DecimalCount = 2)]
|
||||
[Serialize("-0.15, 0.0", IsPropertySaveable.Yes, description: "Added to the calculated hand positions, e.g. a value of {-1.0, 0.0f} would make the character \"drag\" their hands one unit behind them."), Editable(DecimalCount = 2)]
|
||||
public Vector2 HandMoveOffset { get; set; }
|
||||
|
||||
[Serialize(-1.0f, true, description: "The position of the hands is clamped below this (relative to the position of the character's torso)."), Editable(DecimalCount = 2)]
|
||||
[Serialize(-1.0f, IsPropertySaveable.Yes, description: "The position of the hands is clamped below this (relative to the position of the character's torso)."), Editable(DecimalCount = 2)]
|
||||
public float HandClampY { get; set; }
|
||||
|
||||
[Serialize(1f, true, description: "How much force is used to move the arms."), Editable(MinValueFloat = 0, MaxValueFloat = 10, DecimalCount = 2)]
|
||||
[Serialize(1f, IsPropertySaveable.Yes, description: "How much force is used to move the arms."), Editable(MinValueFloat = 0, MaxValueFloat = 10, DecimalCount = 2)]
|
||||
public float ArmMoveStrength { get; set; }
|
||||
|
||||
[Serialize(1f, true, description: "How much force is used to move the hands."), Editable(MinValueFloat = 0, MaxValueFloat = 10, DecimalCount = 2)]
|
||||
[Serialize(1f, IsPropertySaveable.Yes, description: "How much force is used to move the hands."), Editable(MinValueFloat = 0, MaxValueFloat = 10, DecimalCount = 2)]
|
||||
public float HandMoveStrength { get; set; }
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Xml.Linq;
|
||||
using System.Xml;
|
||||
using System.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
using System.Collections.Immutable;
|
||||
#if CLIENT
|
||||
using SoundType = Barotrauma.CharacterSound.SoundType;
|
||||
#endif
|
||||
@@ -16,94 +17,94 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
class CharacterParams : EditableParams
|
||||
{
|
||||
[Serialize("", true), Editable]
|
||||
public string SpeciesName { get; private set; }
|
||||
[Serialize("", IsPropertySaveable.Yes), Editable]
|
||||
public Identifier SpeciesName { get; private set; }
|
||||
|
||||
[Serialize("", true, description: "If the creature is a variant that needs to use a pre-existing translation."), Editable]
|
||||
[Serialize("", IsPropertySaveable.Yes, description: "If the creature is a variant that needs to use a pre-existing translation."), Editable]
|
||||
public string SpeciesTranslationOverride { get; private set; }
|
||||
|
||||
[Serialize("", true, description: "If the display name is not defined, the game first tries to find the translated name. If that is not found, the species name will be used."), Editable]
|
||||
[Serialize("", IsPropertySaveable.Yes, description: "If the display name is not defined, the game first tries to find the translated name. If that is not found, the species name will be used."), Editable]
|
||||
public string DisplayName { get; private set; }
|
||||
|
||||
[Serialize("", true, description: "If defined, different species of the same group are considered like the characters of the same species by the AI."), Editable]
|
||||
public string Group { get; private set; }
|
||||
[Serialize("", IsPropertySaveable.Yes, description: "If defined, different species of the same group are considered like the characters of the same species by the AI."), Editable]
|
||||
public Identifier Group { get; private set; }
|
||||
|
||||
[Serialize(false, true), Editable(ReadOnly = true)]
|
||||
[Serialize(false, IsPropertySaveable.Yes), Editable(ReadOnly = true)]
|
||||
public bool Humanoid { get; private set; }
|
||||
|
||||
[Serialize(false, true), Editable(ReadOnly = true)]
|
||||
[Serialize(false, IsPropertySaveable.Yes), Editable(ReadOnly = true)]
|
||||
public bool HasInfo { get; private set; }
|
||||
|
||||
[Serialize(false, true, description: "Can the creature interact with items?"), Editable]
|
||||
[Serialize(false, IsPropertySaveable.Yes, description: "Can the creature interact with items?"), Editable]
|
||||
public bool CanInteract { get; private set; }
|
||||
|
||||
[Serialize(false, true), Editable]
|
||||
[Serialize(false, IsPropertySaveable.Yes), Editable]
|
||||
public bool Husk { get; private set; }
|
||||
|
||||
[Serialize(false, true), Editable]
|
||||
[Serialize(false, IsPropertySaveable.Yes), Editable]
|
||||
public bool UseHuskAppendage { get; private set; }
|
||||
|
||||
[Serialize(false, true), Editable]
|
||||
[Serialize(false, IsPropertySaveable.Yes), Editable]
|
||||
public bool NeedsAir { get; set; }
|
||||
|
||||
[Serialize(false, true, description: "Can the creature live without water or does it die on dry land?"), Editable]
|
||||
[Serialize(false, IsPropertySaveable.Yes, description: "Can the creature live without water or does it die on dry land?"), Editable]
|
||||
public bool NeedsWater { get; set; }
|
||||
|
||||
[Serialize(false, false), Editable]
|
||||
[Serialize(false, IsPropertySaveable.No), Editable]
|
||||
public bool CanSpeak { get; set; }
|
||||
|
||||
[Serialize(false, true), Editable]
|
||||
[Serialize(false, IsPropertySaveable.Yes), Editable]
|
||||
public bool UseBossHealthBar { get; private set; }
|
||||
|
||||
[Serialize(100f, true, description: "How much noise the character makes when moving?"), Editable(minValue: 0f, maxValue: 100000f)]
|
||||
[Serialize(100f, IsPropertySaveable.Yes, description: "How much noise the character makes when moving?"), Editable(minValue: 0f, maxValue: 100000f)]
|
||||
public float Noise { get; set; }
|
||||
|
||||
[Serialize(100f, true, description: "How visible the character is?"), Editable(minValue: 0f, maxValue: 100000f)]
|
||||
[Serialize(100f, IsPropertySaveable.Yes, description: "How visible the character is?"), Editable(minValue: 0f, maxValue: 100000f)]
|
||||
public float Visibility { get; set; }
|
||||
|
||||
[Serialize("blood", true), Editable]
|
||||
[Serialize("blood", IsPropertySaveable.Yes), Editable]
|
||||
public string BloodDecal { get; private set; }
|
||||
|
||||
[Serialize("blooddrop", true), Editable]
|
||||
[Serialize("blooddrop", IsPropertySaveable.Yes), Editable]
|
||||
public string BleedParticleAir { get; private set; }
|
||||
|
||||
[Serialize("waterblood", true), Editable]
|
||||
[Serialize("waterblood", IsPropertySaveable.Yes), Editable]
|
||||
public string BleedParticleWater { get; private set; }
|
||||
|
||||
[Serialize(1f, true), Editable]
|
||||
[Serialize(1f, IsPropertySaveable.Yes), Editable]
|
||||
public float BleedParticleMultiplier { get; private set; }
|
||||
|
||||
[Serialize(true, true, description: "Can the creature eat bodies? Used by player controlled creatures to allow them to eat. Currently applicable only to non-humanoids. To allow an AI controller to eat, just add an ai target with the state \"eat\""), Editable]
|
||||
|
||||
[Serialize(true, IsPropertySaveable.Yes, description: "Can the creature eat bodies? Used by player controlled creatures to allow them to eat. Currently applicable only to non-humanoids. To allow an AI controller to eat, just add an ai target with the state \"eat\""), Editable]
|
||||
public bool CanEat { get; set; }
|
||||
|
||||
[Serialize(10f, true, description: "How effectively/easily the character eats other characters. Affects the forces, the amount of particles, and the time required before the target is eaten away"), Editable(MinValueFloat = 1, MaxValueFloat = 1000, ValueStep = 1)]
|
||||
[Serialize(10f, IsPropertySaveable.Yes, description: "How effectively/easily the character eats other characters. Affects the forces, the amount of particles, and the time required before the target is eaten away"), Editable(MinValueFloat = 1, MaxValueFloat = 1000, ValueStep = 1)]
|
||||
public float EatingSpeed { get; set; }
|
||||
|
||||
[Serialize(true, true), Editable]
|
||||
[Serialize(true, IsPropertySaveable.Yes), Editable]
|
||||
public bool UsePathFinding { get; set; }
|
||||
|
||||
[Serialize(1f, true, "Decreases the intensive path finding call frequency. Set to a lower value for insignificant creatures to improve performance."), Editable(minValue: 0f, maxValue: 1f)]
|
||||
[Serialize(1f, IsPropertySaveable.Yes, "Decreases the intensive path finding call frequency. Set to a lower value for insignificant creatures to improve performance."), Editable(minValue: 0f, maxValue: 1f)]
|
||||
public float PathFinderPriority { get; set; }
|
||||
|
||||
[Serialize(false, true), Editable]
|
||||
[Serialize(false, IsPropertySaveable.Yes), Editable]
|
||||
public bool HideInSonar { get; set; }
|
||||
|
||||
[Serialize(false, true), Editable]
|
||||
[Serialize(false, IsPropertySaveable.Yes), Editable]
|
||||
public bool HideInThermalGoggles { get; set; }
|
||||
|
||||
[Serialize(0f, true), Editable]
|
||||
[Serialize(0f, IsPropertySaveable.Yes), Editable]
|
||||
public float SonarDisruption { get; set; }
|
||||
|
||||
[Serialize(0f, true), Editable]
|
||||
[Serialize(0f, IsPropertySaveable.Yes), Editable]
|
||||
public float DistantSonarRange { get; set; }
|
||||
|
||||
[Serialize(25000f, true, "If the character is farther than this (in pixels) from the sub and the players, it will be disabled. The halved value is used for triggering simple physics where the ragdoll is disabled and only the main collider is updated."), Editable(MinValueFloat = 10000f, MaxValueFloat = 100000f)]
|
||||
[Serialize(25000f, IsPropertySaveable.Yes, "If the character is farther than this (in pixels) from the sub and the players, it will be disabled. The halved value is used for triggering simple physics where the ragdoll is disabled and only the main collider is updated."), Editable(MinValueFloat = 10000f, MaxValueFloat = 100000f)]
|
||||
public float DisableDistance { get; set; }
|
||||
|
||||
[Serialize(10f, true, "How frequent the recurring idle and attack sounds are?"), Editable(MinValueFloat = 1f, MaxValueFloat = 100f)]
|
||||
[Serialize(10f, IsPropertySaveable.Yes, "How frequent the recurring idle and attack sounds are?"), Editable(MinValueFloat = 1f, MaxValueFloat = 100f)]
|
||||
public float SoundInterval { get; set; }
|
||||
|
||||
public readonly string File;
|
||||
public readonly CharacterFile File;
|
||||
|
||||
public XDocument VariantFile { get; private set; }
|
||||
|
||||
@@ -116,7 +117,7 @@ namespace Barotrauma
|
||||
public HealthParams Health { get; private set; }
|
||||
public AIParams AI { get; private set; }
|
||||
|
||||
public CharacterParams(string file)
|
||||
public CharacterParams(CharacterFile file)
|
||||
{
|
||||
File = file;
|
||||
Load();
|
||||
@@ -124,45 +125,58 @@ namespace Barotrauma
|
||||
|
||||
protected override string GetName() => "Character Config File";
|
||||
|
||||
public override XElement MainElement => doc.Root.IsOverride() ? doc.Root.FirstElement() : doc.Root;
|
||||
public override ContentXElement MainElement => base.MainElement.IsOverride() ? base.MainElement.FirstElement() : base.MainElement;
|
||||
|
||||
public static XElement CreateVariantXml(XElement selfXml, XElement parentXml)
|
||||
{
|
||||
XElement newXml = selfXml.CreateVariantXML(parentXml);
|
||||
|
||||
XElement selfAi = selfXml.GetChildElement("ai");
|
||||
XElement parentAi = parentXml.GetChildElement("ai");
|
||||
|
||||
if (parentAi is null || parentAi.Elements().None()
|
||||
|| selfAi is null || selfAi.Elements().None())
|
||||
{
|
||||
return newXml;
|
||||
}
|
||||
|
||||
//discard the inherited targets, just keep the new ones
|
||||
var finalAiElement = newXml.GetChildElement("ai");
|
||||
foreach (var finalTarget in finalAiElement!.Elements().ToArray())
|
||||
{
|
||||
finalTarget.Remove();
|
||||
}
|
||||
|
||||
foreach (var inheritorTarget in selfAi.Elements())
|
||||
{
|
||||
finalAiElement.Add(new XElement(inheritorTarget));
|
||||
}
|
||||
|
||||
return newXml;
|
||||
}
|
||||
|
||||
public bool Load()
|
||||
{
|
||||
bool success = base.Load(File);
|
||||
if (doc.Root.IsCharacterVariant())
|
||||
UpdatePath(File.Path);
|
||||
doc = XMLExtensions.TryLoadXml(Path);
|
||||
Identifier variantOf = MainElement.VariantOf();
|
||||
if (!variantOf.IsEmpty)
|
||||
{
|
||||
VariantFile = doc;
|
||||
var original = CharacterPrefab.FindBySpeciesName(doc.Root.GetAttributeString("inherit", string.Empty));
|
||||
success = Load(original.FilePath);
|
||||
CreateSubParams();
|
||||
TryLoadOverride(this, VariantFile.Root, SerializableProperties);
|
||||
foreach (XElement subElement in VariantFile.Root.Elements())
|
||||
{
|
||||
var matchingParams = SubParams.FirstOrDefault(p => p.Name.Equals(subElement.Name.ToString(), StringComparison.OrdinalIgnoreCase));
|
||||
if (matchingParams != null)
|
||||
{
|
||||
TryLoadOverride(matchingParams, subElement, matchingParams.SerializableProperties);
|
||||
// TODO: Make recursive? In practice we don't have to go deeper than this, but the implementation would be a lot cleaner with recursion.
|
||||
foreach (XElement subSubElement in subElement.Elements())
|
||||
{
|
||||
if (subSubElement.Name.ToString().Equals("item", StringComparison.OrdinalIgnoreCase)) { continue; }
|
||||
var matchingSubParams = matchingParams.SubParams.FirstOrDefault(p => p.Name.Equals(subSubElement.Name.ToString(), StringComparison.OrdinalIgnoreCase));
|
||||
if (matchingSubParams != null)
|
||||
{
|
||||
TryLoadOverride(matchingSubParams, subSubElement, matchingSubParams.SerializableProperties);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return success;
|
||||
#warning TODO: determine that CreateVariantXML is equipped to do this
|
||||
XElement newRoot = CreateVariantXml(MainElement, CharacterPrefab.FindBySpeciesName(variantOf).ConfigElement);
|
||||
var oldElement = MainElement;
|
||||
var parentElement = (XContainer)oldElement.Parent ?? doc; oldElement.Remove(); parentElement.Add(newRoot);
|
||||
}
|
||||
if (string.IsNullOrEmpty(SpeciesName) && MainElement != null)
|
||||
IsLoaded = Deserialize(MainElement);
|
||||
OriginalElement = new XElement(MainElement).FromPackage(Path.ContentPackage);
|
||||
if (SpeciesName.IsEmpty && MainElement != null)
|
||||
{
|
||||
//backwards compatibility
|
||||
SpeciesName = MainElement.GetAttributeString("name", "");
|
||||
SpeciesName = MainElement.GetAttributeIdentifier("name", "");
|
||||
}
|
||||
CreateSubParams();
|
||||
return success;
|
||||
return IsLoaded;
|
||||
}
|
||||
|
||||
public bool Save(string fileNameWithoutExtension = null)
|
||||
@@ -189,7 +203,7 @@ namespace Barotrauma
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool CompareGroup(string group) => !string.IsNullOrWhiteSpace(group) && !string.IsNullOrWhiteSpace(Group) && group.Equals(Group, StringComparison.OrdinalIgnoreCase);
|
||||
public bool CompareGroup(Identifier group) => group != Identifier.Empty && Group != Identifier.Empty && group == Group;
|
||||
|
||||
protected void CreateSubParams()
|
||||
{
|
||||
@@ -239,26 +253,14 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private void TryLoadOverride(object parentObject, XElement element, Dictionary<string, SerializableProperty> properties)
|
||||
{
|
||||
foreach (var property in properties)
|
||||
{
|
||||
var matchingAttribute = element.GetAttribute(property.Key);
|
||||
if (matchingAttribute != null)
|
||||
{
|
||||
property.Value.TrySetValue(parentObject, matchingAttribute.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool Deserialize(XElement element = null, bool alsoChildren = true, bool recursive = true, bool loadDefaultValues = true)
|
||||
{
|
||||
if (base.Deserialize(element))
|
||||
{
|
||||
//backwards compatibility
|
||||
if (string.IsNullOrEmpty(SpeciesName))
|
||||
if (SpeciesName.IsEmpty)
|
||||
{
|
||||
SpeciesName = element.GetAttributeString("name", "[NAME NOT GIVEN]");
|
||||
SpeciesName = element.GetAttributeIdentifier("name", "[NAME NOT GIVEN]");
|
||||
}
|
||||
if (alsoChildren)
|
||||
{
|
||||
@@ -300,9 +302,9 @@ namespace Barotrauma
|
||||
}
|
||||
#endif
|
||||
|
||||
public bool AddSound() => TryAddSubParam(new XElement("sound"), (e, c) => new SoundParams(e, c), out _, Sounds);
|
||||
public bool AddSound() => TryAddSubParam(CreateElement("sound"), (e, c) => new SoundParams(e, c), out _, Sounds);
|
||||
|
||||
public void AddInventory() => TryAddSubParam(new XElement("inventory", new XElement("item")), (e, c) => new InventoryParams(e, c), out _, Inventories);
|
||||
public void AddInventory() => TryAddSubParam(CreateElement("inventory", new XElement("item")), (e, c) => new InventoryParams(e, c), out _, Inventories);
|
||||
|
||||
public void AddBloodEmitter() => AddEmitter("bloodemitter");
|
||||
public void AddGibEmitter() => AddEmitter("gibemitter");
|
||||
@@ -313,13 +315,13 @@ namespace Barotrauma
|
||||
switch (type)
|
||||
{
|
||||
case "gibemitter":
|
||||
TryAddSubParam(new XElement(type), (e, c) => new ParticleParams(e, c), out _, GibEmitters);
|
||||
TryAddSubParam(CreateElement(type), (e, c) => new ParticleParams(e, c), out _, GibEmitters);
|
||||
break;
|
||||
case "bloodemitter":
|
||||
TryAddSubParam(new XElement(type), (e, c) => new ParticleParams(e, c), out _, BloodEmitters);
|
||||
TryAddSubParam(CreateElement(type), (e, c) => new ParticleParams(e, c), out _, BloodEmitters);
|
||||
break;
|
||||
case "damageemitter":
|
||||
TryAddSubParam(new XElement(type), (e, c) => new ParticleParams(e, c), out _, DamageEmitters);
|
||||
TryAddSubParam(CreateElement(type), (e, c) => new ParticleParams(e, c), out _, DamageEmitters);
|
||||
break;
|
||||
default: throw new NotImplementedException(type);
|
||||
}
|
||||
@@ -342,7 +344,7 @@ namespace Barotrauma
|
||||
return true;
|
||||
}
|
||||
|
||||
protected bool TryAddSubParam<T>(XElement element, Func<XElement, CharacterParams, T> constructor, out T subParam, IList<T> collection = null, Func<IList<T>, bool> filter = null) where T : SubParam
|
||||
protected bool TryAddSubParam<T>(ContentXElement element, Func<ContentXElement, CharacterParams, T> constructor, out T subParam, IList<T> collection = null, Func<IList<T>, bool> filter = null) where T : SubParam
|
||||
{
|
||||
subParam = constructor(element, this);
|
||||
if (collection != null && filter != null)
|
||||
@@ -360,24 +362,39 @@ namespace Barotrauma
|
||||
{
|
||||
public override string Name => "Sound";
|
||||
|
||||
[Serialize("", true), Editable]
|
||||
[Serialize("", IsPropertySaveable.Yes), Editable]
|
||||
public string File { get; private set; }
|
||||
|
||||
#if CLIENT
|
||||
[Serialize(SoundType.Idle, true), Editable]
|
||||
[Serialize(SoundType.Idle, IsPropertySaveable.Yes), Editable]
|
||||
public SoundType State { get; private set; }
|
||||
#endif
|
||||
|
||||
[Serialize(1000f, true), Editable(minValue: 0f, maxValue: 10000f)]
|
||||
[Serialize(1000f, IsPropertySaveable.Yes), Editable(minValue: 0f, maxValue: 10000f)]
|
||||
public float Range { get; private set; }
|
||||
|
||||
[Serialize(1.0f, true), Editable(minValue: 0f, maxValue: 2.0f)]
|
||||
[Serialize(1.0f, IsPropertySaveable.Yes), Editable(minValue: 0f, maxValue: 2.0f)]
|
||||
public float Volume { get; private set; }
|
||||
|
||||
[Serialize(Gender.None, true, description: "Is the sound gender specific?"), Editable()]
|
||||
public Gender Gender { get; private set; }
|
||||
[Serialize("", IsPropertySaveable.Yes, description: "Which tags are required for this sound to play?"), Editable()]
|
||||
public string Tags
|
||||
{
|
||||
get { return string.Join(',', TagSet); }
|
||||
private set { TagSet = value.Split(',').ToIdentifiers().ToImmutableHashSet(); }
|
||||
}
|
||||
|
||||
public SoundParams(XElement element, CharacterParams character) : base(element, character) { }
|
||||
public ImmutableHashSet<Identifier> TagSet { get; private set; }
|
||||
|
||||
public SoundParams(ContentXElement element, CharacterParams character) : base(element, character)
|
||||
{
|
||||
HashSet<Identifier> tags = TagSet.ToHashSet();
|
||||
Identifier genderFallback = element.GetAttributeIdentifier("gender", "");
|
||||
if (genderFallback != Identifier.Empty && genderFallback != "None")
|
||||
{
|
||||
tags.Add(genderFallback);
|
||||
}
|
||||
TagSet = tags.ToImmutableHashSet();
|
||||
}
|
||||
}
|
||||
|
||||
public class ParticleParams : SubParam
|
||||
@@ -395,83 +412,83 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
[Serialize("", true), Editable]
|
||||
[Serialize("", IsPropertySaveable.Yes), Editable]
|
||||
public string Particle { get; set; }
|
||||
|
||||
[Serialize(0f, true), Editable(-360f, 360f, decimals: 0)]
|
||||
[Serialize(0f, IsPropertySaveable.Yes), Editable(-360f, 360f, decimals: 0)]
|
||||
public float AngleMin { get; private set; }
|
||||
|
||||
[Serialize(0f, true), Editable(-360f, 360f, decimals: 0)]
|
||||
[Serialize(0f, IsPropertySaveable.Yes), Editable(-360f, 360f, decimals: 0)]
|
||||
public float AngleMax { get; private set; }
|
||||
|
||||
[Serialize(1.0f, true), Editable(0f, 100f, decimals: 2)]
|
||||
[Serialize(1.0f, IsPropertySaveable.Yes), Editable(0f, 100f, decimals: 2)]
|
||||
public float ScaleMin { get; private set; }
|
||||
|
||||
[Serialize(1.0f, true), Editable(0f, 100f, decimals: 2)]
|
||||
[Serialize(1.0f, IsPropertySaveable.Yes), Editable(0f, 100f, decimals: 2)]
|
||||
public float ScaleMax { get; private set; }
|
||||
|
||||
[Serialize(0f, true), Editable(0f, 10000f, decimals: 0)]
|
||||
[Serialize(0f, IsPropertySaveable.Yes), Editable(0f, 10000f, decimals: 0)]
|
||||
public float VelocityMin { get; private set; }
|
||||
|
||||
[Serialize(0f, true), Editable(0f, 10000f, decimals: 0)]
|
||||
[Serialize(0f, IsPropertySaveable.Yes), Editable(0f, 10000f, decimals: 0)]
|
||||
public float VelocityMax { get; private set; }
|
||||
|
||||
[Serialize(0f, true), Editable(0f, 100f, decimals: 2)]
|
||||
[Serialize(0f, IsPropertySaveable.Yes), Editable(0f, 100f, decimals: 2)]
|
||||
public float EmitInterval { get; private set; }
|
||||
|
||||
[Serialize(0, true), Editable(0, 1000)]
|
||||
[Serialize(0, IsPropertySaveable.Yes), Editable(0, 1000)]
|
||||
public int ParticlesPerSecond { get; private set; }
|
||||
|
||||
[Serialize(0, true), Editable(0, 1000)]
|
||||
[Serialize(0, IsPropertySaveable.Yes), Editable(0, 1000)]
|
||||
public int ParticleAmount { get; private set; }
|
||||
|
||||
[Serialize(false, true), Editable]
|
||||
[Serialize(false, IsPropertySaveable.Yes), Editable]
|
||||
public bool HighQualityCollisionDetection { get; private set; }
|
||||
|
||||
[Serialize(false, true), Editable]
|
||||
[Serialize(false, IsPropertySaveable.Yes), Editable]
|
||||
public bool CopyEntityAngle { get; private set; }
|
||||
|
||||
public ParticleParams(XElement element, CharacterParams character) : base(element, character) { }
|
||||
public ParticleParams(ContentXElement element, CharacterParams character) : base(element, character) { }
|
||||
}
|
||||
|
||||
public class HealthParams : SubParam
|
||||
{
|
||||
public override string Name => "Health";
|
||||
|
||||
[Serialize(100f, true, description: "How much (max) health does the character have?"), Editable(minValue: 1, maxValue: 10000f)]
|
||||
[Serialize(100f, IsPropertySaveable.Yes, description: "How much (max) health does the character have?"), Editable(minValue: 1, maxValue: 10000f)]
|
||||
public float Vitality { get; set; }
|
||||
|
||||
[Serialize(true, true), Editable]
|
||||
[Serialize(true, IsPropertySaveable.Yes), Editable]
|
||||
public bool DoesBleed { get; set; }
|
||||
|
||||
[Serialize(float.NegativeInfinity, true), Editable(minValue: float.NegativeInfinity, maxValue: 0)]
|
||||
[Serialize(float.NegativeInfinity, IsPropertySaveable.Yes), Editable(minValue: float.NegativeInfinity, maxValue: 0)]
|
||||
public float CrushDepth { get; set; }
|
||||
|
||||
// Make editable?
|
||||
[Serialize(false, true)]
|
||||
[Serialize(false, IsPropertySaveable.Yes)]
|
||||
public bool UseHealthWindow { get; set; }
|
||||
|
||||
[Serialize(0f, true, description: "How easily the character heals from the bleeding wounds. Default 0 (no extra healing)."), Editable(MinValueFloat = 0, MaxValueFloat = 100, DecimalCount = 2)]
|
||||
[Serialize(0f, IsPropertySaveable.Yes, description: "How easily the character heals from the bleeding wounds. Default 0 (no extra healing)."), Editable(MinValueFloat = 0, MaxValueFloat = 100, DecimalCount = 2)]
|
||||
public float BleedingReduction { get; private set; }
|
||||
|
||||
[Serialize(0f, true, description: "How easily the character heals from the burn wounds. Default 0 (no extra healing)."), Editable(MinValueFloat = 0, MaxValueFloat = 100, DecimalCount = 2)]
|
||||
[Serialize(0f, IsPropertySaveable.Yes, description: "How easily the character heals from the burn wounds. Default 0 (no extra healing)."), Editable(MinValueFloat = 0, MaxValueFloat = 100, DecimalCount = 2)]
|
||||
public float BurnReduction { get; private set; }
|
||||
|
||||
[Serialize(0f, true), Editable(MinValueFloat = 0, MaxValueFloat = 10, DecimalCount = 2)]
|
||||
[Serialize(0f, IsPropertySaveable.Yes), Editable(MinValueFloat = 0, MaxValueFloat = 10, DecimalCount = 2)]
|
||||
public float ConstantHealthRegeneration { get; private set; }
|
||||
|
||||
[Serialize(0f, true), Editable(MinValueFloat = 0, MaxValueFloat = 10, DecimalCount = 2)]
|
||||
[Serialize(0f, IsPropertySaveable.Yes), Editable(MinValueFloat = 0, MaxValueFloat = 10, DecimalCount = 2)]
|
||||
public float HealthRegenerationWhenEating { get; private set; }
|
||||
|
||||
[Serialize(false, true), Editable]
|
||||
[Serialize(false, IsPropertySaveable.Yes), Editable]
|
||||
public bool StunImmunity { get; set; }
|
||||
|
||||
[Serialize(false, true, description: "Can afflictions affect the face/body tint of the character."), Editable]
|
||||
[Serialize(false, IsPropertySaveable.Yes, description: "Can afflictions affect the face/body tint of the character."), Editable]
|
||||
public bool ApplyAfflictionColors { get; private set; }
|
||||
|
||||
// TODO: limbhealths, sprite?
|
||||
|
||||
public HealthParams(XElement element, CharacterParams character) : base(element, character) { }
|
||||
public HealthParams(ContentXElement element, CharacterParams character) : base(element, character) { }
|
||||
}
|
||||
|
||||
public class InventoryParams : SubParam
|
||||
@@ -480,26 +497,26 @@ namespace Barotrauma
|
||||
{
|
||||
public override string Name => "Item";
|
||||
|
||||
[Serialize("", true, description: "Item identifier."), Editable()]
|
||||
[Serialize("", IsPropertySaveable.Yes, description: "Item identifier."), Editable()]
|
||||
public string Identifier { get; private set; }
|
||||
|
||||
public InventoryItem(XElement element, CharacterParams character) : base(element, character) { }
|
||||
public InventoryItem(ContentXElement element, CharacterParams character) : base(element, character) { }
|
||||
}
|
||||
|
||||
public override string Name => "Inventory";
|
||||
|
||||
[Serialize("Any, Any", true, description: "Which slots the inventory holds? Accepted types: None, Any, RightHand, LeftHand, Head, InnerClothes, OuterClothes, Headset, and Card."), Editable()]
|
||||
[Serialize("Any, Any", IsPropertySaveable.Yes, description: "Which slots the inventory holds? Accepted types: None, Any, RightHand, LeftHand, Head, InnerClothes, OuterClothes, Headset, and Card."), Editable()]
|
||||
public string Slots { get; private set; }
|
||||
|
||||
[Serialize(false, true), Editable]
|
||||
[Serialize(false, IsPropertySaveable.Yes), Editable]
|
||||
public bool AccessibleWhenAlive { get; private set; }
|
||||
|
||||
[Serialize(1.0f, true, description: "What are the odds that this inventory is spawned on the character?"), Editable(minValue: 0f, maxValue: 1.0f)]
|
||||
[Serialize(1.0f, IsPropertySaveable.Yes, description: "What are the odds that this inventory is spawned on the character?"), Editable(minValue: 0f, maxValue: 1.0f)]
|
||||
public float Commonness { get; private set; }
|
||||
|
||||
public List<InventoryItem> Items { get; private set; } = new List<InventoryItem>();
|
||||
|
||||
public InventoryParams(XElement element, CharacterParams character) : base(element, character)
|
||||
public InventoryParams(ContentXElement element, CharacterParams character) : base(element, character)
|
||||
{
|
||||
foreach (var itemElement in element.GetChildElements("item"))
|
||||
{
|
||||
@@ -512,7 +529,7 @@ namespace Barotrauma
|
||||
public void AddItem(string identifier = null)
|
||||
{
|
||||
identifier = identifier ?? "";
|
||||
var element = new XElement("item", new XAttribute("identifier", identifier));
|
||||
var element = CreateElement("item", new XAttribute("identifier", identifier));
|
||||
Element.Add(element);
|
||||
var item = new InventoryItem(element, Character);
|
||||
SubParams.Add(item);
|
||||
@@ -526,89 +543,89 @@ namespace Barotrauma
|
||||
{
|
||||
public override string Name => "AI";
|
||||
|
||||
[Serialize(1.0f, true, description: "How strong other characters think this character is? Only affects AI."), Editable()]
|
||||
[Serialize(1.0f, IsPropertySaveable.Yes, description: "How strong other characters think this character is? Only affects AI."), Editable()]
|
||||
public float CombatStrength { get; private set; }
|
||||
|
||||
[Serialize(1.0f, true, description: "Affects how far the character can see the targets. Used as a multiplier."), Editable(minValue: 0f, maxValue: 10f)]
|
||||
[Serialize(1.0f, IsPropertySaveable.Yes, description: "Affects how far the character can see the targets. Used as a multiplier."), Editable(minValue: 0f, maxValue: 10f)]
|
||||
public float Sight { get; private set; }
|
||||
|
||||
[Serialize(1.0f, true, description: "Affects how far the character can hear the targets. Used as a multiplier."), Editable(minValue: 0f, maxValue: 10f)]
|
||||
[Serialize(1.0f, IsPropertySaveable.Yes, description: "Affects how far the character can hear the targets. Used as a multiplier."), Editable(minValue: 0f, maxValue: 10f)]
|
||||
public float Hearing { get; private set; }
|
||||
|
||||
[Serialize(100f, true, description: "How much the targeting priority increases each time the character takes damage. Works like the greed value, described above. The default value is 100."), Editable(minValue: -1000f, maxValue: 1000f)]
|
||||
[Serialize(100f, IsPropertySaveable.Yes, description: "How much the targeting priority increases each time the character takes damage. Works like the greed value, described above. The default value is 100."), Editable(minValue: -1000f, maxValue: 1000f)]
|
||||
public float AggressionHurt { get; private set; }
|
||||
|
||||
[Serialize(10f, true, description: "How much the targeting priority increases each time the character does damage to the target. The actual priority adjustment is calculated based on the damage percentage multiplied by the greed value. The default value is 10, which means the priority will increase by 1 every time the character does damage 10% of the target's current health. If the damage is 50%, then the priority increase is 5."), Editable(minValue: 0f, maxValue: 1000f)]
|
||||
[Serialize(10f, IsPropertySaveable.Yes, description: "How much the targeting priority increases each time the character does damage to the target. The actual priority adjustment is calculated based on the damage percentage multiplied by the greed value. The default value is 10, which means the priority will increase by 1 every time the character does damage 10% of the target's current health. If the damage is 50%, then the priority increase is 5."), Editable(minValue: 0f, maxValue: 1000f)]
|
||||
public float AggressionGreed { get; private set; }
|
||||
|
||||
[Serialize(0f, true, description: "If the health drops below this threshold, the character flees. In percentages."), Editable(minValue: 0f, maxValue: 100f)]
|
||||
[Serialize(0f, IsPropertySaveable.Yes, description: "If the health drops below this threshold, the character flees. In percentages."), Editable(minValue: 0f, maxValue: 100f)]
|
||||
public float FleeHealthThreshold { get; private set; }
|
||||
|
||||
[Serialize(false, true, description: "Does the character attack when provoked? When enabled, overrides the predefined targeting state with Attack and increases the priority of it."), Editable()]
|
||||
[Serialize(false, IsPropertySaveable.Yes, description: "Does the character attack when provoked? When enabled, overrides the predefined targeting state with Attack and increases the priority of it."), Editable()]
|
||||
public bool AttackWhenProvoked { get; private set; }
|
||||
|
||||
[Serialize(false, true, description: "The character will flee for a brief moment when being shot at if not performing an attack."), Editable]
|
||||
[Serialize(false, IsPropertySaveable.Yes, description: "The character will flee for a brief moment when being shot at if not performing an attack."), Editable]
|
||||
public bool AvoidGunfire { get; private set; }
|
||||
|
||||
[Serialize(3f, true, description: "How long the creature avoids gunfire. Also used when the creature is unlatched."), Editable(minValue: 0f, maxValue: 100f)]
|
||||
[Serialize(3f, IsPropertySaveable.Yes, description: "How long the creature avoids gunfire. Also used when the creature is unlatched."), Editable(minValue: 0f, maxValue: 100f)]
|
||||
public float AvoidTime { get; private set; }
|
||||
|
||||
[Serialize(20f, true, description: "How long the creature flees before returning to normal state. When the creature sees the target or is being chased, it will always flee, if it's in the flee state."), Editable(minValue: 0f, maxValue: 100f)]
|
||||
[Serialize(20f, IsPropertySaveable.Yes, description: "How long the creature flees before returning to normal state. When the creature sees the target or is being chased, it will always flee, if it's in the flee state."), Editable(minValue: 0f, maxValue: 100f)]
|
||||
public float MinFleeTime { get; private set; }
|
||||
|
||||
[Serialize(false, true, description: "Does the character try to break inside the sub?"), Editable]
|
||||
[Serialize(false, IsPropertySaveable.Yes, description: "Does the character try to break inside the sub?"), Editable]
|
||||
public bool AggressiveBoarding { get; private set; }
|
||||
|
||||
[Serialize(true, true, description: "Enforce aggressive behavior if the creature is spawned as a target of a monster mission."), Editable]
|
||||
[Serialize(true, IsPropertySaveable.Yes, description: "Enforce aggressive behavior if the creature is spawned as a target of a monster mission."), Editable]
|
||||
public bool EnforceAggressiveBehaviorForMissions { get; private set; }
|
||||
|
||||
[Serialize(true, true, description: "Should the character target or ignore walls when it's outside the submarine."), Editable]
|
||||
[Serialize(true, IsPropertySaveable.Yes, description: "Should the character target or ignore walls when it's outside the submarine."), Editable]
|
||||
public bool TargetOuterWalls { get; private set; }
|
||||
|
||||
[Serialize(false, true, description: "If enabled, the character chooses randomly from the available attacks. The priority is used as a weight for weighted random."), Editable]
|
||||
[Serialize(false, IsPropertySaveable.Yes, description: "If enabled, the character chooses randomly from the available attacks. The priority is used as a weight for weighted random."), Editable]
|
||||
public bool RandomAttack { get; private set; }
|
||||
|
||||
[Serialize(false, true, description:"Does the creature know how to open doors (still requires a proper ID card). Humans can always open doors (They don't use this AI definition)."), Editable]
|
||||
[Serialize(false, IsPropertySaveable.Yes, description:"Does the creature know how to open doors (still requires a proper ID card). Humans can always open doors (They don't use this AI definition)."), Editable]
|
||||
public bool CanOpenDoors { get; private set; }
|
||||
|
||||
[Serialize(false, true, description: "Does the creature close the doors behind it. Humans don't use this AI definition."), Editable]
|
||||
[Serialize(false, IsPropertySaveable.Yes, description: "Does the creature close the doors behind it. Humans don't use this AI definition."), Editable]
|
||||
public bool KeepDoorsClosed { get; private set; }
|
||||
|
||||
[Serialize(true, true, "Is the creature allowed to navigate from and into the depths of the abyss? When enabled, the creatures will try to avoid the depths."), Editable]
|
||||
[Serialize(true, IsPropertySaveable.Yes, "Is the creature allowed to navigate from and into the depths of the abyss? When enabled, the creatures will try to avoid the depths."), Editable]
|
||||
public bool AvoidAbyss { get; set; }
|
||||
|
||||
[Serialize(false, true, "Does the creature try to keep in the abyss? Has effect only when AvoidAbyss is false."), Editable]
|
||||
[Serialize(false, IsPropertySaveable.Yes, "Does the creature try to keep in the abyss? Has effect only when AvoidAbyss is false."), Editable]
|
||||
public bool StayInAbyss { get; set; }
|
||||
|
||||
[Serialize(false, true, "Does the creature patrol the flooded hulls while idling inside a friendly submarine?"), Editable]
|
||||
|
||||
[Serialize(false, IsPropertySaveable.Yes, "Does the creature patrol the flooded hulls while idling inside a friendly submarine?"), Editable]
|
||||
public bool PatrolFlooded { get; set; }
|
||||
|
||||
[Serialize(false, true, "Does the creature patrol the dry hulls while idling inside a friendly submarine?"), Editable]
|
||||
[Serialize(false, IsPropertySaveable.Yes, "Does the creature patrol the dry hulls while idling inside a friendly submarine?"), Editable]
|
||||
public bool PatrolDry { get; set; }
|
||||
|
||||
[Serialize(0f, true, description: ""), Editable]
|
||||
[Serialize(0f, IsPropertySaveable.Yes, description: ""), Editable]
|
||||
public float StartAggression { get; private set; }
|
||||
|
||||
[Serialize(100f, true, description: ""), Editable]
|
||||
[Serialize(100f, IsPropertySaveable.Yes, description: ""), Editable]
|
||||
public float MaxAggression { get; private set; }
|
||||
|
||||
[Serialize(0f, true, description: ""), Editable]
|
||||
[Serialize(0f, IsPropertySaveable.Yes, description: ""), Editable]
|
||||
public float AggressionCumulation { get; private set; }
|
||||
|
||||
[Serialize(WallTargetingMethod.Target, true, description: ""), Editable]
|
||||
[Serialize(WallTargetingMethod.Target, IsPropertySaveable.Yes, description: ""), Editable]
|
||||
public WallTargetingMethod WallTargetingMethod { get; private set; }
|
||||
|
||||
public IEnumerable<TargetParams> Targets => targets;
|
||||
protected readonly List<TargetParams> targets = new List<TargetParams>();
|
||||
|
||||
public AIParams(XElement element, CharacterParams character) : base(element, character)
|
||||
public AIParams(ContentXElement element, CharacterParams character) : base(element, character)
|
||||
{
|
||||
if (element == null) { return; }
|
||||
element.GetChildElements("target").ForEach(t => TryAddTarget(t, out _));
|
||||
element.GetChildElements("targetpriority").ForEach(t => TryAddTarget(t, out _));
|
||||
}
|
||||
|
||||
private bool TryAddTarget(XElement targetElement, out TargetParams target)
|
||||
private bool TryAddTarget(ContentXElement targetElement, out TargetParams target)
|
||||
{
|
||||
string tag = targetElement.GetAttributeString("tag", null);
|
||||
if (HasTag(tag))
|
||||
@@ -628,9 +645,12 @@ namespace Barotrauma
|
||||
|
||||
public bool TryAddEmptyTarget(out TargetParams targetParams) => TryAddNewTarget("newtarget" + targets.Count, AIState.Attack, 0f, out targetParams);
|
||||
|
||||
public bool TryAddNewTarget(string tag, AIState state, float priority, out TargetParams targetParams)
|
||||
public bool TryAddNewTarget(string tag, AIState state, float priority, out TargetParams targetParams) =>
|
||||
TryAddNewTarget(tag.ToIdentifier(), state, priority, out targetParams);
|
||||
|
||||
public bool TryAddNewTarget(Identifier tag, AIState state, float priority, out TargetParams targetParams)
|
||||
{
|
||||
var element = TargetParams.CreateNewElement(tag, state, priority);
|
||||
var element = TargetParams.CreateNewElement(Character, tag, state, priority);
|
||||
if (TryAddTarget(element, out targetParams))
|
||||
{
|
||||
Element.Add(element);
|
||||
@@ -642,17 +662,22 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasTag(string tag)
|
||||
public bool HasTag(string tag) => HasTag(tag.ToIdentifier());
|
||||
|
||||
public bool HasTag(Identifier tag)
|
||||
{
|
||||
if (tag == null) { return false; }
|
||||
return targets.Any(t => t.Tag.Equals(tag, StringComparison.OrdinalIgnoreCase));
|
||||
return targets.Any(t => t.Tag == tag);
|
||||
}
|
||||
|
||||
public bool RemoveTarget(TargetParams target) => RemoveSubParam(target, targets);
|
||||
|
||||
public bool TryGetTarget(string targetTag, out TargetParams target)
|
||||
=> TryGetTarget(targetTag.ToIdentifier(), out target);
|
||||
|
||||
public bool TryGetTarget(Identifier targetTag, out TargetParams target)
|
||||
{
|
||||
target = targets.FirstOrDefault(t => string.Equals(t.Tag, targetTag, StringComparison.OrdinalIgnoreCase));
|
||||
target = targets.FirstOrDefault(t => t.Tag == targetTag);
|
||||
return target != null;
|
||||
}
|
||||
|
||||
@@ -665,7 +690,7 @@ namespace Barotrauma
|
||||
return target != null;
|
||||
}
|
||||
|
||||
public bool TryGetTarget(IEnumerable<string> tags, out TargetParams target)
|
||||
public bool TryGetTarget(IEnumerable<Identifier> tags, out TargetParams target)
|
||||
{
|
||||
target = null;
|
||||
if (tags == null || tags.None()) { return false; }
|
||||
@@ -674,7 +699,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (potentialTarget.Priority > priority)
|
||||
{
|
||||
if (tags.Any(t => string.Equals(t, potentialTarget.Tag, StringComparison.OrdinalIgnoreCase)))
|
||||
if (tags.Any(t => t == potentialTarget.Tag))
|
||||
{
|
||||
target = potentialTarget;
|
||||
priority = target.Priority;
|
||||
@@ -685,7 +710,11 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
public TargetParams GetTarget(string targetTag, bool throwError = true)
|
||||
=> GetTarget(targetTag.ToIdentifier(), throwError);
|
||||
|
||||
public TargetParams GetTarget(Identifier targetTag, bool throwError = true)
|
||||
{
|
||||
if (targetTag.IsEmpty) { return null; }
|
||||
if (!TryGetTarget(targetTag, out TargetParams target))
|
||||
{
|
||||
if (throwError)
|
||||
@@ -701,102 +730,108 @@ namespace Barotrauma
|
||||
{
|
||||
public override string Name => "Target";
|
||||
|
||||
[Serialize("", true, description: "Can be an item tag, species name or something else. Examples: decoy, provocative, light, dead, human, crawler, wall, nasonov, sonar, door, stronger, weaker, light, human, room..."), Editable()]
|
||||
[Serialize("", IsPropertySaveable.Yes, description: "Can be an item tag, species name or something else. Examples: decoy, provocative, light, dead, human, crawler, wall, nasonov, sonar, door, stronger, weaker, light, human, room..."), Editable()]
|
||||
public string Tag { get; private set; }
|
||||
|
||||
[Serialize(AIState.Idle, true), Editable]
|
||||
[Serialize(AIState.Idle, IsPropertySaveable.Yes), Editable]
|
||||
public AIState State { get; set; }
|
||||
|
||||
[Serialize(0f, true, description: "What base priority is given to the target?"), Editable(minValue: 0f, maxValue: 1000f, ValueStep = 1, DecimalCount = 0)]
|
||||
[Serialize(0f, IsPropertySaveable.Yes, description: "What base priority is given to the target?"), Editable(minValue: 0f, maxValue: 1000f, ValueStep = 1, DecimalCount = 0)]
|
||||
public float Priority { get; set; }
|
||||
|
||||
[Serialize(0f, true, description: "Generic distance that can be used for different purposes depending on the state. E.g. in Avoid state this defines the distance that the character tries to keep to the target. If the distance is 0, it's not used."), Editable(MinValueFloat = 0, ValueStep = 10, DecimalCount = 0)]
|
||||
[Serialize(0f, IsPropertySaveable.Yes, description: "Generic distance that can be used for different purposes depending on the state. E.g. in Avoid state this defines the distance that the character tries to keep to the target. If the distance is 0, it's not used."), Editable(MinValueFloat = 0, ValueStep = 10, DecimalCount = 0)]
|
||||
public float ReactDistance { get; set; }
|
||||
|
||||
[Serialize(0f, true, description: "Used for defining the attack distance for PassiveAggressive and Aggressive states. If the distance is 0, it's not used."), Editable(MinValueFloat = 0, ValueStep = 10, DecimalCount = 0)]
|
||||
[Serialize(0f, IsPropertySaveable.Yes, description: "Used for defining the attack distance for PassiveAggressive and Aggressive states. If the distance is 0, it's not used."), Editable(MinValueFloat = 0, ValueStep = 10, DecimalCount = 0)]
|
||||
public float AttackDistance { get; set; }
|
||||
|
||||
[Serialize(0f, true, description: "Generic timer that can be used for different purposes depending on the state. E.g. in Observe state this defines how long the character in general keeps staring the targets (Some random is always applied)."), Editable]
|
||||
[Serialize(0f, IsPropertySaveable.Yes, description: "Generic timer that can be used for different purposes depending on the state. E.g. in Observe state this defines how long the character in general keeps staring the targets (Some random is always applied)."), Editable]
|
||||
public float Timer { get; set; }
|
||||
|
||||
[Serialize(false, true, description: "Should the target be ignored if it's inside a container/inventory. Only affects items."), Editable]
|
||||
[Serialize(false, IsPropertySaveable.Yes, description: "Should the target be ignored if it's inside a container/inventory. Only affects items."), Editable]
|
||||
public bool IgnoreContained { get; set; }
|
||||
|
||||
[Serialize(false, true, description: "Should the target be ignored while the creature is inside. Doesn't matter where the target is."), Editable]
|
||||
[Serialize(false, IsPropertySaveable.Yes, description: "Should the target be ignored while the creature is inside. Doesn't matter where the target is."), Editable]
|
||||
public bool IgnoreInside { get; set; }
|
||||
|
||||
[Serialize(false, true, description: "Should the target be ignored while the creature is outside. Doesn't matter where the target is."), Editable]
|
||||
[Serialize(false, IsPropertySaveable.Yes, description: "Should the target be ignored while the creature is outside. Doesn't matter where the target is."), Editable]
|
||||
public bool IgnoreOutside { get; set; }
|
||||
|
||||
[Serialize(false, true, description: "Should the target be ignored if it's inside a different submarine than us? Normally only some targets are ignored when they are not inside the same sub."), Editable]
|
||||
[Serialize(false, IsPropertySaveable.Yes, description: "Should the target be ignored if it's inside a different submarine than us? Normally only some targets are ignored when they are not inside the same sub."), Editable]
|
||||
public bool IgnoreIfNotInSameSub { get; set; }
|
||||
|
||||
[Serialize(false, true), Editable]
|
||||
[Serialize(false, IsPropertySaveable.Yes), Editable]
|
||||
public bool IgnoreIncapacitated { get; set; }
|
||||
|
||||
[Serialize(0f, true, description: "A generic threshold. For example, how much damage the protected target should take from an attacker before the creature starts defending it."), Editable]
|
||||
[Serialize(0f, IsPropertySaveable.Yes, description: "A generic threshold. For example, how much damage the protected target should take from an attacker before the creature starts defending it."), Editable]
|
||||
public float Threshold { get; private set; }
|
||||
|
||||
[Serialize(-1f, true, description: "A generic min threshold. Not used if set to negative."), Editable]
|
||||
[Serialize(-1f, IsPropertySaveable.Yes, description: "A generic min threshold. Not used if set to negative."), Editable]
|
||||
public float ThresholdMin { get; private set; }
|
||||
|
||||
[Serialize(-1f, true, description: "A generic max threshold. Not used if set to negative."), Editable]
|
||||
[Serialize(-1f, IsPropertySaveable.Yes, description: "A generic max threshold. Not used if set to negative."), Editable]
|
||||
public float ThresholdMax { get; private set; }
|
||||
|
||||
[Serialize("0.0, 0.0", true), Editable]
|
||||
[Serialize("0.0, 0.0", IsPropertySaveable.Yes), Editable]
|
||||
public Vector2 Offset { get; private set; }
|
||||
|
||||
[Serialize(AttackPattern.Straight, true), Editable]
|
||||
[Serialize(AttackPattern.Straight, IsPropertySaveable.Yes), Editable]
|
||||
public AttackPattern AttackPattern { get; set; }
|
||||
|
||||
#region Sweep
|
||||
[Serialize(0f, true, description: "Use to define a distance at which the creature starts the sweeping movement."), Editable(MinValueFloat = 0, MaxValueFloat = 10000, ValueStep = 1, DecimalCount = 0)]
|
||||
[Serialize(0f, IsPropertySaveable.Yes, description: "Use to define a distance at which the creature starts the sweeping movement."), Editable(MinValueFloat = 0, MaxValueFloat = 10000, ValueStep = 1, DecimalCount = 0)]
|
||||
public float SweepDistance { get; private set; }
|
||||
|
||||
[Serialize(10f, true, description: "How much the sweep affects the steering?"), Editable(MinValueFloat = 0, MaxValueFloat = 100, ValueStep = 1f, DecimalCount = 1)]
|
||||
[Serialize(10f, IsPropertySaveable.Yes, description: "How much the sweep affects the steering?"), Editable(MinValueFloat = 0, MaxValueFloat = 100, ValueStep = 1f, DecimalCount = 1)]
|
||||
public float SweepStrength { get; private set; }
|
||||
|
||||
[Serialize(1f, true, description: "How quickly the sweep direction changes. Uses the sine wave pattern."), Editable(MinValueFloat = 0, MaxValueFloat = 10, ValueStep = 0.1f, DecimalCount = 2)]
|
||||
[Serialize(1f, IsPropertySaveable.Yes, description: "How quickly the sweep direction changes. Uses the sine wave pattern."), Editable(MinValueFloat = 0, MaxValueFloat = 10, ValueStep = 0.1f, DecimalCount = 2)]
|
||||
public float SweepSpeed { get; private set; }
|
||||
#endregion
|
||||
|
||||
#region Circle
|
||||
[Serialize(5000f, true), Editable(MinValueFloat = 0f, MaxValueFloat = 20000f)]
|
||||
[Serialize(5000f, IsPropertySaveable.Yes), Editable(MinValueFloat = 0f, MaxValueFloat = 20000f)]
|
||||
public float CircleStartDistance { get; private set; }
|
||||
|
||||
[Serialize(1f, true), Editable(MinValueFloat = 0.5f, MaxValueFloat = 2f)]
|
||||
[Serialize(1f, IsPropertySaveable.Yes), Editable(MinValueFloat = 0.5f, MaxValueFloat = 2f)]
|
||||
public float CircleRotationSpeed { get; private set; }
|
||||
|
||||
[Serialize(5f, true), Editable(MinValueFloat = 1f, MaxValueFloat = 10f)]
|
||||
[Serialize(5f, IsPropertySaveable.Yes), Editable(MinValueFloat = 1f, MaxValueFloat = 10f)]
|
||||
public float CircleStrikeDistanceMultiplier { get; private set; }
|
||||
|
||||
[Serialize(0f, true), Editable(MinValueFloat = 0f, MaxValueFloat = 50f)]
|
||||
[Serialize(0f, IsPropertySaveable.Yes), Editable(MinValueFloat = 0f, MaxValueFloat = 50f)]
|
||||
public float CircleMaxRandomOffset { get; private set; }
|
||||
#endregion
|
||||
|
||||
public TargetParams(XElement element, CharacterParams character) : base(element, character) { }
|
||||
public TargetParams(ContentXElement element, CharacterParams character) : base(element, character) { }
|
||||
|
||||
public TargetParams(string tag, AIState state, float priority, CharacterParams character) : base(CreateNewElement(tag, state, priority), character) { }
|
||||
public TargetParams(string tag, AIState state, float priority, CharacterParams character) : base(CreateNewElement(character, tag, state, priority), character) { }
|
||||
|
||||
public static XElement CreateNewElement(string tag, AIState state, float priority)
|
||||
public static ContentXElement CreateNewElement(CharacterParams character, Identifier tag, AIState state, float priority) =>
|
||||
CreateNewElement(character, tag.Value, state, priority);
|
||||
|
||||
public static ContentXElement CreateNewElement(CharacterParams character, string tag, AIState state, float priority)
|
||||
{
|
||||
return new XElement("target",
|
||||
new XAttribute("tag", tag),
|
||||
new XAttribute("state", state),
|
||||
new XAttribute("priority", priority));
|
||||
new XAttribute("priority", priority)).FromPackage(character.File.ContentPackage);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class SubParam : ISerializableEntity
|
||||
{
|
||||
public virtual string Name { get; set; }
|
||||
public Dictionary<string, SerializableProperty> SerializableProperties { get; private set; }
|
||||
public XElement Element { get; set; }
|
||||
public Dictionary<Identifier, SerializableProperty> SerializableProperties { get; private set; }
|
||||
public ContentXElement Element { get; set; }
|
||||
public List<SubParam> SubParams { get; set; } = new List<SubParam>();
|
||||
|
||||
public CharacterParams Character { get; private set; }
|
||||
|
||||
public SubParam(XElement element, CharacterParams character)
|
||||
protected ContentXElement CreateElement(string name, params object[] attrs)
|
||||
=> new XElement(name, attrs).FromPackage(Element.ContentPackage);
|
||||
|
||||
public SubParam(ContentXElement element, CharacterParams character)
|
||||
{
|
||||
Element = element;
|
||||
Character = character;
|
||||
@@ -843,12 +878,12 @@ namespace Barotrauma
|
||||
|
||||
#if CLIENT
|
||||
public SerializableEntityEditor SerializableEntityEditor { get; protected set; }
|
||||
public virtual void AddToEditor(ParamsEditor editor, bool recursive = true, int space = 0, ScalableFont titleFont = null)
|
||||
public virtual void AddToEditor(ParamsEditor editor, bool recursive = true, int space = 0, GUIFont titleFont = null)
|
||||
{
|
||||
SerializableEntityEditor = new SerializableEntityEditor(editor.EditorBox.Content.RectTransform, this, inGame: false, showName: true, titleFont: titleFont ?? GUI.LargeFont);
|
||||
SerializableEntityEditor = new SerializableEntityEditor(editor.EditorBox.Content.RectTransform, this, inGame: false, showName: true, titleFont: titleFont ?? GUIStyle.LargeFont);
|
||||
if (recursive)
|
||||
{
|
||||
SubParams.ForEach(sp => sp.AddToEditor(editor, true, titleFont: titleFont ?? GUI.SmallFont));
|
||||
SubParams.ForEach(sp => sp.AddToEditor(editor, true, titleFont: titleFont ?? GUIStyle.SmallFont));
|
||||
}
|
||||
if (space > 0)
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.Xna.Framework;
|
||||
using File = Barotrauma.IO.File;
|
||||
#if DEBUG
|
||||
using System.IO;
|
||||
using System.Xml;
|
||||
@@ -16,11 +17,13 @@ namespace Barotrauma
|
||||
public string Name { get; private set; }
|
||||
public string FileName { get; private set; }
|
||||
public string Folder { get; private set; }
|
||||
public string FullPath { get; private set; }
|
||||
public Dictionary<string, SerializableProperty> SerializableProperties { get; protected set; }
|
||||
public ContentPath Path { get; protected set; } = ContentPath.Empty;
|
||||
public Dictionary<Identifier, SerializableProperty> SerializableProperties { get; protected set; }
|
||||
|
||||
protected ContentXElement rootElement;
|
||||
protected XDocument doc;
|
||||
public XDocument Doc
|
||||
|
||||
private XDocument Doc
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -31,16 +34,30 @@ namespace Barotrauma
|
||||
}
|
||||
return doc;
|
||||
}
|
||||
protected set
|
||||
set
|
||||
{
|
||||
doc = value;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual XElement MainElement => doc.Root;
|
||||
public XElement OriginalElement { get; protected set; }
|
||||
public virtual ContentXElement MainElement
|
||||
{
|
||||
get
|
||||
{
|
||||
if (rootElement?.Element != doc.Root)
|
||||
{
|
||||
rootElement = doc.Root.FromPackage(Path.ContentPackage);
|
||||
}
|
||||
return rootElement;
|
||||
}
|
||||
}
|
||||
|
||||
public ContentXElement OriginalElement { get; protected set; }
|
||||
|
||||
protected virtual string GetName() => Path.GetFileNameWithoutExtension(FullPath).FormatCamelCaseWithSpaces();
|
||||
protected ContentXElement CreateElement(string name, params object[] attrs)
|
||||
=> new XElement(name, attrs).FromPackage(Path.ContentPackage);
|
||||
|
||||
protected virtual string GetName() => System.IO.Path.GetFileNameWithoutExtension(Path.Value).FormatCamelCaseWithSpaces();
|
||||
|
||||
protected virtual bool Deserialize(XElement element = null)
|
||||
{
|
||||
@@ -61,22 +78,22 @@ namespace Barotrauma
|
||||
return true;
|
||||
}
|
||||
|
||||
protected virtual bool Load(string file)
|
||||
protected virtual bool Load(ContentPath file)
|
||||
{
|
||||
UpdatePath(file);
|
||||
doc = XMLExtensions.TryLoadXml(FullPath);
|
||||
doc = XMLExtensions.TryLoadXml(Path);
|
||||
if (doc == null) { return false; }
|
||||
IsLoaded = Deserialize(MainElement);
|
||||
OriginalElement = new XElement(MainElement);
|
||||
OriginalElement = new XElement(MainElement).FromPackage(MainElement.ContentPackage);
|
||||
return IsLoaded;
|
||||
}
|
||||
|
||||
protected virtual void UpdatePath(string fullPath)
|
||||
protected virtual void UpdatePath(ContentPath fullPath)
|
||||
{
|
||||
FullPath = fullPath;
|
||||
Path = fullPath;
|
||||
Name = GetName();
|
||||
FileName = Path.GetFileName(FullPath);
|
||||
Folder = Path.GetDirectoryName(FullPath);
|
||||
FileName = System.IO.Path.GetFileName(Path.Value);
|
||||
Folder = System.IO.Path.GetDirectoryName(Path.Value);
|
||||
}
|
||||
|
||||
public virtual bool Save(string fileNameWithoutExtension = null, System.Xml.XmlWriterSettings settings = null)
|
||||
@@ -98,9 +115,9 @@ namespace Barotrauma
|
||||
}
|
||||
if (fileNameWithoutExtension != null)
|
||||
{
|
||||
UpdatePath(Path.Combine(Folder, $"{fileNameWithoutExtension}.xml"));
|
||||
UpdatePath(ContentPath.FromRaw(Path.ContentPackage, System.IO.Path.Combine(Folder, $"{fileNameWithoutExtension}.xml")));
|
||||
}
|
||||
using (var writer = XmlWriter.Create(FullPath, settings))
|
||||
using (var writer = XmlWriter.Create(Path.Value, settings))
|
||||
{
|
||||
Doc.WriteTo(writer);
|
||||
writer.Flush();
|
||||
@@ -112,7 +129,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (forceReload)
|
||||
{
|
||||
return Load(FullPath);
|
||||
return Load(Path);
|
||||
}
|
||||
return Deserialize(OriginalElement);
|
||||
}
|
||||
@@ -126,7 +143,7 @@ namespace Barotrauma
|
||||
DebugConsole.ThrowError("[Params] Not loaded!");
|
||||
return;
|
||||
}
|
||||
SerializableEntityEditor = new SerializableEntityEditor(editor.EditorBox.Content.RectTransform, this, false, true, titleFont: GUI.LargeFont);
|
||||
SerializableEntityEditor = new SerializableEntityEditor(editor.EditorBox.Content.RectTransform, this, false, true, titleFont: GUIStyle.LargeFont);
|
||||
if (space > 0)
|
||||
{
|
||||
new GUIFrame(new RectTransform(new Point(editor.EditorBox.Rect.Width, space), editor.EditorBox.Content.RectTransform), style: null, color: ParamsEditor.Color)
|
||||
|
||||
+228
-198
@@ -14,13 +14,13 @@ namespace Barotrauma
|
||||
{
|
||||
class HumanRagdollParams : RagdollParams
|
||||
{
|
||||
public static HumanRagdollParams GetRagdollParams(string speciesName, string fileName = null) => GetRagdollParams<HumanRagdollParams>(speciesName, fileName);
|
||||
public static HumanRagdollParams GetDefaultRagdollParams(string speciesName) => GetDefaultRagdollParams<HumanRagdollParams>(speciesName);
|
||||
public static HumanRagdollParams GetRagdollParams(Identifier speciesName, string fileName = null) => GetRagdollParams<HumanRagdollParams>(speciesName, fileName);
|
||||
public static HumanRagdollParams GetDefaultRagdollParams(Identifier speciesName) => GetDefaultRagdollParams<HumanRagdollParams>(speciesName);
|
||||
}
|
||||
|
||||
class FishRagdollParams : RagdollParams
|
||||
{
|
||||
public static FishRagdollParams GetDefaultRagdollParams(string speciesName) => GetDefaultRagdollParams<FishRagdollParams>(speciesName);
|
||||
public static FishRagdollParams GetDefaultRagdollParams(Identifier speciesName) => GetDefaultRagdollParams<FishRagdollParams>(speciesName);
|
||||
}
|
||||
|
||||
class RagdollParams : EditableParams, IMemorizable<RagdollParams>
|
||||
@@ -29,15 +29,15 @@ namespace Barotrauma
|
||||
public const float MIN_SCALE = 0.1f;
|
||||
public const float MAX_SCALE = 2;
|
||||
|
||||
public string SpeciesName { get; private set; }
|
||||
public Identifier SpeciesName { get; private set; }
|
||||
|
||||
[Serialize("", true, description: "Default path for the limb sprite textures. Used only if the limb specific path for the limb is not defined"), Editable]
|
||||
[Serialize("", IsPropertySaveable.Yes, description: "Default path for the limb sprite textures. Used only if the limb specific path for the limb is not defined"), Editable]
|
||||
public string Texture { get; set; }
|
||||
|
||||
[Serialize("1.0,1.0,1.0,1.0", true), Editable()]
|
||||
|
||||
[Serialize("1.0,1.0,1.0,1.0", IsPropertySaveable.Yes), Editable()]
|
||||
public Color Color { get; set; }
|
||||
|
||||
[Serialize(0.0f, true, description: "The orientation of the sprites as drawn on the sprite sheet. Can be overridden by setting a value for Limb's 'Sprite Orientation'."), Editable(-360, 360)]
|
||||
|
||||
[Serialize(0.0f, IsPropertySaveable.Yes, description: "The orientation of the sprites as drawn on the sprite sheet. Can be overridden by setting a value for Limb's 'Sprite Orientation'."), Editable(-360, 360)]
|
||||
public float SpritesheetOrientation { get; set; }
|
||||
|
||||
public bool IsSpritesheetOrientationHorizontal
|
||||
@@ -51,85 +51,85 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
private float limbScale;
|
||||
[Serialize(1.0f, true), Editable(MIN_SCALE, MAX_SCALE, DecimalCount = 3)]
|
||||
[Serialize(1.0f, IsPropertySaveable.Yes), Editable(MIN_SCALE, MAX_SCALE, DecimalCount = 3)]
|
||||
public float LimbScale { get { return limbScale; } set { limbScale = MathHelper.Clamp(value, MIN_SCALE, MAX_SCALE); } }
|
||||
|
||||
private float jointScale;
|
||||
[Serialize(1.0f, true), Editable(MIN_SCALE, MAX_SCALE, DecimalCount = 3)]
|
||||
[Serialize(1.0f, IsPropertySaveable.Yes), Editable(MIN_SCALE, MAX_SCALE, DecimalCount = 3)]
|
||||
public float JointScale { get { return jointScale; } set { jointScale = MathHelper.Clamp(value, MIN_SCALE, MAX_SCALE); } }
|
||||
|
||||
// Don't show in the editor, because shouldn't be edited in runtime. Requires that the limb scale and the collider sizes are adjusted. TODO: automatize?
|
||||
[Serialize(1f, false)]
|
||||
[Serialize(1f, IsPropertySaveable.No)]
|
||||
public float TextureScale { get; set; }
|
||||
|
||||
[Serialize(45f, true, description: "How high from the ground the main collider levitates when the character is standing? Doesn't affect swimming."), Editable(0f, 1000f)]
|
||||
[Serialize(45f, IsPropertySaveable.Yes, description: "How high from the ground the main collider levitates when the character is standing? Doesn't affect swimming."), Editable(0f, 1000f)]
|
||||
public float ColliderHeightFromFloor { get; set; }
|
||||
|
||||
[Serialize(50f, true, description: "How much impact is required before the character takes impact damage?"), Editable(MinValueFloat = 0, MaxValueFloat = 1000)]
|
||||
[Serialize(50f, IsPropertySaveable.Yes, description: "How much impact is required before the character takes impact damage?"), Editable(MinValueFloat = 0, MaxValueFloat = 1000)]
|
||||
public float ImpactTolerance { get; set; }
|
||||
|
||||
[Serialize(true, true, description: "Can the creature enter submarine. Creatures that cannot enter submarines, always collide with it, even when there is a gap."), Editable()]
|
||||
[Serialize(true, IsPropertySaveable.Yes, description: "Can the creature enter submarine. Creatures that cannot enter submarines, always collide with it, even when there is a gap."), Editable()]
|
||||
public bool CanEnterSubmarine { get; set; }
|
||||
|
||||
[Serialize(true, true), Editable]
|
||||
[Serialize(true, IsPropertySaveable.Yes), Editable]
|
||||
public bool CanWalk { get; set; }
|
||||
|
||||
[Serialize(true, true, description: "Can the character be dragged around by other creatures?"), Editable()]
|
||||
[Serialize(true, IsPropertySaveable.Yes, description: "Can the character be dragged around by other creatures?"), Editable()]
|
||||
public bool Draggable { get; set; }
|
||||
|
||||
[Serialize(LimbType.Torso, true), Editable]
|
||||
[Serialize(LimbType.Torso, IsPropertySaveable.Yes), Editable]
|
||||
public LimbType MainLimb { get; set; }
|
||||
|
||||
private readonly static Dictionary<string, Dictionary<string, RagdollParams>> allRagdolls = new Dictionary<string, Dictionary<string, RagdollParams>>();
|
||||
/// <summary>
|
||||
/// key1: Species name
|
||||
/// key2: File path
|
||||
/// value: Ragdoll parameters
|
||||
/// </summary>
|
||||
private readonly static Dictionary<Identifier, Dictionary<string, RagdollParams>> allRagdolls = new Dictionary<Identifier, Dictionary<string, RagdollParams>>();
|
||||
|
||||
public List<ColliderParams> Colliders { get; private set; } = new List<ColliderParams>();
|
||||
public List<LimbParams> Limbs { get; private set; } = new List<LimbParams>();
|
||||
public List<JointParams> Joints { get; private set; } = new List<JointParams>();
|
||||
|
||||
protected IEnumerable<SubParam> GetAllSubParams() =>
|
||||
Colliders.Select(c => c as SubParam)
|
||||
.Concat(Limbs.Select(j => j as SubParam)
|
||||
.Concat(Joints.Select(j => j as SubParam)));
|
||||
Colliders
|
||||
.Concat<SubParam>(Limbs)
|
||||
.Concat(Joints);
|
||||
|
||||
public static string GetDefaultFileName(string speciesName) => $"{speciesName.CapitaliseFirstInvariant()}DefaultRagdoll";
|
||||
public static string GetDefaultFile(string speciesName, ContentPackage contentPackage = null)
|
||||
=> Path.Combine(GetFolder(speciesName, contentPackage), $"{GetDefaultFileName(speciesName)}.xml");
|
||||
public static string GetDefaultFileName(Identifier speciesName) => $"{speciesName.Value.CapitaliseFirstInvariant()}DefaultRagdoll";
|
||||
public static string GetDefaultFile(Identifier speciesName, ContentPackage contentPackage = null)
|
||||
=> IO.Path.Combine(GetFolder(speciesName, contentPackage), $"{GetDefaultFileName(speciesName)}.xml");
|
||||
|
||||
public static string GetFolder(string speciesName, ContentPackage contentPackage = null)
|
||||
public static string GetFolder(Identifier speciesName, ContentPackage contentPackage = null)
|
||||
{
|
||||
CharacterPrefab prefab = CharacterPrefab.Find(p => p.Identifier.Equals(speciesName, StringComparison.OrdinalIgnoreCase) && (contentPackage == null || p.ContentPackage == contentPackage));
|
||||
if (prefab?.XDocument == null)
|
||||
CharacterPrefab prefab = CharacterPrefab.Find(p => p.Identifier == speciesName && (contentPackage == null || p.ContentFile.ContentPackage == contentPackage));
|
||||
if (prefab?.ConfigElement == null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Failed to find config file for '{speciesName}' (content package {contentPackage?.Name ?? "null"})");
|
||||
return string.Empty;
|
||||
}
|
||||
return GetFolder(prefab.XDocument, prefab.FilePath);
|
||||
return GetFolder(prefab.ConfigElement, prefab.ContentFile.Path.Value);
|
||||
}
|
||||
|
||||
public static string GetFolder(XDocument doc, string filePath)
|
||||
private static string GetFolder(ContentXElement root, string filePath)
|
||||
{
|
||||
var root = doc.Root;
|
||||
if (root?.IsOverride() ?? false)
|
||||
var folder = root?.GetChildElement("ragdolls")?.GetAttributeContentPath("folder")?.Value;
|
||||
if (folder.IsNullOrEmpty() || folder.Equals("default", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
root = root.FirstElement();
|
||||
}
|
||||
var folder = root?.Element("ragdolls")?.GetAttributeString("folder", string.Empty);
|
||||
if (string.IsNullOrEmpty(folder) || folder.Equals("default", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
folder = Path.Combine(Path.GetDirectoryName(filePath), "Ragdolls") + Path.DirectorySeparatorChar;
|
||||
folder = IO.Path.Combine(IO.Path.GetDirectoryName(filePath), "Ragdolls") + IO.Path.DirectorySeparatorChar;
|
||||
}
|
||||
return folder.CleanUpPathCrossPlatform(correctFilenameCase: true);
|
||||
}
|
||||
|
||||
public static T GetDefaultRagdollParams<T>(string speciesName) where T : RagdollParams, new() => GetRagdollParams<T>(speciesName, GetDefaultFileName(speciesName));
|
||||
public static T GetDefaultRagdollParams<T>(Identifier speciesName) where T : RagdollParams, new() => GetRagdollParams<T>(speciesName, GetDefaultFileName(speciesName));
|
||||
|
||||
/// <summary>
|
||||
/// If the file name is left null, default file is selected. If fails, will select the default file. Note: Use the filename without the extensions, don't use the full path!
|
||||
/// If a custom folder is used, it's defined in the character info file.
|
||||
/// </summary>
|
||||
public static T GetRagdollParams<T>(string speciesName, string fileName = null) where T : RagdollParams, new()
|
||||
public static T GetRagdollParams<T>(Identifier speciesName, string fileName = null) where T : RagdollParams, new()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(speciesName))
|
||||
if (speciesName.IsEmpty)
|
||||
{
|
||||
throw new Exception($"Species name null or empty!");
|
||||
}
|
||||
@@ -138,66 +138,88 @@ namespace Barotrauma
|
||||
ragdolls = new Dictionary<string, RagdollParams>();
|
||||
allRagdolls.Add(speciesName, ragdolls);
|
||||
}
|
||||
if (string.IsNullOrEmpty(fileName) || !ragdolls.TryGetValue(fileName, out RagdollParams ragdoll))
|
||||
|
||||
if (!string.IsNullOrEmpty(fileName) && ragdolls.TryGetValue(fileName, out RagdollParams ragdoll))
|
||||
{
|
||||
string selectedFile = null;
|
||||
string folder = GetFolder(speciesName);
|
||||
if (Directory.Exists(folder))
|
||||
return (T)ragdoll;
|
||||
}
|
||||
|
||||
string selectedFile = null;
|
||||
|
||||
void tryFolderForSpecies(Identifier species, out string err)
|
||||
{
|
||||
err = null;
|
||||
string folder = GetFolder(species);
|
||||
if (!Directory.Exists(folder))
|
||||
{
|
||||
List<string> files = Directory.GetFiles(folder).ToList();
|
||||
if (files.None())
|
||||
{
|
||||
DebugConsole.ThrowError($"[RagdollParams] Could not find any ragdoll files from the folder: {folder}. Using the default ragdoll.");
|
||||
selectedFile = GetDefaultFile(speciesName);
|
||||
}
|
||||
else if (string.IsNullOrEmpty(fileName))
|
||||
{
|
||||
// Files found, but none specified
|
||||
selectedFile = GetDefaultFile(speciesName);
|
||||
}
|
||||
else
|
||||
{
|
||||
selectedFile = files.FirstOrDefault(f => Path.GetFileNameWithoutExtension(f).Equals(fileName, StringComparison.OrdinalIgnoreCase));
|
||||
if (selectedFile == null)
|
||||
{
|
||||
DebugConsole.ThrowError($"[RagdollParams] Could not find a ragdoll file that matches the name {fileName}. Using the default ragdoll.");
|
||||
selectedFile = GetDefaultFile(speciesName);
|
||||
}
|
||||
}
|
||||
err = $"[RagdollParams] Invalid directory: {folder}. Using the default ragdoll.";
|
||||
selectedFile = GetDefaultFile(species);
|
||||
return;
|
||||
}
|
||||
|
||||
string[] files = Directory.GetFiles(folder);
|
||||
if (files.None())
|
||||
{
|
||||
err = $"[RagdollParams] Could not find any ragdoll files from the folder: {folder}. Using the default ragdoll.";
|
||||
selectedFile = GetDefaultFile(species);
|
||||
}
|
||||
else if (string.IsNullOrEmpty(fileName))
|
||||
{
|
||||
// Files found, but none specified
|
||||
selectedFile = GetDefaultFile(species);
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError($"[RagdollParams] Invalid directory: {folder}. Using the default ragdoll.");
|
||||
selectedFile = GetDefaultFile(speciesName);
|
||||
}
|
||||
if (selectedFile == null)
|
||||
{
|
||||
throw new Exception("[RagdollParams] Selected file null!");
|
||||
}
|
||||
DebugConsole.Log($"[RagdollParams] Loading ragdoll from {selectedFile}.");
|
||||
T r = new T();
|
||||
if (r.Load(selectedFile, speciesName))
|
||||
{
|
||||
if (!ragdolls.ContainsKey(r.Name))
|
||||
selectedFile = files.FirstOrDefault(f => IO.Path.GetFileNameWithoutExtension(f).Equals(fileName, StringComparison.OrdinalIgnoreCase));
|
||||
if (selectedFile == null)
|
||||
{
|
||||
ragdolls.Add(r.Name, r);
|
||||
err = $"[RagdollParams] Could not find a ragdoll file that matches the name {fileName}. Using the default ragdoll.";
|
||||
selectedFile = GetDefaultFile(species);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Failing to create a ragdoll causes so many issues that cannot be handled. Dummy ragdoll just seems to make things harded to debug. It's better to fail early.
|
||||
throw new Exception($"[RagdollParams] Failed to load ragdoll {r.Name} from {selectedFile} for the character {speciesName}.");
|
||||
}
|
||||
}
|
||||
return (T)ragdoll;
|
||||
|
||||
tryFolderForSpecies(speciesName, out var error);
|
||||
Identifier parentSpeciesName = CharacterPrefab.Prefabs.TryGet(speciesName, out var prefab)
|
||||
? prefab.VariantOf
|
||||
: Identifier.Empty;
|
||||
if (!error.IsNullOrEmpty() && !parentSpeciesName.IsEmpty)
|
||||
{
|
||||
tryFolderForSpecies(parentSpeciesName, out error);
|
||||
}
|
||||
|
||||
if (!error.IsNullOrEmpty())
|
||||
{
|
||||
DebugConsole.ThrowError(error);
|
||||
}
|
||||
|
||||
if (selectedFile == null)
|
||||
{
|
||||
throw new Exception("[RagdollParams] Selected file null!");
|
||||
}
|
||||
DebugConsole.Log($"[RagdollParams] Loading ragdoll from {selectedFile}.");
|
||||
var characterPrefab = CharacterPrefab.Prefabs[speciesName];
|
||||
T r = new T();
|
||||
if (r.Load(ContentPath.FromRaw(characterPrefab.ContentPackage, selectedFile), speciesName))
|
||||
{
|
||||
if (!ragdolls.ContainsKey(r.Name))
|
||||
{
|
||||
ragdolls.Add(r.Name, r);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Failing to create a ragdoll causes so many issues that cannot be handled. Dummy ragdoll just seems to make things harded to debug. It's better to fail early.
|
||||
throw new Exception($"[RagdollParams] Failed to load ragdoll {r.Name} from {selectedFile} for the character {speciesName}.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a default ragdoll for the species using a predefined configuration.
|
||||
/// Note: Use only to create ragdolls for new characters, because this overrides the old ragdoll!
|
||||
/// </summary>
|
||||
public static T CreateDefault<T>(string fullPath, string speciesName, XElement mainElement) where T : RagdollParams, new()
|
||||
public static T CreateDefault<T>(string fullPath, Identifier speciesName, XElement mainElement) where T : RagdollParams, new()
|
||||
{
|
||||
// Remove the old ragdolls, if found.
|
||||
if (allRagdolls.ContainsKey(speciesName))
|
||||
@@ -211,10 +233,12 @@ namespace Barotrauma
|
||||
{
|
||||
doc = new XDocument(mainElement)
|
||||
};
|
||||
instance.UpdatePath(fullPath);
|
||||
var characterPrefab = CharacterPrefab.Prefabs[speciesName];
|
||||
var contentPath = ContentPath.FromRaw(characterPrefab.ContentPackage, fullPath);
|
||||
instance.UpdatePath(contentPath);
|
||||
instance.IsLoaded = instance.Deserialize(mainElement);
|
||||
instance.Save();
|
||||
instance.Load(fullPath, speciesName);
|
||||
instance.Load(contentPath, speciesName);
|
||||
ragdolls.Add(instance.Name, instance);
|
||||
DebugConsole.NewMessage("[RagdollParams] New default ragdoll params successfully created at " + fullPath, Color.NavajoWhite);
|
||||
return instance as T;
|
||||
@@ -222,7 +246,7 @@ namespace Barotrauma
|
||||
|
||||
public static void ClearCache() => allRagdolls.Clear();
|
||||
|
||||
protected override void UpdatePath(string fullPath)
|
||||
protected override void UpdatePath(ContentPath fullPath)
|
||||
{
|
||||
if (SpeciesName == null)
|
||||
{
|
||||
@@ -259,7 +283,7 @@ namespace Barotrauma
|
||||
});
|
||||
}
|
||||
|
||||
protected bool Load(string file, string speciesName)
|
||||
protected bool Load(ContentPath file, Identifier speciesName)
|
||||
{
|
||||
if (Load(file))
|
||||
{
|
||||
@@ -287,7 +311,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (forceReload)
|
||||
{
|
||||
return Load(FullPath, SpeciesName);
|
||||
return Load(Path, SpeciesName);
|
||||
}
|
||||
// Don't use recursion, because the reset method might be overriden
|
||||
Deserialize(OriginalElement, alsoChildren: false, recursive: false);
|
||||
@@ -401,8 +425,10 @@ namespace Barotrauma
|
||||
}
|
||||
var copy = new RagdollParams
|
||||
{
|
||||
SpeciesName = SpeciesName,
|
||||
IsLoaded = true,
|
||||
doc = new XDocument(doc)
|
||||
doc = new XDocument(doc),
|
||||
Path = Path
|
||||
};
|
||||
copy.CreateColliders();
|
||||
copy.CreateLimbs();
|
||||
@@ -453,7 +479,7 @@ namespace Barotrauma
|
||||
public class JointParams : SubParam
|
||||
{
|
||||
private string name;
|
||||
[Serialize("", true), Editable]
|
||||
[Serialize("", IsPropertySaveable.Yes), Editable]
|
||||
public override string Name
|
||||
{
|
||||
get
|
||||
@@ -472,61 +498,61 @@ namespace Barotrauma
|
||||
|
||||
public override string GenerateName() => $"Joint {Limb1} - {Limb2}";
|
||||
|
||||
[Serialize(-1, true), Editable]
|
||||
[Serialize(-1, IsPropertySaveable.Yes), Editable]
|
||||
public int Limb1 { get; set; }
|
||||
|
||||
[Serialize(-1, true), Editable]
|
||||
[Serialize(-1, IsPropertySaveable.Yes), Editable]
|
||||
public int Limb2 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Should be converted to sim units.
|
||||
/// </summary>
|
||||
[Serialize("1.0, 1.0", true, description: "Local position of the joint in the Limb1."), Editable()]
|
||||
[Serialize("1.0, 1.0", IsPropertySaveable.Yes, description: "Local position of the joint in the Limb1."), Editable()]
|
||||
public Vector2 Limb1Anchor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Should be converted to sim units.
|
||||
/// </summary>
|
||||
[Serialize("1.0, 1.0", true, description: "Local position of the joint in the Limb2."), Editable()]
|
||||
[Serialize("1.0, 1.0", IsPropertySaveable.Yes, description: "Local position of the joint in the Limb2."), Editable()]
|
||||
public Vector2 Limb2Anchor { get; set; }
|
||||
|
||||
[Serialize(true, true), Editable]
|
||||
[Serialize(true, IsPropertySaveable.Yes), Editable]
|
||||
public bool CanBeSevered { get; set; }
|
||||
|
||||
[Serialize(0f, true, description:"Default 0 (Can't be severed when the creature is alive). Modifies the severance probability (defined per item/attack) when the character is alive. Currently only affects non-humanoid ragdolls. Also note that if CanBeSevered is false, this property doesn't have any effect."), Editable(MinValueFloat = 0, MaxValueFloat = 10, ValueStep = 0.1f, DecimalCount = 2)]
|
||||
[Serialize(0f, IsPropertySaveable.Yes, description:"Default 0 (Can't be severed when the creature is alive). Modifies the severance probability (defined per item/attack) when the character is alive. Currently only affects non-humanoid ragdolls. Also note that if CanBeSevered is false, this property doesn't have any effect."), Editable(MinValueFloat = 0, MaxValueFloat = 10, ValueStep = 0.1f, DecimalCount = 2)]
|
||||
public float SeveranceProbabilityModifier { get; set; }
|
||||
|
||||
[Serialize("gore", true), Editable]
|
||||
[Serialize("gore", IsPropertySaveable.Yes), Editable]
|
||||
public string BreakSound { get; set; }
|
||||
|
||||
[Serialize(true, true), Editable]
|
||||
[Serialize(true, IsPropertySaveable.Yes), Editable]
|
||||
public bool LimitEnabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// In degrees.
|
||||
/// </summary>
|
||||
[Serialize(0f, true), Editable]
|
||||
[Serialize(0f, IsPropertySaveable.Yes), Editable]
|
||||
public float UpperLimit { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// In degrees.
|
||||
/// </summary>
|
||||
[Serialize(0f, true), Editable]
|
||||
[Serialize(0f, IsPropertySaveable.Yes), Editable]
|
||||
public float LowerLimit { get; set; }
|
||||
|
||||
[Serialize(0.25f, true), Editable]
|
||||
[Serialize(0.25f, IsPropertySaveable.Yes), Editable]
|
||||
public float Stiffness { get; set; }
|
||||
|
||||
[Serialize(1f, true, description: "CAUTION: Not fully implemented. Only use for limb joints that connect non-animated limbs!"), Editable]
|
||||
[Serialize(1f, IsPropertySaveable.Yes, description: "CAUTION: Not fully implemented. Only use for limb joints that connect non-animated limbs!"), Editable]
|
||||
public float Scale { get; set; }
|
||||
|
||||
[Serialize(false, false), Editable(ReadOnly = true)]
|
||||
[Serialize(false, IsPropertySaveable.No), Editable(ReadOnly = true)]
|
||||
public bool WeldJoint { get; set; }
|
||||
|
||||
[Serialize(false, true), Editable]
|
||||
[Serialize(false, IsPropertySaveable.Yes), Editable]
|
||||
public bool ClockWiseRotation { get; set; }
|
||||
|
||||
public JointParams(XElement element, RagdollParams ragdoll) : base(element, ragdoll) { }
|
||||
public JointParams(ContentXElement element, RagdollParams ragdoll) : base(element, ragdoll) { }
|
||||
}
|
||||
|
||||
public class LimbParams : SubParam
|
||||
@@ -542,7 +568,7 @@ namespace Barotrauma
|
||||
public List<DamageModifierParams> DamageModifiers { get; private set; } = new List<DamageModifierParams>();
|
||||
|
||||
private string name;
|
||||
[Serialize("", true), Editable]
|
||||
[Serialize("", IsPropertySaveable.Yes), Editable]
|
||||
public override string Name
|
||||
{
|
||||
get
|
||||
@@ -563,10 +589,10 @@ namespace Barotrauma
|
||||
|
||||
public SpriteParams GetSprite() => deformSpriteParams ?? normalSpriteParams;
|
||||
|
||||
[Serialize(-1, true), Editable(ReadOnly = true)]
|
||||
[Serialize(-1, IsPropertySaveable.Yes), Editable(ReadOnly = true)]
|
||||
public int ID { get; set; }
|
||||
|
||||
[Serialize(LimbType.None, true, description: "The limb type affects many things, like the animations. Torso or Head are considered as the main limbs. Every character should have at least one Torso or Head."), Editable()]
|
||||
[Serialize(LimbType.None, IsPropertySaveable.Yes, description: "The limb type affects many things, like the animations. Torso or Head are considered as the main limbs. Every character should have at least one Torso or Head."), Editable()]
|
||||
public LimbType Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
@@ -576,136 +602,136 @@ namespace Barotrauma
|
||||
|
||||
public float GetSpriteOrientationInDegrees() => float.IsNaN(SpriteOrientation) ? Ragdoll.SpritesheetOrientation : SpriteOrientation;
|
||||
|
||||
[Serialize("", true), Editable]
|
||||
[Serialize("", IsPropertySaveable.Yes), Editable]
|
||||
public string Notes { get; set; }
|
||||
|
||||
[Serialize(1f, true), Editable(DecimalCount = 2)]
|
||||
[Serialize(1f, IsPropertySaveable.Yes), Editable(DecimalCount = 2)]
|
||||
public float Scale { get; set; }
|
||||
|
||||
[Serialize(true, true, description: "Does the limb flip when the character flips?"), Editable()]
|
||||
[Serialize(true, IsPropertySaveable.Yes, description: "Does the limb flip when the character flips?"), Editable()]
|
||||
public bool Flip { get; set; }
|
||||
|
||||
[Serialize(false, true, description: "Currently only works with non-deformable (normal) sprites."), Editable()]
|
||||
[Serialize(false, IsPropertySaveable.Yes, description: "Currently only works with non-deformable (normal) sprites."), Editable()]
|
||||
public bool MirrorVertically { get; set; }
|
||||
|
||||
[Serialize(false, true), Editable]
|
||||
[Serialize(false, IsPropertySaveable.Yes), Editable]
|
||||
public bool MirrorHorizontally { get; set; }
|
||||
|
||||
[Serialize(false, true, description: "Disable drawing for this limb."), Editable()]
|
||||
[Serialize(false, IsPropertySaveable.Yes, description: "Disable drawing for this limb."), Editable()]
|
||||
public bool Hide { get; set; }
|
||||
|
||||
[Serialize(float.NaN, true, description: "The orientation of the sprite as drawn on the sprite sheet. Overrides the value defined in the Ragdoll settings."), Editable(-360, 360, ValueStep = 90, DecimalCount = 0)]
|
||||
[Serialize(float.NaN, IsPropertySaveable.Yes, description: "The orientation of the sprite as drawn on the sprite sheet. Overrides the value defined in the Ragdoll settings."), Editable(-360, 360, ValueStep = 90, DecimalCount = 0)]
|
||||
public float SpriteOrientation { get; set; }
|
||||
|
||||
[Serialize(LimbType.None, true, description: "If set, the limb sprite will use the same sprite depth as the specified limb. Generally only useful for limbs that get added on the ragdoll on the fly (e.g. extra limbs added via gene splicing).")]
|
||||
[Serialize(LimbType.None, IsPropertySaveable.Yes, description: "If set, the limb sprite will use the same sprite depth as the specified limb. Generally only useful for limbs that get added on the ragdoll on the fly (e.g. extra limbs added via gene splicing).")]
|
||||
public LimbType InheritLimbDepth { get; set; }
|
||||
|
||||
[Serialize(0f, true), Editable(MinValueFloat = 0, MaxValueFloat = 500)]
|
||||
[Serialize(0f, IsPropertySaveable.Yes), Editable(MinValueFloat = 0, MaxValueFloat = 500)]
|
||||
public float SteerForce { get; set; }
|
||||
|
||||
[Serialize(0f, true, description: "Radius of the collider."), Editable(MinValueFloat = 0, MaxValueFloat = 1000)]
|
||||
[Serialize(0f, IsPropertySaveable.Yes, description: "Radius of the collider."), Editable(MinValueFloat = 0, MaxValueFloat = 1000)]
|
||||
public float Radius { get; set; }
|
||||
|
||||
[Serialize(0f, true, description: "Height of the collider."), Editable(MinValueFloat = 0, MaxValueFloat = 1000)]
|
||||
[Serialize(0f, IsPropertySaveable.Yes, description: "Height of the collider."), Editable(MinValueFloat = 0, MaxValueFloat = 1000)]
|
||||
public float Height { get; set; }
|
||||
|
||||
[Serialize(0f, true, description: "Width of the collider."), Editable(MinValueFloat = 0, MaxValueFloat = 1000)]
|
||||
[Serialize(0f, IsPropertySaveable.Yes, description: "Width of the collider."), Editable(MinValueFloat = 0, MaxValueFloat = 1000)]
|
||||
public float Width { get; set; }
|
||||
|
||||
[Serialize(10f, true, description: "The more the density the heavier the limb is."), Editable(MinValueFloat = 0.01f, MaxValueFloat = 100, DecimalCount = 2)]
|
||||
[Serialize(10f, IsPropertySaveable.Yes, description: "The more the density the heavier the limb is."), Editable(MinValueFloat = 0.01f, MaxValueFloat = 100, DecimalCount = 2)]
|
||||
public float Density { get; set; }
|
||||
|
||||
[Serialize(false, true), Editable]
|
||||
[Serialize(false, IsPropertySaveable.Yes), Editable]
|
||||
public bool IgnoreCollisions { get; set; }
|
||||
|
||||
[Serialize(7f, true, description: "Increasing the damping makes the limb stop rotating more quickly."), Editable]
|
||||
[Serialize(7f, IsPropertySaveable.Yes, description: "Increasing the damping makes the limb stop rotating more quickly."), Editable]
|
||||
public float AngularDamping { get; set; }
|
||||
|
||||
[Serialize(1f, true, description: "Higher values make AI characters prefer attacking this limb."), Editable(MinValueFloat = 0.1f, MaxValueFloat = 10)]
|
||||
[Serialize(1f, IsPropertySaveable.Yes, description: "Higher values make AI characters prefer attacking this limb."), Editable(MinValueFloat = 0.1f, MaxValueFloat = 10)]
|
||||
public float AttackPriority { get; set; }
|
||||
|
||||
[Serialize("0, 0", true, description: "The position which is used to lead the IK chain to the IK goal. Only applicable if the limb is hand or foot."), Editable()]
|
||||
[Serialize("0, 0", IsPropertySaveable.Yes, description: "The position which is used to lead the IK chain to the IK goal. Only applicable if the limb is hand or foot."), Editable()]
|
||||
public Vector2 PullPos { get; set; }
|
||||
|
||||
[Serialize("0, 0", true, description: "Only applicable if this limb is a foot. Determines the \"neutral position\" of the foot relative to a joint determined by the \"RefJoint\" parameter. For example, a value of {-100, 0} would mean that the foot is positioned on the floor, 100 units behind the reference joint."), Editable()]
|
||||
[Serialize("0, 0", IsPropertySaveable.Yes, description: "Only applicable if this limb is a foot. Determines the \"neutral position\" of the foot relative to a joint determined by the \"RefJoint\" parameter. For example, a value of {-100, 0} would mean that the foot is positioned on the floor, 100 units behind the reference joint."), Editable()]
|
||||
public Vector2 StepOffset { get; set; }
|
||||
|
||||
[Serialize(-1, true, description: "The id of the refecence joint. Determines which joint is used as the \"neutral x-position\" for the foot movement. For example in the case of a humanoid-shaped characters this would usually be the waist. The position can be offset using the StepOffset parameter. Only applicable if this limb is a foot."), Editable()]
|
||||
[Serialize(-1, IsPropertySaveable.Yes, description: "The id of the refecence joint. Determines which joint is used as the \"neutral x-position\" for the foot movement. For example in the case of a humanoid-shaped characters this would usually be the waist. The position can be offset using the StepOffset parameter. Only applicable if this limb is a foot."), Editable()]
|
||||
public int RefJoint { get; set; }
|
||||
|
||||
[Serialize("0, 0", true, description: "Relative offset for the mouth position (starting from the center). Only applicable for LimbType.Head. Used for eating."), Editable(DecimalCount = 2, MinValueFloat = -10f, MaxValueFloat = 10f)]
|
||||
[Serialize("0, 0", IsPropertySaveable.Yes, description: "Relative offset for the mouth position (starting from the center). Only applicable for LimbType.Head. Used for eating."), Editable(DecimalCount = 2, MinValueFloat = -10f, MaxValueFloat = 10f)]
|
||||
public Vector2 MouthPos { get; set; }
|
||||
|
||||
[Serialize(0f, true), Editable]
|
||||
[Serialize(0f, IsPropertySaveable.Yes), Editable]
|
||||
public float ConstantTorque { get; set; }
|
||||
|
||||
[Serialize(0f, true), Editable]
|
||||
[Serialize(0f, IsPropertySaveable.Yes), Editable]
|
||||
public float ConstantAngle { get; set; }
|
||||
|
||||
[Serialize(1f, true), Editable(DecimalCount = 2, MinValueFloat = 0, MaxValueFloat = 10)]
|
||||
[Serialize(1f, IsPropertySaveable.Yes), Editable(DecimalCount = 2, MinValueFloat = 0, MaxValueFloat = 10)]
|
||||
public float AttackForceMultiplier { get; set; }
|
||||
|
||||
[Serialize(1f, true, description:"How much damage must be done by the attack in order to be able to cut off the limb. Note that it's evaluated after the damage modifiers."), Editable(DecimalCount = 0, MinValueFloat = 0, MaxValueFloat = 1000)]
|
||||
[Serialize(1f, IsPropertySaveable.Yes, description:"How much damage must be done by the attack in order to be able to cut off the limb. Note that it's evaluated after the damage modifiers."), Editable(DecimalCount = 0, MinValueFloat = 0, MaxValueFloat = 1000)]
|
||||
public float MinSeveranceDamage { get; set; }
|
||||
|
||||
[Serialize(true, true, description: "Disable if you don't want to allow severing this joint while the creature is alive. Note: Does nothing if the 'Severance Probability Modifier' in the joint settings is 0 (default). Also note that the setting doesn't override certain limitations, e.g. severing the main limb, or legs of a walking creature is not allowed."), Editable]
|
||||
[Serialize(true, IsPropertySaveable.Yes, description: "Disable if you don't want to allow severing this joint while the creature is alive. Note: Does nothing if the 'Severance Probability Modifier' in the joint settings is 0 (default). Also note that the setting doesn't override certain limitations, e.g. severing the main limb, or legs of a walking creature is not allowed."), Editable]
|
||||
public bool CanBeSeveredAlive { get; set; }
|
||||
|
||||
//how long it takes for severed limbs to fade out
|
||||
[Serialize(10f, true, "How long it takes for the severed limb to fade out"), Editable(MinValueFloat = 0, MaxValueFloat = 100, ValueStep = 1)]
|
||||
[Serialize(10f, IsPropertySaveable.Yes, "How long it takes for the severed limb to fade out"), Editable(MinValueFloat = 0, MaxValueFloat = 100, ValueStep = 1)]
|
||||
public float SeveredFadeOutTime { get; set; } = 10.0f;
|
||||
|
||||
[Serialize(false, true, description: "Only applied when the limb is of type Tail. If none of the tails have been defined to use the angle and an angle is defined in the animation parameters, the first tail limb is used."), Editable]
|
||||
[Serialize(false, IsPropertySaveable.Yes, description: "Only applied when the limb is of type Tail. If none of the tails have been defined to use the angle and an angle is defined in the animation parameters, the first tail limb is used."), Editable]
|
||||
public bool ApplyTailAngle { get; set; }
|
||||
|
||||
[Serialize(1f, true), Editable(ValueStep = 0.1f, DecimalCount = 2)]
|
||||
[Serialize(1f, IsPropertySaveable.Yes), Editable(ValueStep = 0.1f, DecimalCount = 2)]
|
||||
public float SineFrequencyMultiplier { get; set; }
|
||||
|
||||
[Serialize(1f, true), Editable(ValueStep = 0.1f, DecimalCount = 2)]
|
||||
[Serialize(1f, IsPropertySaveable.Yes), Editable(ValueStep = 0.1f, DecimalCount = 2)]
|
||||
public float SineAmplitudeMultiplier { get; set; }
|
||||
|
||||
[Serialize(0f, true), Editable(0, 100, ValueStep = 1, DecimalCount = 1)]
|
||||
[Serialize(0f, IsPropertySaveable.Yes), Editable(0, 100, ValueStep = 1, DecimalCount = 1)]
|
||||
public float BlinkFrequency { get; set; }
|
||||
|
||||
[Serialize(0.2f, true), Editable(0.01f, 10, ValueStep = 1, DecimalCount = 2)]
|
||||
[Serialize(0.2f, IsPropertySaveable.Yes), Editable(0.01f, 10, ValueStep = 1, DecimalCount = 2)]
|
||||
public float BlinkDurationIn { get; set; }
|
||||
|
||||
[Serialize(0.5f, true), Editable(0.01f, 10, ValueStep = 1, DecimalCount = 2)]
|
||||
[Serialize(0.5f, IsPropertySaveable.Yes), Editable(0.01f, 10, ValueStep = 1, DecimalCount = 2)]
|
||||
public float BlinkDurationOut { get; set; }
|
||||
|
||||
[Serialize(0f, true), Editable(0, 10, ValueStep = 1, DecimalCount = 2)]
|
||||
[Serialize(0f, IsPropertySaveable.Yes), Editable(0, 10, ValueStep = 1, DecimalCount = 2)]
|
||||
public float BlinkHoldTime { get; set; }
|
||||
|
||||
[Serialize(0f, true), Editable(-360, 360, ValueStep = 1, DecimalCount = 0)]
|
||||
[Serialize(0f, IsPropertySaveable.Yes), Editable(-360, 360, ValueStep = 1, DecimalCount = 0)]
|
||||
public float BlinkRotationIn { get; set; }
|
||||
|
||||
[Serialize(45f, true), Editable(-360, 360, ValueStep = 1, DecimalCount = 0)]
|
||||
[Serialize(45f, IsPropertySaveable.Yes), Editable(-360, 360, ValueStep = 1, DecimalCount = 0)]
|
||||
public float BlinkRotationOut { get; set; }
|
||||
|
||||
[Serialize(50f, true), Editable]
|
||||
[Serialize(50f, IsPropertySaveable.Yes), Editable]
|
||||
public float BlinkForce { get; set; }
|
||||
|
||||
[Serialize(false, true), Editable]
|
||||
[Serialize(false, IsPropertySaveable.Yes), Editable]
|
||||
public bool OnlyBlinkInWater { get; set; }
|
||||
|
||||
[Serialize(TransitionMode.Linear, true), Editable]
|
||||
[Serialize(TransitionMode.Linear, IsPropertySaveable.Yes), Editable]
|
||||
public TransitionMode BlinkTransitionIn { get; private set; }
|
||||
|
||||
[Serialize(TransitionMode.Linear, true), Editable]
|
||||
[Serialize(TransitionMode.Linear, IsPropertySaveable.Yes), Editable]
|
||||
public TransitionMode BlinkTransitionOut { get; private set; }
|
||||
|
||||
// Non-editable ->
|
||||
// TODO: make read-only
|
||||
[Serialize(0, true)]
|
||||
[Serialize(0, IsPropertySaveable.Yes)]
|
||||
public int HealthIndex { get; set; }
|
||||
|
||||
[Serialize(0.3f, true)]
|
||||
[Serialize(0.3f, IsPropertySaveable.Yes)]
|
||||
public float Friction { get; set; }
|
||||
|
||||
[Serialize(0.05f, true)]
|
||||
[Serialize(0.05f, IsPropertySaveable.Yes)]
|
||||
public float Restitution { get; set; }
|
||||
|
||||
public LimbParams(XElement element, RagdollParams ragdoll) : base(element, ragdoll)
|
||||
public LimbParams(ContentXElement element, RagdollParams ragdoll) : base(element, ragdoll)
|
||||
{
|
||||
var spriteElement = element.GetChildElement("sprite");
|
||||
if (spriteElement != null)
|
||||
@@ -761,7 +787,7 @@ namespace Barotrauma
|
||||
public bool AddAttack()
|
||||
{
|
||||
if (Attack != null) { return false; }
|
||||
TryAddSubParam(new XElement("attack"), (e, c) => new AttackParams(e, c), out AttackParams newAttack);
|
||||
TryAddSubParam(CreateElement("attack"), (e, c) => new AttackParams(e, c), out AttackParams newAttack);
|
||||
Attack = newAttack;
|
||||
return Attack != null;
|
||||
}
|
||||
@@ -770,7 +796,7 @@ namespace Barotrauma
|
||||
public bool AddSound()
|
||||
{
|
||||
if (Sound != null) { return false; }
|
||||
TryAddSubParam(new XElement("sound"), (e, c) => new SoundParams(e, c), out SoundParams newSound);
|
||||
TryAddSubParam(CreateElement("sound"), (e, c) => new SoundParams(e, c), out SoundParams newSound);
|
||||
Sound = newSound;
|
||||
return Sound != null;
|
||||
}
|
||||
@@ -778,14 +804,14 @@ namespace Barotrauma
|
||||
public bool AddLight()
|
||||
{
|
||||
if (LightSource != null) { return false; }
|
||||
var lightSourceElement = new XElement("lightsource",
|
||||
var lightSourceElement = CreateElement("lightsource",
|
||||
new XElement("lighttexture", new XAttribute("texture", "Content/Lights/pointlight_bright.png")));
|
||||
TryAddSubParam(lightSourceElement, (e, c) => new LightSourceParams(e, c), out LightSourceParams newLightSource);
|
||||
LightSource = newLightSource;
|
||||
return LightSource != null;
|
||||
}
|
||||
|
||||
public bool AddDamageModifier() => TryAddSubParam(new XElement("damagemodifier"), (e, c) => new DamageModifierParams(e, c), out _, DamageModifiers);
|
||||
public bool AddDamageModifier() => TryAddSubParam(CreateElement("damagemodifier"), (e, c) => new DamageModifierParams(e, c), out _, DamageModifiers);
|
||||
|
||||
public bool RemoveAttack()
|
||||
{
|
||||
@@ -819,7 +845,7 @@ namespace Barotrauma
|
||||
|
||||
public bool RemoveDamageModifier(DamageModifierParams damageModifier) => RemoveSubParam(damageModifier, DamageModifiers);
|
||||
|
||||
protected bool TryAddSubParam<T>(XElement element, Func<XElement, RagdollParams, T> constructor, out T subParam, IList<T> collection = null, Func<IList<T>, bool> filter = null) where T : SubParam
|
||||
protected bool TryAddSubParam<T>(ContentXElement element, Func<ContentXElement, RagdollParams, T> constructor, out T subParam, IList<T> collection = null, Func<IList<T>, bool> filter = null) where T : SubParam
|
||||
{
|
||||
subParam = constructor(element, Ragdoll);
|
||||
if (collection != null && filter != null)
|
||||
@@ -846,7 +872,7 @@ namespace Barotrauma
|
||||
|
||||
public class DecorativeSpriteParams : SpriteParams
|
||||
{
|
||||
public DecorativeSpriteParams(XElement element, RagdollParams ragdoll) : base(element, ragdoll)
|
||||
public DecorativeSpriteParams(ContentXElement element, RagdollParams ragdoll) : base(element, ragdoll)
|
||||
{
|
||||
#if CLIENT
|
||||
DecorativeSprite = new DecorativeSprite(element);
|
||||
@@ -882,7 +908,7 @@ namespace Barotrauma
|
||||
{
|
||||
public DeformationParams Deformation { get; private set; }
|
||||
|
||||
public DeformSpriteParams(XElement element, RagdollParams ragdoll) : base(element, ragdoll)
|
||||
public DeformSpriteParams(ContentXElement element, RagdollParams ragdoll) : base(element, ragdoll)
|
||||
{
|
||||
Deformation = new DeformationParams(element, ragdoll);
|
||||
SubParams.Add(Deformation);
|
||||
@@ -891,40 +917,40 @@ namespace Barotrauma
|
||||
|
||||
public class SpriteParams : SubParam
|
||||
{
|
||||
[Serialize("0, 0, 0, 0", true), Editable]
|
||||
[Serialize("0, 0, 0, 0", IsPropertySaveable.Yes), Editable]
|
||||
public Rectangle SourceRect { get; set; }
|
||||
|
||||
[Serialize("0.5, 0.5", true, description: "The origin of the sprite relative to the collider."), Editable(DecimalCount = 3)]
|
||||
[Serialize("0.5, 0.5", IsPropertySaveable.Yes, description: "The origin of the sprite relative to the collider."), Editable(DecimalCount = 3)]
|
||||
public Vector2 Origin { get; set; }
|
||||
|
||||
[Serialize(0f, true, description: "The Z-depth of the limb relative to other limbs of the same character. 1 is front, 0 is behind."), Editable(MinValueFloat = 0, MaxValueFloat = 1, DecimalCount = 3)]
|
||||
[Serialize(0f, IsPropertySaveable.Yes, description: "The Z-depth of the limb relative to other limbs of the same character. 1 is front, 0 is behind."), Editable(MinValueFloat = 0, MaxValueFloat = 1, DecimalCount = 3)]
|
||||
public float Depth { get; set; }
|
||||
|
||||
[Serialize("", true), Editable()]
|
||||
[Serialize("", IsPropertySaveable.Yes), Editable()]
|
||||
public string Texture { get; set; }
|
||||
|
||||
[Serialize(false, true), Editable()]
|
||||
[Serialize(false, IsPropertySaveable.Yes), Editable()]
|
||||
public bool IgnoreTint { get; set; }
|
||||
|
||||
[Serialize("1.0,1.0,1.0,1.0", true), Editable()]
|
||||
[Serialize("1.0,1.0,1.0,1.0", IsPropertySaveable.Yes), Editable()]
|
||||
public Color Color { get; set; }
|
||||
|
||||
[Serialize("1.0,1.0,1.0,1.0", true, description: "Target color when the character is dead."), Editable()]
|
||||
[Serialize("1.0,1.0,1.0,1.0", IsPropertySaveable.Yes, description: "Target color when the character is dead."), Editable()]
|
||||
public Color DeadColor { get; set; }
|
||||
|
||||
[Serialize(0f, true, "How long it takes to fade into the dead color? 0 = Not applied."), Editable(DecimalCount = 1, MinValueFloat = 0, MaxValueFloat = 10)]
|
||||
[Serialize(0f, IsPropertySaveable.Yes, "How long it takes to fade into the dead color? 0 = Not applied."), Editable(DecimalCount = 1, MinValueFloat = 0, MaxValueFloat = 10)]
|
||||
public float DeadColorTime { get; set; }
|
||||
|
||||
public override string Name => "Sprite";
|
||||
|
||||
public SpriteParams(XElement element, RagdollParams ragdoll) : base(element, ragdoll) { }
|
||||
public SpriteParams(ContentXElement element, RagdollParams ragdoll) : base(element, ragdoll) { }
|
||||
|
||||
public string GetTexturePath() => string.IsNullOrWhiteSpace(Texture) ? Ragdoll.Texture : Texture;
|
||||
}
|
||||
|
||||
public class DeformationParams : SubParam
|
||||
{
|
||||
public DeformationParams(XElement element, RagdollParams ragdoll) : base(element, ragdoll)
|
||||
public DeformationParams(ContentXElement element, RagdollParams ragdoll) : base(element, ragdoll)
|
||||
{
|
||||
#if CLIENT
|
||||
Deformations = new Dictionary<SpriteDeformationParams, XElement>();
|
||||
@@ -991,7 +1017,7 @@ namespace Barotrauma
|
||||
public class ColliderParams : SubParam
|
||||
{
|
||||
private string name;
|
||||
[Serialize("", true), Editable]
|
||||
[Serialize("", IsPropertySaveable.Yes), Editable]
|
||||
public override string Name
|
||||
{
|
||||
get
|
||||
@@ -1008,16 +1034,16 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
[Serialize(0f, true), Editable(MinValueFloat = 0, MaxValueFloat = 1000)]
|
||||
[Serialize(0f, IsPropertySaveable.Yes), Editable(MinValueFloat = 0, MaxValueFloat = 1000)]
|
||||
public float Radius { get; set; }
|
||||
|
||||
[Serialize(0f, true), Editable(MinValueFloat = 0, MaxValueFloat = 1000)]
|
||||
[Serialize(0f, IsPropertySaveable.Yes), Editable(MinValueFloat = 0, MaxValueFloat = 1000)]
|
||||
public float Height { get; set; }
|
||||
|
||||
[Serialize(0f, true), Editable(MinValueFloat = 0, MaxValueFloat = 1000)]
|
||||
[Serialize(0f, IsPropertySaveable.Yes), Editable(MinValueFloat = 0, MaxValueFloat = 1000)]
|
||||
public float Width { get; set; }
|
||||
|
||||
public ColliderParams(XElement element, RagdollParams ragdoll, string name = null) : base(element, ragdoll)
|
||||
public ColliderParams(ContentXElement element, RagdollParams ragdoll, string name = null) : base(element, ragdoll)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
@@ -1029,16 +1055,16 @@ namespace Barotrauma
|
||||
{
|
||||
public override string Name => "Light Texture";
|
||||
|
||||
[Serialize("Content/Lights/pointlight_bright.png", true), Editable]
|
||||
[Serialize("Content/Lights/pointlight_bright.png", IsPropertySaveable.Yes), Editable]
|
||||
public string Texture { get; private set; }
|
||||
|
||||
[Serialize("0.5, 0.5", true), Editable(DecimalCount = 2)]
|
||||
[Serialize("0.5, 0.5", IsPropertySaveable.Yes), Editable(DecimalCount = 2)]
|
||||
public Vector2 Origin { get; set; }
|
||||
|
||||
[Serialize("1.0, 1.0", true), Editable(DecimalCount = 2)]
|
||||
[Serialize("1.0, 1.0", IsPropertySaveable.Yes), Editable(DecimalCount = 2)]
|
||||
public Vector2 Size { get; set; }
|
||||
|
||||
public LightTexture(XElement element, RagdollParams ragdoll) : base(element, ragdoll) { }
|
||||
public LightTexture(ContentXElement element, RagdollParams ragdoll) : base(element, ragdoll) { }
|
||||
}
|
||||
|
||||
public LightTexture Texture { get; private set; }
|
||||
@@ -1047,7 +1073,7 @@ namespace Barotrauma
|
||||
public Lights.LightSourceParams LightSource { get; private set; }
|
||||
#endif
|
||||
|
||||
public LightSourceParams(XElement element, RagdollParams ragdoll) : base(element, ragdoll)
|
||||
public LightSourceParams(ContentXElement element, RagdollParams ragdoll) : base(element, ragdoll)
|
||||
{
|
||||
#if CLIENT
|
||||
LightSource = new Lights.LightSourceParams(element);
|
||||
@@ -1088,9 +1114,10 @@ namespace Barotrauma
|
||||
{
|
||||
public Attack Attack { get; private set; }
|
||||
|
||||
public AttackParams(XElement element, RagdollParams ragdoll) : base(element, ragdoll)
|
||||
public AttackParams(ContentXElement element, RagdollParams ragdoll) : base(element, ragdoll)
|
||||
{
|
||||
Attack = new Attack(element, ragdoll.SpeciesName);
|
||||
var prefab = CharacterPrefab.Prefabs[ragdoll.SpeciesName];
|
||||
Attack = new Attack(element, ragdoll.SpeciesName.Value);
|
||||
}
|
||||
|
||||
public override bool Deserialize(XElement element = null, bool recursive = true)
|
||||
@@ -1117,7 +1144,7 @@ namespace Barotrauma
|
||||
public bool AddNewAffliction()
|
||||
{
|
||||
Serialize();
|
||||
var subElement = new XElement("affliction",
|
||||
var subElement = CreateElement("affliction",
|
||||
new XAttribute("identifier", "internaldamage"),
|
||||
new XAttribute("strength", 0f),
|
||||
new XAttribute("probability", 1.0f));
|
||||
@@ -1140,9 +1167,9 @@ namespace Barotrauma
|
||||
{
|
||||
public DamageModifier DamageModifier { get; private set; }
|
||||
|
||||
public DamageModifierParams(XElement element, RagdollParams ragdoll) : base(element, ragdoll)
|
||||
public DamageModifierParams(ContentXElement element, RagdollParams ragdoll) : base(element, ragdoll)
|
||||
{
|
||||
DamageModifier = new DamageModifier(element, ragdoll.SpeciesName);
|
||||
DamageModifier = new DamageModifier(element, ragdoll.SpeciesName.Value);
|
||||
}
|
||||
|
||||
public override bool Deserialize(XElement element = null, bool recursive = true)
|
||||
@@ -1170,24 +1197,27 @@ namespace Barotrauma
|
||||
{
|
||||
public override string Name => "Sound";
|
||||
|
||||
[Serialize("", true), Editable]
|
||||
[Serialize("", IsPropertySaveable.Yes), Editable]
|
||||
public string Tag { get; private set; }
|
||||
|
||||
public SoundParams(XElement element, RagdollParams ragdoll) : base(element, ragdoll) { }
|
||||
public SoundParams(ContentXElement element, RagdollParams ragdoll) : base(element, ragdoll) { }
|
||||
}
|
||||
|
||||
public abstract class SubParam : ISerializableEntity
|
||||
{
|
||||
public virtual string Name { get; set; }
|
||||
public Dictionary<string, SerializableProperty> SerializableProperties { get; private set; }
|
||||
public XElement Element { get; set; }
|
||||
public Dictionary<Identifier, SerializableProperty> SerializableProperties { get; private set; }
|
||||
public ContentXElement Element { get; set; }
|
||||
public XElement OriginalElement { get; protected set; }
|
||||
public List<SubParam> SubParams { get; set; } = new List<SubParam>();
|
||||
public RagdollParams Ragdoll { get; private set; }
|
||||
|
||||
public virtual string GenerateName() => Element.Name.ToString();
|
||||
|
||||
public SubParam(XElement element, RagdollParams ragdoll)
|
||||
protected ContentXElement CreateElement(string name, params object[] attrs)
|
||||
=> new XElement(name, attrs).FromPackage(Element.ContentPackage);
|
||||
|
||||
public SubParam(ContentXElement element, RagdollParams ragdoll)
|
||||
{
|
||||
Element = element;
|
||||
OriginalElement = new XElement(element);
|
||||
@@ -1226,7 +1256,7 @@ namespace Barotrauma
|
||||
public virtual void Reset()
|
||||
{
|
||||
// Don't use recursion, because the reset method might be overriden
|
||||
Deserialize(OriginalElement, false);
|
||||
Deserialize(OriginalElement, recursive: false);
|
||||
SubParams.ForEach(sp => sp.Reset());
|
||||
}
|
||||
|
||||
@@ -1235,21 +1265,21 @@ namespace Barotrauma
|
||||
public Dictionary<Affliction, SerializableEntityEditor> AfflictionEditors { get; private set; }
|
||||
public virtual void AddToEditor(ParamsEditor editor, bool recursive = true, int space = 0)
|
||||
{
|
||||
SerializableEntityEditor = new SerializableEntityEditor(editor.EditorBox.Content.RectTransform, this, inGame: false, showName: true, titleFont: GUI.LargeFont);
|
||||
SerializableEntityEditor = new SerializableEntityEditor(editor.EditorBox.Content.RectTransform, this, inGame: false, showName: true, titleFont: GUIStyle.LargeFont);
|
||||
if (this is DecorativeSpriteParams decSpriteParams)
|
||||
{
|
||||
new SerializableEntityEditor(editor.EditorBox.Content.RectTransform, decSpriteParams.DecorativeSprite, inGame: false, showName: true, titleFont: GUI.LargeFont);
|
||||
new SerializableEntityEditor(editor.EditorBox.Content.RectTransform, decSpriteParams.DecorativeSprite, inGame: false, showName: true, titleFont: GUIStyle.LargeFont);
|
||||
}
|
||||
else if (this is DeformSpriteParams deformSpriteParams)
|
||||
{
|
||||
foreach (var deformation in deformSpriteParams.Deformation.Deformations.Keys)
|
||||
{
|
||||
new SerializableEntityEditor(editor.EditorBox.Content.RectTransform, deformation, inGame: false, showName: true, titleFont: GUI.LargeFont);
|
||||
new SerializableEntityEditor(editor.EditorBox.Content.RectTransform, deformation, inGame: false, showName: true, titleFont: GUIStyle.LargeFont);
|
||||
}
|
||||
}
|
||||
else if (this is AttackParams attackParams)
|
||||
{
|
||||
SerializableEntityEditor = new SerializableEntityEditor(editor.EditorBox.Content.RectTransform, attackParams.Attack, inGame: false, showName: true, titleFont: GUI.LargeFont);
|
||||
SerializableEntityEditor = new SerializableEntityEditor(editor.EditorBox.Content.RectTransform, attackParams.Attack, inGame: false, showName: true, titleFont: GUIStyle.LargeFont);
|
||||
if (AfflictionEditors == null)
|
||||
{
|
||||
AfflictionEditors = new Dictionary<Affliction, SerializableEntityEditor>();
|
||||
@@ -1267,11 +1297,11 @@ namespace Barotrauma
|
||||
}
|
||||
else if (this is LightSourceParams lightParams)
|
||||
{
|
||||
SerializableEntityEditor = new SerializableEntityEditor(editor.EditorBox.Content.RectTransform, lightParams.LightSource, inGame: false, showName: true, titleFont: GUI.LargeFont);
|
||||
SerializableEntityEditor = new SerializableEntityEditor(editor.EditorBox.Content.RectTransform, lightParams.LightSource, inGame: false, showName: true, titleFont: GUIStyle.LargeFont);
|
||||
}
|
||||
else if (this is DamageModifierParams damageModifierParams)
|
||||
{
|
||||
SerializableEntityEditor = new SerializableEntityEditor(editor.EditorBox.Content.RectTransform, damageModifierParams.DamageModifier, inGame: false, showName: true, titleFont: GUI.LargeFont);
|
||||
SerializableEntityEditor = new SerializableEntityEditor(editor.EditorBox.Content.RectTransform, damageModifierParams.DamageModifier, inGame: false, showName: true, titleFont: GUIStyle.LargeFont);
|
||||
}
|
||||
if (recursive)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user