Unstable 1.8.4.0
This commit is contained in:
+34
-9
@@ -54,6 +54,11 @@ namespace Barotrauma
|
||||
activeEffectDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Armor penetration for status effects. Normally defined per attack, but that doesn't work on status effects.
|
||||
/// </summary>
|
||||
public float Penetration { get; set; }
|
||||
|
||||
private float _nonClampedStrength = -1;
|
||||
public float NonClampedStrength => _nonClampedStrength > 0 ? _nonClampedStrength : _strength;
|
||||
@@ -64,7 +69,7 @@ namespace Barotrauma
|
||||
[Serialize(1.0f, IsPropertySaveable.Yes, description: "The probability for the affliction to be applied."), Editable(minValue: 0f, maxValue: 1f)]
|
||||
public float Probability { get; set; } = 1.0f;
|
||||
|
||||
[Serialize(true, IsPropertySaveable.Yes, description: "Explosion damage is applied per each affected limb. Should this affliction damage be divided by the count of affected limbs (1-15) or applied in full? Default: true. Only affects explosions."), Editable]
|
||||
[Serialize(true, IsPropertySaveable.Yes, description: "Explosion damage is applied per each affected limb. Should this affliction damage be divided by the count of affected limbs (1-15) or applied in full? Default: true. Only affects status effects and explosions."), Editable]
|
||||
public bool DivideByLimbCount { get; set; }
|
||||
|
||||
[Serialize(false, IsPropertySaveable.Yes, description: "Is the damage relative to the max vitality (percentage) or absolute (normal)"), Editable]
|
||||
@@ -120,6 +125,7 @@ namespace Barotrauma
|
||||
Probability = source.Probability;
|
||||
DivideByLimbCount = source.DivideByLimbCount;
|
||||
MultiplyByMaxVitality = source.MultiplyByMaxVitality;
|
||||
Penetration = source.Penetration;
|
||||
}
|
||||
|
||||
public void Serialize(XElement element)
|
||||
@@ -337,18 +343,24 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public float GetResistance(Identifier afflictionId)
|
||||
/// <summary>
|
||||
/// How much resistance to the specified affliction does this affliction currently give?
|
||||
/// </summary>
|
||||
public float GetResistance(Identifier afflictionId, LimbType limbType)
|
||||
{
|
||||
if (Strength < Prefab.ActivationThreshold) { return 0.0f; }
|
||||
var affliction = AfflictionPrefab.Prefabs[afflictionId];
|
||||
AfflictionPrefab.Effect currentEffect = GetActiveEffect();
|
||||
if (currentEffect == null) { return 0.0f; }
|
||||
if (!currentEffect.ResistanceFor.Any(r =>
|
||||
r == affliction.Identifier ||
|
||||
r == affliction.AfflictionType))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
bool hasResistanceForAffliction = currentEffect.ResistanceFor.Any(identifier =>
|
||||
identifier == affliction.Identifier ||
|
||||
identifier == affliction.AfflictionType);
|
||||
if (!hasResistanceForAffliction) { return 0.0f; }
|
||||
|
||||
bool hasResistanceForLimb = limbType == LimbType.None || currentEffect.ResistanceLimbs.None() || currentEffect.ResistanceLimbs.Contains(limbType);
|
||||
if (!hasResistanceForLimb) { return 0.0f; }
|
||||
|
||||
return MathHelper.Lerp(
|
||||
currentEffect.MinResistance,
|
||||
currentEffect.MaxResistance,
|
||||
@@ -402,6 +414,8 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
//force an update when a periodic effect triggers to get it to trigger client-side
|
||||
characterHealth.Character.healthUpdateTimer = 0.0f;
|
||||
foreach (StatusEffect statusEffect in periodicEffect.StatusEffects)
|
||||
{
|
||||
ApplyStatusEffect(ActionType.OnActive, statusEffect, 1.0f, characterHealth, targetLimb);
|
||||
@@ -430,7 +444,7 @@ namespace Barotrauma
|
||||
}
|
||||
else if (currentEffect.StrengthChange > 0) // Reduce strengthening of afflictions if resistant
|
||||
{
|
||||
_strength += currentEffect.StrengthChange * deltaTime * (1f - characterHealth.GetResistance(Prefab));
|
||||
_strength += currentEffect.StrengthChange * deltaTime * (1f - characterHealth.GetResistance(Prefab, targetLimb?.type ?? LimbType.None));
|
||||
}
|
||||
// Don't use the property, because it's virtual and some afflictions like husk overload it for external use.
|
||||
_strength = MathHelper.Clamp(_strength, 0.0f, Prefab.MaxStrength);
|
||||
@@ -441,6 +455,17 @@ namespace Barotrauma
|
||||
ApplyStatusEffect(ActionType.OnActive, statusEffect, deltaTime, characterHealth, targetLimb);
|
||||
}
|
||||
|
||||
if (currentEffect.ConvulseAmount > 0f)
|
||||
{
|
||||
foreach (Limb limb in characterHealth.Character.AnimController.Limbs)
|
||||
{
|
||||
if (limb.IsSevered) { continue; }
|
||||
if (limb.Hidden) { continue; }
|
||||
float force = Rand.Value() * limb.Mass * currentEffect.ConvulseAmount;
|
||||
limb.body.ApplyLinearImpulse(Rand.Vector(force), maxVelocity: Networking.NetConfig.MaxPhysicsBodyVelocity * 0.5f);
|
||||
}
|
||||
}
|
||||
|
||||
float amount = deltaTime;
|
||||
if (Prefab.GrainBurst > 0)
|
||||
{
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@
|
||||
public override void Update(CharacterHealth characterHealth, Limb targetLimb, float deltaTime)
|
||||
{
|
||||
base.Update(characterHealth, targetLimb, deltaTime);
|
||||
float bloodlossResistance = GetResistance(characterHealth.BloodlossAffliction.Identifier);
|
||||
float bloodlossResistance = characterHealth.GetResistance(characterHealth.BloodlossAffliction.Prefab, targetLimb?.type ?? LimbType.None);
|
||||
characterHealth.BloodlossAmount += Strength * (1.0f - bloodlossResistance) / 60.0f * deltaTime;
|
||||
if (Source != null)
|
||||
{
|
||||
|
||||
+74
-46
@@ -28,8 +28,6 @@ namespace Barotrauma
|
||||
|
||||
private bool stun = false;
|
||||
|
||||
private readonly List<Affliction> huskInfection = new List<Affliction>();
|
||||
|
||||
[Serialize(0f, IsPropertySaveable.Yes), Editable]
|
||||
public override float Strength
|
||||
{
|
||||
@@ -62,7 +60,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private readonly AfflictionPrefabHusk HuskPrefab;
|
||||
public readonly AfflictionPrefabHusk HuskPrefab;
|
||||
|
||||
private float DormantThreshold => HuskPrefab.DormantThreshold;
|
||||
private float ActiveThreshold => HuskPrefab.ActiveThreshold;
|
||||
@@ -129,7 +127,7 @@ namespace Barotrauma
|
||||
{
|
||||
State = InfectionState.Final;
|
||||
ActivateHusk();
|
||||
ApplyDamage(deltaTime, applyForce: true);
|
||||
ApplyDamage(deltaTime);
|
||||
character.SetStun(5);
|
||||
}
|
||||
}
|
||||
@@ -192,27 +190,41 @@ namespace Barotrauma
|
||||
prevDisplayedMessage = State;
|
||||
}
|
||||
|
||||
private void ApplyDamage(float deltaTime, bool applyForce)
|
||||
private const float DamageCooldown = 0.1f;
|
||||
private float damageCooldownTimer;
|
||||
private void ApplyDamage(float deltaTime)
|
||||
{
|
||||
int limbCount = character.AnimController.Limbs.Count(l => !l.IgnoreCollisions && !l.IsSevered && !l.Hidden);
|
||||
if (damageCooldownTimer > 0)
|
||||
{
|
||||
damageCooldownTimer -= deltaTime;
|
||||
return;
|
||||
}
|
||||
damageCooldownTimer = DamageCooldown;
|
||||
int limbCount = character.AnimController.Limbs.Count(IsValidLimb);
|
||||
foreach (Limb limb in character.AnimController.Limbs)
|
||||
{
|
||||
if (limb.IsSevered) { continue; }
|
||||
if (limb.Hidden) { continue; }
|
||||
if (!IsValidLimb(limb)) { continue; }
|
||||
float random = Rand.Value();
|
||||
huskInfection.Clear();
|
||||
huskInfection.Add(AfflictionPrefab.InternalDamage.Instantiate(random * 10 * deltaTime / limbCount));
|
||||
if (random == 0) { continue; }
|
||||
const float damageRate = 2;
|
||||
float dmg = random / limbCount * damageRate;
|
||||
character.LastDamageSource = null;
|
||||
float force = applyForce ? random * 0.5f * limb.Mass : 0;
|
||||
character.DamageLimb(limb.WorldPosition, limb, huskInfection, 0, false, Rand.Vector(force));
|
||||
var afflictions = AfflictionPrefab.InternalDamage.Instantiate(dmg).ToEnumerable();
|
||||
const float forceMultiplier = 5;
|
||||
float force = dmg * limb.Mass * forceMultiplier;
|
||||
character.DamageLimb(limb.WorldPosition, limb, afflictions, stun: 0, playSound: false, Rand.Vector(force), ignoreDamageOverlay: true, recalculateVitality: false);
|
||||
}
|
||||
character.CharacterHealth.RecalculateVitality();
|
||||
|
||||
static bool IsValidLimb(Limb limb) => !limb.IgnoreCollisions && !limb.IsSevered && !limb.Hidden;
|
||||
}
|
||||
|
||||
public void ActivateHusk()
|
||||
{
|
||||
if (huskAppendage == null && character.Params.UseHuskAppendage)
|
||||
{
|
||||
huskAppendage = AttachHuskAppendage(character, Prefab as AfflictionPrefabHusk);
|
||||
var huskAffliction = Prefab as AfflictionPrefabHusk;
|
||||
huskAppendage = AttachHuskAppendage(character, huskAffliction, GetHuskedSpeciesName(character.Params, huskAffliction));
|
||||
}
|
||||
|
||||
if (Prefab is AfflictionPrefabHusk { NeedsAir: false })
|
||||
@@ -287,7 +299,7 @@ namespace Barotrauma
|
||||
Entity.Spawner.AddEntityToRemoveQueue(character);
|
||||
UnsubscribeFromDeathEvent();
|
||||
|
||||
Identifier huskedSpeciesName = GetHuskedSpeciesName(character.SpeciesName, Prefab as AfflictionPrefabHusk);
|
||||
Identifier huskedSpeciesName = GetHuskedSpeciesName(character.Params, Prefab as AfflictionPrefabHusk);
|
||||
CharacterPrefab prefab = CharacterPrefab.FindBySpeciesName(huskedSpeciesName);
|
||||
|
||||
if (prefab == null)
|
||||
@@ -309,11 +321,16 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
var husk = Character.Create(huskedSpeciesName, character.WorldPosition, ToolBox.RandomSeed(8), huskCharacterInfo, isRemotePlayer: false, hasAi: true);
|
||||
if (character.HasAbilityFlag(AbilityFlags.IgnoredByEnemyAI))
|
||||
{
|
||||
husk.AddAbilityFlag(AbilityFlags.IgnoredByEnemyAI);
|
||||
}
|
||||
if (husk.Info != null)
|
||||
{
|
||||
husk.Info.Character = husk;
|
||||
husk.Info.TeamID = CharacterTeamType.None;
|
||||
}
|
||||
husk.AllowPlayDead = character.AllowPlayDead;
|
||||
|
||||
if (Prefab is AfflictionPrefabHusk huskPrefab)
|
||||
{
|
||||
@@ -379,17 +396,15 @@ namespace Barotrauma
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
public static List<Limb> AttachHuskAppendage(Character character, AfflictionPrefabHusk matchingAffliction, ContentXElement appendageDefinition = null, Ragdoll ragdoll = null)
|
||||
public static List<Limb> AttachHuskAppendage(Character character, AfflictionPrefabHusk matchingAffliction, Identifier huskedSpeciesName, ContentXElement appendageDefinition = null, Ragdoll ragdoll = null)
|
||||
{
|
||||
var appendage = new List<Limb>();
|
||||
Identifier nonhuskedSpeciesName = GetNonHuskedSpeciesName(character.SpeciesName, matchingAffliction);
|
||||
Identifier huskedSpeciesName = GetHuskedSpeciesName(nonhuskedSpeciesName, matchingAffliction);
|
||||
var appendageLimbs = new List<Limb>();
|
||||
CharacterPrefab huskPrefab = CharacterPrefab.FindBySpeciesName(huskedSpeciesName);
|
||||
if (huskPrefab?.ConfigElement == null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Failed to find the config file for the husk infected species with the species name '{huskedSpeciesName}'!",
|
||||
contentPackage: matchingAffliction.ContentPackage);
|
||||
return appendage;
|
||||
return appendageLimbs;
|
||||
}
|
||||
var mainElement = huskPrefab.ConfigElement;
|
||||
var element = appendageDefinition;
|
||||
@@ -401,28 +416,25 @@ namespace Barotrauma
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in '{huskPrefab.FilePath}': Failed to find a huskappendage that matches the affliction with an identifier '{matchingAffliction.Identifier}'!",
|
||||
contentPackage: matchingAffliction.ContentPackage);
|
||||
return appendage;
|
||||
return appendageLimbs;
|
||||
}
|
||||
ContentPath pathToAppendage = element.GetAttributeContentPath("path") ?? ContentPath.Empty;
|
||||
XDocument doc = XMLExtensions.TryLoadXml(pathToAppendage);
|
||||
if (doc == null) { return appendage; }
|
||||
if (ragdoll == null)
|
||||
{
|
||||
ragdoll = character.AnimController;
|
||||
}
|
||||
if (doc == null) { return appendageLimbs; }
|
||||
ragdoll ??= character.AnimController;
|
||||
if (ragdoll.Dir < 1.0f)
|
||||
{
|
||||
ragdoll.Flip();
|
||||
}
|
||||
|
||||
var root = doc.Root.FromPackage(pathToAppendage.ContentPackage);
|
||||
var limbElements = root.GetChildElements("limb").ToDictionary(e => e.GetAttributeString("id", null), e => e);
|
||||
var limbElements = root.GetChildElements("limb").ToDictionary(e => e.GetAttributeInt("id", -1), e => e);
|
||||
//the IDs may need to be offset if the character has other extra appendages (e.g. from gene splicing)
|
||||
//that take up the IDs of this appendage
|
||||
int idOffset = 0;
|
||||
int? idOffset = null;
|
||||
foreach (var jointElement in root.GetChildElements("joint"))
|
||||
{
|
||||
if (!limbElements.TryGetValue(jointElement.GetAttributeString("limb2", null), out ContentXElement limbElement)) { continue; }
|
||||
if (!limbElements.TryGetValue(jointElement.GetAttributeInt("limb2", -1), out ContentXElement limbElement)) { continue; }
|
||||
|
||||
var jointParams = new RagdollParams.JointParams(jointElement, ragdoll.RagdollParams);
|
||||
Limb attachLimb = null;
|
||||
@@ -444,38 +456,54 @@ namespace Barotrauma
|
||||
}
|
||||
if (attachLimb != null)
|
||||
{
|
||||
jointParams.Limb1 = attachLimb.Params.ID;
|
||||
//the joint attaches to a limb outside the character's normal limb count = to another part of the appendage
|
||||
// -> if the appendage's IDs have been offset, we need to take that into account to attach to the correct limb
|
||||
if (jointParams.Limb1 >= ragdoll.RagdollParams.Limbs.Count)
|
||||
{
|
||||
jointParams.Limb1 += idOffset;
|
||||
}
|
||||
var appendageLimbParams = new RagdollParams.LimbParams(limbElement, ragdoll.RagdollParams);
|
||||
if (idOffset == 0)
|
||||
idOffset ??= ragdoll.Limbs.Length - appendageLimbParams.ID;
|
||||
jointParams.Limb1 = attachLimb.Params.ID;
|
||||
//the joint attaches to one of the limbs we're creating = to another part of the appendage
|
||||
// -> if the appendage's IDs have been offset, we need to take that into account to attach to the correct limb
|
||||
if (limbElements.ContainsKey(jointParams.Limb1))
|
||||
{
|
||||
idOffset = ragdoll.Limbs.Length - appendageLimbParams.ID;
|
||||
jointParams.Limb1 += idOffset.Value;
|
||||
}
|
||||
jointParams.Limb2 = appendageLimbParams.ID = ragdoll.Limbs.Length;
|
||||
Limb huskAppendage = new Limb(ragdoll, character, appendageLimbParams);
|
||||
if (limbElements.ContainsKey(jointParams.Limb2))
|
||||
{
|
||||
jointParams.Limb2 += idOffset.Value;
|
||||
}
|
||||
Limb huskAppendage =
|
||||
//check if this joint is supposed to attach to a limb we already created
|
||||
appendageLimbs.Find(limb => limb.Params.ID == appendageLimbParams.ID) ??
|
||||
//if not, create a new limb
|
||||
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);
|
||||
}
|
||||
appendageLimbs.Add(huskAppendage);
|
||||
}
|
||||
}
|
||||
return appendage;
|
||||
return appendageLimbs;
|
||||
}
|
||||
|
||||
public static Identifier GetHuskedSpeciesName(Identifier speciesName, AfflictionPrefabHusk prefab)
|
||||
public static Identifier GetHuskedSpeciesName(CharacterParams character, AfflictionPrefabHusk prefab)
|
||||
{
|
||||
return new Identifier(speciesName.Value + prefab.HuskedSpeciesName.Value);
|
||||
Identifier huskedSpecies = character.HuskedSpecies;
|
||||
if (huskedSpecies.IsEmpty)
|
||||
{
|
||||
// Default pattern: Crawler -> Crawlerhusk, Human -> Humanhusk
|
||||
return new Identifier(character.SpeciesName.Value + prefab.HuskedSpeciesName.Value);
|
||||
}
|
||||
return huskedSpecies;
|
||||
}
|
||||
|
||||
public static Identifier GetNonHuskedSpeciesName(Identifier huskedSpeciesName, AfflictionPrefabHusk prefab)
|
||||
public static Identifier GetNonHuskedSpeciesName(CharacterParams character, AfflictionPrefabHusk prefab)
|
||||
{
|
||||
return huskedSpeciesName.Remove(prefab.HuskedSpeciesName);
|
||||
Identifier nonHuskedSpecies = character.NonHuskedSpecies;
|
||||
if (nonHuskedSpecies.IsEmpty)
|
||||
{
|
||||
// Default pattern: Crawlerhusk -> Crawler, Humanhusk -> Human
|
||||
return character.SpeciesName.Remove(prefab.HuskedSpeciesName);
|
||||
}
|
||||
return nonHuskedSpecies;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+48
-4
@@ -329,6 +329,11 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
public readonly ImmutableArray<Identifier> ResistanceFor;
|
||||
|
||||
/// <summary>
|
||||
/// List of limb types that the resistance applies to. If empty, the resistance applies to the whole body.
|
||||
/// </summary>
|
||||
public readonly ImmutableArray<LimbType> ResistanceLimbs;
|
||||
|
||||
[Serialize(0.0f, IsPropertySaveable.No,
|
||||
description: "The amount of resistance to the afflictions specified by ResistanceFor to apply at this effect's lowest strength.")]
|
||||
public float MinResistance { get; private set; }
|
||||
@@ -359,6 +364,18 @@ namespace Barotrauma
|
||||
description: "Color to tint the affected character's entire body with at this effect's highest strength. The alpha channel is used to determine how much to tint the character.")]
|
||||
public Color MaxBodyTint { get; private set; }
|
||||
|
||||
[Serialize(0.0f, IsPropertySaveable.No,
|
||||
description: "Range of the \"thermal goggles overlay\" enabled by the affliction.")]
|
||||
public float ThermalOverlayRange { get; private set; }
|
||||
|
||||
[Serialize("255,0,0,255", IsPropertySaveable.No,
|
||||
description: $"Color of the \"thermal goggles overlay\" enabled by the affliction. Only has an effect if {nameof(ThermalOverlayRange)} is larger than 0.")]
|
||||
public Color ThermalOverlayColor { get; private set; }
|
||||
|
||||
[Serialize(0f, IsPropertySaveable.No,
|
||||
description: "Multiplier for the convulsion/seizure effect on the character's ragdoll when this effect is active.")]
|
||||
public float ConvulseAmount { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// StatType that will be applied to the affected character when the effect is active that is proportional to the effect's strength.
|
||||
/// </summary>
|
||||
@@ -385,7 +402,7 @@ namespace Barotrauma
|
||||
public readonly float MinValue;
|
||||
|
||||
/// <summary>
|
||||
/// Minimum value to apply
|
||||
/// Maximum value to apply
|
||||
/// </summary>
|
||||
public readonly float MaxValue;
|
||||
|
||||
@@ -423,6 +440,8 @@ namespace Barotrauma
|
||||
SerializableProperty.DeserializeProperties(this, element);
|
||||
|
||||
ResistanceFor = element.GetAttributeIdentifierArray("resistancefor", Array.Empty<Identifier>())!.ToImmutableArray();
|
||||
ResistanceLimbs = element.GetAttributeEnumArray<LimbType>("resistancelimbs", Array.Empty<LimbType>()).ToImmutableArray();
|
||||
|
||||
BlockTransformation = element.GetAttributeIdentifierArray("blocktransformation", Array.Empty<Identifier>())!.ToImmutableArray();
|
||||
|
||||
var afflictionStatValues = new Dictionary<StatTypes, AppliedStatValue>();
|
||||
@@ -594,7 +613,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
MinInterval = Math.Max(element.GetAttributeFloat(nameof(MinInterval), 1.0f), 1.0f);
|
||||
MinInterval = Math.Max(element.GetAttributeFloat(nameof(MinInterval), 1.0f), 0.1f);
|
||||
MaxInterval = Math.Max(element.GetAttributeFloat(nameof(MaxInterval), 1.0f), MinInterval);
|
||||
MinStrength = Math.Max(element.GetAttributeFloat(nameof(MinStrength), 0f), 0f);
|
||||
MaxStrength = Math.Max(element.GetAttributeFloat(nameof(MaxStrength), MinStrength), MinStrength);
|
||||
@@ -612,6 +631,7 @@ namespace Barotrauma
|
||||
public static readonly Identifier SpaceHerpesType = "spaceherpes".ToIdentifier();
|
||||
public static readonly Identifier AlienInfectedType = "alieninfected".ToIdentifier();
|
||||
public static readonly Identifier InvertControlsType = "invertcontrols".ToIdentifier();
|
||||
public static readonly Identifier DisguisedAsHuskType = "disguiseashusk".ToIdentifier();
|
||||
|
||||
public static AfflictionPrefab InternalDamage => Prefabs["internaldamage"];
|
||||
public static AfflictionPrefab BiteWounds => Prefabs["bitewounds"];
|
||||
@@ -624,7 +644,8 @@ namespace Barotrauma
|
||||
public static AfflictionPrefab OrganDamage => Prefabs["organdamage"];
|
||||
public static AfflictionPrefab Stun => Prefabs[StunType];
|
||||
public static AfflictionPrefab RadiationSickness => Prefabs["radiationsickness"];
|
||||
|
||||
public static AfflictionPrefab HuskInfection => Prefabs["huskinfection"];
|
||||
public static AfflictionPrefab JovianRadiation => Prefabs["jovianradiation"];
|
||||
|
||||
public static readonly PrefabCollection<AfflictionPrefab> Prefabs = new PrefabCollection<AfflictionPrefab>();
|
||||
|
||||
@@ -641,6 +662,11 @@ namespace Barotrauma
|
||||
private readonly LocalizedString defaultDescription;
|
||||
public readonly ImmutableList<Description> Descriptions;
|
||||
|
||||
/// <summary>
|
||||
/// Should the affliction's description be included in the tooltips on the affliction icons above the health bar?
|
||||
/// </summary>
|
||||
public readonly bool ShowDescriptionInTooltip;
|
||||
|
||||
/// <summary>
|
||||
/// Arbitrary string that is used to identify the type of the affliction.
|
||||
/// </summary>
|
||||
@@ -748,6 +774,12 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
public readonly float TreatmentThreshold;
|
||||
|
||||
/// <summary>
|
||||
/// How strong the affliction needs to be for treatment suggestions to be shown in the health interface.
|
||||
/// Defaults to <see cref="TreatmentThreshold"/>.
|
||||
/// </summary>
|
||||
public readonly float TreatmentSuggestionThreshold;
|
||||
|
||||
/// <summary>
|
||||
/// Bots will not try to treat the affliction if the character has any of these afflictions
|
||||
/// </summary>
|
||||
@@ -880,6 +912,8 @@ namespace Barotrauma
|
||||
{
|
||||
defaultDescription = defaultDescription.Fallback(fallbackDescription);
|
||||
}
|
||||
ShowDescriptionInTooltip = element.GetAttributeBool(nameof(ShowDescriptionInTooltip), true);
|
||||
|
||||
IsBuff = element.GetAttributeBool(nameof(IsBuff), false);
|
||||
AffectMachines = element.GetAttributeBool(nameof(AffectMachines), true);
|
||||
|
||||
@@ -898,7 +932,10 @@ namespace Barotrauma
|
||||
|
||||
if (element.GetAttribute("nameidentifier") != null)
|
||||
{
|
||||
Name = TextManager.Get(element.GetAttributeString("nameidentifier", string.Empty)).Fallback(Name);
|
||||
string nameIdentifier = element.GetAttributeString("nameidentifier", string.Empty);
|
||||
Name = TextManager.Get(nameIdentifier)
|
||||
.Fallback(TextManager.Get($"AfflictionName.{nameIdentifier}"))
|
||||
.Fallback(Name);
|
||||
}
|
||||
|
||||
LimbSpecific = element.GetAttributeBool("limbspecific", false);
|
||||
@@ -914,6 +951,12 @@ namespace Barotrauma
|
||||
HideIconAfterDelay = element.GetAttributeBool(nameof(HideIconAfterDelay), false);
|
||||
|
||||
ActivationThreshold = element.GetAttributeFloat(nameof(ActivationThreshold), 0.0f);
|
||||
if (Identifier == StunType && ActivationThreshold > 0.0f)
|
||||
{
|
||||
ActivationThreshold = 0.0f;
|
||||
DebugConsole.AddWarning($"Error in affliction prefab {Identifier}: activation threshold of the stun affliction must be 0, because the strength of the affliction represents the length of the stun and any amount of stun has an effect.");
|
||||
}
|
||||
|
||||
ShowIconThreshold = element.GetAttributeFloat(nameof(ShowIconThreshold), Math.Max(ActivationThreshold, 0.05f));
|
||||
ShowIconToOthersThreshold = element.GetAttributeFloat(nameof(ShowIconToOthersThreshold), ShowIconThreshold);
|
||||
MaxStrength = element.GetAttributeFloat(nameof(MaxStrength), 100.0f);
|
||||
@@ -922,6 +965,7 @@ namespace Barotrauma
|
||||
ShowInHealthScannerThreshold = element.GetAttributeFloat(nameof(ShowInHealthScannerThreshold),
|
||||
Math.Max(ActivationThreshold, AfflictionType == "talentbuff" ? float.MaxValue : ShowIconToOthersThreshold));
|
||||
TreatmentThreshold = element.GetAttributeFloat(nameof(TreatmentThreshold), Math.Max(ActivationThreshold, 10.0f));
|
||||
TreatmentSuggestionThreshold = element.GetAttributeFloat(nameof(TreatmentSuggestionThreshold), TreatmentThreshold);
|
||||
|
||||
DamageOverlayAlpha = element.GetAttributeFloat(nameof(DamageOverlayAlpha), 0.0f);
|
||||
BurnOverlayAlpha = element.GetAttributeFloat(nameof(BurnOverlayAlpha), 0.0f);
|
||||
|
||||
@@ -248,6 +248,12 @@ namespace Barotrauma
|
||||
/// Was the character in full health at the beginning of the frame?
|
||||
/// </summary>
|
||||
public bool WasInFullHealth { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Show the blood overlay screen space effect when the character takes damage.
|
||||
/// Enabled normally, but can be disabled for some special cases.
|
||||
/// </summary>
|
||||
public bool ShowDamageOverlay = true;
|
||||
|
||||
public Affliction PressureAffliction
|
||||
{
|
||||
@@ -442,7 +448,7 @@ namespace Barotrauma
|
||||
return strength;
|
||||
}
|
||||
|
||||
public void ApplyAffliction(Limb targetLimb, Affliction affliction, bool allowStacking = true, bool ignoreUnkillability = false)
|
||||
public void ApplyAffliction(Limb targetLimb, Affliction affliction, bool allowStacking = true, bool ignoreUnkillability = false, bool recalculateVitality = true)
|
||||
{
|
||||
if (Character.GodMode) { return; }
|
||||
if (!ignoreUnkillability)
|
||||
@@ -456,12 +462,12 @@ namespace Barotrauma
|
||||
//if a limb-specific affliction is applied to no specific limb, apply to all limbs
|
||||
foreach (LimbHealth limbHealth in limbHealths)
|
||||
{
|
||||
AddLimbAffliction(limbHealth, affliction, allowStacking: allowStacking);
|
||||
AddLimbAffliction(limbHealth, limb: null, affliction, allowStacking: allowStacking, recalculateVitality: recalculateVitality);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AddLimbAffliction(targetLimb, affliction, allowStacking: allowStacking);
|
||||
AddLimbAffliction(targetLimb, affliction, allowStacking: allowStacking, recalculateVitality: recalculateVitality);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -470,14 +476,17 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public float GetResistance(AfflictionPrefab afflictionPrefab)
|
||||
/// <summary>
|
||||
/// How much resistance all the afflictions the character has give to the specified affliction?
|
||||
/// </summary>
|
||||
public float GetResistance(AfflictionPrefab afflictionPrefab, LimbType limbType)
|
||||
{
|
||||
// This is a % resistance (0 to 1.0)
|
||||
float resistance = 0.0f;
|
||||
foreach (KeyValuePair<Affliction, LimbHealth> kvp in afflictions)
|
||||
{
|
||||
var affliction = kvp.Key;
|
||||
resistance += affliction.GetResistance(afflictionPrefab.Identifier);
|
||||
resistance += affliction.GetResistance(afflictionPrefab.Identifier, limbType);
|
||||
}
|
||||
// This is a multiplier, ie. 0.0 = 100% resistance and 1.0 = 0% resistance
|
||||
float abilityResistanceMultiplier = Character.GetAbilityResistance(afflictionPrefab);
|
||||
@@ -610,7 +619,11 @@ namespace Barotrauma
|
||||
CalculateVitality();
|
||||
}
|
||||
|
||||
public void ApplyDamage(Limb hitLimb, AttackResult attackResult, bool allowStacking = true)
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="recalculateVitality">Set false only as an optimization when you manually call <see cref="RecalculateVitality"/>. Only applies to limb specific afflictions.</param>
|
||||
public void ApplyDamage(Limb hitLimb, AttackResult attackResult, bool allowStacking = true, bool recalculateVitality = true)
|
||||
{
|
||||
if (Unkillable || Character.GodMode) { return; }
|
||||
if (hitLimb.HealthIndex < 0 || hitLimb.HealthIndex >= limbHealths.Count)
|
||||
@@ -619,18 +632,19 @@ namespace Barotrauma
|
||||
"\" only has health configured for" + limbHealths.Count + " limbs but the limb " + hitLimb.type + " is targeting index " + hitLimb.HealthIndex);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
foreach (Affliction newAffliction in attackResult.Afflictions)
|
||||
{
|
||||
if (newAffliction.Prefab.LimbSpecific)
|
||||
{
|
||||
AddLimbAffliction(hitLimb, newAffliction, allowStacking);
|
||||
AddLimbAffliction(hitLimb, newAffliction, allowStacking, recalculateVitality: recalculateVitality);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Always recalculate vitality for non-limb specific afflictions.
|
||||
AddAffliction(newAffliction, allowStacking);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void KillIfOutOfVitality()
|
||||
@@ -664,9 +678,8 @@ namespace Barotrauma
|
||||
if (bleedingDamageAmount > 0.0f && DoesBleed) { afflictions.Add(AfflictionPrefab.Bleeding.Instantiate(bleedingDamageAmount), limbHealth); }
|
||||
if (burnDamageAmount > 0.0f) { afflictions.Add(AfflictionPrefab.Burn.Instantiate(burnDamageAmount), limbHealth); }
|
||||
}
|
||||
|
||||
CalculateVitality();
|
||||
KillIfOutOfVitality();
|
||||
|
||||
RecalculateVitality();
|
||||
}
|
||||
|
||||
public float GetLimbDamage(Limb limb, Identifier afflictionType)
|
||||
@@ -697,12 +710,25 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveAfflictions(Func<Affliction, bool> predicate)
|
||||
{
|
||||
afflictionsToRemove.Clear();
|
||||
afflictionsToRemove.AddRange(afflictions.Keys.Where(affliction => predicate(affliction)));
|
||||
foreach (var affliction in afflictionsToRemove)
|
||||
{
|
||||
afflictions.Remove(affliction);
|
||||
}
|
||||
CalculateVitality();
|
||||
}
|
||||
|
||||
public void RemoveAllAfflictions()
|
||||
{
|
||||
afflictionsToRemove.Clear();
|
||||
afflictionsToRemove.AddRange(afflictions.Keys.Where(a => !irremovableAfflictions.Contains(a)));
|
||||
foreach (var affliction in afflictionsToRemove)
|
||||
{
|
||||
//set strength to 0 in case the affliction needs to react to becoming inactive
|
||||
affliction.Strength = 0.0f;
|
||||
afflictions.Remove(affliction);
|
||||
}
|
||||
foreach (Affliction affliction in irremovableAfflictions)
|
||||
@@ -731,7 +757,11 @@ namespace Barotrauma
|
||||
CalculateVitality();
|
||||
}
|
||||
|
||||
private void AddLimbAffliction(Limb limb, Affliction newAffliction, bool allowStacking = true)
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="recalculateVitality">Set false only as an optimization when you manually call <see cref="RecalculateVitality"/></param>
|
||||
private void AddLimbAffliction(Limb limb, Affliction newAffliction, bool allowStacking = true, bool recalculateVitality = true)
|
||||
{
|
||||
if (!newAffliction.Prefab.LimbSpecific || limb == null) { return; }
|
||||
if (limb.HealthIndex < 0 || limb.HealthIndex >= limbHealths.Count)
|
||||
@@ -740,11 +770,16 @@ namespace Barotrauma
|
||||
"\" only has health configured for" + limbHealths.Count + " limbs but the limb " + limb.type + " is targeting index " + limb.HealthIndex);
|
||||
return;
|
||||
}
|
||||
AddLimbAffliction(limbHealths[limb.HealthIndex], newAffliction, allowStacking);
|
||||
AddLimbAffliction(limbHealths[limb.HealthIndex], limb, newAffliction, allowStacking, recalculateVitality);
|
||||
}
|
||||
|
||||
private void AddLimbAffliction(LimbHealth limbHealth, Affliction newAffliction, bool allowStacking = true)
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="recalculateVitality">Set false only as an optimization when you manually call <see cref="RecalculateVitality"/></param>
|
||||
private void AddLimbAffliction(LimbHealth limbHealth, Limb limb, Affliction newAffliction, bool allowStacking = true, bool recalculateVitality = true)
|
||||
{
|
||||
LimbType limbType = limb?.type ?? LimbType.None;
|
||||
if (Character.Params.IsMachine && !newAffliction.Prefab.AffectMachines) { return; }
|
||||
if (!DoesBleed && newAffliction is AfflictionBleeding) { return; }
|
||||
if (!Character.NeedsOxygen && newAffliction.Prefab == AfflictionPrefab.OxygenLow) { return; }
|
||||
@@ -778,26 +813,29 @@ namespace Barotrauma
|
||||
|
||||
if (existingAffliction != null)
|
||||
{
|
||||
float newStrength = newAffliction.Strength * (100.0f / MaxVitality) * (1f - GetResistance(existingAffliction.Prefab));
|
||||
float newStrength = newAffliction.Strength * (100.0f / MaxVitality) * (1f - GetResistance(existingAffliction.Prefab, limbType));
|
||||
if (allowStacking)
|
||||
{
|
||||
// Add the existing strength
|
||||
newStrength += existingAffliction.Strength;
|
||||
}
|
||||
newStrength = Math.Min(existingAffliction.Prefab.MaxStrength, newStrength);
|
||||
if (existingAffliction == stunAffliction) { Character.SetStun(newStrength, true, true); }
|
||||
existingAffliction.Strength = newStrength;
|
||||
//set stun after setting the strength, because stun multipliers might want to set the strength to something else
|
||||
if (existingAffliction == stunAffliction) { Character.SetStun(newStrength, allowStunDecrease: true, isNetworkMessage: true); }
|
||||
existingAffliction.Duration = existingAffliction.Prefab.Duration;
|
||||
if (newAffliction.Source != null) { existingAffliction.Source = newAffliction.Source; }
|
||||
CalculateVitality();
|
||||
KillIfOutOfVitality();
|
||||
if (recalculateVitality)
|
||||
{
|
||||
RecalculateVitality();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
//create a new instance of the affliction to make sure we don't use the same instance for multiple characters
|
||||
//or modify the affliction instance of an Attack or a StatusEffect
|
||||
var copyAffliction = newAffliction.Prefab.Instantiate(
|
||||
Math.Min(newAffliction.Prefab.MaxStrength, newAffliction.Strength * (100.0f / MaxVitality) * (1f - GetResistance(newAffliction.Prefab))),
|
||||
Math.Min(newAffliction.Prefab.MaxStrength, newAffliction.Strength * (100.0f / MaxVitality) * (1f - GetResistance(newAffliction.Prefab, limbType))),
|
||||
newAffliction.Source);
|
||||
afflictions.Add(copyAffliction, limbHealth);
|
||||
AchievementManager.OnAfflictionReceived(copyAffliction, Character);
|
||||
@@ -805,8 +843,10 @@ namespace Barotrauma
|
||||
|
||||
Character.HealthUpdateInterval = 0.0f;
|
||||
|
||||
CalculateVitality();
|
||||
KillIfOutOfVitality();
|
||||
if (recalculateVitality)
|
||||
{
|
||||
RecalculateVitality();
|
||||
}
|
||||
#if CLIENT
|
||||
if (OpenHealthWindow != this && limbHealth != null)
|
||||
{
|
||||
@@ -817,7 +857,7 @@ namespace Barotrauma
|
||||
|
||||
private void AddAffliction(Affliction newAffliction, bool allowStacking = true)
|
||||
{
|
||||
AddLimbAffliction(limbHealth: null, newAffliction, allowStacking);
|
||||
AddLimbAffliction(limbHealth: null, limb: null, newAffliction, allowStacking);
|
||||
}
|
||||
|
||||
partial void UpdateSkinTint();
|
||||
@@ -850,6 +890,8 @@ namespace Barotrauma
|
||||
affliction.Duration -= deltaTime;
|
||||
if (affliction.Duration <= 0.0f)
|
||||
{
|
||||
//set strength to 0 in case the affliction needs to react to becoming inactive
|
||||
affliction.Strength = 0.0f;
|
||||
afflictionsToRemove.Add(affliction);
|
||||
continue;
|
||||
}
|
||||
@@ -902,14 +944,15 @@ namespace Barotrauma
|
||||
if (!Character.GodMode)
|
||||
{
|
||||
#if CLIENT
|
||||
if (Character.IsVisible)
|
||||
updateVisualsTimer -= deltaTime;
|
||||
if (Character.IsVisible && updateVisualsTimer <= 0.0f)
|
||||
{
|
||||
UpdateLimbAfflictionOverlays();
|
||||
UpdateSkinTint();
|
||||
updateVisualsTimer = UpdateVisualsInterval;
|
||||
}
|
||||
#endif
|
||||
CalculateVitality();
|
||||
KillIfOutOfVitality();
|
||||
RecalculateVitality();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -941,7 +984,7 @@ namespace Barotrauma
|
||||
/// <summary>
|
||||
/// 0-1.
|
||||
/// </summary>
|
||||
public float OxygenLowResistance => !Character.NeedsOxygen ? 1 : GetResistance(oxygenLowAffliction.Prefab);
|
||||
public float OxygenLowResistance => !Character.NeedsOxygen ? 1 : GetResistance(oxygenLowAffliction.Prefab, LimbType.None);
|
||||
|
||||
private void UpdateOxygen(float deltaTime)
|
||||
{
|
||||
@@ -951,7 +994,7 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
|
||||
float oxygenlowResistance = GetResistance(oxygenLowAffliction.Prefab);
|
||||
float oxygenlowResistance = GetResistance(oxygenLowAffliction.Prefab, LimbType.None);
|
||||
float prevOxygen = OxygenAmount;
|
||||
if (IsUnconscious)
|
||||
{
|
||||
@@ -991,13 +1034,13 @@ namespace Barotrauma
|
||||
CalculateVitality();
|
||||
}
|
||||
|
||||
public void CalculateVitality()
|
||||
private void CalculateVitality()
|
||||
{
|
||||
vitality = MaxVitality;
|
||||
IsParalyzed = false;
|
||||
if (Unkillable || Character.GodMode) { return; }
|
||||
|
||||
foreach (var (affliction, limbHealth) in afflictions)
|
||||
foreach ((Affliction affliction, LimbHealth limbHealth) in afflictions)
|
||||
{
|
||||
float vitalityDecrease = affliction.GetVitalityDecrease(this);
|
||||
if (limbHealth != null)
|
||||
@@ -1020,6 +1063,12 @@ namespace Barotrauma
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public void RecalculateVitality()
|
||||
{
|
||||
CalculateVitality();
|
||||
KillIfOutOfVitality();
|
||||
}
|
||||
|
||||
private static float GetVitalityMultiplier(Affliction affliction, LimbHealth limbHealth)
|
||||
{
|
||||
@@ -1139,7 +1188,11 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
/// <param name="treatmentSuitability">A dictionary where the key is the identifier of the item and the value the suitability</param>
|
||||
/// <param name="predictFutureDuration">If above 0, the method will take into account how much currently active status effects while affect the afflictions in the next x seconds.</param>
|
||||
public void GetSuitableTreatments(Dictionary<Identifier, float> treatmentSuitability, Character user, Limb limb = null, bool ignoreHiddenAfflictions = false, float predictFutureDuration = 0.0f)
|
||||
/// <param name="checkTreatmentThreshold">Should the method check whether the afflictions are above <see cref="AfflictionPrefab.TreatmentThreshold"/> (whether they're severe enough for AI to treat)?</param>
|
||||
/// <param name="checkTreatmentSuggestionThreshold">Should the method check whether the afflictions are above <see cref="AfflictionPrefab.TreatmentSuggestionThreshold"/> (whether treatment suggestions are shown in the health interface)?</param>
|
||||
public void GetSuitableTreatments(Dictionary<Identifier, float> treatmentSuitability, Character user, Limb limb = null, bool ignoreHiddenAfflictions = false,
|
||||
bool checkTreatmentThreshold = true, bool checkTreatmentSuggestionThreshold = true,
|
||||
float predictFutureDuration = 0.0f)
|
||||
{
|
||||
//key = item identifier
|
||||
//float = suitability
|
||||
@@ -1190,7 +1243,14 @@ namespace Barotrauma
|
||||
//if this a suitable treatment, ignore it if the affliction isn't severe enough to treat
|
||||
//if the suitability is negative though, we need to take it into account!
|
||||
//otherwise we may end up e.g. giving too much opiates to someone already close to overdosing
|
||||
if (totalAfflictionStrength < affliction.Prefab.TreatmentThreshold) { continue; }
|
||||
if (checkTreatmentThreshold)
|
||||
{
|
||||
if (totalAfflictionStrength < affliction.Prefab.TreatmentThreshold) { continue; }
|
||||
}
|
||||
if (checkTreatmentSuggestionThreshold)
|
||||
{
|
||||
if (totalAfflictionStrength < affliction.Prefab.TreatmentSuggestionThreshold) { continue; }
|
||||
}
|
||||
}
|
||||
if (treatment.Value > strength)
|
||||
{
|
||||
|
||||
@@ -53,7 +53,7 @@ namespace Barotrauma
|
||||
private set
|
||||
{
|
||||
rawAfflictionIdentifierString = value;
|
||||
ParseAfflictionIdentifiers();
|
||||
parsedAfflictionIdentifiers = rawAfflictionIdentifierString.ToIdentifiers().ToImmutableArray();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ namespace Barotrauma
|
||||
private set
|
||||
{
|
||||
rawAfflictionTypeString = value;
|
||||
ParseAfflictionTypes();
|
||||
parsedAfflictionTypes = rawAfflictionTypeString.ToIdentifiers().ToImmutableArray();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,30 +119,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private void ParseAfflictionTypes()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(rawAfflictionTypeString))
|
||||
{
|
||||
parsedAfflictionTypes = Enumerable.Empty<Identifier>().ToImmutableArray();
|
||||
return;
|
||||
}
|
||||
|
||||
parsedAfflictionTypes = rawAfflictionTypeString.Split(',', ',')
|
||||
.Select(s => s.Trim()).ToIdentifiers().ToImmutableArray();
|
||||
}
|
||||
|
||||
private void ParseAfflictionIdentifiers()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(rawAfflictionIdentifierString))
|
||||
{
|
||||
parsedAfflictionIdentifiers = Enumerable.Empty<Identifier>().ToImmutableArray();
|
||||
return;
|
||||
}
|
||||
|
||||
parsedAfflictionIdentifiers = rawAfflictionIdentifierString.Split(',', ',')
|
||||
.Select(s => s.Trim()).ToIdentifiers().ToImmutableArray();
|
||||
}
|
||||
|
||||
public bool MatchesAfflictionIdentifier(string identifier) =>
|
||||
MatchesAfflictionIdentifier(identifier.ToIdentifier());
|
||||
|
||||
|
||||
Reference in New Issue
Block a user