Unstable 1.8.4.0

This commit is contained in:
Markus Isberg
2025-03-12 12:56:27 +00:00
parent a4c3e868e4
commit a4a3427e4e
627 changed files with 29860 additions and 10018 deletions
@@ -44,9 +44,6 @@ namespace Barotrauma
[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, IsPropertySaveable.Yes), Editable]
public bool MultiplyByDir { get; set; }
[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; }
@@ -56,6 +53,48 @@ namespace Barotrauma
[Header("Movement")]
[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; }
[Serialize(1.0f, IsPropertySaveable.Yes, description: "Adjusts the maximum speed while climbing. The actual speed is affected by the MovementSpeed."), Editable(MinValueFloat = 0.1f, MaxValueFloat = 10f, DecimalCount = 2)]
public float ClimbSpeed { get; set; }
[Serialize(2.0f, IsPropertySaveable.Yes, description: "Used instead of ClimbSpeed when descending ladders while moving fast (running). Not used if lower than ClimbSpeed."), Editable(MinValueFloat = 0.1f, MaxValueFloat = 10f, DecimalCount = 2)]
public float SlideSpeed { get; set; }
[Serialize(10.5f, IsPropertySaveable.Yes, description: "Force applied to the main collider, torso and head, when climbing ladders."), Editable(MinValueFloat = 0.1f, MaxValueFloat = 100f, DecimalCount = 1)]
public float ClimbBodyMoveForce { get; set; }
[Serialize(5.2f, IsPropertySaveable.Yes, description: "Force applied to the hands when climbing ladders."), Editable(MinValueFloat = 0.1f, MaxValueFloat = 100f, DecimalCount = 1)]
public float ClimbHandMoveForce { get; set; }
[Serialize(10.0f, IsPropertySaveable.Yes, description: "Force applied to the feet when climbing ladders."), Editable(MinValueFloat = 0.1f, MaxValueFloat = 100f, DecimalCount = 1)]
public float ClimbFootMoveForce { get; set; }
[Serialize(30.0f, IsPropertySaveable.Yes), Editable(MinValueFloat = 0.1f, MaxValueFloat = 100f, DecimalCount = 1)]
public float ClimbStepHeight { get; set; }
protected override bool Deserialize(XElement element = null)
{
if (element.GetAttributeEnum(nameof(AnimationType), AnimationType.NotDefined) is AnimationType.Run)
{
// These values were previously hard-coded when running, so we need to set different default values for the run animations, when they are not defined.
const string climbSpeedName = nameof(ClimbSpeed);
if (element.GetAttribute(climbSpeedName) == null)
{
element.SetAttribute(climbSpeedName, 2.0f);
}
const string climbStepName = nameof(ClimbStepHeight);
if (element.GetAttribute(climbStepName) == null)
{
element.SetAttribute(climbStepName, 60.0f);
}
const string slideSpeedName = nameof(SlideSpeed);
if (element.GetAttribute(slideSpeedName) == null)
{
element.SetAttribute(slideSpeedName, 4.0f);
}
}
return base.Deserialize(element);
}
}
abstract class SwimParams : AnimationParams
@@ -92,7 +131,7 @@ namespace Barotrauma
/// <summary>
/// In degrees.
/// </summary>
[Header("Standing")]
[Header("Orientation")]
[Serialize(float.NaN, IsPropertySaveable.Yes), Editable(-360f, 360f)]
public float HeadAngle
{
@@ -143,14 +182,14 @@ namespace Barotrauma
public float HandIKStrength { get; set; }
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 GetDefaultFilePath(Identifier speciesName, AnimationType animType) => Barotrauma.IO.Path.Combine(GetFolder(speciesName), $"{GetDefaultFileName(speciesName, animType)}.xml");
public static string GetFolder(Identifier speciesName)
{
CharacterPrefab prefab = CharacterPrefab.FindBySpeciesName(speciesName);
if (prefab?.ConfigElement == null)
{
DebugConsole.ThrowError($"Failed to find config file for '{speciesName}'");
DebugConsole.ThrowError($"Failed to find config file for '{speciesName}'", contentPackage: prefab?.ContentPackage);
return string.Empty;
}
return GetFolder(prefab.ConfigElement, prefab.FilePath.Value);
@@ -275,25 +314,27 @@ namespace Barotrauma
else if (string.IsNullOrEmpty(fileName))
{
// Files found, but none specified -> Get a matching animation from the specified folder.
// First try to find a file that matches the default file name. If that fails, just take any file.
// First try to find a file that matches the default file name. If that fails, just take any file of the matching type.
string defaultFileName = GetDefaultFileName(animSpecies, animType);
selectedFile = filteredFiles.FirstOrDefault(path => PathMatchesFile(path, defaultFileName)) ?? filteredFiles.First();
}
else
{
// Try to get the specified file. If that fails, just take any file of the matching type.
selectedFile = filteredFiles.FirstOrDefault(path => PathMatchesFile(path, fileName));
if (selectedFile == null)
{
errorMessages.Add($"[AnimationParams] Could not find an animation file that matches the name {fileName} and the animation type {animType}. Using the default animations.");
errorMessages.Add($"[AnimationParams] Could not find an animation file that matches the name {fileName} and the animation type {animType}. Using the first file of the matching type.");
selectedFile = filteredFiles.First();
}
}
}
}
}
else
{
errorMessages.Add($"[AnimationParams] Invalid directory: {folder}. Using the default animation.");
}
selectedFile ??= GetDefaultFile(fallbackSpecies, animType);
selectedFile ??= GetDefaultFilePath(fallbackSpecies, animType);
Debug.Assert(selectedFile != null);
if (errorMessages.None())
{
@@ -375,7 +416,7 @@ namespace Barotrauma
{
if (animationType == AnimationType.NotDefined)
{
throw new Exception("Cannot create an animation file of type " + animationType.ToString());
throw new Exception("Cannot create an animation file of type " + animationType);
}
if (!allAnimations.TryGetValue(speciesName, out Dictionary<string, AnimationParams> anims))
{
@@ -504,7 +545,7 @@ namespace Barotrauma
{
if (doc == null)
{
DebugConsole.ThrowError("[AnimationParams] The source XML Document is null!");
DebugConsole.ThrowError("[AnimationParams] The source XML Document is null!", contentPackage: Path.ContentPackage);
return;
}
Serialize();
@@ -19,6 +19,16 @@ namespace Barotrauma
{
[Serialize("", IsPropertySaveable.Yes), Editable]
public Identifier SpeciesName { get; private set; }
[Serialize("", IsPropertySaveable.Yes), Editable]
public string Tags
{
get => tags.ConvertToString();
set => tags = value.ToIdentifiers().ToHashSet();
}
private HashSet<Identifier> tags = new HashSet<Identifier>();
public bool HasTag(Identifier tag) => tags.Contains(tag);
[Serialize("", IsPropertySaveable.Yes, description: "References to another species. Define only if the creature is a variant that needs to use a pre-existing translation."), Editable]
public Identifier SpeciesTranslationOverride { get; private set; }
@@ -37,9 +47,21 @@ namespace Barotrauma
[Serialize(false, IsPropertySaveable.Yes, description: "Can the creature interact with items?"), Editable]
public bool CanInteract { get; private set; }
[Serialize(true, IsPropertySaveable.Yes, description: "Can the creature use ladders? Doesn't have an effect, if CanInteract is false."), Editable]
public bool CanClimb { get; private set; }
[Serialize(false, IsPropertySaveable.Yes, description: "If set true, this character only uses the climbing parameters defined in the walk parameters (not run)."), Editable]
public bool ForceSlowClimbing { get; private set; }
[Serialize(false, IsPropertySaveable.Yes, description: "Should this character be treated as a husk?"), Editable]
public bool Husk { get; private set; }
[Serialize("", IsPropertySaveable.Yes, description: "If this character can turn into a husk, which character it turns to? If not defined, uses the default pattern (e.g. Crawler -> Crawlerhusk, Human -> Humanhusk)."), Editable]
public Identifier HuskedSpecies { get; private set; }
[Serialize("", IsPropertySaveable.Yes, description: "If this character is a husk, from what species it can be turned into? If not defined, uses the default pattern (e.g. Crawlerhusk -> Crawler, Humanhusk -> Human)."), Editable]
public Identifier NonHuskedSpecies { get; private set; }
[Serialize(false, IsPropertySaveable.Yes, description:"Should this character use a special husk appendage, attached to the ragdoll, when it turns into a husk?"), Editable]
public bool UseHuskAppendage { get; private set; }
@@ -125,7 +147,17 @@ namespace Barotrauma
[Serialize("", IsPropertySaveable.Yes, description: "Identifier or tag of the item the character's items are placed inside when the character despawns."), Editable]
public Identifier DespawnContainer { get; private set; }
[Serialize("monster", IsPropertySaveable.Yes, description: "If changed, this character will try to play a custom music track with the specified identifier when encountered."), Editable]
public Identifier MusicType { get; private set; }
[Serialize(1.0f, IsPropertySaveable.Yes, description: "The commonness of this character's music when a random track will be chosen."), Editable]
public float MusicCommonness { get; private set; }
[Serialize(1.0f, IsPropertySaveable.Yes, description: "The multiplier of the minimum distance required between this character and the player/submarine before the music starts playing. The default distance is twice the length of the submarine, or a minimum of 50 meters."), Editable]
public float MusicRangeMultiplier { get; private set; }
public readonly CharacterFile File;
public bool IsPet => AI?.IsPet ?? false;
public XDocument VariantFile { get; private set; }
@@ -161,7 +193,7 @@ namespace Barotrauma
}
}
public static XElement CreateVariantXml(XElement variantXML, XElement baseXML)
public static XElement CreateVariantXml(ContentXElement variantXML, ContentXElement baseXML)
{
XElement newXml = variantXML.CreateVariantXML(baseXML);
XElement variantAi = variantXML.GetChildElement("ai");
@@ -171,25 +203,32 @@ namespace Barotrauma
{
return newXml;
}
// CreateVariantXML seems to merge the ai targets so that in the new xml we have both the old and the new target definitions.
// CreateVariantXML does not understand anything about targeting tags, it just replaces the <target> elements in the order they're defined in.
// We can do better here by replacing the target with a matching tag, so let's clear the element and do that.
var finalAiElement = newXml.GetChildElement("ai");
var processedTags = new HashSet<string>();
foreach (var aiTarget in finalAiElement.Elements().ToArray())
finalAiElement.Elements().Remove();
//add all the targets from the base character
baseAi.Elements().ForEach(e => finalAiElement.Add(e));
var processedTags = new List<Identifier>();
foreach (var variantTargetElement in variantAi.Elements())
{
string tag = aiTarget.GetAttributeString("tag", null);
if (tag == null) { continue; }
if (processedTags.Contains(tag))
Identifier tag = variantTargetElement.GetAttributeIdentifier("tag", Identifier.Empty);
var matchingElements = finalAiElement.Elements().Where(e => e.GetAttributeIdentifier("tag", Identifier.Empty) == tag);
int alreadyProcessed = processedTags.Count(t => t == tag);
if (matchingElements.Count() > alreadyProcessed)
{
aiTarget.Remove();
continue;
//more matching elements found, replace the first one that hasn't been processed yet
matchingElements.Skip(alreadyProcessed).First().ReplaceWith(variantTargetElement);
}
else
{
//no more matching elements in the base XML, this must be a new target
finalAiElement.Add(variantTargetElement);
}
processedTags.Add(tag);
var matchInSelf = variantAi.Elements().FirstOrDefault(e => e.GetAttributeString("tag", null) == tag);
var matchInParent = baseAi.Elements().FirstOrDefault(e => e.GetAttributeString("tag", null) == tag);
if (matchInSelf != null && matchInParent != null)
{
aiTarget.ReplaceWith(new XElement(matchInSelf));
}
}
return newXml;
}
@@ -433,17 +472,11 @@ namespace Barotrauma
[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()
.Where(id => !id.IsEmpty)
.ToImmutableHashSet();
}
get => TagSet.ConvertToString();
private set => TagSet = value.ToIdentifiers().ToImmutableHashSet();
}
public ImmutableHashSet<Identifier> TagSet { get; private set; }
public ImmutableHashSet<Identifier> TagSet { get; private set; } = ImmutableHashSet<Identifier>.Empty;
public SoundParams(ContentXElement element, CharacterParams character) : base(element, character)
{
@@ -549,6 +582,15 @@ namespace Barotrauma
[Serialize(0f, IsPropertySaveable.Yes), Editable]
public float EmpVulnerability { get; set; }
[Serialize(true, IsPropertySaveable.Yes, description: "Apply movement penalties when legs or tail limbs get damaged. Enabled by default."), Editable]
public bool ApplyMovementPenalties { get; set; }
[Serialize(true, IsPropertySaveable.Yes, description: "Normally characters die when they don't have a head. But maybe not all of them?"), Editable]
public bool DieFromBeheading { get; set; }
[Serialize(false, IsPropertySaveable.Yes, description: "Severing legs doesn't work with most characters, because we'd need to take that into account with the walking animations and the standing position of the main collider etc. But there might be cases where you'll want to override this default."), Editable]
public bool AllowSeveringLegs { get; set; }
[Serialize(false, IsPropertySaveable.Yes, description: "Can afflictions affect the face/body tint of the character."), Editable]
public bool ApplyAfflictionColors { get; private set; }
@@ -719,34 +761,50 @@ namespace Barotrauma
[Serialize(WallTargetingMethod.Target, IsPropertySaveable.Yes, description: "Defines the method of checking whether there's a blocking (submarine) wall."), Editable]
public WallTargetingMethod WallTargetingMethod { get; private set; }
[Serialize(0f, IsPropertySaveable.Yes, "How likely it is that the creature plays dead (= ragdolls) while idling? Only allowed inside a sub (not in the open waters). Evaluated once, when the creature spawns."), Editable]
public float PlayDeadProbability { get; set; }
public readonly bool IsPet;
public IEnumerable<TargetParams> Targets => targets;
protected readonly List<TargetParams> targets = new List<TargetParams>();
private readonly List<TargetParams> targets = new List<TargetParams>();
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 _));
element.GetChildElements("target").ForEach(t => AddTarget(t));
element.GetChildElements("targetpriority").ForEach(t => AddTarget(t));
IsPet = element.GetChildElement("petbehavior") != null;
}
/// <summary>
/// Adds a target but checks for duplicates first. Doesn't allow adding multiple targets with the same tag (see <see cref="AddTarget"/>).
/// </summary>
private bool TryAddTarget(ContentXElement targetElement, out TargetParams target)
{
string tag = targetElement.GetAttributeString("tag", null);
if (HasTag(tag))
{
target = null;
DebugConsole.AddWarning($"Trying to add multiple targets with the same tag ('{tag}') defined! Only the first will be used!",
targetElement.ContentPackage);
return false;
DebugConsole.AddWarning($"Trying to add multiple targets with the same tag ('{tag}') defined! Only the first will be used!", targetElement.ContentPackage);
}
else
{
target = new TargetParams(targetElement, Character);
targets.Add(target);
SubParams.Add(target);
return true;
target = AddTarget(targetElement);
}
return target != null;
}
/// <summary>
/// This method allows adding multiple targets with the same tag.
/// </summary>
private TargetParams AddTarget(ContentXElement targetElement)
{
var target = new TargetParams(targetElement, Character);
targets.Add(target);
SubParams.Add(target);
return target;
}
public bool TryAddEmptyTarget(out TargetParams targetParams) => TryAddNewTarget("newtarget" + targets.Count, AIState.Attack, 0f, out targetParams);
@@ -782,26 +840,40 @@ namespace Barotrauma
}
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)
public IEnumerable<TargetParams> GetMatchingTargets(Func<TargetParams, bool> predicate) => targets.Where(predicate);
public IEnumerable<TargetParams> GetTargets(Identifier target) => GetMatchingTargets(t => t.Tag == target);
public IEnumerable<TargetParams> GetTargets(Character target) => GetMatchingTargets(t => t.Tag == target.SpeciesName || t.Tag == target.Params.Group || target.Params.HasTag(t.Tag));
public TargetParams GetHighestPriorityTarget(Identifier target) => GetHighestPriorityTarget(GetTargets(target));
public TargetParams GetHighestPriorityTarget(Character target) => GetHighestPriorityTarget(GetTargets(target));
private static TargetParams GetHighestPriorityTarget(IEnumerable<TargetParams> targetParams) => targetParams.MaxBy(static t => t.Priority);
public bool TryGetTargets(Identifier target, out IEnumerable<TargetParams> targetParams)
{
target = targets.FirstOrDefault(t => t.Tag == targetTag);
return target != null;
targetParams = GetTargets(target);
return targetParams.Any();
}
public bool TryGetTargets(Character target, out IEnumerable<TargetParams> targetParams)
{
targetParams = GetTargets(target);
return targetParams.Any();
}
public bool TryGetHighestPriorityTarget(Identifier target, out TargetParams targetParams)
{
targetParams = GetHighestPriorityTarget(target);
return targetParams != null;
}
public bool TryGetHighestPriorityTarget(Character target, out TargetParams targetParams)
{
targetParams = GetHighestPriorityTarget(target);
return targetParams != null;
}
public bool TryGetTarget(Character targetCharacter, out TargetParams target)
{
if (!TryGetTarget(targetCharacter.SpeciesName, out target))
{
target = targets.FirstOrDefault(t => t.Tag == targetCharacter.Params.Group);
}
return target != null;
}
public bool TryGetTarget(IEnumerable<Identifier> tags, out TargetParams target)
public bool TryGetHighestPriorityTarget(IEnumerable<Identifier> tags, out TargetParams target)
{
target = null;
if (tags == null || tags.None()) { return false; }
@@ -819,22 +891,6 @@ namespace Barotrauma
}
return target != null;
}
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)
{
DebugConsole.ThrowError($"Cannot find a target with the tag {targetTag}!");
}
}
return target;
}
}
public class TargetParams : SubParam
@@ -889,7 +945,7 @@ namespace Barotrauma
[Serialize(-1f, IsPropertySaveable.Yes, description: "A generic max threshold. Not used if set to negative."), Editable]
public float ThresholdMax { get; private set; }
[Serialize(1.0f, IsPropertySaveable.Yes, description: "Can be used to make the monster perceive the target further than it normally can."), Editable]
[Serialize(1.0f, IsPropertySaveable.Yes, description: "Can be used to make the monster perceive the target further or closer than it normally can."), Editable]
public float PerceptionDistanceMultiplier { get; private set; }
[Serialize(-1.0f, IsPropertySaveable.Yes, description: "Maximum distance at which the monster can perceive the target, regardless of the sight/hearing or how visible or how much noise the target is making. Not used if set to negative."), Editable]
@@ -113,7 +113,7 @@ namespace Barotrauma
[Serialize(true, IsPropertySaveable.Yes), Editable]
public bool CanWalk { get; set; }
[Serialize(true, IsPropertySaveable.Yes, description: "Can the character be dragged around by other creatures?"), Editable()]
public bool Draggable { get; set; }
@@ -137,15 +137,14 @@ namespace Barotrauma
.Concat(Joints);
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(Identifier speciesName, ContentPackage contentPackage = null)
public static string GetDefaultFile(Identifier speciesName) => IO.Path.Combine(GetFolder(speciesName), $"{GetDefaultFileName(speciesName)}.xml");
public static string GetFolder(Identifier speciesName)
{
CharacterPrefab prefab = CharacterPrefab.Find(p => p.Identifier == speciesName && (contentPackage == null || p.ContentFile.ContentPackage == contentPackage));
CharacterPrefab prefab = CharacterPrefab.FindBySpeciesName(speciesName);
if (prefab?.ConfigElement == null)
{
DebugConsole.ThrowError($"Failed to find config file for '{speciesName}'", contentPackage: contentPackage);
DebugConsole.ThrowError($"Failed to find config file for '{speciesName}'");
return string.Empty;
}
return GetFolder(prefab.ConfigElement, prefab.ContentFile.Path.Value);
@@ -199,10 +198,10 @@ namespace Barotrauma
}
}
}
else if (!variantOf.IsEmpty && CharacterPrefab.FindBySpeciesName(variantOf) is CharacterPrefab prefab)
else if (!variantOf.IsEmpty && CharacterPrefab.FindBySpeciesName(variantOf) is CharacterPrefab parentPrefab)
{
// Ragdoll element not defined -> use the ragdoll defined in the base definition file.
ragdollSpecies = prefab.GetBaseCharacterSpeciesName(variantOf);
//get the params from the parent prefab if this one doesn't re-define them
return GetDefaultRagdollParams<T>(variantOf, parentPrefab.ConfigElement, parentPrefab.ContentPackage);
}
// Using a null file definition means we use the default animations found in the Ragdolls folder.
return GetRagdollParams<T>(speciesName, ragdollSpecies, file: null, contentPackage);
@@ -245,7 +244,7 @@ namespace Barotrauma
}
else
{
DebugConsole.ThrowError($"[AnimationParams] Failed to load an animation {ragdollInstance} from {contentPath.Value} for the character {speciesName}. Using the default ragdoll.", contentPackage: contentPackage);
DebugConsole.ThrowError($"[RagdollParams] Failed to load a ragdoll {ragdollInstance} from {contentPath.Value} for the character {speciesName}. Using the default ragdoll.", contentPackage: contentPackage);
}
}
// Seek the default ragdoll from the character's ragdoll folder.
@@ -294,8 +293,30 @@ namespace Barotrauma
}
else
{
// Failing to create a ragdoll causes so many issues that cannot be handled. Dummy ragdoll just seems to make things harder to debug. It's better to fail early.
throw new Exception($"[RagdollParams] Failed to load ragdoll {r.Name} from {selectedFile} for the character {speciesName}.");
string error = $"[RagdollParams] Failed to load ragdoll {r.Name} from {selectedFile} for the character {speciesName}.";
if (contentPackage == GameMain.VanillaContent)
{
// Check if the base character content package is vanilla too.
CharacterPrefab characterPrefab = CharacterPrefab.FindBySpeciesName(speciesName);
if (characterPrefab?.ParentPrefab == null || characterPrefab.ParentPrefab.ContentPackage == GameMain.VanillaContent)
{
// If the error is in the vanilla content, it's just better to crash early.
// If dodging with the solution below fails, we'll also get here.
throw new Exception(error);
}
}
// Try to dodge crashing on modded content.
DebugConsole.ThrowError(error, contentPackage: contentPackage);
if (typeof(T) == typeof(HumanRagdollParams))
{
Identifier fallbackSpecies = CharacterPrefab.HumanSpeciesName;
r = GetRagdollParams<T>(fallbackSpecies, fallbackSpecies, file: ContentPath.FromRaw(contentPackage, "Content/Characters/Human/Ragdolls/HumanDefaultRagdoll.xml"), contentPackage: GameMain.VanillaContent);
}
else
{
Identifier fallbackSpecies = "crawler".ToIdentifier();
r = GetRagdollParams<T>(fallbackSpecies, fallbackSpecies, file: ContentPath.FromRaw(contentPackage, "Content/Characters/Crawler/Ragdolls/CrawlerDefaultRagdoll.xml"), contentPackage: GameMain.VanillaContent);
}
}
return r;
}
@@ -654,7 +675,7 @@ namespace Barotrauma
[Serialize(0.25f, IsPropertySaveable.Yes), Editable]
public float Stiffness { get; set; }
[Serialize(1f, IsPropertySaveable.Yes, 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(DecimalCount = 2)]
public float Scale { get; set; }
[Serialize(false, IsPropertySaveable.No), Editable(ReadOnly = true)]
@@ -705,6 +726,9 @@ namespace Barotrauma
[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; }
[Serialize(LimbType.None, IsPropertySaveable.Yes, description: "Secondary limb type to be used for generic purposes. Currently only used in climbing animations."), Editable()]
public LimbType SecondaryType { get; set; }
/// <summary>
/// The orientation of the sprite as drawn on the sprite sheet (in radians).
@@ -775,6 +799,12 @@ namespace Barotrauma
[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(50f, IsPropertySaveable.Yes, description: "How much torque is applied on the head while updating the eating animations?"), Editable]
public float EatTorque { get; set; }
[Serialize(2f, IsPropertySaveable.Yes, description: "How strong a linear impulse is applied on the head while updating the eating animations?"), Editable]
public float EatImpulse { get; set; }
[Serialize(0f, IsPropertySaveable.Yes), Editable]
public float ConstantTorque { get; set; }
@@ -795,8 +825,11 @@ namespace Barotrauma
[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, 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]
[Serialize(false, IsPropertySaveable.Yes, description: "Should the tail angle be applied on this limb? If none of the limbs 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(false, IsPropertySaveable.Yes, description: "Should this limb be moved like a tail when swimming? Always true for tail limbs. On tails, disable by setting SineFrequencyMultiplier to 0."), Editable]
public bool ApplySineMovement { get; set; }
[Serialize(1f, IsPropertySaveable.Yes), Editable(ValueStep = 0.1f, DecimalCount = 2)]
public float SineFrequencyMultiplier { get; set; }
@@ -857,6 +890,9 @@ namespace Barotrauma
[Serialize(true, IsPropertySaveable.Yes, description: "Can the limb enter submarines? Only valid if the ragdoll's CanEnterSubmarine is set to Partial, otherwise the limb can enter if the ragdoll can."), Editable]
public bool CanEnterSubmarine { get; private set; }
[Serialize(LimbType.None, IsPropertySaveable.Yes, description: "When set to something else than None, this limb will be hidden if the limb of the specified type is hidden."), Editable]
public LimbType InheritHiding { get; set; }
public LimbParams(ContentXElement element, RagdollParams ragdoll) : base(element, ragdoll)
{
var spriteElement = element.GetChildElement("sprite");