Unstable 0.17.0.0

This commit is contained in:
Markus Isberg
2022-02-26 02:43:01 +09:00
parent a83f375681
commit 3974067915
913 changed files with 32472 additions and 32364 deletions
@@ -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)
{