(d9829ac) v0.9.4.0
This commit is contained in:
@@ -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)
|
||||
{
|
||||
|
||||
+114
-57
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+143
-31
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user