Release v0.15.12.0
This commit is contained in:
+111
-25
@@ -17,6 +17,8 @@ namespace Barotrauma
|
||||
public float PendingAdditionStrength { get; set; }
|
||||
public float AdditionStrength { get; set; }
|
||||
|
||||
private float fluctuationTimer;
|
||||
|
||||
protected float _strength;
|
||||
|
||||
[Serialize(0f, true), Editable]
|
||||
@@ -56,6 +58,8 @@ namespace Barotrauma
|
||||
|
||||
public readonly Dictionary<AfflictionPrefab.PeriodicEffect, float> PeriodicEffectTimers = new Dictionary<AfflictionPrefab.PeriodicEffect, float>();
|
||||
|
||||
public double AppliedAsSuccessfulTreatmentTime, AppliedAsFailedTreatmentTime;
|
||||
|
||||
/// <summary>
|
||||
/// Which character gave this affliction
|
||||
/// </summary>
|
||||
@@ -123,7 +127,7 @@ namespace Barotrauma
|
||||
float amount = MathHelper.Lerp(
|
||||
currentEffect.MinGrainStrength,
|
||||
currentEffect.MaxGrainStrength,
|
||||
(Strength - currentEffect.MinStrength) / (currentEffect.MaxStrength - currentEffect.MinStrength));
|
||||
(Strength - currentEffect.MinStrength) / (currentEffect.MaxStrength - currentEffect.MinStrength)) * GetScreenEffectFluctuation(currentEffect);
|
||||
|
||||
if (Prefab.GrainBurst > 0 && AdditionStrength > amount)
|
||||
{
|
||||
@@ -138,12 +142,12 @@ namespace Barotrauma
|
||||
if (Strength < Prefab.ActivationThreshold) { return 0.0f; }
|
||||
AfflictionPrefab.Effect currentEffect = GetActiveEffect();
|
||||
if (currentEffect == null) { return 0.0f; }
|
||||
if (currentEffect.MaxScreenDistortStrength - currentEffect.MinScreenDistortStrength < 0.0f) { return 0.0f; }
|
||||
if (currentEffect.MaxScreenDistort - currentEffect.MinScreenDistort < 0.0f) { return 0.0f; }
|
||||
|
||||
return MathHelper.Lerp(
|
||||
currentEffect.MinScreenDistortStrength,
|
||||
currentEffect.MaxScreenDistortStrength,
|
||||
(Strength - currentEffect.MinStrength) / (currentEffect.MaxStrength - currentEffect.MinStrength));
|
||||
currentEffect.MinScreenDistort,
|
||||
currentEffect.MaxScreenDistort,
|
||||
(Strength - currentEffect.MinStrength) / (currentEffect.MaxStrength - currentEffect.MinStrength)) * GetScreenEffectFluctuation(currentEffect);
|
||||
}
|
||||
|
||||
public float GetRadialDistortStrength()
|
||||
@@ -151,12 +155,12 @@ namespace Barotrauma
|
||||
if (Strength < Prefab.ActivationThreshold) { return 0.0f; }
|
||||
AfflictionPrefab.Effect currentEffect = GetActiveEffect();
|
||||
if (currentEffect == null) { return 0.0f; }
|
||||
if (currentEffect.MaxRadialDistortStrength - currentEffect.MinRadialDistortStrength < 0.0f) { return 0.0f; }
|
||||
if (currentEffect.MaxRadialDistort - currentEffect.MinRadialDistort < 0.0f) { return 0.0f; }
|
||||
|
||||
return MathHelper.Lerp(
|
||||
currentEffect.MinRadialDistortStrength,
|
||||
currentEffect.MaxRadialDistortStrength,
|
||||
(Strength - currentEffect.MinStrength) / (currentEffect.MaxStrength - currentEffect.MinStrength));
|
||||
currentEffect.MinRadialDistort,
|
||||
currentEffect.MaxRadialDistort,
|
||||
(Strength - currentEffect.MinStrength) / (currentEffect.MaxStrength - currentEffect.MinStrength)) * GetScreenEffectFluctuation(currentEffect);
|
||||
}
|
||||
|
||||
public float GetChromaticAberrationStrength()
|
||||
@@ -164,11 +168,50 @@ namespace Barotrauma
|
||||
if (Strength < Prefab.ActivationThreshold) { return 0.0f; }
|
||||
AfflictionPrefab.Effect currentEffect = GetActiveEffect();
|
||||
if (currentEffect == null) { return 0.0f; }
|
||||
if (currentEffect.MaxChromaticAberrationStrength - currentEffect.MinChromaticAberrationStrength < 0.0f) { return 0.0f; }
|
||||
if (currentEffect.MaxChromaticAberration - currentEffect.MinChromaticAberration < 0.0f) { return 0.0f; }
|
||||
|
||||
return MathHelper.Lerp(
|
||||
currentEffect.MinChromaticAberrationStrength,
|
||||
currentEffect.MaxChromaticAberrationStrength,
|
||||
currentEffect.MinChromaticAberration,
|
||||
currentEffect.MaxChromaticAberration,
|
||||
(Strength - currentEffect.MinStrength) / (currentEffect.MaxStrength - currentEffect.MinStrength)) * GetScreenEffectFluctuation(currentEffect);
|
||||
}
|
||||
|
||||
public float GetAfflictionOverlayMultiplier()
|
||||
{
|
||||
//If the overlay's alpha progresses linearly, then don't worry about affliction effects.
|
||||
if (Prefab.AfflictionOverlayAlphaIsLinear) { return (Strength / Prefab.MaxStrength); }
|
||||
if (Strength < Prefab.ActivationThreshold) { return 0.0f; }
|
||||
AfflictionPrefab.Effect currentEffect = GetActiveEffect();
|
||||
if (currentEffect == null) { return 0.0f; }
|
||||
if (currentEffect.MaxAfflictionOverlayAlphaMultiplier - currentEffect.MinAfflictionOverlayAlphaMultiplier < 0.0f) { return 0.0f; }
|
||||
|
||||
return MathHelper.Lerp(
|
||||
currentEffect.MinAfflictionOverlayAlphaMultiplier,
|
||||
currentEffect.MaxAfflictionOverlayAlphaMultiplier,
|
||||
(Strength - currentEffect.MinStrength) / (currentEffect.MaxStrength - currentEffect.MinStrength));
|
||||
}
|
||||
|
||||
public Color GetFaceTint()
|
||||
{
|
||||
if (Strength < Prefab.ActivationThreshold) { return Color.TransparentBlack; }
|
||||
AfflictionPrefab.Effect currentEffect = GetActiveEffect();
|
||||
if (currentEffect == null) { return Color.TransparentBlack; }
|
||||
|
||||
return Color.Lerp(
|
||||
currentEffect.MinFaceTint,
|
||||
currentEffect.MaxFaceTint,
|
||||
(Strength - currentEffect.MinStrength) / (currentEffect.MaxStrength - currentEffect.MinStrength));
|
||||
}
|
||||
|
||||
public Color GetBodyTint()
|
||||
{
|
||||
if (Strength < Prefab.ActivationThreshold) { return Color.TransparentBlack; }
|
||||
AfflictionPrefab.Effect currentEffect = GetActiveEffect();
|
||||
if (currentEffect == null) { return Color.TransparentBlack; }
|
||||
|
||||
return Color.Lerp(
|
||||
currentEffect.MinBodyTint,
|
||||
currentEffect.MaxBodyTint,
|
||||
(Strength - currentEffect.MinStrength) / (currentEffect.MaxStrength - currentEffect.MinStrength));
|
||||
}
|
||||
|
||||
@@ -177,12 +220,18 @@ namespace Barotrauma
|
||||
if (Strength < Prefab.ActivationThreshold) { return 0.0f; }
|
||||
AfflictionPrefab.Effect currentEffect = GetActiveEffect();
|
||||
if (currentEffect == null) { return 0.0f; }
|
||||
if (currentEffect.MaxScreenBlurStrength - currentEffect.MinScreenBlurStrength < 0.0f) { return 0.0f; }
|
||||
if (currentEffect.MaxScreenBlur - currentEffect.MinScreenBlur < 0.0f) { return 0.0f; }
|
||||
|
||||
return MathHelper.Lerp(
|
||||
currentEffect.MinScreenBlurStrength,
|
||||
currentEffect.MaxScreenBlurStrength,
|
||||
(Strength - currentEffect.MinStrength) / (currentEffect.MaxStrength - currentEffect.MinStrength));
|
||||
currentEffect.MinScreenBlur,
|
||||
currentEffect.MaxScreenBlur,
|
||||
(Strength - currentEffect.MinStrength) / (currentEffect.MaxStrength - currentEffect.MinStrength)) * GetScreenEffectFluctuation(currentEffect);
|
||||
}
|
||||
|
||||
private float GetScreenEffectFluctuation(AfflictionPrefab.Effect currentEffect)
|
||||
{
|
||||
if (currentEffect == null || currentEffect.ScreenEffectFluctuationFrequency <= 0.0f) { return 1.0f; }
|
||||
return ((float)Math.Sin(fluctuationTimer * MathHelper.TwoPi) + 1.0f) * 0.5f;
|
||||
}
|
||||
|
||||
public float GetSkillMultiplier()
|
||||
@@ -210,14 +259,17 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public float GetResistance(string afflictionId)
|
||||
public float GetResistance(AfflictionPrefab affliction)
|
||||
{
|
||||
if (Strength < Prefab.ActivationThreshold) { return 0.0f; }
|
||||
AfflictionPrefab.Effect currentEffect = GetActiveEffect();
|
||||
if (currentEffect == null) { return 0.0f; }
|
||||
if (currentEffect.MaxResistance - currentEffect.MinResistance <= 0.0f) { return 0.0f; }
|
||||
if (afflictionId != null && afflictionId != currentEffect.ResistanceFor) { return 0.0f; }
|
||||
|
||||
if (!currentEffect.ResistanceFor.Any(r =>
|
||||
r.Equals(affliction.Identifier, StringComparison.OrdinalIgnoreCase) ||
|
||||
r.Equals(affliction.AfflictionType, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
return MathHelper.Lerp(
|
||||
currentEffect.MinResistance,
|
||||
currentEffect.MaxResistance,
|
||||
@@ -229,14 +281,39 @@ namespace Barotrauma
|
||||
if (Strength < Prefab.ActivationThreshold) { return 1.0f; }
|
||||
AfflictionPrefab.Effect currentEffect = GetActiveEffect();
|
||||
if (currentEffect == null) { return 1.0f; }
|
||||
if (currentEffect.MaxSpeedMultiplier - currentEffect.MinSpeedMultiplier <= 0.0f) { return 1.0f; }
|
||||
|
||||
return MathHelper.Lerp(
|
||||
currentEffect.MinSpeedMultiplier,
|
||||
currentEffect.MaxSpeedMultiplier,
|
||||
(Strength - currentEffect.MinStrength) / (currentEffect.MaxStrength - currentEffect.MinStrength));
|
||||
}
|
||||
|
||||
public float GetStatValue(StatTypes statType)
|
||||
{
|
||||
if (!(GetViableEffect() is AfflictionPrefab.Effect currentEffect)) { return 0.0f; }
|
||||
|
||||
if (currentEffect.AfflictionStatValues.TryGetValue(statType, out var value))
|
||||
{
|
||||
return MathHelper.Lerp(
|
||||
value.minValue,
|
||||
value.maxValue,
|
||||
(Strength - currentEffect.MinStrength) / (currentEffect.MaxStrength - currentEffect.MinStrength));
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
public bool HasFlag(AbilityFlags flagType)
|
||||
{
|
||||
if (!(GetViableEffect() is AfflictionPrefab.Effect currentEffect)) { return false; }
|
||||
|
||||
return currentEffect.AfflictionAbilityFlags.Contains(flagType);
|
||||
}
|
||||
|
||||
private AfflictionPrefab.Effect GetViableEffect()
|
||||
{
|
||||
if (Strength < Prefab.ActivationThreshold) { return null; }
|
||||
return GetActiveEffect();
|
||||
}
|
||||
|
||||
public virtual void Update(CharacterHealth characterHealth, Limb targetLimb, float deltaTime)
|
||||
{
|
||||
foreach (AfflictionPrefab.PeriodicEffect periodicEffect in Prefab.PeriodicEffects)
|
||||
@@ -262,13 +339,20 @@ namespace Barotrauma
|
||||
AfflictionPrefab.Effect currentEffect = GetActiveEffect();
|
||||
if (currentEffect == null) { return; }
|
||||
|
||||
fluctuationTimer += deltaTime * currentEffect.ScreenEffectFluctuationFrequency;
|
||||
fluctuationTimer %= 1.0f;
|
||||
|
||||
if (currentEffect.StrengthChange < 0) // Reduce diminishing of buffs if boosted
|
||||
{
|
||||
_strength += currentEffect.StrengthChange * deltaTime * StrengthDiminishMultiplier;
|
||||
float durationMultiplier = 1 / (1 + (Prefab.IsBuff ? characterHealth.Character.GetStatValue(StatTypes.BuffDurationMultiplier)
|
||||
: characterHealth.Character.GetStatValue(StatTypes.DebuffDurationMultiplier)));
|
||||
|
||||
_strength += currentEffect.StrengthChange * deltaTime * StrengthDiminishMultiplier * durationMultiplier;
|
||||
|
||||
}
|
||||
else // Reduce strengthening of afflictions if resistant
|
||||
else if (currentEffect.StrengthChange > 0) // Reduce strengthening of afflictions if resistant
|
||||
{
|
||||
_strength += currentEffect.StrengthChange * deltaTime * (1f - characterHealth.GetResistance(Prefab.Identifier));
|
||||
_strength += currentEffect.StrengthChange * deltaTime * (1f - characterHealth.GetResistance(Prefab));
|
||||
}
|
||||
// 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);
|
||||
@@ -306,6 +390,8 @@ namespace Barotrauma
|
||||
private readonly List<ISerializableEntity> targets = new List<ISerializableEntity>();
|
||||
public void ApplyStatusEffect(ActionType type, StatusEffect statusEffect, float deltaTime, CharacterHealth characterHealth, Limb targetLimb)
|
||||
{
|
||||
if (type == ActionType.OnDamaged && !statusEffect.HasRequiredAfflictions(characterHealth.Character.LastDamage)) { return; }
|
||||
|
||||
statusEffect.SetUser(Source);
|
||||
if (statusEffect.HasTargetType(StatusEffect.TargetType.Character))
|
||||
{
|
||||
|
||||
+56
-12
@@ -3,6 +3,7 @@ using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using System;
|
||||
using Barotrauma.Extensions;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -21,6 +22,8 @@ namespace Barotrauma
|
||||
|
||||
private Character character;
|
||||
|
||||
private bool stun = true;
|
||||
|
||||
private readonly List<Affliction> huskInfection = new List<Affliction>();
|
||||
|
||||
[Serialize(0f, true), Editable]
|
||||
@@ -34,6 +37,11 @@ namespace Barotrauma
|
||||
float threshold = _strength > ActiveThreshold ? ActiveThreshold + 1 : DormantThreshold - 1;
|
||||
float max = Math.Max(threshold, previousValue);
|
||||
_strength = Math.Clamp(value, 0, max);
|
||||
stun = GameMain.GameSession?.IsRunning ?? true;
|
||||
if (previousValue > 0.0f && value <= 0.0f)
|
||||
{
|
||||
DeactivateHusk();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,8 +59,12 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private float DormantThreshold => Prefab.MaxStrength * 0.5f;
|
||||
private float ActiveThreshold => Prefab.MaxStrength * 0.75f;
|
||||
private float DormantThreshold => (Prefab as AfflictionPrefabHusk)?.DormantThreshold ?? Prefab.MaxStrength * 0.5f;
|
||||
private float ActiveThreshold => (Prefab as AfflictionPrefabHusk)?.ActiveThreshold ?? Prefab.MaxStrength * 0.75f;
|
||||
|
||||
private float TransitionThreshold => (Prefab as AfflictionPrefabHusk)?.TransitionThreshold ?? Prefab.MaxStrength * 0.75f;
|
||||
|
||||
private float TransformThresholdOnDeath => (Prefab as AfflictionPrefabHusk)?.TransformThresholdOnDeath ?? ActiveThreshold;
|
||||
|
||||
public AfflictionHusk(AfflictionPrefab prefab, float strength) : base(prefab, strength) { }
|
||||
|
||||
@@ -83,9 +95,9 @@ namespace Barotrauma
|
||||
}
|
||||
State = InfectionState.Transition;
|
||||
}
|
||||
else if (Strength < Prefab.MaxStrength)
|
||||
else if (Strength < TransitionThreshold)
|
||||
{
|
||||
if (State != InfectionState.Active)
|
||||
if (State != InfectionState.Active && stun)
|
||||
{
|
||||
character.SetStun(Rand.Range(2, 4));
|
||||
}
|
||||
@@ -139,6 +151,7 @@ namespace Barotrauma
|
||||
|
||||
private void DeactivateHusk()
|
||||
{
|
||||
if (character?.AnimController == null || character.Removed) { return; }
|
||||
if (Prefab is AfflictionPrefabHusk { NeedsAir: false })
|
||||
{
|
||||
character.NeedsAir = character.Params.MainElement.GetAttributeBool("needsair", false);
|
||||
@@ -161,7 +174,7 @@ namespace Barotrauma
|
||||
private void CharacterDead(Character character, CauseOfDeath causeOfDeath)
|
||||
{
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; }
|
||||
if (Strength < ActiveThreshold || character.Removed)
|
||||
if (Strength < TransformThresholdOnDeath || character.Removed)
|
||||
{
|
||||
UnsubscribeFromDeathEvent();
|
||||
return;
|
||||
@@ -205,6 +218,14 @@ namespace Barotrauma
|
||||
XElement parentElement = new XElement("CharacterInfo");
|
||||
XElement infoElement = character.Info?.Save(parentElement);
|
||||
CharacterInfo huskCharacterInfo = infoElement == null ? null : new CharacterInfo(infoElement);
|
||||
|
||||
if (huskCharacterInfo != null)
|
||||
{
|
||||
var bodyTint = GetBodyTint();
|
||||
huskCharacterInfo.SkinColor =
|
||||
Color.Lerp(huskCharacterInfo.SkinColor, bodyTint.Opaque(), bodyTint.A / 255.0f);
|
||||
}
|
||||
|
||||
var husk = Character.Create(huskedSpeciesName, character.WorldPosition, ToolBox.RandomSeed(8), huskCharacterInfo, isRemotePlayer: false, hasAi: true);
|
||||
if (husk.Info != null)
|
||||
{
|
||||
@@ -212,6 +233,25 @@ namespace Barotrauma
|
||||
husk.Info.TeamID = CharacterTeamType.None;
|
||||
}
|
||||
|
||||
if (Prefab is AfflictionPrefabHusk huskPrefab)
|
||||
{
|
||||
if (huskPrefab.ControlHusk)
|
||||
{
|
||||
#if SERVER
|
||||
var client = GameMain.Server?.ConnectedClients.FirstOrDefault(c => c.CharacterInfo.Character == character);
|
||||
if (client != null)
|
||||
{
|
||||
GameMain.Server.SetClientCharacter(client, husk);
|
||||
}
|
||||
#else
|
||||
if (!character.IsRemotelyControlled && character == Character.Controlled)
|
||||
{
|
||||
Character.Controlled = husk;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Limb limb in husk.AnimController.Limbs)
|
||||
{
|
||||
if (limb.type == LimbType.None)
|
||||
@@ -229,15 +269,19 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if ((Prefab as AfflictionPrefabHusk)?.TransferBuffs ?? false)
|
||||
{
|
||||
foreach (Affliction affliction in character.CharacterHealth.Afflictions)
|
||||
{
|
||||
if (affliction.Prefab.IsBuff)
|
||||
{
|
||||
husk.CharacterHealth.ApplyAffliction(null, affliction.Prefab.Instantiate(affliction.Strength));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (character.Inventory != null && husk.Inventory != null)
|
||||
{
|
||||
if (character.Inventory.Capacity != husk.Inventory.Capacity)
|
||||
{
|
||||
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;
|
||||
}
|
||||
for (int i = 0; i < character.Inventory.Capacity && i < husk.Inventory.Capacity; i++)
|
||||
{
|
||||
character.Inventory.GetItemsAt(i).ForEachMod(item => husk.Inventory.TryPutItem(item, i, true, false, null));
|
||||
|
||||
+154
-68
@@ -1,10 +1,10 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Barotrauma.Abilities;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Xml.Linq;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -91,9 +91,16 @@ namespace Barotrauma
|
||||
AttachLimbType = LimbType.None;
|
||||
}
|
||||
|
||||
TransferBuffs = element.GetAttributeBool("transferbuffs", true);
|
||||
SendMessages = element.GetAttributeBool("sendmessages", true);
|
||||
CauseSpeechImpediment = element.GetAttributeBool("causespeechimpediment", true);
|
||||
NeedsAir = element.GetAttributeBool("needsair", false);
|
||||
ControlHusk = element.GetAttributeBool("controlhusk", false);
|
||||
|
||||
DormantThreshold = element.GetAttributeFloat("dormantthreshold", MaxStrength * 0.5f);
|
||||
ActiveThreshold = element.GetAttributeFloat("activethreshold", MaxStrength * 0.75f);
|
||||
TransitionThreshold = element.GetAttributeFloat("transitionthreshold", MaxStrength);
|
||||
TransformThresholdOnDeath = element.GetAttributeFloat("transformthresholdondeath", ActiveThreshold);
|
||||
}
|
||||
|
||||
// Use any of these to define which limb the appendage is attached to.
|
||||
@@ -102,13 +109,18 @@ namespace Barotrauma
|
||||
public readonly string AttachLimbName;
|
||||
public readonly LimbType AttachLimbType;
|
||||
|
||||
public float ActiveThreshold, DormantThreshold, TransitionThreshold;
|
||||
public float TransformThresholdOnDeath;
|
||||
|
||||
public readonly string HuskedSpeciesName;
|
||||
public readonly string[] TargetSpecies;
|
||||
public const string Tag = "[speciesname]";
|
||||
|
||||
public readonly bool TransferBuffs;
|
||||
public readonly bool SendMessages;
|
||||
public readonly bool CauseSpeechImpediment;
|
||||
public readonly bool NeedsAir;
|
||||
public readonly bool ControlHusk;
|
||||
}
|
||||
|
||||
class AfflictionPrefab : IPrefab, IDisposable, IHasUintIdentifier
|
||||
@@ -116,83 +128,123 @@ namespace Barotrauma
|
||||
public class Effect
|
||||
{
|
||||
//this effect is applied when the strength is within this range
|
||||
public float MinStrength, MaxStrength;
|
||||
[Serialize(0.0f, false)]
|
||||
public float MinStrength { get; private set; }
|
||||
|
||||
[Serialize(0.0f, false)]
|
||||
public float MaxStrength { get; private set; }
|
||||
|
||||
[Serialize(0.0f, false)]
|
||||
public float MinVitalityDecrease { get; private set; }
|
||||
|
||||
[Serialize(0.0f, false)]
|
||||
public float MaxVitalityDecrease { get; private set; }
|
||||
|
||||
public readonly float MinVitalityDecrease = 0.0f;
|
||||
public readonly float MaxVitalityDecrease = 0.0f;
|
||||
|
||||
//how much the strength of the affliction changes per second
|
||||
public readonly float StrengthChange = 0.0f;
|
||||
[Serialize(0.0f, false)]
|
||||
public float StrengthChange { get; private set; }
|
||||
|
||||
public readonly bool MultiplyByMaxVitality;
|
||||
[Serialize(false, false)]
|
||||
public bool MultiplyByMaxVitality { get; private set; }
|
||||
|
||||
public float MinScreenBlurStrength, MaxScreenBlurStrength;
|
||||
public float MinScreenDistortStrength, MaxScreenDistortStrength;
|
||||
public float MinGrainStrength, MaxGrainStrength;
|
||||
public float MinRadialDistortStrength, MaxRadialDistortStrength;
|
||||
public float MinChromaticAberrationStrength, MaxChromaticAberrationStrength;
|
||||
public float MinSpeedMultiplier, MaxSpeedMultiplier;
|
||||
public float MinBuffMultiplier, MaxBuffMultiplier;
|
||||
[Serialize(0.0f, false)]
|
||||
public float MinScreenBlur { get; private set; }
|
||||
|
||||
public float MinSkillMultiplier, MaxSkillMultiplier;
|
||||
[Serialize(0.0f, false)]
|
||||
public float MaxScreenBlur { get; private set; }
|
||||
|
||||
public float MinResistance, MaxResistance;
|
||||
public string ResistanceFor;
|
||||
public string DialogFlag;
|
||||
[Serialize(0.0f, false)]
|
||||
public float MinScreenDistort { get; private set; }
|
||||
|
||||
[Serialize(0.0f, false)]
|
||||
public float MaxScreenDistort { get; private set; }
|
||||
|
||||
[Serialize(0.0f, false)]
|
||||
public float MinRadialDistort { get; private set; }
|
||||
|
||||
[Serialize(0.0f, false)]
|
||||
public float MaxRadialDistort { get; private set; }
|
||||
|
||||
[Serialize(0.0f, false)]
|
||||
public float MinChromaticAberration { get; private set; }
|
||||
|
||||
[Serialize(0.0f, false)]
|
||||
public float MaxChromaticAberration { get; private set; }
|
||||
|
||||
[Serialize("255,255,255,255", false)]
|
||||
public Color GrainColor { get; private set; }
|
||||
|
||||
[Serialize(0.0f, false)]
|
||||
public float MinGrainStrength { get; private set; }
|
||||
|
||||
[Serialize(0.0f, false)]
|
||||
public float MaxGrainStrength { get; private set; }
|
||||
|
||||
[Serialize(0.0f, false)]
|
||||
public float ScreenEffectFluctuationFrequency { get; private set; }
|
||||
|
||||
[Serialize(1.0f, false)]
|
||||
public float MinAfflictionOverlayAlphaMultiplier { get; private set; }
|
||||
|
||||
[Serialize(1.0f, false)]
|
||||
public float MaxAfflictionOverlayAlphaMultiplier { get; private set; }
|
||||
|
||||
[Serialize(1.0f, false)]
|
||||
public float MinBuffMultiplier { get; private set; }
|
||||
|
||||
[Serialize(1.0f, false)]
|
||||
public float MaxBuffMultiplier { get; private set; }
|
||||
|
||||
[Serialize(1.0f, false)]
|
||||
public float MinSpeedMultiplier { get; private set; }
|
||||
|
||||
[Serialize(1.0f, false)]
|
||||
public float MaxSpeedMultiplier { get; private set; }
|
||||
|
||||
[Serialize(1.0f, false)]
|
||||
public float MinSkillMultiplier { get; private set; }
|
||||
|
||||
[Serialize(1.0f, false)]
|
||||
public float MaxSkillMultiplier { get; private set; }
|
||||
|
||||
private readonly string[] resistanceFor;
|
||||
public IEnumerable<string> ResistanceFor
|
||||
{
|
||||
get { return resistanceFor; }
|
||||
}
|
||||
|
||||
[Serialize(0.0f, false)]
|
||||
public float MinResistance { get; private set; }
|
||||
|
||||
[Serialize(0.0f, false)]
|
||||
public float MaxResistance { get; private set; }
|
||||
|
||||
[Serialize("", false)]
|
||||
public string DialogFlag { get; private set; }
|
||||
|
||||
[Serialize("0,0,0,0", false)]
|
||||
public Color MinFaceTint { get; private set; }
|
||||
|
||||
[Serialize("0,0,0,0", false)]
|
||||
public Color MaxFaceTint { get; private set; }
|
||||
|
||||
[Serialize("0,0,0,0", false)]
|
||||
public Color MinBodyTint { get; private set; }
|
||||
|
||||
[Serialize("0,0,0,0", false)]
|
||||
public Color MaxBodyTint { get; private set; }
|
||||
|
||||
public readonly Dictionary<StatTypes, (float minValue, float maxValue)> AfflictionStatValues = new Dictionary<StatTypes, (float minValue, float maxValue)>();
|
||||
public readonly HashSet<AbilityFlags> AfflictionAbilityFlags = new HashSet<AbilityFlags>();
|
||||
|
||||
//statuseffects applied on the character when the affliction is active
|
||||
public readonly List<StatusEffect> StatusEffects = new List<StatusEffect>();
|
||||
|
||||
public Effect(XElement element, string parentDebugName)
|
||||
{
|
||||
MinStrength = element.GetAttributeFloat("minstrength", 0);
|
||||
MaxStrength = element.GetAttributeFloat("maxstrength", 0);
|
||||
SerializableProperty.DeserializeProperties(this, element);
|
||||
|
||||
MultiplyByMaxVitality = element.GetAttributeBool("multiplybymaxvitality", false);
|
||||
|
||||
MinVitalityDecrease = element.GetAttributeFloat("minvitalitydecrease", 0.0f);
|
||||
MaxVitalityDecrease = element.GetAttributeFloat("maxvitalitydecrease", 0.0f);
|
||||
MaxVitalityDecrease = Math.Max(MinVitalityDecrease, MaxVitalityDecrease);
|
||||
|
||||
MinScreenDistortStrength = element.GetAttributeFloat("minscreendistort", 0.0f);
|
||||
MaxScreenDistortStrength = element.GetAttributeFloat("maxscreendistort", 0.0f);
|
||||
MaxScreenDistortStrength = Math.Max(MinScreenDistortStrength, MaxScreenDistortStrength);
|
||||
|
||||
MinRadialDistortStrength = element.GetAttributeFloat("minradialdistort", 0.0f);
|
||||
MaxRadialDistortStrength = element.GetAttributeFloat("maxradialdistort", 0.0f);
|
||||
MaxRadialDistortStrength = Math.Max(MinRadialDistortStrength, MaxRadialDistortStrength);
|
||||
|
||||
MinChromaticAberrationStrength = element.GetAttributeFloat("minchromaticaberration", 0.0f);
|
||||
MaxChromaticAberrationStrength = element.GetAttributeFloat("maxchromaticaberration", 0.0f);
|
||||
MaxChromaticAberrationStrength = Math.Max(MinChromaticAberrationStrength, MaxChromaticAberrationStrength);
|
||||
|
||||
MinGrainStrength = element.GetAttributeFloat(nameof(MinGrainStrength).ToLower(), 0.0f);
|
||||
MaxGrainStrength = element.GetAttributeFloat(nameof(MaxGrainStrength).ToLower(), 0.0f);
|
||||
MaxGrainStrength = Math.Max(MinGrainStrength, MaxGrainStrength);
|
||||
|
||||
MinScreenBlurStrength = element.GetAttributeFloat("minscreenblur", 0.0f);
|
||||
MaxScreenBlurStrength = element.GetAttributeFloat("maxscreenblur", 0.0f);
|
||||
MaxScreenBlurStrength = Math.Max(MinScreenBlurStrength, MaxScreenBlurStrength);
|
||||
|
||||
MinSkillMultiplier = element.GetAttributeFloat("minskillmultiplier", 1.0f);
|
||||
MaxSkillMultiplier = element.GetAttributeFloat("maxskillmultiplier", 1.0f);
|
||||
|
||||
ResistanceFor = element.GetAttributeString("resistancefor", "");
|
||||
MinResistance = element.GetAttributeFloat("minresistance", 0.0f);
|
||||
MaxResistance = element.GetAttributeFloat("maxresistance", 0.0f);
|
||||
MaxResistance = Math.Max(MinResistance, MaxResistance);
|
||||
|
||||
MinSpeedMultiplier = element.GetAttributeFloat("minspeedmultiplier", 1.0f);
|
||||
MaxSpeedMultiplier = element.GetAttributeFloat("maxspeedmultiplier", 1.0f);
|
||||
MaxSpeedMultiplier = Math.Max(MinSpeedMultiplier, MaxSpeedMultiplier);
|
||||
|
||||
MinBuffMultiplier = element.GetAttributeFloat("minbuffmultiplier", 1.0f);
|
||||
MaxBuffMultiplier = element.GetAttributeFloat("maxbuffmultiplier", 1.0f);
|
||||
MaxBuffMultiplier = Math.Max(MinBuffMultiplier, MaxBuffMultiplier);
|
||||
|
||||
DialogFlag = element.GetAttributeString("dialogflag", "");
|
||||
|
||||
StrengthChange = element.GetAttributeFloat("strengthchange", 0.0f);
|
||||
resistanceFor = element.GetAttributeStringArray("resistancefor", new string[0], convertToLowerInvariant: true);
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
@@ -201,6 +253,19 @@ namespace Barotrauma
|
||||
case "statuseffect":
|
||||
StatusEffects.Add(StatusEffect.Load(subElement, parentDebugName));
|
||||
break;
|
||||
case "statvalue":
|
||||
var statType = CharacterAbilityGroup.ParseStatType(subElement.GetAttributeString("stattype", ""), parentDebugName);
|
||||
|
||||
float defaultValue = subElement.GetAttributeFloat("value", 0f);
|
||||
float minValue = subElement.GetAttributeFloat("minvalue", defaultValue);
|
||||
float maxValue = subElement.GetAttributeFloat("maxvalue", defaultValue);
|
||||
|
||||
AfflictionStatValues.TryAdd(statType, (minValue, maxValue));
|
||||
break;
|
||||
case "abilityflag":
|
||||
var flagType = CharacterAbilityGroup.ParseFlagType(subElement.GetAttributeString("flagtype", ""), parentDebugName);
|
||||
AfflictionAbilityFlags.Add(flagType);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -317,6 +382,9 @@ namespace Barotrauma
|
||||
public readonly Sprite Icon;
|
||||
public readonly Color[] IconColors;
|
||||
|
||||
public readonly Sprite AfflictionOverlay;
|
||||
public readonly bool AfflictionOverlayAlphaIsLinear;
|
||||
|
||||
private readonly List<Effect> effects = new List<Effect>();
|
||||
private readonly List<PeriodicEffect> periodicEffects = new List<PeriodicEffect>();
|
||||
|
||||
@@ -576,6 +644,11 @@ namespace Barotrauma
|
||||
Description = TextManager.Get("AfflictionDescription." + translationId, true) ?? element.GetAttributeString("description", "");
|
||||
IsBuff = element.GetAttributeBool("isbuff", false);
|
||||
|
||||
if (element.Attribute("nameidentifier") != null)
|
||||
{
|
||||
Name = TextManager.Get(element.GetAttributeString("nameidentifier", string.Empty), returnNull: true) ?? Name;
|
||||
}
|
||||
|
||||
LimbSpecific = element.GetAttributeBool("limbspecific", false);
|
||||
if (!LimbSpecific)
|
||||
{
|
||||
@@ -590,7 +663,7 @@ namespace Barotrauma
|
||||
ShowIconThreshold = element.GetAttributeFloat("showiconthreshold", Math.Max(ActivationThreshold, 0.05f));
|
||||
ShowIconToOthersThreshold = element.GetAttributeFloat("showicontoothersthreshold", ShowIconThreshold);
|
||||
MaxStrength = element.GetAttributeFloat("maxstrength", 100.0f);
|
||||
GrainBurst = element.GetAttributeFloat(nameof(GrainBurst).ToLower(), 0.0f);
|
||||
GrainBurst = element.GetAttributeFloat(nameof(GrainBurst).ToLowerInvariant(), 0.0f);
|
||||
|
||||
ShowInHealthScannerThreshold = element.GetAttributeFloat("showinhealthscannerthreshold", Math.Max(ActivationThreshold, 0.05f));
|
||||
TreatmentThreshold = element.GetAttributeFloat("treatmentthreshold", Math.Max(ActivationThreshold, 5.0f));
|
||||
@@ -604,6 +677,7 @@ namespace Barotrauma
|
||||
SelfCauseOfDeathDescription = TextManager.Get("AfflictionCauseOfDeathSelf." + translationId, true) ?? element.GetAttributeString("selfcauseofdeathdescription", "");
|
||||
|
||||
IconColors = element.GetAttributeColorArray("iconcolors", null);
|
||||
AfflictionOverlayAlphaIsLinear = element.GetAttributeBool("afflictionoverlayalphaislinear", false);
|
||||
AchievementOnRemoved = element.GetAttributeString("achievementonremoved", "");
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
@@ -613,6 +687,18 @@ namespace Barotrauma
|
||||
case "icon":
|
||||
Icon = new Sprite(subElement);
|
||||
break;
|
||||
case "afflictionoverlay":
|
||||
AfflictionOverlay = new Sprite(subElement);
|
||||
break;
|
||||
case "statvalue":
|
||||
DebugConsole.ThrowError($"Error in affliction \"{Identifier}\" - stat values should be configured inside the affliction's effects.");
|
||||
break;
|
||||
case "effect":
|
||||
case "periodiceffect":
|
||||
break;
|
||||
default:
|
||||
DebugConsole.AddWarning($"Unrecognized element in affliction \"{Identifier}\" ({subElement.Name})");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Xml.Linq;
|
||||
using Barotrauma.Networking;
|
||||
using Barotrauma.Extensions;
|
||||
using System.Globalization;
|
||||
using Barotrauma.Abilities;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -116,15 +117,15 @@ namespace Barotrauma
|
||||
private set => Character.Params.Health.CrushDepth = value;
|
||||
}
|
||||
|
||||
private List<LimbHealth> limbHealths = new List<LimbHealth>();
|
||||
private readonly List<LimbHealth> limbHealths = new List<LimbHealth>();
|
||||
//non-limb-specific afflictions
|
||||
private List<Affliction> afflictions = new List<Affliction>();
|
||||
private readonly List<Affliction> afflictions = new List<Affliction>();
|
||||
/// <summary>
|
||||
/// Note: returns only the non-limb-secific afflictions. Use GetAllAfflictions or some other method for getting also the limb-specific afflictions.
|
||||
/// </summary>
|
||||
public IEnumerable<Affliction> Afflictions => afflictions;
|
||||
|
||||
private HashSet<Affliction> irremovableAfflictions = new HashSet<Affliction>();
|
||||
private readonly HashSet<Affliction> irremovableAfflictions = new HashSet<Affliction>();
|
||||
private Affliction bloodlossAffliction;
|
||||
private Affliction oxygenLowAffliction;
|
||||
private Affliction pressureAffliction;
|
||||
@@ -132,7 +133,7 @@ namespace Barotrauma
|
||||
|
||||
public bool IsUnconscious
|
||||
{
|
||||
get { return Vitality <= 0.0f || Character.IsDead; }
|
||||
get { return (Vitality <= 0.0f || Character.IsDead) && !Character.HasAbilityFlag(AbilityFlags.AlwaysStayConscious); }
|
||||
}
|
||||
|
||||
public float PressureKillDelay { get; private set; } = 5.0f;
|
||||
@@ -151,6 +152,7 @@ namespace Barotrauma
|
||||
max += Character.Info.Job.Prefab.VitalityModifier;
|
||||
}
|
||||
max *= Character.StaticHealthMultiplier;
|
||||
max *= 1f + Character.GetStatValue(StatTypes.MaximumHealthMultiplier);
|
||||
return max * Character.HealthMultiplier;
|
||||
}
|
||||
}
|
||||
@@ -167,6 +169,20 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public Color DefaultFaceTint = Color.TransparentBlack;
|
||||
|
||||
public Color FaceTint
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public Color BodyTint
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public float OxygenAmount
|
||||
{
|
||||
get
|
||||
@@ -190,7 +206,11 @@ namespace Barotrauma
|
||||
public float Stun
|
||||
{
|
||||
get { return stunAffliction.Strength; }
|
||||
set { stunAffliction.Strength = MathHelper.Clamp(value, 0.0f, stunAffliction.Prefab.MaxStrength); }
|
||||
set
|
||||
{
|
||||
if (Character.GodMode) { return; }
|
||||
stunAffliction.Strength = MathHelper.Clamp(value, 0.0f, stunAffliction.Prefab.MaxStrength);
|
||||
}
|
||||
}
|
||||
|
||||
public float StunTimer { get; private set; }
|
||||
@@ -265,6 +285,12 @@ namespace Barotrauma
|
||||
private LimbHealth GetMatchingLimbHealth(Limb limb) => limb == null ? null : limbHealths[limb.HealthIndex];
|
||||
private LimbHealth GetMatchingLimbHealth(Affliction affliction) => GetMatchingLimbHealth(Character.AnimController.GetLimb(affliction.Prefab.IndicatorLimb, excludeSevered: false));
|
||||
|
||||
/// <summary>
|
||||
/// Returns the limb afflictions and non-limbspecific afflictions that are set to be displayed on this limb.
|
||||
/// </summary>
|
||||
private IEnumerable<Affliction> GetMatchingAfflictions(LimbHealth limb)
|
||||
=> limb.Afflictions.Union(afflictions.Where(a => GetMatchingLimbHealth(a) == limb));
|
||||
|
||||
/// <summary>
|
||||
/// Returns the limb afflictions and non-limbspecific afflictions that are set to be displayed on this limb.
|
||||
/// </summary>
|
||||
@@ -401,7 +427,7 @@ namespace Barotrauma
|
||||
return strength;
|
||||
}
|
||||
|
||||
public void ApplyAffliction(Limb targetLimb, Affliction affliction)
|
||||
public void ApplyAffliction(Limb targetLimb, Affliction affliction, bool allowStacking = true)
|
||||
{
|
||||
if (!affliction.Prefab.IsBuff && Unkillable || Character.GodMode) { return; }
|
||||
if (affliction.Prefab.LimbSpecific)
|
||||
@@ -411,35 +437,51 @@ 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);
|
||||
AddLimbAffliction(limbHealth, affliction, allowStacking: allowStacking);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AddLimbAffliction(targetLimb, affliction);
|
||||
AddLimbAffliction(targetLimb, affliction, allowStacking: allowStacking);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AddAffliction(affliction);
|
||||
AddAffliction(affliction, allowStacking: allowStacking);
|
||||
}
|
||||
}
|
||||
|
||||
public float GetResistance(string resistanceId)
|
||||
public float GetResistance(AfflictionPrefab affliction)
|
||||
{
|
||||
float resistance = 0.0f;
|
||||
for (int i = 0; i < afflictions.Count; i++)
|
||||
{
|
||||
if (!afflictions[i].Prefab.IsBuff) continue;
|
||||
float temp = afflictions[i].GetResistance(resistanceId);
|
||||
if (temp > resistance) resistance = temp;
|
||||
resistance += afflictions[i].GetResistance(affliction);
|
||||
}
|
||||
return 1 - ((1 - resistance) * Character.GetAbilityResistance(affliction));
|
||||
}
|
||||
|
||||
return resistance;
|
||||
public float GetStatValue(StatTypes statType)
|
||||
{
|
||||
float value = 0f;
|
||||
for (int i = 0; i < afflictions.Count; i++)
|
||||
{
|
||||
value += afflictions[i].GetStatValue(statType);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public bool HasFlag(AbilityFlags flagType)
|
||||
{
|
||||
for (int i = 0; i < afflictions.Count; i++)
|
||||
{
|
||||
if (afflictions[i].HasFlag(flagType)) { return true; }
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private readonly List<Affliction> matchingAfflictions = new List<Affliction>();
|
||||
public void ReduceAffliction(Limb targetLimb, string affliction, float amount)
|
||||
public void ReduceAffliction(Limb targetLimb, string affliction, float amount, ActionType? treatmentAction = null)
|
||||
{
|
||||
matchingAfflictions.Clear();
|
||||
matchingAfflictions.AddRange(afflictions);
|
||||
@@ -468,6 +510,14 @@ namespace Barotrauma
|
||||
for (int i = matchingAfflictions.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var matchingAffliction = matchingAfflictions[i];
|
||||
|
||||
// this logic runs very often, so culling unnecessary object creation and talent checking with this method
|
||||
if (Character.HasTalents())
|
||||
{
|
||||
var afflictionReduction = new AbilityValueAffliction(reduceAmount, matchingAffliction);
|
||||
Character.CheckTalents(AbilityEffectType.OnReduceAffliction, afflictionReduction);
|
||||
}
|
||||
|
||||
if (matchingAffliction.Strength < reduceAmount)
|
||||
{
|
||||
float surplus = reduceAmount - matchingAffliction.Strength;
|
||||
@@ -482,6 +532,17 @@ namespace Barotrauma
|
||||
{
|
||||
matchingAffliction.Strength -= reduceAmount;
|
||||
amount -= reduceAmount;
|
||||
if (treatmentAction != null)
|
||||
{
|
||||
if (treatmentAction.Value == ActionType.OnUse)
|
||||
{
|
||||
matchingAffliction.AppliedAsSuccessfulTreatmentTime = Timing.TotalTime;
|
||||
}
|
||||
else if (treatmentAction.Value == ActionType.OnFailure)
|
||||
{
|
||||
matchingAffliction.AppliedAsFailedTreatmentTime = Timing.TotalTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
CalculateVitality();
|
||||
@@ -539,9 +600,9 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
// Instead of using the limbhealth count here, I think it's best to define the max vitality per limb roughly with a constant value.
|
||||
// Therefore with e.g. 80 health, the max damage per limb would be 20.
|
||||
// Having at least 20 damage on both legs would cause maximum limping.
|
||||
float max = MaxVitality / 4;
|
||||
// Therefore with e.g. 80 health, the max damage per limb would be 40.
|
||||
// Having at least 40 damage on both legs would cause maximum limping.
|
||||
float max = MaxVitality / 2;
|
||||
if (string.IsNullOrEmpty(afflictionType))
|
||||
{
|
||||
float damage = GetAfflictionStrength("damage", limb, true);
|
||||
@@ -572,6 +633,22 @@ namespace Barotrauma
|
||||
CalculateVitality();
|
||||
}
|
||||
|
||||
public void RemoveNegativeAfflictions()
|
||||
{
|
||||
// also don't remove genetic effects, even if they're negative
|
||||
foreach (LimbHealth limbHealth in limbHealths)
|
||||
{
|
||||
limbHealth.Afflictions.RemoveAll(a => !a.Prefab.IsBuff && a.Prefab.AfflictionType != "geneticmaterialbuff" && a.Prefab.AfflictionType != "geneticmaterialdebuff");
|
||||
}
|
||||
|
||||
afflictions.RemoveAll(a => !irremovableAfflictions.Contains(a) && !a.Prefab.IsBuff && a.Prefab.AfflictionType != "geneticmaterialbuff" && a.Prefab.AfflictionType != "geneticmaterialdebuff");
|
||||
foreach (Affliction affliction in irremovableAfflictions)
|
||||
{
|
||||
affliction.Strength = 0.0f;
|
||||
}
|
||||
CalculateVitality();
|
||||
}
|
||||
|
||||
private void AddLimbAffliction(Limb limb, Affliction newAffliction, bool allowStacking = true)
|
||||
{
|
||||
if (!newAffliction.Prefab.LimbSpecific || limb == null) { return; }
|
||||
@@ -593,7 +670,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (newAffliction.Prefab == affliction.Prefab)
|
||||
{
|
||||
float newStrength = newAffliction.Strength * (100.0f / MaxVitality) * (1f - GetResistance(affliction.Prefab.Identifier));
|
||||
float newStrength = newAffliction.Strength * (100.0f / MaxVitality) * (1f - GetResistance(affliction.Prefab));
|
||||
if (allowStacking)
|
||||
{
|
||||
// Add the existing strength
|
||||
@@ -615,7 +692,7 @@ namespace Barotrauma
|
||||
//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.Identifier))),
|
||||
Math.Min(newAffliction.Prefab.MaxStrength, newAffliction.Strength * (100.0f / MaxVitality) * (1f - GetResistance(newAffliction.Prefab))),
|
||||
newAffliction.Source);
|
||||
limbHealth.Afflictions.Add(copyAffliction);
|
||||
|
||||
@@ -637,6 +714,7 @@ namespace Barotrauma
|
||||
private void AddAffliction(Affliction newAffliction, bool allowStacking = true)
|
||||
{
|
||||
if (!DoesBleed && newAffliction is AfflictionBleeding) { return; }
|
||||
if (Character.Params.Health.StunImmunity && newAffliction.Prefab.AfflictionType == "stun") { return; }
|
||||
if (!Character.NeedsOxygen && newAffliction.Prefab == AfflictionPrefab.OxygenLow) { return; }
|
||||
if (newAffliction.Prefab is AfflictionPrefabHusk huskPrefab)
|
||||
{
|
||||
@@ -649,7 +727,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (newAffliction.Prefab == affliction.Prefab)
|
||||
{
|
||||
float newStrength = newAffliction.Strength * (100.0f / MaxVitality) * (1f - GetResistance(affliction.Prefab.Identifier));
|
||||
float newStrength = newAffliction.Strength * (100.0f / MaxVitality) * (1f - GetResistance(affliction.Prefab));
|
||||
if (allowStacking)
|
||||
{
|
||||
// Add the existing strength
|
||||
@@ -671,7 +749,7 @@ namespace Barotrauma
|
||||
//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
|
||||
afflictions.Add(newAffliction.Prefab.Instantiate(
|
||||
Math.Min(newAffliction.Prefab.MaxStrength, newAffliction.Strength * (100.0f / MaxVitality) * (1f - GetResistance(newAffliction.Prefab.Identifier))),
|
||||
Math.Min(newAffliction.Prefab.MaxStrength, newAffliction.Strength * (100.0f / MaxVitality) * (1f - GetResistance(newAffliction.Prefab))),
|
||||
source: newAffliction.Source));
|
||||
|
||||
Character.HealthUpdateInterval = 0.0f;
|
||||
@@ -683,8 +761,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
partial void UpdateProjSpecific(float deltaTime);
|
||||
|
||||
partial void UpdateLimbAfflictionOverlays();
|
||||
|
||||
public void Update(float deltaTime)
|
||||
@@ -693,6 +769,8 @@ namespace Barotrauma
|
||||
|
||||
StunTimer = Stun > 0 ? StunTimer + deltaTime : 0;
|
||||
|
||||
if (Character.GodMode) { return; }
|
||||
|
||||
for (int i = 0; i < limbHealths.Count; i++)
|
||||
{
|
||||
for (int j = limbHealths[i].Afflictions.Count - 1; j >= 0; j--)
|
||||
@@ -720,11 +798,11 @@ namespace Barotrauma
|
||||
Character.StackSpeedMultiplier(affliction.GetSpeedMultiplier());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (int i = afflictions.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var affliction = afflictions[i];
|
||||
if (irremovableAfflictions.Contains(affliction)) continue;
|
||||
if (irremovableAfflictions.Contains(affliction)) { continue; }
|
||||
if (affliction.Strength <= 0.0f)
|
||||
{
|
||||
SteamAchievementManager.OnAfflictionRemoved(affliction, Character);
|
||||
@@ -738,9 +816,21 @@ namespace Barotrauma
|
||||
affliction.DamagePerSecondTimer += deltaTime;
|
||||
Character.StackSpeedMultiplier(affliction.GetSpeedMultiplier());
|
||||
}
|
||||
|
||||
UpdateLimbAfflictionOverlays();
|
||||
|
||||
Character.StackSpeedMultiplier(1f + Character.GetStatValue(StatTypes.MovementSpeed));
|
||||
|
||||
// maybe a bit of a hacky way to do this. should inquire if there is a better way. M61T
|
||||
if (Character.InWater)
|
||||
{
|
||||
Character.StackSpeedMultiplier(1f + Character.GetStatValue(StatTypes.SwimmingSpeed));
|
||||
}
|
||||
else
|
||||
{
|
||||
Character.StackSpeedMultiplier(1f + Character.GetStatValue(StatTypes.WalkingSpeed));
|
||||
}
|
||||
|
||||
UpdateLimbAfflictionOverlays();
|
||||
UpdateSkinTint();
|
||||
CalculateVitality();
|
||||
|
||||
if (Vitality <= MinVitality)
|
||||
@@ -749,6 +839,32 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateSkinTint()
|
||||
{
|
||||
FaceTint = DefaultFaceTint;
|
||||
BodyTint = Color.TransparentBlack;
|
||||
|
||||
for (int i = 0; i < limbHealths.Count; i++)
|
||||
{
|
||||
for (int j = limbHealths[i].Afflictions.Count - 1; j >= 0; j--)
|
||||
{
|
||||
var affliction = limbHealths[i].Afflictions[j];
|
||||
Color faceTint = affliction.GetFaceTint();
|
||||
if (faceTint.A > FaceTint.A) { FaceTint = faceTint; }
|
||||
Color bodyTint = affliction.GetBodyTint();
|
||||
if (bodyTint.A > BodyTint.A) { BodyTint = bodyTint; }
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < afflictions.Count; i++)
|
||||
{
|
||||
var affliction = afflictions[i];
|
||||
Color faceTint = affliction.GetFaceTint();
|
||||
if (faceTint.A > FaceTint.A) { FaceTint = faceTint; }
|
||||
Color bodyTint = affliction.GetBodyTint();
|
||||
if (bodyTint.A > BodyTint.A) { BodyTint = bodyTint; }
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateOxygen(float deltaTime)
|
||||
{
|
||||
if (!Character.NeedsOxygen) { return; }
|
||||
@@ -761,7 +877,12 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
OxygenAmount = MathHelper.Clamp(OxygenAmount + deltaTime * (Character.OxygenAvailable < InsufficientOxygenThreshold ? -5.0f : 10.0f), -100.0f, 100.0f);
|
||||
float decreaseSpeed = -5.0f;
|
||||
float increaseSpeed = 10.0f;
|
||||
float oxygenlowResistance = GetResistance(oxygenLowAffliction.Prefab);
|
||||
decreaseSpeed *= (1f - oxygenlowResistance);
|
||||
increaseSpeed *= (1f + oxygenlowResistance);
|
||||
OxygenAmount = MathHelper.Clamp(OxygenAmount + deltaTime * (Character.OxygenAvailable < InsufficientOxygenThreshold ? decreaseSpeed : increaseSpeed), -100.0f, 100.0f);
|
||||
}
|
||||
|
||||
UpdateOxygenProjSpecific(prevOxygen, deltaTime);
|
||||
@@ -782,8 +903,6 @@ namespace Barotrauma
|
||||
Vitality = MaxVitality;
|
||||
if (Unkillable || Character.GodMode) { return; }
|
||||
|
||||
float damageResistanceMultiplier = 1f - GetResistance("damage");
|
||||
|
||||
foreach (LimbHealth limbHealth in limbHealths)
|
||||
{
|
||||
foreach (Affliction affliction in limbHealth.Afflictions)
|
||||
@@ -799,7 +918,6 @@ namespace Barotrauma
|
||||
{
|
||||
vitalityDecrease *= limbHealth.VitalityTypeMultipliers[type];
|
||||
}
|
||||
vitalityDecrease *= damageResistanceMultiplier;
|
||||
Vitality -= vitalityDecrease;
|
||||
affliction.CalculateDamagePerSecond(vitalityDecrease);
|
||||
}
|
||||
@@ -808,7 +926,6 @@ namespace Barotrauma
|
||||
foreach (Affliction affliction in afflictions)
|
||||
{
|
||||
float vitalityDecrease = affliction.GetVitalityDecrease(this);
|
||||
vitalityDecrease *= damageResistanceMultiplier;
|
||||
Vitality -= vitalityDecrease;
|
||||
affliction.CalculateDamagePerSecond(vitalityDecrease);
|
||||
}
|
||||
@@ -825,8 +942,9 @@ namespace Barotrauma
|
||||
{
|
||||
if (Unkillable || Character.GodMode) { return; }
|
||||
|
||||
var causeOfDeath = GetCauseOfDeath();
|
||||
Character.Kill(causeOfDeath.First, causeOfDeath.Second);
|
||||
var (type, affliction) = GetCauseOfDeath();
|
||||
UpdateSkinTint();
|
||||
Character.Kill(type, affliction);
|
||||
#if CLIENT
|
||||
DisplayVitalityDelay = 0.0f;
|
||||
DisplayedVitality = Vitality;
|
||||
@@ -859,7 +977,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public Pair<CauseOfDeathType, Affliction> GetCauseOfDeath()
|
||||
public (CauseOfDeathType type, Affliction affliction) GetCauseOfDeath()
|
||||
{
|
||||
List<Affliction> currentAfflictions = GetAllAfflictions(true);
|
||||
|
||||
@@ -880,7 +998,7 @@ namespace Barotrauma
|
||||
causeOfDeath = Character.AnimController.InWater ? CauseOfDeathType.Drowning : CauseOfDeathType.Suffocation;
|
||||
}
|
||||
|
||||
return new Pair<CauseOfDeathType, Affliction>(causeOfDeath, strongestAffliction);
|
||||
return (causeOfDeath, strongestAffliction);
|
||||
}
|
||||
|
||||
// TODO: this method is called a lot (every half second) -> optimize, don't create new class instances and lists every time!
|
||||
@@ -926,15 +1044,16 @@ namespace Barotrauma
|
||||
/// <param name="treatmentSuitability">A dictionary where the key is the identifier of the item and the value the suitability</param>
|
||||
/// <param name="normalize">If true, the suitability values are normalized between 0 and 1. If not, they're arbitrary values defined in the medical item XML, where negative values are unsuitable, and positive ones suitable.</param>
|
||||
/// <param name="randomization">Amount of randomization to apply to the values (0 = the values are accurate, 1 = the values are completely random)</param>
|
||||
public void GetSuitableTreatments(Dictionary<string, float> treatmentSuitability, bool normalize, float randomization = 0.0f)
|
||||
public void GetSuitableTreatments(Dictionary<string, float> treatmentSuitability, bool normalize, Limb limb = null, bool ignoreHiddenAfflictions = false, float randomization = 0.0f)
|
||||
{
|
||||
//key = item identifier
|
||||
//float = suitability
|
||||
treatmentSuitability.Clear();
|
||||
float minSuitability = -10, maxSuitability = 10;
|
||||
foreach (Affliction affliction in GetAllAfflictions())
|
||||
foreach (Affliction affliction in getAfflictions(limb))
|
||||
{
|
||||
if (affliction.Strength < affliction.Prefab.TreatmentThreshold) { continue; }
|
||||
if (affliction.Strength <= affliction.Prefab.TreatmentThreshold) { continue; }
|
||||
if (ignoreHiddenAfflictions && affliction.Strength < affliction.Prefab.ShowIconThreshold) { continue; }
|
||||
foreach (KeyValuePair<string, float> treatment in affliction.Prefab.TreatmentSuitability)
|
||||
{
|
||||
if (!treatmentSuitability.ContainsKey(treatment.Key))
|
||||
@@ -965,10 +1084,22 @@ namespace Barotrauma
|
||||
treatmentSuitability[treatment] += Rand.Range(-100.0f, 100.0f) * randomization;
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerable<Affliction> getAfflictions(Limb limb)
|
||||
{
|
||||
if (limb == null)
|
||||
{
|
||||
return GetAllAfflictions();
|
||||
}
|
||||
else
|
||||
{
|
||||
return GetMatchingAfflictions(GetMatchingLimbHealth(limb));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private readonly List<Affliction> activeAfflictions = new List<Affliction>();
|
||||
private readonly List<Pair<LimbHealth, Affliction>> limbAfflictions = new List<Pair<LimbHealth, Affliction>>();
|
||||
private readonly List<(LimbHealth limbHealth, Affliction affliction)> limbAfflictions = new List<(LimbHealth limbHealth, Affliction affliction)>();
|
||||
public void ServerWrite(IWriteMessage msg)
|
||||
{
|
||||
activeAfflictions.Clear();
|
||||
@@ -999,22 +1130,22 @@ namespace Barotrauma
|
||||
foreach (Affliction limbAffliction in limbHealth.Afflictions)
|
||||
{
|
||||
if (limbAffliction.Strength <= 0.0f || limbAffliction.Strength < limbAffliction.Prefab.ActivationThreshold) continue;
|
||||
limbAfflictions.Add(new Pair<LimbHealth, Affliction>(limbHealth, limbAffliction));
|
||||
limbAfflictions.Add((limbHealth, limbAffliction));
|
||||
}
|
||||
}
|
||||
|
||||
msg.Write((byte)limbAfflictions.Count);
|
||||
foreach (var limbAffliction in limbAfflictions)
|
||||
foreach (var (limbHealth, affliction) in limbAfflictions)
|
||||
{
|
||||
msg.WriteRangedInteger(limbHealths.IndexOf(limbAffliction.First), 0, limbHealths.Count - 1);
|
||||
msg.Write(limbAffliction.Second.Prefab.UIntIdentifier);
|
||||
msg.WriteRangedInteger(limbHealths.IndexOf(limbHealth), 0, limbHealths.Count - 1);
|
||||
msg.Write(affliction.Prefab.UIntIdentifier);
|
||||
msg.WriteRangedSingle(
|
||||
MathHelper.Clamp(limbAffliction.Second.Strength, 0.0f, limbAffliction.Second.Prefab.MaxStrength),
|
||||
0.0f, limbAffliction.Second.Prefab.MaxStrength, 8);
|
||||
msg.Write((byte)limbAffliction.Second.Prefab.PeriodicEffects.Count());
|
||||
foreach (AfflictionPrefab.PeriodicEffect periodicEffect in limbAffliction.Second.Prefab.PeriodicEffects)
|
||||
MathHelper.Clamp(affliction.Strength, 0.0f, affliction.Prefab.MaxStrength),
|
||||
0.0f, affliction.Prefab.MaxStrength, 8);
|
||||
msg.Write((byte)affliction.Prefab.PeriodicEffects.Count());
|
||||
foreach (AfflictionPrefab.PeriodicEffect periodicEffect in affliction.Prefab.PeriodicEffects)
|
||||
{
|
||||
msg.WriteRangedSingle(limbAffliction.Second.PeriodicEffectTimers[periodicEffect], periodicEffect.MinInterval, periodicEffect.MaxInterval, 8);
|
||||
msg.WriteRangedSingle(affliction.PeriodicEffectTimers[periodicEffect], periodicEffect.MinInterval, periodicEffect.MaxInterval, 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1030,7 +1161,7 @@ namespace Barotrauma
|
||||
/// Automatically filters out buffs.
|
||||
/// </summary>
|
||||
public static IEnumerable<Affliction> SortAfflictionsBySeverity(IEnumerable<Affliction> afflictions, bool excludeBuffs = true) =>
|
||||
afflictions.Where(a => !excludeBuffs || !a.Prefab.IsBuff).OrderByDescending(a => a.DamagePerSecond).ThenByDescending(a => a.Strength);
|
||||
afflictions.Where(a => !excludeBuffs || !a.Prefab.IsBuff).OrderByDescending(a => a.DamagePerSecond).ThenByDescending(a => a.Strength / a.Prefab.MaxStrength);
|
||||
|
||||
public void Save(XElement healthElement)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user