Release 1.11.4.1 (Winter Update)
This commit is contained in:
@@ -1223,6 +1223,7 @@ namespace Barotrauma
|
||||
if (otherCharacter.SelectedCharacter == null ||
|
||||
!otherCharacter.SelectedCharacter.IsDead ||
|
||||
otherCharacter.SelectedCharacter.TeamID != Character.TeamID ||
|
||||
otherCharacter.IsPet ||
|
||||
otherCharacter.IsInstigator)
|
||||
{
|
||||
continue;
|
||||
|
||||
@@ -114,7 +114,7 @@ namespace Barotrauma
|
||||
if (target.Submarine != character.Submarine) { return; }
|
||||
Reset();
|
||||
TargetCharacter = target;
|
||||
targetBody = target.AnimController.Collider.FarseerBody;
|
||||
targetBody = target.AnimController.MainLimb.body.FarseerBody;
|
||||
attachSurfaceNormal = Vector2.Normalize(character.WorldPosition - target.WorldPosition);
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -769,7 +769,7 @@ namespace Barotrauma
|
||||
Attack attack = GetAttackDefinition(weapon);
|
||||
if (attack != null)
|
||||
{
|
||||
lethalDmg = attack.GetTotalDamage();
|
||||
lethalDmg = attack.GetTotalCharacterDamage();
|
||||
float max = lethalDmg + 1;
|
||||
if (weapon.Item.HasTag(Tags.StunnerItem))
|
||||
{
|
||||
@@ -795,7 +795,7 @@ namespace Barotrauma
|
||||
Attack attack = GetAttackDefinition(weapon);
|
||||
if (attack != null)
|
||||
{
|
||||
lethalDmg = attack.GetTotalDamage();
|
||||
lethalDmg = attack.GetTotalCharacterDamage();
|
||||
float stunDmg = ApproximateStunDamage(weapon, attack);
|
||||
float diff = stunDmg - lethalDmg;
|
||||
if (diff < 0)
|
||||
@@ -809,7 +809,7 @@ namespace Barotrauma
|
||||
{
|
||||
// Cannot do stun damage -> use the melee damage to determine the priority.
|
||||
Attack attack = GetAttackDefinition(weapon);
|
||||
priority = attack?.GetTotalDamage() ?? priority / 2;
|
||||
priority = attack?.GetTotalCharacterDamage() ?? priority / 2;
|
||||
}
|
||||
// Reduce the priority of the weapon, if we don't have requires skills to use it.
|
||||
float startPriority = priority;
|
||||
@@ -960,7 +960,7 @@ namespace Barotrauma
|
||||
Attack attack = GetAttackDefinition(weapon);
|
||||
if (attack != null)
|
||||
{
|
||||
lethalDmg = attack.GetTotalDamage();
|
||||
lethalDmg = attack.GetTotalCharacterDamage();
|
||||
}
|
||||
return lethalDmg;
|
||||
}
|
||||
|
||||
+3
-1
@@ -1,4 +1,5 @@
|
||||
using Barotrauma.Items.Components;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Items.Components;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
@@ -94,6 +95,7 @@ namespace Barotrauma
|
||||
if (potentialDeconstructor?.InputContainer == null) { continue; }
|
||||
if (!potentialDeconstructor.InputContainer.Inventory.CanBePut(Item)) { continue; }
|
||||
if (!potentialDeconstructor.Item.HasAccess(character)) { continue; }
|
||||
if (Item.Prefab.DeconstructItems.None(it => it.IsValidDeconstructor(otherItem))) { continue; }
|
||||
float distFactor = GetDistanceFactor(Item.WorldPosition, potentialDeconstructor.Item.WorldPosition, factorAtMaxDistance: 0.2f);
|
||||
if (distFactor > bestDistFactor)
|
||||
{
|
||||
|
||||
+5
@@ -1,6 +1,7 @@
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Items.Components;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -30,6 +31,7 @@ namespace Barotrauma
|
||||
if (character.Submarine == null ||
|
||||
Item.ItemList.None(it =>
|
||||
it.GetComponent<Deconstructor>() != null &&
|
||||
!it.IgnoreByAI(character) &&
|
||||
it.IsInteractable(character) &&
|
||||
character.Submarine.IsEntityFoundOnThisSub(it, includingConnectedSubs: true, allowDifferentTeam: true, allowDifferentType: true)))
|
||||
{
|
||||
@@ -60,6 +62,9 @@ namespace Barotrauma
|
||||
protected override bool IsValidTarget(Item target)
|
||||
{
|
||||
if (target == null || target.Removed) { return false; }
|
||||
//bots can't handle deconstructing items that require another item to deconstruct, let's not try to do that
|
||||
//in the vanilla game, this means unidentified genetic materials, which we don't want to "deconstruct" anyway
|
||||
if (target.Prefab.DeconstructItems.All(d => d.RequiredOtherItem.Length > 0)) { return false; }
|
||||
// If the target was selected as a valid target, we'll have to accept it so that the objective can be completed.
|
||||
// The validity changes when a character picks the item up.
|
||||
if (!IsValidTarget(target, character, checkInventory: true))
|
||||
|
||||
+2
-9
@@ -98,18 +98,11 @@ namespace Barotrauma
|
||||
vitality -= affliction.GetVitalityDecrease(character.CharacterHealth, strength) / character.MaxVitality * 100;
|
||||
if (affliction.Strength > affliction.Prefab.TreatmentThreshold)
|
||||
{
|
||||
if (affliction.Prefab.AfflictionType == AfflictionPrefab.ParalysisType)
|
||||
//vitality loss is not required to treat this affliction -> evaluate the strength of the affliction too
|
||||
if (!affliction.Prefab.VitalityLossRequiredForTreatment)
|
||||
{
|
||||
vitality -= affliction.Strength;
|
||||
}
|
||||
else if (affliction.Prefab.AfflictionType == AfflictionPrefab.PoisonType)
|
||||
{
|
||||
vitality -= affliction.Strength;
|
||||
}
|
||||
else if (affliction.Prefab == AfflictionPrefab.HuskInfection)
|
||||
{
|
||||
vitality -= affliction.Strength;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Math.Clamp(vitality, 0, 100);
|
||||
|
||||
@@ -437,8 +437,25 @@ namespace Barotrauma
|
||||
public bool TargetItemsMatchItem(Item item, Identifier option = default)
|
||||
{
|
||||
if (item == null) { return false; }
|
||||
if (Identifier == Tags.DeconstructThis && item.AllowDeconstruct && !Item.DeconstructItems.Contains(item)) { return true; }
|
||||
if (Identifier == Tags.DontDeconstructThis && Item.DeconstructItems.Contains(item)) { return true; }
|
||||
|
||||
if (Identifier == Tags.DeconstructThis && item.AllowDeconstruct)
|
||||
{
|
||||
if (item.AllowDeconstruct && !Item.DeconstructItems.Contains(item) &&
|
||||
//only allow deconstructing if there are deconstruction recipes that
|
||||
item.Prefab.DeconstructItems.Any(deconstructItem =>
|
||||
//1. don't require any additional items (bots can't handle that)
|
||||
deconstructItem.RequiredOtherItem.None() &&
|
||||
//2. don't require a research station (bots don't know how to use those)
|
||||
(deconstructItem.RequiredDeconstructor.Length == 0 || deconstructItem.RequiredDeconstructor.Any(d => d != Tags.GeneticResearchStation))))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (Identifier == Tags.DontDeconstructThis)
|
||||
{
|
||||
if (Item.DeconstructItems.Contains(item)) { return true; }
|
||||
}
|
||||
|
||||
ImmutableArray<Identifier> targetItems = GetTargetItems(option);
|
||||
return TargetItemsMatchItem(targetItems, item);
|
||||
}
|
||||
|
||||
@@ -216,6 +216,7 @@ namespace Barotrauma
|
||||
{
|
||||
UpdateTemporaryAnimations();
|
||||
UpdateAnim(deltaTime);
|
||||
CheckRopeState();
|
||||
}
|
||||
|
||||
protected abstract void UpdateAnim(float deltaTime);
|
||||
@@ -1134,6 +1135,25 @@ namespace Barotrauma
|
||||
character.TeleportTo(pos);
|
||||
}
|
||||
|
||||
protected void CheckRopeState()
|
||||
{
|
||||
if (!shouldHangWithRope)
|
||||
{
|
||||
StopHangingWithRope();
|
||||
}
|
||||
if (!shouldHoldToRope)
|
||||
{
|
||||
StopHoldingToRope();
|
||||
}
|
||||
if (!shouldBeDraggedWithRope)
|
||||
{
|
||||
StopGettingDraggedWithRope();
|
||||
}
|
||||
shouldHoldToRope = false;
|
||||
shouldHangWithRope = false;
|
||||
shouldBeDraggedWithRope = false;
|
||||
}
|
||||
|
||||
private void StartAnimation(Animation animation)
|
||||
{
|
||||
if (animation == Animation.UsingItem)
|
||||
|
||||
+9
-33
@@ -478,21 +478,6 @@ namespace Barotrauma
|
||||
aiming = false;
|
||||
wasAimingMelee = aimingMelee;
|
||||
aimingMelee = false;
|
||||
if (!shouldHangWithRope)
|
||||
{
|
||||
StopHangingWithRope();
|
||||
}
|
||||
if (!shouldHoldToRope)
|
||||
{
|
||||
StopHoldingToRope();
|
||||
}
|
||||
if (!shouldBeDraggedWithRope)
|
||||
{
|
||||
StopGettingDraggedWithRope();
|
||||
}
|
||||
shouldHoldToRope = false;
|
||||
shouldHangWithRope = false;
|
||||
shouldBeDraggedWithRope = false;
|
||||
}
|
||||
|
||||
void UpdateStanding()
|
||||
@@ -1256,34 +1241,25 @@ namespace Barotrauma
|
||||
float prevVitality = target.Vitality;
|
||||
bool wasCritical = prevVitality < 0.0f;
|
||||
|
||||
if (GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient) //Serverside code
|
||||
float cprBoost = character.GetStatValue(StatTypes.CPRBoost);
|
||||
float skill = character.GetSkillLevel(Tags.MedicalSkill);
|
||||
bool oxygenAvailable = target.OxygenAvailable >= CharacterHealth.InsufficientOxygenThreshold;
|
||||
|
||||
//Serverside code
|
||||
if (oxygenAvailable && GameMain.NetworkMember is not { IsClient: true })
|
||||
{
|
||||
target.Oxygen += deltaTime * 0.5f; //Stabilize them
|
||||
}
|
||||
|
||||
float cprBoost = character.GetStatValue(StatTypes.CPRBoost);
|
||||
|
||||
int skill = (int)character.GetSkillLevel(Tags.MedicalSkill);
|
||||
|
||||
if (GameMain.NetworkMember is not { IsClient: true })
|
||||
{
|
||||
if (cprBoost >= 1f)
|
||||
{
|
||||
//prevent the patient from suffocating no matter how fast their oxygen level is dropping
|
||||
target.Oxygen = Math.Max(target.Oxygen, -10.0f);
|
||||
}
|
||||
}
|
||||
|
||||
//Serverside code
|
||||
if (GameMain.NetworkMember is not { IsClient: true })
|
||||
{
|
||||
if (target.Oxygen < -10.0f)
|
||||
{
|
||||
//stabilize the oxygen level but don't allow it to go positive and revive the character yet
|
||||
float stabilizationAmount = skill * CPRSettings.Active.StabilizationPerSkill;
|
||||
stabilizationAmount = MathHelper.Clamp(stabilizationAmount, CPRSettings.Active.StabilizationMin, CPRSettings.Active.StabilizationMax);
|
||||
character.Oxygen -= 1.0f / stabilizationAmount * deltaTime; //Worse skill = more oxygen required
|
||||
if (character.Oxygen > 0.0f) { target.Oxygen += stabilizationAmount * deltaTime; } //we didn't suffocate yet did we
|
||||
target.Oxygen += stabilizationAmount * deltaTime;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1317,7 +1293,7 @@ namespace Barotrauma
|
||||
}
|
||||
//need to CPR for at least a couple of seconds before the target can be revived
|
||||
//(reviving the target when the CPR has barely started looks strange)
|
||||
if (cprAnimTimer > 2.0f && GameMain.NetworkMember is not { IsClient: true })
|
||||
if (oxygenAvailable && cprAnimTimer > 2.0f && GameMain.NetworkMember is not { IsClient: true })
|
||||
{
|
||||
float reviveChance = skill * CPRSettings.Active.ReviveChancePerSkill;
|
||||
reviveChance = (float)Math.Pow(reviveChance, CPRSettings.Active.ReviveChanceExponent);
|
||||
@@ -1338,7 +1314,7 @@ namespace Barotrauma
|
||||
//got the character back into a non-critical state, increase medical skill
|
||||
//BUT only if it has been more than 10 seconds since the character revived someone
|
||||
//otherwise it's easy to abuse the system by repeatedly reviving in a low-oxygen room
|
||||
if (!target.IsDead)
|
||||
if (!target.IsDead || !oxygenAvailable)
|
||||
{
|
||||
target.CharacterHealth.RecalculateVitality();
|
||||
if (wasCritical && target.Vitality > 0.0f && Timing.TotalTime > lastReviveTime + 10.0f)
|
||||
|
||||
@@ -75,7 +75,7 @@ namespace Barotrauma
|
||||
get { return frozen; }
|
||||
set
|
||||
{
|
||||
if (frozen == value) return;
|
||||
if (frozen == value) { return; }
|
||||
|
||||
frozen = value;
|
||||
|
||||
@@ -1055,7 +1055,8 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public void FindHull(Vector2? worldPosition = null, bool setSubmarine = true)
|
||||
/// <param name="setInWater">Should the character be immediately considered "in water" if it's outside hulls (normally checked in Update)</param>
|
||||
public void FindHull(Vector2? worldPosition = null, bool setSubmarine = true, bool setInWater = false)
|
||||
{
|
||||
Vector2 findPos = worldPosition == null ? this.WorldPosition : (Vector2)worldPosition;
|
||||
if (!MathUtils.IsValid(findPos))
|
||||
@@ -1068,6 +1069,10 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
Hull newHull = Hull.FindHull(findPos, currentHull);
|
||||
if (setInWater && newHull == null)
|
||||
{
|
||||
inWater = true;
|
||||
}
|
||||
|
||||
if (newHull == currentHull) { return; }
|
||||
|
||||
|
||||
@@ -403,14 +403,19 @@ namespace Barotrauma
|
||||
return (Duration == 0.0f) ? dmg : dmg * deltaTime;
|
||||
}
|
||||
|
||||
public float GetTotalDamage(bool includeStructureDamage = false)
|
||||
/// <summary>
|
||||
/// Returns the total damage (vitality decrease) this attack causes on characters.
|
||||
/// </summary>
|
||||
public float GetTotalCharacterDamage()
|
||||
{
|
||||
float totalDamage = includeStructureDamage ? StructureDamage : 0.0f;
|
||||
float totalDamage = 0.0f;
|
||||
foreach (Affliction affliction in Afflictions.Keys)
|
||||
{
|
||||
totalDamage += affliction.GetVitalityDecrease(null);
|
||||
float afflictionVitalityDecrease = affliction.GetVitalityDecrease(null);
|
||||
if (affliction.AffectedByAttackMultipliers) { afflictionVitalityDecrease *= DamageMultiplier; }
|
||||
totalDamage += afflictionVitalityDecrease;
|
||||
}
|
||||
return totalDamage * DamageMultiplier;
|
||||
return totalDamage;
|
||||
}
|
||||
|
||||
public Attack(float damage, float bleedingDamage, float burnDamage, float structureDamage, float itemDamage, float range = 0.0f)
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace Barotrauma
|
||||
|
||||
partial class Character : Entity, IDamageable, ISerializableEntity, IClientSerializable, IServerPositionSync
|
||||
{
|
||||
public readonly static List<Character> CharacterList = new List<Character>();
|
||||
public static readonly List<Character> CharacterList = new List<Character>();
|
||||
|
||||
public const float MaxHighlightDistance = 150.0f;
|
||||
public const float MaxDragDistance = 200.0f;
|
||||
@@ -39,7 +39,13 @@ namespace Barotrauma
|
||||
|
||||
partial void UpdateLimbLightSource(Limb limb);
|
||||
|
||||
private bool enabled = true;
|
||||
private bool initialized;
|
||||
private bool enabled;
|
||||
//characters start disabled in the multiplayer mode, and are enabled if/when
|
||||
// - controlled by the player
|
||||
// - client receives a position update from the server
|
||||
// - server receives an input message from the client controlling the character
|
||||
// - if an AICharacter, the server enables it when close enough to any of the players
|
||||
public bool Enabled
|
||||
{
|
||||
get
|
||||
@@ -48,7 +54,12 @@ namespace Barotrauma
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value == enabled) { return; }
|
||||
if (initialized && value == enabled)
|
||||
{
|
||||
// Ensure that we'll set the value and run the code below at least once, because otherwise the states might be out of sync.
|
||||
return;
|
||||
}
|
||||
initialized = true;
|
||||
|
||||
if (Removed)
|
||||
{
|
||||
@@ -80,7 +91,6 @@ namespace Barotrauma
|
||||
//we only want to enable the physics body if it's an actual holdable item, not e.g. a wearable item like handcuffs
|
||||
item.body.Enabled = true;
|
||||
}
|
||||
|
||||
}
|
||||
AnimController.Collider.Enabled = value;
|
||||
}
|
||||
@@ -109,6 +119,13 @@ namespace Barotrauma
|
||||
if (!CharacterList.Contains(this)) { CharacterList.Add(this); }
|
||||
if (AiTarget != null && !AITarget.List.Contains(AiTarget)) { AITarget.List.Add(AiTarget); }
|
||||
}
|
||||
if (Inventory != null)
|
||||
{
|
||||
foreach (var item in Inventory.FindAllItems(recursive: true))
|
||||
{
|
||||
item.IsActive = !disabledByEvent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1619,18 +1636,14 @@ namespace Barotrauma
|
||||
PressureProtection = int.MaxValue;
|
||||
}
|
||||
|
||||
AnimController.SetPosition(ConvertUnits.ToSimUnits(position));
|
||||
CharacterHealth.CheckForErrors();
|
||||
|
||||
AnimController.FindHull(null);
|
||||
AnimController.SetPosition(ConvertUnits.ToSimUnits(position));
|
||||
AnimController.FindHull(setInWater: true);
|
||||
if (AnimController.CurrentHull != null) { Submarine = AnimController.CurrentHull.Submarine; }
|
||||
|
||||
CharacterList.Add(this);
|
||||
|
||||
//characters start disabled in the multiplayer mode, and are enabled if/when
|
||||
// - controlled by the player
|
||||
// - client receives a position update from the server
|
||||
// - server receives an input message from the client controlling the character
|
||||
// - if an AICharacter, the server enables it when close enough to any of the players
|
||||
|
||||
Enabled = GameMain.NetworkMember == null;
|
||||
|
||||
if (info != null)
|
||||
@@ -3304,17 +3317,22 @@ namespace Barotrauma
|
||||
|
||||
public static void UpdateAll(float deltaTime, Camera cam)
|
||||
{
|
||||
if (GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient)
|
||||
if (GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient) // single player or server
|
||||
{
|
||||
foreach (Character c in CharacterList)
|
||||
{
|
||||
if (c is not AICharacter && !c.IsRemotePlayer) { continue; }
|
||||
|
||||
if (c.IsPlayer || (c.IsBot && !c.IsDead))
|
||||
// TODO: The logic below seems to be overly complicated and quite confusing
|
||||
if (c is not AICharacter && !c.IsRemotePlayer) { continue; } // confusing -> what this line is intended for? local player? But that's handled below...
|
||||
if (c.IsRemotePlayer)
|
||||
{
|
||||
// Let the client tell when to enable the character. If we force it enabled here, it may e.g. get killed while still loading a round.
|
||||
continue;
|
||||
}
|
||||
if (c.IsLocalPlayer || (c.IsBot && !c.IsDead))
|
||||
{
|
||||
c.Enabled = true;
|
||||
}
|
||||
else if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
|
||||
else if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer) // mp server
|
||||
{
|
||||
//disable AI characters that are far away from all clients and the host's character and not controlled by anyone
|
||||
float closestPlayerDist = c.GetDistanceToClosestPlayer();
|
||||
@@ -3331,7 +3349,7 @@ namespace Barotrauma
|
||||
c.Enabled = true;
|
||||
}
|
||||
}
|
||||
else if (Submarine.MainSub != null)
|
||||
else if (Submarine.MainSub != null) // sp only?
|
||||
{
|
||||
//disable AI characters that are far away from the sub and the controlled character
|
||||
float distSqr = Vector2.DistanceSquared(Submarine.MainSub.WorldPosition, c.WorldPosition);
|
||||
@@ -3360,10 +3378,9 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < CharacterList.Count; i++)
|
||||
foreach (Character character in CharacterList)
|
||||
{
|
||||
var character = CharacterList[i];
|
||||
System.Diagnostics.Debug.Assert(character != null && !character.Removed);
|
||||
Debug.Assert(character is { Removed: false });
|
||||
character.Update(deltaTime, cam);
|
||||
}
|
||||
|
||||
@@ -3425,8 +3442,7 @@ namespace Barotrauma
|
||||
foreach (Item item in Inventory.GetAllItems(checkForDuplicates: false))
|
||||
{
|
||||
if (item.body == null || item.body.Enabled) { continue; }
|
||||
item.SetTransform(SimPosition, 0.0f);
|
||||
item.Submarine = Submarine;
|
||||
item.SetTransform(SimPosition, 0.0f, forceSubmarine: Submarine);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4575,7 +4591,10 @@ namespace Barotrauma
|
||||
|
||||
SetStun(stun);
|
||||
|
||||
if (attacker != null && attacker != this && GameMain.NetworkMember != null && !GameMain.NetworkMember.ServerSettings.AllowFriendlyFire)
|
||||
if (attacker != null && attacker != this &&
|
||||
attacker.IsOnPlayerTeam &&
|
||||
GameMain.NetworkMember != null &&
|
||||
!GameMain.NetworkMember.ServerSettings.AllowFriendlyFire)
|
||||
{
|
||||
if (attacker.TeamID == TeamID)
|
||||
{
|
||||
|
||||
@@ -85,6 +85,8 @@ namespace Barotrauma
|
||||
|
||||
public double AppliedAsSuccessfulTreatmentTime, AppliedAsFailedTreatmentTime;
|
||||
|
||||
public bool AffectedByAttackMultipliers => Prefab.AffectedByAttackMultipliers;
|
||||
|
||||
public float Duration;
|
||||
|
||||
/// <summary>
|
||||
|
||||
+9
-6
@@ -164,15 +164,18 @@ namespace Barotrauma
|
||||
}
|
||||
break;
|
||||
case InfectionState.Transition:
|
||||
if (character == Character.Controlled)
|
||||
if (Prefab is AfflictionPrefabHusk { CauseSpeechImpediment: true })
|
||||
{
|
||||
if (character == Character.Controlled)
|
||||
{
|
||||
#if CLIENT
|
||||
GUI.AddMessage(TextManager.Get("HuskCantSpeak"), GUIStyle.Red);
|
||||
GUI.AddMessage(TextManager.Get("HuskCantSpeak"), GUIStyle.Red);
|
||||
#endif
|
||||
}
|
||||
else if (character.IsBot)
|
||||
{
|
||||
character.Speak(TextManager.Get("dialoghuskcantspeak").Value, delay: Rand.Range(0.5f, 5.0f), identifier: "huskcantspeak".ToIdentifier());
|
||||
}
|
||||
else if (character.IsBot)
|
||||
{
|
||||
character.Speak(TextManager.Get("dialoghuskcantspeak").Value, delay: Rand.Range(0.5f, 5.0f), identifier: "huskcantspeak".ToIdentifier());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case InfectionState.Active:
|
||||
|
||||
+26
-5
@@ -699,7 +699,13 @@ namespace Barotrauma
|
||||
/// and the health UI will render the affected limb in green rather than red.
|
||||
/// </summary>
|
||||
public readonly bool IsBuff;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Should the affliction be affected by damage multipliers on an attack (e.g. when the attacker has talents that boost damage).
|
||||
/// By default, afflictions defined as buffs aren't affected.
|
||||
/// </summary>
|
||||
public readonly bool AffectedByAttackMultipliers;
|
||||
|
||||
/// <summary>
|
||||
/// If set to true, this affliction can affect characters that are marked as
|
||||
/// machines, such as the Fractal Guardian.
|
||||
@@ -780,6 +786,14 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
public readonly float TreatmentSuggestionThreshold;
|
||||
|
||||
/// <summary>
|
||||
/// Does the affliction need to have caused some amount of vitality loss for bots to consider treating it?
|
||||
/// Normally bots use vitality loss as a way to determine what kind of injuries need treatment, but some afflictions (e.g. poisons, infections)
|
||||
/// might require treatment regardless of the vitality loss. If disabled, the bots will use the strength of the affliction to evaluate the severity instead of the vitality loss.
|
||||
/// Defaults to true for all afflictions that aren't of the type Paralysis, Poison or HuskInfection.
|
||||
/// </summary>
|
||||
public readonly bool VitalityLossRequiredForTreatment;
|
||||
|
||||
/// <summary>
|
||||
/// Bots will not try to treat the affliction if the character has any of these afflictions
|
||||
/// </summary>
|
||||
@@ -838,14 +852,15 @@ namespace Barotrauma
|
||||
public readonly bool DamageParticles;
|
||||
|
||||
/// <summary>
|
||||
/// An arbitrary modifier that affects how much medical skill is increased when you apply the affliction on a target.
|
||||
/// If the affliction causes damage or is of the 'poison' or 'paralysis' type, the skill is increased only when the target is hostile.
|
||||
/// If the affliction is of the 'buff' type, the skill is increased only when the target is friendly.
|
||||
/// A modifier that affects how much medical skill is increased when you apply this affliction on a target.
|
||||
/// If the affliction causes damage or is of the 'poison' or 'paralysis' type, the skill is increased only when the target is hostile, and the modifier is multiplied by the amount of vitality the enemy lost.
|
||||
/// If the affliction is of the 'buff' type, the skill is increased only when the target is friendly, and the modifier is multiplied by the strength of the affliction the target gained.
|
||||
/// </summary>
|
||||
public readonly float MedicalSkillGain;
|
||||
|
||||
/// <summary>
|
||||
/// An arbitrary modifier that affects how much weapons skill is increased when you apply the affliction on a target.
|
||||
/// A modifier that affects how much weapons skill is increased when you apply the affliction on a target.
|
||||
/// Multiplied by the amount of vitality the enemy lost.
|
||||
/// The skill is increased only when the target is hostile.
|
||||
/// </summary>
|
||||
public readonly float WeaponsSkillGain;
|
||||
@@ -925,6 +940,7 @@ namespace Barotrauma
|
||||
ShowDescriptionInTooltip = element.GetAttributeBool(nameof(ShowDescriptionInTooltip), true);
|
||||
|
||||
IsBuff = element.GetAttributeBool(nameof(IsBuff), false);
|
||||
AffectedByAttackMultipliers = element.GetAttributeBool(nameof(AffectedByAttackMultipliers), def: !IsBuff);
|
||||
AffectMachines = element.GetAttributeBool(nameof(AffectMachines), true);
|
||||
|
||||
ShowBarInHealthMenu = element.GetAttributeBool("showbarinhealthmenu", true);
|
||||
@@ -977,6 +993,11 @@ namespace Barotrauma
|
||||
TreatmentThreshold = element.GetAttributeFloat(nameof(TreatmentThreshold), Math.Max(ActivationThreshold, 10.0f));
|
||||
TreatmentSuggestionThreshold = element.GetAttributeFloat(nameof(TreatmentSuggestionThreshold), TreatmentThreshold);
|
||||
|
||||
bool alwaysRequiresTreatment = AfflictionType == ParalysisType || AfflictionType == PoisonType || this is AfflictionPrefabHusk;
|
||||
|
||||
VitalityLossRequiredForTreatment = element.GetAttributeBool(nameof(VitalityLossRequiredForTreatment),
|
||||
def: !alwaysRequiresTreatment);
|
||||
|
||||
DamageOverlayAlpha = element.GetAttributeFloat(nameof(DamageOverlayAlpha), 0.0f);
|
||||
BurnOverlayAlpha = element.GetAttributeFloat(nameof(BurnOverlayAlpha), 0.0f);
|
||||
|
||||
|
||||
@@ -304,6 +304,19 @@ namespace Barotrauma
|
||||
InitProjSpecific(element, character);
|
||||
}
|
||||
|
||||
public void CheckForErrors()
|
||||
{
|
||||
for (int i = 0; i < limbHealths.Count; i++)
|
||||
{
|
||||
if (Character.AnimController.Limbs.None(l => l.HealthIndex == i))
|
||||
{
|
||||
DebugConsole.AddWarning(
|
||||
$"Potential error in character {Character.DisplayName}: none of the limbs have been set to use the LimbHealth #{i}, and it will do nothing. "
|
||||
+ "Did you forget to set the HealthIndex values of the limbs?", contentPackage: Character.ContentPackage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void InitIrremovableAfflictions()
|
||||
{
|
||||
irremovableAfflictions.Add(bloodlossAffliction = new Affliction(AfflictionPrefab.Bloodloss, 0.0f));
|
||||
@@ -1118,20 +1131,38 @@ namespace Barotrauma
|
||||
|
||||
// We need to use another list of the afflictions when we call the status effects triggered by afflictions,
|
||||
// because those status effects may add or remove other afflictions while iterating the collection.
|
||||
private readonly List<Affliction> afflictionsCopy = new List<Affliction>();
|
||||
private readonly List<Affliction> afflictionsCopy = [];
|
||||
|
||||
private bool isApplyingAfflictionStatusEffects;
|
||||
public void ApplyAfflictionStatusEffects(ActionType type)
|
||||
{
|
||||
afflictionsCopy.Clear();
|
||||
afflictionsCopy.AddRange(afflictions.Keys);
|
||||
foreach (Affliction affliction in afflictionsCopy)
|
||||
if (isApplyingAfflictionStatusEffects)
|
||||
{
|
||||
affliction.ApplyStatusEffects(type, 1.0f, this, targetLimb: GetAfflictionLimb(affliction));
|
||||
//pretty hacky: if we're already in the process of applying afflictions' status effects
|
||||
//(i.e. calling this method caused some additional afflictions to appear and trigger status effects)
|
||||
//let's instantiate a new list so we don't end up modifying afflictionsCopy while enumerating it
|
||||
foreach (Affliction affliction in afflictions.Keys.ToList())
|
||||
{
|
||||
affliction.ApplyStatusEffects(type, 1.0f, this, targetLimb: GetAfflictionLimb(affliction));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
isApplyingAfflictionStatusEffects = true;
|
||||
afflictionsCopy.Clear();
|
||||
afflictionsCopy.AddRange(afflictions.Keys);
|
||||
isApplyingAfflictionStatusEffects = true;
|
||||
foreach (Affliction affliction in afflictionsCopy)
|
||||
{
|
||||
affliction.ApplyStatusEffects(type, 1.0f, this, targetLimb: GetAfflictionLimb(affliction));
|
||||
}
|
||||
isApplyingAfflictionStatusEffects = false;
|
||||
}
|
||||
}
|
||||
|
||||
public (CauseOfDeathType type, Affliction affliction) GetCauseOfDeath()
|
||||
{
|
||||
List<Affliction> currentAfflictions = GetAllAfflictions(true);
|
||||
IEnumerable<Affliction> currentAfflictions = GetAllAfflictions(true);
|
||||
|
||||
Affliction strongestAffliction = null;
|
||||
float largestStrength = 0.0f;
|
||||
@@ -1154,7 +1185,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
private readonly List<Affliction> allAfflictions = new List<Affliction>();
|
||||
private List<Affliction> GetAllAfflictions(bool mergeSameAfflictions, Func<Affliction, bool> predicate = null)
|
||||
private IEnumerable<Affliction> GetAllAfflictions(bool mergeSameAfflictions, Func<Affliction, bool> predicate = null)
|
||||
{
|
||||
allAfflictions.Clear();
|
||||
if (!mergeSameAfflictions)
|
||||
|
||||
@@ -843,7 +843,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
if (!foundMatchingModifier && random > affliction.Probability) { continue; }
|
||||
float finalDamageModifier = damageMultiplier;
|
||||
float finalDamageModifier = affliction.AffectedByAttackMultipliers ? damageMultiplier : 1.0f;
|
||||
if (character.EmpVulnerability > 0 && affliction.Prefab.AfflictionType == AfflictionPrefab.EMPType)
|
||||
{
|
||||
finalDamageModifier *= character.EmpVulnerability;
|
||||
|
||||
Reference in New Issue
Block a user