(bcb06cc5c) Unstable v0.9.9.0

This commit is contained in:
Juan Pablo Arce
2020-03-27 15:22:59 -03:00
parent c81486a993
commit b143329701
326 changed files with 9692 additions and 4364 deletions
@@ -14,8 +14,13 @@ namespace Barotrauma
public Dictionary<string, SerializableProperty> SerializableProperties { get; set; }
protected float _strength;
[Serialize(0f, true), Editable]
public float Strength { get; set; }
public virtual float Strength
{
get { return _strength; }
set { _strength = value; }
}
[Serialize("", true), Editable]
public string Identifier { get; private set; }
@@ -38,7 +43,7 @@ namespace Barotrauma
public Affliction(AfflictionPrefab prefab, float strength)
{
Prefab = prefab;
Strength = strength;
_strength = strength;
Identifier = prefab?.Identifier;
}
@@ -173,11 +178,11 @@ namespace Barotrauma
if (currentEffect.StrengthChange < 0) // Reduce diminishing of buffs if boosted
{
Strength += currentEffect.StrengthChange * deltaTime * StrengthDiminishMultiplier;
_strength += currentEffect.StrengthChange * deltaTime * StrengthDiminishMultiplier;
}
else // Reduce strengthening of afflictions if resistant
{
Strength += currentEffect.StrengthChange * deltaTime * (1f - characterHealth.GetResistance(Prefab.Identifier));
_strength += currentEffect.StrengthChange * deltaTime * (1f - characterHealth.GetResistance(Prefab.Identifier));
}
foreach (StatusEffect statusEffect in currentEffect.StatusEffects)
@@ -1,7 +1,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using System;
namespace Barotrauma
{
@@ -9,7 +9,7 @@ namespace Barotrauma
{
public enum InfectionState
{
Dormant, Transition, Active
Initial, Dormant, Transition, Active, Final
}
private bool subscribedToDeathEvent;
@@ -17,154 +17,157 @@ namespace Barotrauma
private InfectionState state;
private List<Limb> huskAppendage;
private Character character;
private readonly List<Affliction> huskInfection = new List<Affliction>();
[Serialize(0f, true), Editable]
public override float Strength
{
get { return _strength; }
set
{
// Don't allow to set the strength too high (from outside) to avoid rapid transformation into husk when taking lots of damage from husks.
// If the strength is more than the value, this will effectively reset the current strength to the max. That's why we use two steps.
float max = _strength > ActiveThreshold ? ActiveThreshold + 1 : DormantThreshold - 1;
_strength = Math.Clamp(value, 0, max);
}
}
public InfectionState State
{
get { return state; }
private set
{
if (state == value) { return; }
state = value;
if (character != null && character == Character.Controlled)
{
UpdateMessages();
}
}
}
public AfflictionHusk(AfflictionPrefab prefab, float strength) :
base(prefab, strength)
{
}
private float DormantThreshold => Prefab.MaxStrength * 0.5f;
private float ActiveThreshold => Prefab.MaxStrength * 0.75f;
public AfflictionHusk(AfflictionPrefab prefab, float strength) : base(prefab, strength) { }
public override void Update(CharacterHealth characterHealth, Limb targetLimb, float deltaTime)
{
float prevStrength = Strength;
base.Update(characterHealth, targetLimb, deltaTime);
character = characterHealth.Character;
if (character == null) { return; }
if (!subscribedToDeathEvent)
{
characterHealth.Character.OnDeath += CharacterDead;
character.OnDeath += CharacterDead;
subscribedToDeathEvent = true;
}
if (characterHealth.Character == Character.Controlled) UpdateMessages(prevStrength, characterHealth.Character);
if (Strength < Prefab.MaxStrength * 0.5f)
if (Strength < DormantThreshold)
{
UpdateDormantState(deltaTime, characterHealth.Character);
DeactivateHusk();
State = InfectionState.Dormant;
}
else if (Strength < ActiveThreshold)
{
DeactivateHusk();
character.SpeechImpediment = 100;
State = InfectionState.Transition;
}
else if (Strength < Prefab.MaxStrength)
{
characterHealth.Character.SpeechImpediment = 100.0f;
UpdateTransitionState(deltaTime, characterHealth.Character);
if (State != InfectionState.Active)
{
character.SetStun(Rand.Range(2, 4, Rand.RandSync.Server));
}
State = InfectionState.Active;
ActivateHusk();
}
else
{
characterHealth.Character.SpeechImpediment = 100.0f;
UpdateActiveState(deltaTime, characterHealth.Character);
State = InfectionState.Final;
ActivateHusk();
ApplyDamage(deltaTime, applyForce: true);
character.SetStun(1);
}
}
partial void UpdateMessages(float prevStrength, Character character);
partial void UpdateMessages();
private void UpdateDormantState(float deltaTime, Character character)
private void ApplyDamage(float deltaTime, bool applyForce)
{
if (state != InfectionState.Dormant)
{
DeactivateHusk(character);
}
state = InfectionState.Dormant;
}
private void UpdateTransitionState(float deltaTime, Character character)
{
if (state != InfectionState.Transition)
{
DeactivateHusk(character);
}
state = InfectionState.Transition;
}
private void UpdateActiveState(float deltaTime, Character character)
{
if (state != InfectionState.Active)
{
ActivateHusk(character);
state = InfectionState.Active;
}
foreach (Limb limb in character.AnimController.Limbs)
{
float random = Rand.Value(Rand.RandSync.Server);
huskInfection.Clear();
huskInfection.Add(AfflictionPrefab.InternalDamage.Instantiate(random * deltaTime / character.AnimController.Limbs.Length));
character.LastDamageSource = null;
character.DamageLimb(
limb.WorldPosition, limb,
new List<Affliction>() { AfflictionPrefab.InternalDamage.Instantiate(0.5f * deltaTime / character.AnimController.Limbs.Length) },
0.0f, false, 0.0f);
float force = applyForce ? random * 0.1f * limb.Mass : 0;
character.DamageLimb(limb.WorldPosition, limb, huskInfection, 0, false, force);
}
}
public void ActivateHusk(Character character)
public void ActivateHusk()
{
if (huskAppendage == null)
if (huskAppendage == null && character.Params.UseHuskAppendage)
{
huskAppendage = AttachHuskAppendage(character, Prefab.Identifier);
if (huskAppendage != null)
{
character.NeedsAir = false;
character.SetStun(0.5f);
}
#if CLIENT
character.AnimController.GetLimb(LimbType.Head).EnableHuskSprite = true;
#endif
}
character.NeedsAir = false;
character.SpeechImpediment = 100;
}
private void DeactivateHusk(Character character)
private void DeactivateHusk()
{
character.NeedsAir = character.Params.MainElement.GetAttributeBool("needsair", false);
if (huskAppendage != null)
{
huskAppendage.ForEach(l => character.AnimController.RemoveLimb(l));
huskAppendage = null;
#if CLIENT
character.AnimController.GetLimb(LimbType.Head).EnableHuskSprite = false;
#endif
}
}
public void Remove(Character character)
public void Remove()
{
DeactivateHusk(character);
if (character != null) character.OnDeath -= CharacterDead;
if (character == null) { return; }
DeactivateHusk();
character.OnDeath -= CharacterDead;
subscribedToDeathEvent = false;
}
private void CharacterDead(Character character, CauseOfDeath causeOfDeath)
{
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; }
if (Strength < Prefab.MaxStrength * 0.5f || character.Removed) { return; }
if (Strength < ActiveThreshold || character.Removed) { return; }
//don't turn the character into a husk if any of its limbs are severed
if (character.AnimController?.LimbJoints != null)
{
foreach (var limbJoint in character.AnimController.LimbJoints)
{
if (limbJoint.IsSevered) return;
if (limbJoint.IsSevered) { return; }
}
}
//create the AI husk in a coroutine to ensure that we don't modify the character list while enumerating it
CoroutineManager.StartCoroutine(CreateAIHusk(character));
CoroutineManager.StartCoroutine(CreateAIHusk());
}
private IEnumerable<object> CreateAIHusk(Character character)
private IEnumerable<object> CreateAIHusk()
{
character.Enabled = false;
Entity.Spawner.AddToRemoveQueue(character);
string speciesName = GetHuskedSpeciesName(character.SpeciesName, Prefab as AfflictionPrefabHusk);
CharacterPrefab prefab = CharacterPrefab.FindBySpeciesName(speciesName);
string huskedSpeciesName = GetHuskedSpeciesName(character.SpeciesName, Prefab as AfflictionPrefabHusk);
CharacterPrefab prefab = CharacterPrefab.FindBySpeciesName(huskedSpeciesName);
if (prefab == null)
{
DebugConsole.ThrowError("Failed to turn character \"" + character.Name + "\" into a husk - husk config file not found.");
yield return CoroutineStatus.Success;
}
var husk = Character.Create(speciesName, character.WorldPosition, character.Info.Name, character.Info, isRemotePlayer: false, hasAi: true, ragdoll: character.AnimController.RagdollParams);
var husk = Character.Create(huskedSpeciesName, character.WorldPosition, ToolBox.RandomSeed(8), character.Info, isRemotePlayer: false, hasAi: true);
foreach (Limb limb in husk.AnimController.Limbs)
{
@@ -197,6 +200,11 @@ namespace Barotrauma
husk.Inventory.TryPutItem(character.Inventory.Items[i], i, true, false, null);
}
husk.SetStun(5);
yield return new WaitForSeconds(5, false);
#if CLIENT
husk.PlaySound(CharacterSound.SoundType.Idle);
#endif
yield return CoroutineStatus.Success;
}
@@ -114,6 +114,10 @@ namespace Barotrauma
private List<LimbHealth> limbHealths = new List<LimbHealth>();
//non-limb-specific afflictions
private 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 Affliction bloodlossAffliction;
@@ -160,12 +164,12 @@ namespace Barotrauma
{
get
{
if (!Character.NeedsAir || Unkillable) return 100.0f;
if (!Character.NeedsOxygen || Unkillable) { return 100.0f; }
return -oxygenLowAffliction.Strength + 100;
}
set
{
if (!Character.NeedsAir || Unkillable) return;
if (!Character.NeedsOxygen || Unkillable) { return; }
oxygenLowAffliction.Strength = MathHelper.Clamp(-value + 100, 0.0f, 200.0f);
}
}
@@ -216,7 +220,7 @@ namespace Barotrauma
limbHealths.Clear();
foreach (XElement subElement in element.Elements())
{
if (subElement.Name.ToString().ToLowerInvariant() != "limb") continue;
if (!subElement.Name.ToString().Equals("limb", StringComparison.OrdinalIgnoreCase)) { continue; }
limbHealths.Add(new LimbHealth(subElement, this));
}
if (limbHealths.Count == 0)
@@ -269,30 +273,6 @@ namespace Barotrauma
}
}
public Affliction GetAffliction(string identifier, bool allowLimbAfflictions = true)
{
foreach (Affliction affliction in afflictions)
{
if (affliction.Prefab.Identifier == identifier) return affliction;
}
if (!allowLimbAfflictions) return null;
foreach (LimbHealth limbHealth in limbHealths)
{
foreach (Affliction affliction in limbHealth.Afflictions)
{
if (affliction.Prefab.Identifier == identifier) return affliction;
}
}
return null;
}
public T GetAffliction<T>(string identifier, bool allowLimbAfflictions = true) where T : Affliction
{
return GetAffliction(identifier, allowLimbAfflictions) as T;
}
public IEnumerable<Affliction> GetAfflictionsByType(string afflictionType, Limb limb)
{
if (limb.HealthIndex < 0 || limb.HealthIndex >= limbHealths.Count)
@@ -304,6 +284,37 @@ namespace Barotrauma
return limbHealths[limb.HealthIndex].Afflictions.Where(a => a.Prefab.AfflictionType == afflictionType);
}
public Affliction GetAffliction(string identifier, bool allowLimbAfflictions = true)
=> GetAffliction(a => a.Prefab.Identifier == identifier, allowLimbAfflictions);
public Affliction GetAfflictionOfType(string afflictionType, bool allowLimbAfflictions = true)
=> GetAffliction(a => a.Prefab.AfflictionType == afflictionType, allowLimbAfflictions);
private Affliction GetAffliction(Func<Affliction, bool> predicate, bool allowLimbAfflictions = true)
{
foreach (Affliction affliction in afflictions)
{
if (predicate(affliction)) { return affliction; }
}
if (!allowLimbAfflictions)
{
return null;
}
foreach (LimbHealth limbHealth in limbHealths)
{
foreach (Affliction affliction in limbHealth.Afflictions)
{
if (predicate(affliction)) { return affliction; }
}
}
return null;
}
public T GetAffliction<T>(string identifier, bool allowLimbAfflictions = true) where T : Affliction
{
return GetAffliction(identifier, allowLimbAfflictions) as T;
}
public Affliction GetAffliction(string identifier, Limb limb)
{
if (limb.HealthIndex < 0 || limb.HealthIndex >= limbHealths.Count)
@@ -408,11 +419,10 @@ namespace Barotrauma
return resistance;
}
private List<Affliction> matchingAfflictions = new List<Affliction>();
public void ReduceAffliction(Limb targetLimb, string affliction, float amount)
{
affliction = affliction.ToLowerInvariant();
List<Affliction> matchingAfflictions = new List<Affliction>(afflictions);
matchingAfflictions.Clear();
if (targetLimb != null)
{
@@ -426,8 +436,8 @@ namespace Barotrauma
}
}
matchingAfflictions.RemoveAll(a =>
a.Prefab.Identifier.ToLowerInvariant() != affliction &&
a.Prefab.AfflictionType.ToLowerInvariant() != affliction);
!a.Prefab.Identifier.Equals(affliction, StringComparison.OrdinalIgnoreCase) &&
!a.Prefab.AfflictionType.Equals(affliction, StringComparison.OrdinalIgnoreCase));
if (matchingAfflictions.Count == 0) return;
@@ -526,7 +536,7 @@ namespace Barotrauma
private void AddLimbAffliction(LimbHealth limbHealth, Affliction newAffliction)
{
if (!DoesBleed && newAffliction is AfflictionBleeding) return;
if (!Character.NeedsAir && newAffliction.Prefab == AfflictionPrefab.OxygenLow) return;
if (!Character.NeedsOxygen && newAffliction.Prefab == AfflictionPrefab.OxygenLow) return;
foreach (Affliction affliction in limbHealth.Afflictions)
{
@@ -559,7 +569,7 @@ namespace Barotrauma
private void AddAffliction(Affliction newAffliction)
{
if (!DoesBleed && newAffliction is AfflictionBleeding) return;
if (!Character.NeedsAir && newAffliction.Prefab == AfflictionPrefab.OxygenLow) return;
if (!Character.NeedsOxygen && newAffliction.Prefab == AfflictionPrefab.OxygenLow) return;
if (newAffliction.Prefab.AfflictionType == "huskinfection")
{
var huskPrefab = newAffliction.Prefab as AfflictionPrefabHusk;
@@ -653,7 +663,7 @@ namespace Barotrauma
private void UpdateOxygen(float deltaTime)
{
if (!Character.NeedsAir) return;
if (!Character.NeedsOxygen) { return; }
float prevOxygen = OxygenAmount;
if (IsUnconscious)
@@ -691,13 +701,15 @@ namespace Barotrauma
foreach (Affliction affliction in limbHealth.Afflictions)
{
float vitalityDecrease = affliction.GetVitalityDecrease(this);
if (limbHealth.VitalityMultipliers.ContainsKey(affliction.Prefab.Identifier.ToLowerInvariant()))
string identifier = affliction.Prefab.Identifier.ToLowerInvariant();
string type = affliction.Prefab.AfflictionType.ToLowerInvariant();
if (limbHealth.VitalityMultipliers.ContainsKey(identifier))
{
vitalityDecrease *= limbHealth.VitalityMultipliers[affliction.Prefab.Identifier.ToLowerInvariant()];
vitalityDecrease *= limbHealth.VitalityMultipliers[identifier];
}
if (limbHealth.VitalityTypeMultipliers.ContainsKey(affliction.Prefab.AfflictionType.ToLowerInvariant()))
if (limbHealth.VitalityTypeMultipliers.ContainsKey(type))
{
vitalityDecrease *= limbHealth.VitalityTypeMultipliers[affliction.Prefab.AfflictionType.ToLowerInvariant()];
vitalityDecrease *= limbHealth.VitalityTypeMultipliers[type];
}
vitalityDecrease *= damageResistanceMultiplier;
Vitality -= vitalityDecrease;
@@ -750,6 +762,7 @@ namespace Barotrauma
return new Pair<CauseOfDeathType, Affliction>(causeOfDeath, strongestAffliction);
}
// TODO: this method is called a lot (every half second) -> optimize, don't create new class instances and lists every time!
private List<Affliction> GetAllAfflictions(bool mergeSameAfflictions)
{
List<Affliction> allAfflictions = new List<Affliction>(afflictions);
@@ -791,8 +804,7 @@ 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="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>
/// <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)
{
//key = item identifier
@@ -833,10 +845,18 @@ namespace Barotrauma
}
}
private readonly List<Affliction> activeAfflictions = new List<Affliction>();
private readonly List<Pair<LimbHealth, Affliction>> limbAfflictions = new List<Pair<LimbHealth, Affliction>>();
public void ServerWrite(IWriteMessage msg)
{
List<Affliction> activeAfflictions = afflictions.FindAll(a => a.Strength > 0.0f && a.Strength >= a.Prefab.ActivationThreshold);
activeAfflictions.Clear();
foreach (var affliction in afflictions)
{
if (affliction.Strength > 0.0f && affliction.Strength >= affliction.Prefab.ActivationThreshold)
{
activeAfflictions.Add(affliction);
}
}
msg.Write((byte)activeAfflictions.Count);
foreach (Affliction affliction in activeAfflictions)
{
@@ -846,7 +866,7 @@ namespace Barotrauma
0.0f, affliction.Prefab.MaxStrength, 8);
}
List<Pair<LimbHealth, Affliction>> limbAfflictions = new List<Pair<LimbHealth, Affliction>>();
limbAfflictions.Clear();
foreach (LimbHealth limbHealth in limbHealths)
{
foreach (Affliction limbAffliction in limbHealth.Afflictions)
@@ -873,5 +893,11 @@ namespace Barotrauma
}
partial void RemoveProjSpecific();
/// <summary>
/// Automatically filters out buffs.
/// </summary>
public static IEnumerable<Affliction> SortAfflictionsBySeverity(IEnumerable<Affliction> afflictions) =>
afflictions.Where(a => !a.Prefab.IsBuff).OrderByDescending(a => a.DamagePerSecond).ThenByDescending(a => a.Strength);
}
}