(6eeea9b7c) v0.9.10.0.0
This commit is contained in:
@@ -3,7 +3,7 @@ using FarseerPhysics;
|
||||
using FarseerPhysics.Dynamics.Joints;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.IO;
|
||||
using Barotrauma.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
@@ -43,6 +43,7 @@ namespace Barotrauma
|
||||
|
||||
foreach (Limb limb in AnimController.Limbs)
|
||||
{
|
||||
if (limb.IsSevered) { continue; }
|
||||
if (limb.body != null)
|
||||
{
|
||||
limb.body.Enabled = enabled;
|
||||
@@ -235,6 +236,7 @@ namespace Barotrauma
|
||||
{
|
||||
get
|
||||
{
|
||||
if (info != null && !string.IsNullOrWhiteSpace(info.Name)) { return info.Name; }
|
||||
var displayName = Params.DisplayName;
|
||||
if (string.IsNullOrWhiteSpace(displayName))
|
||||
{
|
||||
@@ -587,30 +589,32 @@ namespace Barotrauma
|
||||
set { canInventoryBeAccessed = value; }
|
||||
}
|
||||
|
||||
private bool accessRemovedCharacterErrorShown;
|
||||
public override Vector2 SimPosition
|
||||
{
|
||||
get
|
||||
{
|
||||
if (AnimController?.Collider == null)
|
||||
{
|
||||
string errorMsg = "Attempted to access a potentially removed character. Character: " + Name + ", id: " + ID + ", removed: " + Removed + ".";
|
||||
if (AnimController == null)
|
||||
if (!accessRemovedCharacterErrorShown)
|
||||
{
|
||||
errorMsg += " AnimController == null";
|
||||
string errorMsg = "Attempted to access a potentially removed character. Character: " + Name + ", id: " + ID + ", removed: " + Removed + ".";
|
||||
if (AnimController == null)
|
||||
{
|
||||
errorMsg += " AnimController == null";
|
||||
}
|
||||
else if (AnimController.Collider == null)
|
||||
{
|
||||
errorMsg += " AnimController.Collider == null";
|
||||
}
|
||||
errorMsg += '\n' + Environment.StackTrace;
|
||||
DebugConsole.NewMessage(errorMsg, Color.Red);
|
||||
GameAnalyticsManager.AddErrorEventOnce(
|
||||
"Character.SimPosition:AccessRemoved",
|
||||
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
|
||||
errorMsg + "\n" + Environment.StackTrace);
|
||||
accessRemovedCharacterErrorShown = true;
|
||||
}
|
||||
else if (AnimController.Collider == null)
|
||||
{
|
||||
errorMsg += " AnimController.Collider == null";
|
||||
}
|
||||
#if DEBUG || UNSTABLE
|
||||
errorMsg += '\n' + Environment.StackTrace;
|
||||
#endif
|
||||
DebugConsole.NewMessage(errorMsg, Color.Red);
|
||||
GameAnalyticsManager.AddErrorEventOnce(
|
||||
"Character.SimPosition:AccessRemoved",
|
||||
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
|
||||
errorMsg + "\n" + Environment.StackTrace);
|
||||
|
||||
return Vector2.Zero;
|
||||
}
|
||||
|
||||
@@ -1140,22 +1144,86 @@ namespace Barotrauma
|
||||
greatestNegativeSpeedMultiplier = 1f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Speed reduction from the current limb specific damage. Min 0, max 1.
|
||||
/// </summary>
|
||||
public float GetTemporarySpeedReduction()
|
||||
{
|
||||
float reduction = 0;
|
||||
reduction = CalculateMovementPenalty(AnimController.GetLimb(LimbType.RightFoot, excludeSevered: false), reduction);
|
||||
reduction = CalculateMovementPenalty(AnimController.GetLimb(LimbType.LeftFoot, excludeSevered: false), reduction);
|
||||
if (AnimController is HumanoidAnimController)
|
||||
{
|
||||
if (AnimController.InWater)
|
||||
{
|
||||
// Currently only humans use hands for swimming.
|
||||
reduction = CalculateMovementPenalty(AnimController.GetLimb(LimbType.RightHand, excludeSevered: false), reduction);
|
||||
reduction = CalculateMovementPenalty(AnimController.GetLimb(LimbType.LeftHand, excludeSevered: false), reduction);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int totalTailLimbs = 0;
|
||||
int destroyedTailLimbs = 0;
|
||||
foreach (var limb in AnimController.Limbs)
|
||||
{
|
||||
if (limb.type == LimbType.Tail)
|
||||
{
|
||||
totalTailLimbs++;
|
||||
if (limb.IsSevered)
|
||||
{
|
||||
destroyedTailLimbs++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (destroyedTailLimbs > 0)
|
||||
{
|
||||
reduction += MathHelper.Lerp(0, AnimController.InWater ? 1f : 0.5f, (float)destroyedTailLimbs / totalTailLimbs);
|
||||
}
|
||||
}
|
||||
return Math.Clamp(reduction, 0, 1f);
|
||||
}
|
||||
|
||||
private float CalculateMovementPenalty(Limb limb, float sum, float max = 0.4f)
|
||||
{
|
||||
if (limb != null)
|
||||
{
|
||||
sum += MathHelper.Lerp(0, max, CharacterHealth.GetLimbDamage(limb, afflictionType: "damage"));
|
||||
}
|
||||
return Math.Clamp(sum, 0, 1f);
|
||||
}
|
||||
|
||||
public float GetRightHandPenalty() => CalculateMovementPenalty(AnimController.GetLimb(LimbType.RightHand, excludeSevered: false), 0, max: 1);
|
||||
public float GetLeftHandPenalty() => CalculateMovementPenalty(AnimController.GetLimb(LimbType.LeftHand, excludeSevered: false), 0, max: 1);
|
||||
|
||||
public float GetLegPenalty(float startSum = 0)
|
||||
{
|
||||
float sum = startSum;
|
||||
foreach (var limb in AnimController.Limbs)
|
||||
{
|
||||
switch (limb.type)
|
||||
{
|
||||
case LimbType.RightFoot:
|
||||
case LimbType.LeftFoot:
|
||||
sum += CalculateMovementPenalty(limb, sum, max: 0.5f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Math.Clamp(sum, 0, 1f);
|
||||
}
|
||||
|
||||
public float ApplyTemporarySpeedLimits(float speed)
|
||||
{
|
||||
var leftFoot = AnimController.GetLimb(LimbType.LeftFoot);
|
||||
if (leftFoot != null)
|
||||
float max;
|
||||
if (AnimController is HumanoidAnimController)
|
||||
{
|
||||
float footAfflictionStrength = CharacterHealth.GetAfflictionStrength("damage", leftFoot, true);
|
||||
speed *= MathHelper.Lerp(1.0f, 0.4f, MathHelper.Clamp(footAfflictionStrength / 80.0f, 0.0f, 1.0f));
|
||||
max = AnimController.InWater ? 0.5f : 0.7f;
|
||||
}
|
||||
|
||||
var rightFoot = AnimController.GetLimb(LimbType.RightFoot);
|
||||
if (rightFoot != null)
|
||||
else
|
||||
{
|
||||
float footAfflictionStrength = CharacterHealth.GetAfflictionStrength("damage", rightFoot, true);
|
||||
speed *= MathHelper.Lerp(1.0f, 0.4f, MathHelper.Clamp(footAfflictionStrength / 80.0f, 0.0f, 1.0f));
|
||||
max = AnimController.InWater ? 0.9f : 0.5f;
|
||||
}
|
||||
|
||||
speed *= 1f - MathHelper.Lerp(0, max, GetTemporarySpeedReduction());
|
||||
return speed;
|
||||
}
|
||||
|
||||
@@ -1256,58 +1324,72 @@ namespace Barotrauma
|
||||
}
|
||||
else if (IsKeyDown(InputType.Attack) && (IsRemotePlayer || Controlled == this || (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient)))
|
||||
{
|
||||
Vector2 attackPos = SimPosition + ConvertUnits.ToSimUnits(cursorPosition - Position);
|
||||
List<Body> ignoredBodies = AnimController.Limbs.Select(l => l.body.FarseerBody).ToList();
|
||||
ignoredBodies.Add(AnimController.Collider.FarseerBody);
|
||||
|
||||
var body = Submarine.PickBody(
|
||||
SimPosition,
|
||||
attackPos,
|
||||
ignoredBodies,
|
||||
Physics.CollisionCharacter | Physics.CollisionWall);
|
||||
|
||||
IDamageable attackTarget = null;
|
||||
if (body != null)
|
||||
{
|
||||
attackPos = Submarine.LastPickedPosition;
|
||||
|
||||
if (body.UserData is Submarine sub)
|
||||
{
|
||||
body = Submarine.PickBody(
|
||||
SimPosition - ((Submarine)body.UserData).SimPosition,
|
||||
attackPos - ((Submarine)body.UserData).SimPosition,
|
||||
ignoredBodies,
|
||||
Physics.CollisionWall);
|
||||
|
||||
if (body != null)
|
||||
{
|
||||
attackPos = Submarine.LastPickedPosition + sub.SimPosition;
|
||||
attackTarget = body.UserData as IDamageable;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (body.UserData is IDamageable)
|
||||
{
|
||||
attackTarget = (IDamageable)body.UserData;
|
||||
}
|
||||
else if (body.UserData is Limb)
|
||||
{
|
||||
attackTarget = ((Limb)body.UserData).character;
|
||||
}
|
||||
}
|
||||
}
|
||||
var currentContexts = GetAttackContexts();
|
||||
var validLimbs = AnimController.Limbs.Where(l => !l.IsSevered && !l.IsStuck && l.attack != null && l.attack.IsValidContext(currentContexts));
|
||||
var validLimbs = AnimController.Limbs.Where(l =>
|
||||
{
|
||||
if (l.IsSevered || l.IsStuck) { return false; }
|
||||
var attack = l.attack;
|
||||
if (attack == null) { return false; }
|
||||
if (attack.CoolDownTimer > 0) { return false; }
|
||||
if (!attack.IsValidContext(currentContexts)) { return false; }
|
||||
if (attackTarget != null)
|
||||
{
|
||||
if (!attack.IsValidTarget(attackTarget)) { return false; }
|
||||
if (attackTarget is ISerializableEntity se && attackTarget is Character)
|
||||
{
|
||||
if (attack.Conditionals.Any(c => !c.Matches(se))) { return false; }
|
||||
}
|
||||
}
|
||||
if (attack.Conditionals.Any(c => c.TargetSelf && !c.Matches(this))) { return false; }
|
||||
return true;
|
||||
});
|
||||
var sortedLimbs = validLimbs.OrderBy(l => Vector2.DistanceSquared(ConvertUnits.ToDisplayUnits(l.SimPosition), cursorPosition));
|
||||
// Select closest
|
||||
var attackLimb = sortedLimbs.FirstOrDefault();
|
||||
if (attackLimb != null)
|
||||
{
|
||||
Vector2 attackPos = attackLimb.SimPosition + Vector2.Normalize(cursorPosition - attackLimb.Position) * ConvertUnits.ToSimUnits(attackLimb.attack.Range);
|
||||
|
||||
List<Body> ignoredBodies = AnimController.Limbs.Select(l => l.body.FarseerBody).ToList();
|
||||
ignoredBodies.Add(AnimController.Collider.FarseerBody);
|
||||
|
||||
var body = Submarine.PickBody(
|
||||
attackLimb.SimPosition,
|
||||
attackPos,
|
||||
ignoredBodies,
|
||||
Physics.CollisionCharacter | Physics.CollisionWall);
|
||||
|
||||
IDamageable attackTarget = null;
|
||||
if (body != null)
|
||||
{
|
||||
attackPos = Submarine.LastPickedPosition;
|
||||
|
||||
if (body.UserData is Submarine sub)
|
||||
{
|
||||
body = Submarine.PickBody(
|
||||
attackLimb.SimPosition - ((Submarine)body.UserData).SimPosition,
|
||||
attackPos - ((Submarine)body.UserData).SimPosition,
|
||||
ignoredBodies,
|
||||
Physics.CollisionWall);
|
||||
|
||||
if (body != null)
|
||||
{
|
||||
attackPos = Submarine.LastPickedPosition + sub.SimPosition;
|
||||
attackTarget = body.UserData as IDamageable;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (body.UserData is IDamageable)
|
||||
{
|
||||
attackTarget = (IDamageable)body.UserData;
|
||||
}
|
||||
else if (body.UserData is Limb)
|
||||
{
|
||||
attackTarget = ((Limb)body.UserData).character;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
attackLimb.UpdateAttack(deltaTime, attackPos, attackTarget, out AttackResult attackResult);
|
||||
|
||||
if (!attackLimb.attack.IsRunning)
|
||||
{
|
||||
attackCoolDown = 1.0f;
|
||||
@@ -1394,22 +1476,23 @@ namespace Barotrauma
|
||||
{
|
||||
Limb selfLimb = AnimController.GetLimb(LimbType.Head);
|
||||
if (selfLimb == null) { selfLimb = AnimController.GetLimb(LimbType.Torso); }
|
||||
if (selfLimb == null) { selfLimb = AnimController.Limbs.FirstOrDefault(); }
|
||||
if (selfLimb == null) { selfLimb = AnimController.MainLimb; }
|
||||
return selfLimb;
|
||||
}
|
||||
|
||||
public bool CanSeeTarget(ISpatialEntity target, Limb seeingLimb = null)
|
||||
{
|
||||
seeingLimb = seeingLimb ?? GetSeeingLimb();
|
||||
seeingLimb ??= GetSeeingLimb();
|
||||
if (seeingLimb == null) { return false; }
|
||||
ISpatialEntity seeingEntity = AnimController.SimplePhysicsEnabled ? this : seeingLimb as ISpatialEntity;
|
||||
// TODO: Could we just use the method below? If not, let's refactor it so that we can.
|
||||
Vector2 diff = ConvertUnits.ToSimUnits(target.WorldPosition - seeingLimb.WorldPosition);
|
||||
Vector2 diff = ConvertUnits.ToSimUnits(target.WorldPosition - seeingEntity.WorldPosition);
|
||||
Body closestBody;
|
||||
//both inside the same sub (or both outside)
|
||||
//OR the we're inside, the other character outside
|
||||
if (target.Submarine == Submarine || target.Submarine == null)
|
||||
{
|
||||
closestBody = Submarine.CheckVisibility(seeingLimb.SimPosition, seeingLimb.SimPosition + diff);
|
||||
closestBody = Submarine.CheckVisibility(seeingEntity.SimPosition, seeingEntity.SimPosition + diff);
|
||||
}
|
||||
//we're outside, the other character inside
|
||||
else if (Submarine == null)
|
||||
@@ -1419,7 +1502,7 @@ namespace Barotrauma
|
||||
//both inside different subs
|
||||
else
|
||||
{
|
||||
closestBody = Submarine.CheckVisibility(seeingLimb.SimPosition, seeingLimb.SimPosition + diff);
|
||||
closestBody = Submarine.CheckVisibility(seeingEntity.SimPosition, seeingEntity.SimPosition + diff);
|
||||
if (!IsBlocking(closestBody))
|
||||
{
|
||||
closestBody = Submarine.CheckVisibility(target.SimPosition, target.SimPosition - diff);
|
||||
@@ -1436,10 +1519,11 @@ namespace Barotrauma
|
||||
}
|
||||
else if (body.UserData is Item item && item != target)
|
||||
{
|
||||
// TODO: The door collider should be disabled, so this check is probably unnecessary.
|
||||
var door = item.GetComponent<Door>();
|
||||
if (door != null)
|
||||
{
|
||||
return !door.IsOpen;
|
||||
return !door.IsOpen && !door.IsBroken;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@@ -1466,7 +1550,7 @@ namespace Barotrauma
|
||||
Structure wall = closestBody.UserData as Structure;
|
||||
Item item = closestBody.UserData as Item;
|
||||
Door door = item?.GetComponent<Door>();
|
||||
return (wall == null || !wall.CastShadow) && (door == null || door.IsOpen);
|
||||
return (wall == null || !wall.CastShadow) && (door == null || door.IsOpen || door.IsBroken);
|
||||
}
|
||||
|
||||
public bool HasItem(Item item, bool requireEquipped = false) => requireEquipped ? HasEquippedItem(item) : item.IsOwnedBy(this);
|
||||
@@ -1601,8 +1685,8 @@ namespace Barotrauma
|
||||
if (IsItemTakenBySomeoneElse(item)) { continue; }
|
||||
float itemPriority = customPriorityFunction != null ? customPriorityFunction(item) : 1;
|
||||
if (itemPriority <= 0) { continue; }
|
||||
Item rootContainer = item.GetRootContainer();
|
||||
Vector2 itemPos = (rootContainer ?? item).WorldPosition;
|
||||
Entity rootInventoryOwner = item.GetRootInventoryOwner();
|
||||
Vector2 itemPos = (rootInventoryOwner ?? item).WorldPosition;
|
||||
float yDist = Math.Abs(WorldPosition.Y - itemPos.Y);
|
||||
yDist = yDist > 100 ? yDist * 5 : 0;
|
||||
float dist = Math.Abs(WorldPosition.X - itemPos.X) + yDist;
|
||||
@@ -1620,13 +1704,16 @@ namespace Barotrauma
|
||||
|
||||
public bool IsItemTakenBySomeoneElse(Item item) => item.FindParentInventory(i => i.Owner != this && i.Owner is Character owner && !owner.IsDead && !owner.Removed) != null;
|
||||
|
||||
public bool CanInteractWith(Character c, float maxDist = 200.0f, bool checkVisibility = true)
|
||||
public bool CanInteractWith(Character c, float maxDist = 200.0f, bool checkVisibility = true, bool skipDistanceCheck = false)
|
||||
{
|
||||
if (c == this || Removed || !c.Enabled || !c.CanBeSelected) return false;
|
||||
if (!c.CharacterHealth.UseHealthWindow && !c.CanBeDragged && c.onCustomInteract == null) return false;
|
||||
if (c == this || Removed || !c.Enabled || !c.CanBeSelected) { return false; }
|
||||
if (!c.CharacterHealth.UseHealthWindow && !c.CanBeDragged && c.onCustomInteract == null) { return false; }
|
||||
|
||||
maxDist = ConvertUnits.ToSimUnits(maxDist);
|
||||
if (Vector2.DistanceSquared(SimPosition, c.SimPosition) > maxDist * maxDist) return false;
|
||||
if (!skipDistanceCheck)
|
||||
{
|
||||
maxDist = ConvertUnits.ToSimUnits(maxDist);
|
||||
if (Vector2.DistanceSquared(SimPosition, c.SimPosition) > maxDist * maxDist) { return false; }
|
||||
}
|
||||
|
||||
return checkVisibility ? CanSeeCharacter(c) : true;
|
||||
}
|
||||
@@ -2394,7 +2481,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private readonly float maxAIRange = 10000;
|
||||
private readonly float maxAIRange = 20000;
|
||||
private readonly float aiTargetChangeSpeed = 5;
|
||||
|
||||
private void UpdateSightRange(float deltaTime)
|
||||
@@ -2628,40 +2715,60 @@ namespace Barotrauma
|
||||
GameServer.Log(sb.ToString(), ServerLog.MessageType.Attack);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool isNotClient = GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient;
|
||||
|
||||
TrySeverLimbJoints(limbHit, attack.SeverLimbsProbability);
|
||||
// Don't allow beheading for monster attacks, because it happens too frequently (crawlers/tigerthreshers etc attacking each other -> they will most often target to the head)
|
||||
TrySeverLimbJoints(limbHit, attack.SeverLimbsProbability, attackResult.Damage, allowBeheading: AIController == null || AIController is HumanAIController);
|
||||
|
||||
return attackResult;
|
||||
}
|
||||
|
||||
public void TrySeverLimbJoints(Limb targetLimb, float severLimbsProbability)
|
||||
public void TrySeverLimbJoints(Limb targetLimb, float severLimbsProbability, float damage, bool allowBeheading)
|
||||
{
|
||||
bool isNotClient = GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient;
|
||||
|
||||
if (isNotClient &&
|
||||
IsDead && Rand.Range(0.0f, 1.0f) < severLimbsProbability)
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; }
|
||||
#if DEBUG
|
||||
if (targetLimb.character != this)
|
||||
{
|
||||
foreach (LimbJoint joint in AnimController.LimbJoints)
|
||||
{
|
||||
if (joint.CanBeSevered && (joint.LimbA == targetLimb || joint.LimbB == targetLimb))
|
||||
{
|
||||
#if CLIENT
|
||||
CurrentHull?.AddDecal("blood", WorldPosition, Rand.Range(0.5f, 1.5f));
|
||||
DebugConsole.ThrowError($"{Name} is attempting to sever joints of {targetLimb.character.Name}!");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
AnimController.SeverLimbJoint(joint);
|
||||
|
||||
if (joint.LimbA == targetLimb)
|
||||
{
|
||||
joint.LimbB.body.LinearVelocity += targetLimb.LinearVelocity * 0.5f;
|
||||
}
|
||||
else
|
||||
{
|
||||
joint.LimbA.body.LinearVelocity += targetLimb.LinearVelocity * 0.5f;
|
||||
}
|
||||
}
|
||||
if (damage < targetLimb.Params.MinSeveranceDamage) { return; }
|
||||
if (!IsDead)
|
||||
{
|
||||
if (!allowBeheading && targetLimb.type == LimbType.Head) { return; }
|
||||
if (!targetLimb.CanBeSeveredAlive) { return; }
|
||||
}
|
||||
bool wasSevered = false;
|
||||
float random = Rand.Value();
|
||||
foreach (LimbJoint joint in AnimController.LimbJoints)
|
||||
{
|
||||
if (!joint.CanBeSevered) { continue; }
|
||||
if (joint.LimbA != targetLimb && joint.LimbB != targetLimb) { continue; }
|
||||
float probability = severLimbsProbability;
|
||||
if (!IsDead)
|
||||
{
|
||||
probability *= joint.Params.SeveranceProbabilityModifier;
|
||||
}
|
||||
if (probability <= 0) { continue; }
|
||||
if (random > probability) { continue; }
|
||||
bool severed = AnimController.SeverLimbJoint(joint);
|
||||
if (!wasSevered)
|
||||
{
|
||||
wasSevered = severed;
|
||||
}
|
||||
if (severed)
|
||||
{
|
||||
Limb otherLimb = joint.LimbA == targetLimb ? joint.LimbB : joint.LimbA;
|
||||
otherLimb.body.ApplyLinearImpulse(targetLimb.LinearVelocity * targetLimb.Mass);
|
||||
}
|
||||
}
|
||||
if (wasSevered)
|
||||
{
|
||||
if (targetLimb.character.AIController is EnemyAIController enemyAI)
|
||||
{
|
||||
enemyAI.ReevaluateAttacks();
|
||||
}
|
||||
ApplyStatusEffects(ActionType.OnSevered, 1.0f);
|
||||
targetLimb.ApplyStatusEffects(ActionType.OnSevered, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2742,6 +2849,10 @@ namespace Barotrauma
|
||||
AttackResult attackResult = hitLimb.AddDamage(simPos, afflictions, playSound);
|
||||
CharacterHealth.ApplyDamage(hitLimb, attackResult);
|
||||
ApplyStatusEffects(ActionType.OnDamaged, 1.0f);
|
||||
if (attackResult.Damage > 0)
|
||||
{
|
||||
hitLimb.ApplyStatusEffects(ActionType.OnDamaged, 1.0f);
|
||||
}
|
||||
if (attacker != this)
|
||||
{
|
||||
OnAttacked?.Invoke(attacker, attackResult);
|
||||
@@ -2800,8 +2911,10 @@ namespace Barotrauma
|
||||
{
|
||||
SelectedConstruction = null;
|
||||
}
|
||||
HealthUpdateInterval = 0.0f;
|
||||
}
|
||||
|
||||
private readonly List<ISerializableEntity> targets = new List<ISerializableEntity>();
|
||||
public void ApplyStatusEffects(ActionType actionType, float deltaTime)
|
||||
{
|
||||
foreach (StatusEffect statusEffect in statusEffects)
|
||||
@@ -2810,30 +2923,61 @@ namespace Barotrauma
|
||||
if (statusEffect.HasTargetType(StatusEffect.TargetType.NearbyItems) ||
|
||||
statusEffect.HasTargetType(StatusEffect.TargetType.NearbyCharacters))
|
||||
{
|
||||
var targets = new List<ISerializableEntity>();
|
||||
targets.Clear();
|
||||
statusEffect.GetNearbyTargets(WorldPosition, targets);
|
||||
statusEffect.Apply(ActionType.OnActive, deltaTime, this, targets);
|
||||
}
|
||||
else
|
||||
{
|
||||
statusEffect.Apply(actionType, deltaTime, this, this);
|
||||
if (statusEffect.targetLimbs != null)
|
||||
{
|
||||
foreach (var limbType in statusEffect.targetLimbs)
|
||||
{
|
||||
if (statusEffect.HasTargetType(StatusEffect.TargetType.AllLimbs))
|
||||
{
|
||||
// Target all matching limbs
|
||||
foreach (var limb in AnimController.Limbs)
|
||||
{
|
||||
if (limb.IsSevered) { continue; }
|
||||
if (limb.type == limbType)
|
||||
{
|
||||
statusEffect.Apply(actionType, deltaTime, this, limb);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (statusEffect.HasTargetType(StatusEffect.TargetType.Limb))
|
||||
{
|
||||
// Target just the first matching limb
|
||||
Limb limb = AnimController.GetLimb(limbType);
|
||||
statusEffect.Apply(actionType, deltaTime, this, limb);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (actionType != ActionType.OnDamaged && actionType != ActionType.OnSevered)
|
||||
{
|
||||
// OnDamaged is called only for the limb that is hit.
|
||||
AnimController.Limbs.ForEach(l => l.ApplyStatusEffects(actionType, deltaTime));
|
||||
}
|
||||
}
|
||||
|
||||
private void Implode(bool isNetworkMessage = false)
|
||||
{
|
||||
if (CharacterHealth.Unkillable) { return; }
|
||||
if (CharacterHealth.Unkillable || IsDead) { return; }
|
||||
|
||||
if (!isNetworkMessage)
|
||||
{
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) return;
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; }
|
||||
}
|
||||
|
||||
Kill(CauseOfDeathType.Pressure, null, isNetworkMessage);
|
||||
CharacterHealth.PressureAffliction.Strength = CharacterHealth.PressureAffliction.Prefab.MaxStrength;
|
||||
CharacterHealth.SetAllDamage(200.0f, 0.0f, 0.0f);
|
||||
BreakJoints();
|
||||
CharacterHealth.ApplyAffliction(null, new Affliction(AfflictionPrefab.Pressure, AfflictionPrefab.Pressure.MaxStrength));
|
||||
if (isNetworkMessage && GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient && Vitality <= CharacterHealth.MinVitality) { Kill(CauseOfDeathType.Pressure, null, isNetworkMessage: true); }
|
||||
if (IsDead)
|
||||
{
|
||||
BreakJoints();
|
||||
}
|
||||
}
|
||||
|
||||
public void BreakJoints()
|
||||
@@ -2841,6 +2985,7 @@ namespace Barotrauma
|
||||
Vector2 centerOfMass = AnimController.GetCenterOfMass();
|
||||
foreach (Limb limb in AnimController.Limbs)
|
||||
{
|
||||
if (limb.IsSevered) { continue; }
|
||||
limb.AddDamage(limb.SimPosition, 500.0f, 0.0f, 0.0f, false);
|
||||
|
||||
Vector2 diff = centerOfMass - limb.SimPosition;
|
||||
@@ -2861,7 +3006,10 @@ namespace Barotrauma
|
||||
|
||||
foreach (var joint in AnimController.LimbJoints)
|
||||
{
|
||||
joint.LimitEnabled = false;
|
||||
if (joint.revoluteJoint != null)
|
||||
{
|
||||
joint.revoluteJoint.LimitEnabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2928,9 +3076,12 @@ namespace Barotrauma
|
||||
|
||||
AnimController.ResetPullJoints();
|
||||
|
||||
foreach (RevoluteJoint joint in AnimController.LimbJoints)
|
||||
foreach (var joint in AnimController.LimbJoints)
|
||||
{
|
||||
joint.MotorEnabled = false;
|
||||
if (joint.revoluteJoint != null)
|
||||
{
|
||||
joint.revoluteJoint.MotorEnabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (GameMain.GameSession != null)
|
||||
@@ -2961,7 +3112,11 @@ namespace Barotrauma
|
||||
|
||||
foreach (LimbJoint joint in AnimController.LimbJoints)
|
||||
{
|
||||
joint.MotorEnabled = true;
|
||||
var revoluteJoint = joint.revoluteJoint;
|
||||
if (revoluteJoint != null)
|
||||
{
|
||||
revoluteJoint.MotorEnabled = true;
|
||||
}
|
||||
joint.Enabled = true;
|
||||
joint.IsSevered = false;
|
||||
}
|
||||
@@ -3176,5 +3331,15 @@ namespace Barotrauma
|
||||
}
|
||||
return targetPos;
|
||||
}
|
||||
|
||||
public bool IsCaptain => HasJob("captain");
|
||||
public bool IsEngineer => HasJob("engineer");
|
||||
public bool IsMechanic => HasJob("mechanic");
|
||||
public bool IsMedic => HasJob("medicaldoctor");
|
||||
public bool IsOfficer => HasJob("securityofficer");
|
||||
public bool IsAsssitant => HasJob("assistant");
|
||||
public bool IsWatchman => HasJob("watchman");
|
||||
|
||||
public bool HasJob(string identifier) => Info?.Job?.Prefab.Identifier == identifier;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user