(d9829ac) v0.9.4.0

This commit is contained in:
Regalis
2019-10-24 18:05:42 +02:00
parent 9aa12bcac2
commit b39922a074
319 changed files with 12516 additions and 6815 deletions
@@ -2,14 +2,26 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma
{
class Affliction
class Affliction : ISerializableEntity
{
public readonly AfflictionPrefab Prefab;
public float Strength;
public string Name => ToString();
public Dictionary<string, SerializableProperty> SerializableProperties { get; set; }
[Serialize(0f, true), Editable]
public float Strength { get; set; }
[Serialize("", true), Editable]
public string Identifier { get; private set; }
[Serialize(1.0f, true, description: "The probability for the affliction to be applied."), Editable(minValue: 0f, maxValue: 1f)]
public float Probability { get; private set; } = 1.0f;
public float DamagePerSecond;
public float DamagePerSecondTimer;
@@ -18,11 +30,6 @@ namespace Barotrauma
public float StrengthDiminishMultiplier = 1.0f;
public Affliction MultiplierSource;
/// <summary>
/// Probability for the affliction to be applied. Used by attacks.
/// </summary>
public float ApplyProbability = 1.0f;
/// <summary>
/// Which character gave this affliction
/// </summary>
@@ -32,6 +39,17 @@ namespace Barotrauma
{
Prefab = prefab;
Strength = strength;
Identifier = prefab?.Identifier;
}
public void Serialize(XElement element)
{
SerializableProperty.SerializeProperties(this, element);
}
public void Deserialize(XElement element)
{
SerializableProperties = SerializableProperty.DeserializeProperties(this, element);
}
public Affliction CreateMultiplied(float multiplier)
@@ -39,10 +57,7 @@ namespace Barotrauma
return Prefab.Instantiate(Strength * multiplier, Source);
}
public override string ToString()
{
return "Affliction (" + Prefab.Name + ")";
}
public override string ToString() => Prefab == null ? "Affliction (Invalid)" : $"Affliction ({Prefab.Name})";
public float GetVitalityDecrease(CharacterHealth characterHealth)
{
@@ -22,7 +22,7 @@ namespace Barotrauma
{
get { return state; }
}
public AfflictionHusk(AfflictionPrefab prefab, float strength) :
base(prefab, strength)
{
@@ -96,60 +96,27 @@ namespace Barotrauma
}
}
private void ActivateHusk(Character character)
public void ActivateHusk(Character character)
{
character.NeedsAir = false;
if (huskAppendage == null)
{
huskAppendage = AttachHuskAppendage(character);
character.SetStun(0.5f);
}
}
public static List<Limb> AttachHuskAppendage(Character character, Ragdoll ragdoll = null)
{
var huskDoc = XMLExtensions.TryLoadXml(Character.GetConfigFile("humanhusk"));
string pathToAppendage = huskDoc.Root.Element("huskappendage").GetAttributeString("path", string.Empty);
XDocument doc = XMLExtensions.TryLoadXml(pathToAppendage);
if (doc == null || doc.Root == null) { return null; }
if (ragdoll == null)
{
ragdoll = character.AnimController;
}
if (ragdoll.Dir < 1.0f)
{
ragdoll.Flip();
}
var huskAppendages = new List<Limb>();
var limbElements = doc.Root.Elements("limb").ToDictionary(e => e.GetAttributeString("id", null), e => e);
foreach (var jointElement in doc.Root.Elements("joint"))
{
if (limbElements.TryGetValue(jointElement.GetAttributeString("limb2", null), out XElement limbElement))
huskAppendage = AttachHuskAppendage(character, Prefab.Identifier);
if (huskAppendage != null)
{
JointParams jointParams = new JointParams(jointElement, ragdoll.RagdollParams);
Limb attachLimb = ragdoll.Limbs[jointParams.Limb1];
Limb huskAppendage = new Limb(ragdoll, character, new LimbParams(limbElement, ragdoll.RagdollParams));
huskAppendage.body.Submarine = character.Submarine;
huskAppendage.body.SetTransform(attachLimb.SimPosition, attachLimb.Rotation);
ragdoll.AddLimb(huskAppendage);
ragdoll.AddJoint(jointParams);
huskAppendages.Add(huskAppendage);
character.NeedsAir = false;
character.SetStun(0.5f);
}
}
return huskAppendages;
}
private void DeactivateHusk(Character character)
{
character.NeedsAir = true;
RemoveHuskAppendage(character);
}
private void RemoveHuskAppendage(Character character)
{
if (huskAppendage == null) return;
huskAppendage.ForEach(l => character.AnimController.RemoveLimb(l));
huskAppendage = null;
character.NeedsAir = character.Params.MainElement.GetAttributeBool("needsair", false);
if (huskAppendage != null)
{
huskAppendage.ForEach(l => character.AnimController.RemoveLimb(l));
huskAppendage = null;
}
}
public void Remove(Character character)
@@ -182,7 +149,8 @@ namespace Barotrauma
character.Enabled = false;
Entity.Spawner.AddToRemoveQueue(character);
var configFile = Character.GetConfigFile("humanhusk");
string speciesName = GetHuskedSpeciesName(character.SpeciesName, Prefab as AfflictionPrefabHusk);
string configFile = Character.GetConfigFilePath(speciesName);
if (string.IsNullOrEmpty(configFile))
{
@@ -190,16 +158,7 @@ namespace Barotrauma
yield return CoroutineStatus.Success;
}
//XDocument doc = XMLExtensions.TryLoadXml(configFile);
//if (doc?.Root == null)
//{
// DebugConsole.ThrowError("Failed to turn character \"" + character.Name + "\" into a husk - husk config file ("+configFile+") could not be read.");
// yield return CoroutineStatus.Success;
//}
//character.Info.Ragdoll = null;
//character.Info.SourceElement = doc.Root;
var husk = Character.Create(configFile, character.WorldPosition, character.Info.Name, character.Info, isRemotePlayer: false, hasAi: true);
var husk = Character.Create(configFile, character.WorldPosition, character.Info.Name, character.Info, isRemotePlayer: false, hasAi: true, ragdoll: character.AnimController.RagdollParams);
foreach (Limb limb in husk.AnimController.Limbs)
{
@@ -220,7 +179,7 @@ namespace Barotrauma
if (character.Inventory.Items.Length != husk.Inventory.Items.Length)
{
string errorMsg = "Failed to move items from a human's inventory into a humanhusk's inventory (inventory sizes don't match)";
string errorMsg = "Failed to move items from the source character's inventory into a husk's inventory (inventory sizes don't match)";
DebugConsole.ThrowError(errorMsg);
GameAnalyticsManager.AddErrorEventOnce("AfflictionHusk.CreateAIHusk:InventoryMismatch", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
yield return CoroutineStatus.Success;
@@ -234,5 +193,103 @@ namespace Barotrauma
yield return CoroutineStatus.Success;
}
public static List<Limb> AttachHuskAppendage(Character character, string afflictionIdentifier, XElement appendageDefinition = null, Ragdoll ragdoll = null)
{
var appendage = new List<Limb>();
if (!(AfflictionPrefab.List.FirstOrDefault(ap => ap.Identifier == afflictionIdentifier) is AfflictionPrefabHusk matchingAffliction))
{
DebugConsole.ThrowError($"Could not find an affliction of type 'huskinfection' that matches the affliction '{afflictionIdentifier}'!");
return appendage;
}
string nonhuskedSpeciesName = GetNonHuskedSpeciesName(character.SpeciesName, matchingAffliction);
string huskedSpeciesName = GetHuskedSpeciesName(nonhuskedSpeciesName, matchingAffliction);
string filePath = Character.GetConfigFilePath(huskedSpeciesName);
if (!Character.TryGetConfigFile(filePath, out XDocument huskDoc))
{
DebugConsole.ThrowError($"Error in '{filePath}': Failed to load the config file for the husk infected species with the species name '{huskedSpeciesName}'!");
return appendage;
}
var mainElement = huskDoc.Root.IsOverride() ? huskDoc.Root.FirstElement() : huskDoc.Root;
var element = appendageDefinition;
if (element == null)
{
element = mainElement.GetChildElements("huskappendage").FirstOrDefault(e => e.GetAttributeString("affliction", string.Empty).Equals(afflictionIdentifier));
}
if (element == null)
{
DebugConsole.ThrowError($"Error in '{filePath}': Failed to find a huskappendage that matches the affliction with an identifier '{afflictionIdentifier}'!");
return appendage;
}
string pathToAppendage = element.GetAttributeString("path", string.Empty);
XDocument doc = XMLExtensions.TryLoadXml(pathToAppendage);
if (doc == null) { return appendage; }
if (ragdoll == null)
{
ragdoll = character.AnimController;
}
if (ragdoll.Dir < 1.0f)
{
ragdoll.Flip();
}
var limbElements = doc.Root.Elements("limb").ToDictionary(e => e.GetAttributeString("id", null), e => e);
foreach (var jointElement in doc.Root.Elements("joint"))
{
if (limbElements.TryGetValue(jointElement.GetAttributeString("limb2", null), out XElement limbElement))
{
var jointParams = new RagdollParams.JointParams(jointElement, ragdoll.RagdollParams);
Limb attachLimb = null;
if (matchingAffliction.AttachLimbId > -1)
{
attachLimb = ragdoll.Limbs.FirstOrDefault(l => l.Params.ID == matchingAffliction.AttachLimbId);
}
else if (matchingAffliction.AttachLimbName != null)
{
attachLimb = ragdoll.Limbs.FirstOrDefault(l => l.Name == matchingAffliction.AttachLimbName);
}
else if (matchingAffliction.AttachLimbType != LimbType.None)
{
attachLimb = ragdoll.Limbs.FirstOrDefault(l => l.type == matchingAffliction.AttachLimbType);
}
if (attachLimb == null)
{
DebugConsole.Log("Attachment limb not defined in the affliction prefab or no matching limb could be found. Using the appendage definition as it is.");
attachLimb = ragdoll.Limbs.FirstOrDefault(l => l.Params.ID == jointParams.Limb1);
}
if (attachLimb != null)
{
jointParams.Limb1 = attachLimb.Params.ID;
var appendageLimbParams = new RagdollParams.LimbParams(limbElement, ragdoll.RagdollParams)
{
// Ensure that we have a valid id for the new limb
ID = ragdoll.Limbs.Length
};
jointParams.Limb2 = appendageLimbParams.ID;
Limb huskAppendage = new Limb(ragdoll, character, appendageLimbParams);
huskAppendage.body.Submarine = character.Submarine;
huskAppendage.body.SetTransform(attachLimb.SimPosition, attachLimb.Rotation);
ragdoll.AddLimb(huskAppendage);
ragdoll.AddJoint(jointParams);
appendage.Add(huskAppendage);
}
else
{
DebugConsole.ThrowError("Attachment limb not found!");
}
}
}
return appendage;
}
public static string GetHuskedSpeciesName(string speciesName, AfflictionPrefabHusk prefab)
{
return prefab.HuskedSpeciesName.Replace(AfflictionPrefabHusk.Tag, speciesName);
}
public static string GetNonHuskedSpeciesName(string huskedSpeciesName, AfflictionPrefabHusk prefab)
{
string nonTag = prefab.HuskedSpeciesName.Remove(AfflictionPrefabHusk.Tag);
return huskedSpeciesName.Remove(nonTag);
}
}
}
@@ -3,11 +3,13 @@ using System;
using System.Collections.Generic;
using System.Reflection;
using System.Xml.Linq;
using System.Linq;
namespace Barotrauma
{
public static class CPRSettings
{
public static bool IsLoaded { get; private set; }
public static float ReviveChancePerSkill { get; private set; }
public static float ReviveChanceExponent { get; private set; }
public static float ReviveChanceMin { get; private set; }
@@ -31,9 +33,52 @@ namespace Barotrauma
DamageSkillThreshold = MathHelper.Clamp(element.GetAttributeFloat("damageskillthreshold", 40.0f), 0.0f, 100.0f);
DamageSkillMultiplier = MathHelper.Clamp(element.GetAttributeFloat("damageskillmultiplier", 0.1f), 0.0f, 100.0f);
IsLoaded = true;
}
}
class AfflictionPrefabHusk : AfflictionPrefab
{
public AfflictionPrefabHusk(XElement element, Type type = null) : base(element, type)
{
HuskedSpeciesName = element.GetAttributeString("huskedspeciesname", null);
if (HuskedSpeciesName == null)
{
DebugConsole.NewMessage($"No 'huskedspeciesname' defined for the husk affliction ({Identifier}) in {element.ToString()}", Color.Orange);
HuskedSpeciesName = "[speciesname]husk";
}
TargetSpecies = element.GetAttributeStringArray("targets", new string[0] { }, trim: true, convertToLowerInvariant: true);
if (TargetSpecies.Length == 0)
{
DebugConsole.NewMessage($"No 'targets' defined for the husk affliction ({Identifier}) in {element.ToString()}", Color.Orange);
TargetSpecies = new string[] { "human" };
}
var attachElement = element.GetChildElement("attachlimb");
if (attachElement != null)
{
AttachLimbId = attachElement.GetAttributeInt("id", -1);
AttachLimbName = attachElement.GetAttributeString("name", null);
AttachLimbType = Enum.TryParse(attachElement.GetAttributeString("type", "none"), true, out LimbType limbType) ? limbType : LimbType.None;
}
else
{
AttachLimbId = -1;
AttachLimbName = null;
AttachLimbType = LimbType.None;
}
}
// Use any of these to define which limb the appendage is attached to.
// If multiple are defined, the order of preference is: id, name, type.
public readonly int AttachLimbId;
public readonly string AttachLimbName;
public readonly LimbType AttachLimbType;
public readonly string HuskedSpeciesName;
public readonly string[] TargetSpecies;
public const string Tag = "[speciesname]";
}
class AfflictionPrefab
{
public class Effect
@@ -126,7 +171,6 @@ namespace Barotrauma
public static AfflictionPrefab Bloodloss;
public static AfflictionPrefab Pressure;
public static AfflictionPrefab Stun;
public static AfflictionPrefab Husk;
public static List<AfflictionPrefab> List = new List<AfflictionPrefab>();
@@ -187,44 +231,109 @@ namespace Barotrauma
foreach (string filePath in filePaths)
{
XDocument doc = XMLExtensions.TryLoadXml(filePath);
if (doc == null || doc.Root == null) continue;
foreach (XElement element in doc.Root.Elements())
if (doc == null) { continue; }
var mainElement = doc.Root.IsOverride() ? doc.Root.FirstElement() : doc.Root;
if (doc.Root.IsOverride())
{
switch (element.Name.ToString().ToLowerInvariant())
DebugConsole.ThrowError("Cannot override all afflictions, because many of them are required by the main game! Please try overriding them one by one.");
}
foreach (XElement element in mainElement.Elements())
{
bool isOverride = element.IsOverride();
XElement sourceElement = isOverride ? element.FirstElement() : element;
string elementName = sourceElement.Name.ToString().ToLowerInvariant();
string identifier = sourceElement.GetAttributeString("identifier", null);
if (!elementName.Equals("cprsettings", StringComparison.OrdinalIgnoreCase))
{
if (string.IsNullOrWhiteSpace(identifier))
{
DebugConsole.ThrowError($"No identifier defined for the affliction '{elementName}' in file '{filePath}'");
continue;
}
var duplicate = List.FirstOrDefault(a => a.Identifier == identifier);
if (duplicate != null)
{
if (isOverride)
{
DebugConsole.NewMessage($"Overriding an affliction or a buff with the identifier '{identifier}' using the file '{filePath}'", Color.Yellow);
List.Remove(duplicate);
}
else
{
DebugConsole.ThrowError($"Duplicate affliction: '{identifier}' defined in {elementName} of '{filePath}'");
continue;
}
}
}
string type = sourceElement.GetAttributeString("type", null);
if (sourceElement.Name.ToString().ToLowerInvariant() == "cprsettings")
{
//backwards compatibility
type = "cprsettings";
}
AfflictionPrefab prefab = null;
switch (type)
{
case "internaldamage":
List.Add(InternalDamage = new AfflictionPrefab(element, typeof(Affliction)));
break;
case "bleeding":
List.Add(Bleeding = new AfflictionPrefab(element, typeof(AfflictionBleeding)));
prefab = new AfflictionPrefab(sourceElement, typeof(AfflictionBleeding));
break;
case "burn":
List.Add(Burn = new AfflictionPrefab(element, typeof(Affliction)));
break;
case "oxygenlow":
List.Add(OxygenLow = new AfflictionPrefab(element, typeof(Affliction)));
break;
case "bloodloss":
List.Add(Bloodloss = new AfflictionPrefab(element, typeof(Affliction)));
break;
case "pressure":
List.Add(Pressure = new AfflictionPrefab(element, typeof(Affliction)));
break;
case "stun":
List.Add(Stun = new AfflictionPrefab(element, typeof(Affliction)));
break;
case "husk":
case "afflictionhusk":
List.Add(Husk = new AfflictionPrefab(element, typeof(AfflictionHusk)));
case "huskinfection":
prefab = new AfflictionPrefabHusk(sourceElement, typeof(AfflictionHusk));
break;
case "cprsettings":
CPRSettings.Load(element);
if (CPRSettings.IsLoaded)
{
if (isOverride)
{
DebugConsole.NewMessage($"Overriding the CPR settings with '{filePath}'", Color.Yellow);
}
else
{
DebugConsole.ThrowError($"Error in '{filePath}': CPR settings already loaded. Add <override></override> tags as the parent of the custom CPRSettings to allow overriding the vanilla values.");
break;
}
}
CPRSettings.Load(sourceElement);
break;
case "damage":
case "burn":
case "oxygenlow":
case "bloodloss":
case "stun":
case "pressure":
case "internaldamage":
prefab = new AfflictionPrefab(sourceElement, typeof(Affliction));
break;
default:
List.Add(new AfflictionPrefab(element));
prefab = new AfflictionPrefab(sourceElement);
break;
}
switch (identifier)
{
case "internaldamage":
InternalDamage = prefab;
break;
case "bleeding":
Bleeding = prefab;
break;
case "burn":
Burn = prefab;
break;
case "oxygenlow":
OxygenLow = prefab;
break;
case "bloodloss":
Bloodloss = prefab;
break;
case "pressure":
Pressure = prefab;
break;
case "stun":
Stun = prefab;
break;
}
if (prefab != null) { List.Add(prefab); }
}
}
@@ -235,12 +344,15 @@ namespace Barotrauma
if (Bloodloss == null) DebugConsole.ThrowError("Affliction \"Bloodloss\" not defined in the affliction prefabs.");
if (Pressure == null) DebugConsole.ThrowError("Affliction \"Pressure\" not defined in the affliction prefabs.");
if (Stun == null) DebugConsole.ThrowError("Affliction \"Stun\" not defined in the affliction prefabs.");
if (Husk == null) DebugConsole.ThrowError("Affliction \"Husk\" not defined in the affliction prefabs.");
}
public AfflictionPrefab(XElement element, Type type = null)
{
typeName = type == null ? element.Name.ToString() : type.Name;
if (typeName == "InternalDamage" && type == null)
{
type = typeof(Affliction);
}
Identifier = element.GetAttributeString("identifier", "");
@@ -305,7 +417,7 @@ namespace Barotrauma
catch
{
DebugConsole.ThrowError("Could not find an affliction class of the type \"" + typeName + "\".");
return;
type = typeof(Affliction);
}
constructor = type.GetConstructor(new[] { typeof(AfflictionPrefab), typeof(float) });
@@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Barotrauma.Networking;
using Barotrauma.Extensions;
namespace Barotrauma
{
@@ -78,14 +79,33 @@ namespace Barotrauma
public const float InsufficientOxygenThreshold = 30.0f;
public const float LowOxygenThreshold = 50.0f;
protected float minVitality, maxVitality;
protected float minVitality;
protected float maxVitality
{
get => Character.Params.Health.Vitality;
set => Character.Params.Health.Vitality = value;
}
public bool Unkillable;
//bleeding settings
public bool DoesBleed { get; private set; }
public bool DoesBleed
{
get => Character.Params.Health.DoesBleed;
private set => Character.Params.Health.DoesBleed = value;
}
public bool UseHealthWindow { get; set; }
public bool UseHealthWindow
{
get => Character.Params.Health.UseHealthWindow;
set => Character.Params.Health.UseHealthWindow = value;
}
public float CrushDepth
{
get => Character.Params.Health.CrushDepth;
private set => Character.Params.Health.CrushDepth = value;
}
private List<LimbHealth> limbHealths = new List<LimbHealth>();
//non-limb-specific afflictions
@@ -102,11 +122,12 @@ namespace Barotrauma
get { return Vitality <= 0.0f; }
}
public float CrushDepth { get; private set; }
public float PressureKillDelay { get; private set; } = 5.0f;
public float Vitality { get; private set; }
public float HealthPercentage => MathUtils.Percentage(Vitality, MaxVitality);
public float MaxVitality
{
get
@@ -168,7 +189,6 @@ namespace Barotrauma
{
this.Character = character;
Vitality = 100.0f;
maxVitality = 100.0f;
DoesBleed = true;
UseHealthWindow = false;
@@ -185,15 +205,9 @@ namespace Barotrauma
this.Character = character;
InitIrremovableAfflictions();
CrushDepth = element.GetAttributeFloat("crushdepth", float.NegativeInfinity);
maxVitality = element.GetAttributeFloat("vitality", 100.0f);
Vitality = maxVitality;
DoesBleed = element.GetAttributeBool("doesbleed", true);
UseHealthWindow = element.GetAttributeBool("usehealthwindow", false);
minVitality = (character.ConfigPath == Character.HumanConfigFile) ? -100.0f : 0.0f;
minVitality = character.IsHuman ? -100.0f : 0.0f;
limbHealths.Clear();
foreach (XElement subElement in element.Elements())
@@ -225,10 +239,9 @@ namespace Barotrauma
public IEnumerable<Affliction> GetAllAfflictions(Func<Affliction, bool> limbHealthFilter = null)
{
// TODO: If there can be duplicates, we should use Union instead.
return limbHealthFilter == null
? afflictions.Concat(limbHealths.SelectMany(lh => lh.Afflictions))
: afflictions.Concat(limbHealths.SelectMany(lh => lh.Afflictions.Where(limbHealthFilter)));
? afflictions.Union(limbHealths.SelectMany(lh => lh.Afflictions))
: afflictions.Where(limbHealthFilter).Union(limbHealths.SelectMany(lh => lh.Afflictions.Where(limbHealthFilter)));
}
private LimbHealth GetMatchingLimbHealth(Limb limb) => limbHealths[limb.HealthIndex];
@@ -240,11 +253,23 @@ namespace Barotrauma
private IEnumerable<Affliction> GetMatchingAfflictions(LimbHealth limb, Func<Affliction, bool> predicate)
=> limb.Afflictions.Where(predicate).Union(afflictions.Where(a => predicate(a) && GetMatchingLimbHealth(a) == limb));
public Affliction GetAffliction(string afflictionType, bool allowLimbAfflictions = true)
public IEnumerable<Affliction> GetAfflictionsByType(string afflictionType, bool allowLimbAfflictions = true)
{
if (allowLimbAfflictions)
{
return GetAllAfflictions(a => a.Prefab.AfflictionType == afflictionType);
}
else
{
return afflictions.Where(a => a.Prefab.AfflictionType == afflictionType);
}
}
public Affliction GetAffliction(string identifier, bool allowLimbAfflictions = true)
{
foreach (Affliction affliction in afflictions)
{
if (affliction.Prefab.AfflictionType == afflictionType) return affliction;
if (affliction.Prefab.Identifier == identifier) return affliction;
}
if (!allowLimbAfflictions) return null;
@@ -252,19 +277,30 @@ namespace Barotrauma
{
foreach (Affliction affliction in limbHealth.Afflictions)
{
if (affliction.Prefab.AfflictionType == afflictionType) return affliction;
if (affliction.Prefab.Identifier == identifier) return affliction;
}
}
return null;
}
public T GetAffliction<T>(string afflictionType, bool allowLimbAfflictions = true) where T : Affliction
public T GetAffliction<T>(string identifier, bool allowLimbAfflictions = true) where T : Affliction
{
return GetAffliction(afflictionType, allowLimbAfflictions) as T;
return GetAffliction(identifier, allowLimbAfflictions) as T;
}
public Affliction GetAffliction(string afflictionType, Limb limb)
public IEnumerable<Affliction> GetAfflictionsByType(string afflictionType, Limb limb)
{
if (limb.HealthIndex < 0 || limb.HealthIndex >= limbHealths.Count)
{
DebugConsole.ThrowError("Limb health index out of bounds. Character\"" + Character.Name +
"\" only has health configured for" + limbHealths.Count + " limbs but the limb " + limb.type + " is targeting index " + limb.HealthIndex);
return null;
}
return limbHealths[limb.HealthIndex].Afflictions.Where(a => a.Prefab.AfflictionType == afflictionType);
}
public Affliction GetAffliction(string identifier, Limb limb)
{
if (limb.HealthIndex < 0 || limb.HealthIndex >= limbHealths.Count)
{
@@ -274,7 +310,7 @@ namespace Barotrauma
}
foreach (Affliction affliction in limbHealths[limb.HealthIndex].Afflictions)
{
if (affliction.Prefab.AfflictionType == afflictionType) return affliction;
if (affliction.Prefab.Identifier == identifier) return affliction;
}
return null;
}
@@ -520,8 +556,14 @@ namespace Barotrauma
{
if (!DoesBleed && newAffliction is AfflictionBleeding) return;
if (!Character.NeedsAir && newAffliction.Prefab == AfflictionPrefab.OxygenLow) return;
// Currently only human can get the husk infection.
if (newAffliction.Prefab == AfflictionPrefab.Husk && Character.SpeciesName.ToLowerInvariant() != "human") { return; }
if (newAffliction.Prefab.AfflictionType == "huskinfection")
{
var huskPrefab = newAffliction.Prefab as AfflictionPrefabHusk;
if (huskPrefab.TargetSpecies.None(s => s.Equals(Character.SpeciesName, StringComparison.OrdinalIgnoreCase)))
{
return;
}
}
foreach (Affliction affliction in afflictions)
{
if (newAffliction.Prefab == affliction.Prefab)
@@ -547,7 +589,10 @@ namespace Barotrauma
CalculateVitality();
if (Vitality <= MinVitality) Kill();
}
partial void UpdateProjSpecific(float deltaTime);
partial void UpdateLimbAfflictionOverlays();
public void Update(float deltaTime)
@@ -671,6 +716,10 @@ namespace Barotrauma
var causeOfDeath = GetCauseOfDeath();
Character.Kill(causeOfDeath.First, causeOfDeath.Second);
#if CLIENT
DisplayVitalityDelay = 0.0f;
DisplayedVitality = Vitality;
#endif
}
public Pair<CauseOfDeathType, Affliction> GetCauseOfDeath()
@@ -1,86 +1,120 @@
using Microsoft.Xna.Framework;
using System;
using System.Xml.Linq;
using System.Collections.Generic;
using System.Linq;
namespace Barotrauma
{
partial class DamageModifier
partial class DamageModifier : ISerializableEntity
{
[Serialize(1.0f, false)]
public string Name => "Damage Modifier";
public Dictionary<string, SerializableProperty> SerializableProperties { get; private set; }
[Serialize(1.0f, false), Editable(DecimalCount = 2)]
public float DamageMultiplier
{
get;
private set;
}
[Serialize("0.0,360", false)]
[Serialize("0.0,360", false), Editable]
public Vector2 ArmorSector
{
get;
private set;
}
[Serialize(true, false)]
public bool IsArmor
{
get;
private set;
}
public Vector2 ArmorSectorInRadians => new Vector2(MathHelper.ToRadians(ArmorSector.X), MathHelper.ToRadians(ArmorSector.Y));
[Serialize(false, false)]
[Serialize(false, false), Editable]
public bool DeflectProjectiles
{
get;
private set;
}
public string[] AfflictionIdentifiers
[Serialize("", true), Editable]
public string AfflictionIdentifiers
{
get;
private set;
get
{
return rawAfflictionIdentifierString;
}
private set
{
rawAfflictionIdentifierString = value;
ParseAfflictionIdentifiers();
}
}
public string[] AfflictionTypes
[Serialize("", true), Editable]
public string AfflictionTypes
{
get;
private set;
get
{
return rawAfflictionTypeString;
}
private set
{
rawAfflictionTypeString = value;
ParseAfflictionTypes();
}
}
private string rawAfflictionIdentifierString;
private string rawAfflictionTypeString;
private string[] parsedAfflictionIdentifiers;
private string[] parsedAfflictionTypes;
public DamageModifier(XElement element, string parentDebugName)
{
SerializableProperty.DeserializeProperties(this, element);
ArmorSector = new Vector2(MathHelper.ToRadians(ArmorSector.X), MathHelper.ToRadians(ArmorSector.Y));
Deserialize(element);
if (element.Attribute("afflictionnames") != null)
{
DebugConsole.ThrowError("Error in DamageModifier config (" + parentDebugName + ") - define afflictions using identifiers or types instead of names.");
}
}
AfflictionIdentifiers = element.GetAttributeStringArray("afflictionidentifiers", new string[0]);
for (int i = 0; i < AfflictionIdentifiers.Length; i++)
private void ParseAfflictionTypes()
{
string[] splitValue = rawAfflictionTypeString.Split(',', '');
for (int i = 0; i < splitValue.Length; i++)
{
AfflictionIdentifiers[i] = AfflictionIdentifiers[i].ToLowerInvariant();
splitValue[i] = splitValue[i].ToLowerInvariant().Trim();
}
AfflictionTypes = element.GetAttributeStringArray("afflictiontypes", new string[0]);
for (int i = 0; i < AfflictionTypes.Length; i++)
parsedAfflictionTypes = splitValue;
}
private void ParseAfflictionIdentifiers()
{
string[] splitValue = rawAfflictionIdentifierString.Split(',', '');
for (int i = 0; i < splitValue.Length; i++)
{
AfflictionTypes[i] = AfflictionTypes[i].ToLowerInvariant();
splitValue[i] = splitValue[i].ToLowerInvariant().Trim();
}
parsedAfflictionIdentifiers = splitValue;
}
public bool MatchesAffliction(Affliction affliction)
{
//if no identifiers or types have been defined, the damage modifier affects all afflictions
if (AfflictionIdentifiers.Length == 0 && AfflictionTypes.Length == 0) { return true; }
return parsedAfflictionIdentifiers.Any(id => id.Equals(affliction.Identifier, StringComparison.OrdinalIgnoreCase))
|| parsedAfflictionTypes.Any(t => t.Equals(affliction.Prefab.AfflictionType, StringComparison.OrdinalIgnoreCase));
}
foreach (string afflictionName in AfflictionIdentifiers)
{
if (affliction.Prefab.Identifier.ToLowerInvariant() == afflictionName) return true;
}
foreach (string afflictionType in AfflictionTypes)
{
if (affliction.Prefab.AfflictionType.ToLowerInvariant() == afflictionType) return true;
}
return false;
public void Serialize(XElement element)
{
if (element == null) { return; }
SerializableProperty.SerializeProperties(this, element);
}
public void Deserialize(XElement element)
{
if (element == null) { return; }
SerializableProperties = SerializableProperty.DeserializeProperties(this, element);
}
}
}