Unstable 0.17.3.0

This commit is contained in:
Juan Pablo Arce
2022-03-22 14:44:12 -03:00
parent cefac171f5
commit 4206f6db42
100 changed files with 1380 additions and 655 deletions
@@ -56,7 +56,7 @@ namespace Barotrauma
private readonly float updateTargetsInterval = 1;
private readonly float updateMemoriesInverval = 1;
private readonly float attackLimbResetInterval = 2;
private readonly float attackLimbSelectionInterval = 3;
// Min priority for the memorized targets. The actual value fades gradually, unless kept fresh by selecting the target.
private const float minPriority = 10;
@@ -65,10 +65,10 @@ namespace Barotrauma
private float updateTargetsTimer;
private float updateMemoriesTimer;
private float attackLimbResetTimer;
private float attackLimbSelectionTimer;
private bool IsAttackRunning => AttackingLimb != null && AttackingLimb.attack.IsRunning;
private bool IsCoolDownRunning => AttackingLimb != null && AttackingLimb.attack.CoolDownTimer > 0 || _previousAttackingLimb != null && _previousAttackingLimb.attack.CoolDownTimer > 0;
private bool IsAttackRunning => AttackLimb != null && AttackLimb.attack.IsRunning;
private bool IsCoolDownRunning => AttackLimb != null && AttackLimb.attack.CoolDownTimer > 0 || _previousAttackLimb != null && _previousAttackLimb.attack.CoolDownTimer > 0;
public float CombatStrength => AIParams.CombatStrength;
private float Sight => AIParams.Sight;
private float Hearing => AIParams.Hearing;
@@ -77,25 +77,25 @@ namespace Barotrauma
private FishAnimController FishAnimController => Character.AnimController as FishAnimController;
private Limb _attackingLimb;
private Limb _previousAttackingLimb;
public Limb AttackingLimb
private Limb _attackLimb;
private Limb _previousAttackLimb;
public Limb AttackLimb
{
get { return _attackingLimb; }
get { return _attackLimb; }
private set
{
attackLimbResetTimer = 0;
if (_attackingLimb != value)
if (_attackLimb != value)
{
_previousAttackingLimb = _attackingLimb;
_previousAttackLimb = _attackLimb;
_previousAttackLimb?.AttachedRope?.Snap();
}
if (_attackingLimb != null && value != _attackingLimb && _attackingLimb.attack.CoolDownTimer > 0)
else if (_attackLimb != null && _attackLimb.attack.CoolDownTimer <= 0)
{
SetAimTimer();
_attackLimb.AttachedRope?.Snap();
}
_attackingLimb = value;
_attackLimb = value;
attackVector = null;
Reverse = _attackingLimb != null && _attackingLimb.attack.Reverse;
Reverse = _attackLimb != null && _attackLimb.attack.Reverse;
}
}
@@ -425,7 +425,8 @@ namespace Barotrauma
private void ReleaseDragTargets()
{
if (Character.Inventory != null)
AttackLimb?.AttachedRope?.Snap();
if (Character.Params.CanInteract && Character.Inventory != null)
{
Character.HeldItems.ForEach(i => i.GetComponent<Holdable>()?.GetRope()?.Snap());
}
@@ -600,7 +601,7 @@ namespace Barotrauma
UpdatePatrol(deltaTime);
break;
case AIState.Attack:
run = !IsCoolDownRunning || AttackingLimb != null && AttackingLimb.attack.FullSpeedAfterAttack;
run = !IsCoolDownRunning || AttackLimb != null && AttackLimb.attack.FullSpeedAfterAttack;
UpdateAttack(deltaTime);
break;
case AIState.Eat:
@@ -620,7 +621,7 @@ namespace Barotrauma
return;
}
float squaredDistance = Vector2.DistanceSquared(WorldPosition, SelectedAiTarget.WorldPosition);
var attackLimb = AttackingLimb ?? GetAttackLimb(SelectedAiTarget.WorldPosition);
var attackLimb = AttackLimb ?? GetAttackLimb(SelectedAiTarget.WorldPosition);
if (attackLimb != null && squaredDistance <= Math.Pow(attackLimb.attack.Range, 2))
{
run = true;
@@ -875,7 +876,10 @@ namespace Barotrauma
if (followLastTarget)
{
var target = SelectedAiTarget ?? _lastAiTarget;
if (target?.Entity != null && !target.Entity.Removed && PreviousState == AIState.Attack && Character.CurrentHull == null)
if (target?.Entity != null && !target.Entity.Removed &&
PreviousState == AIState.Attack && Character.CurrentHull == null &&
(_previousAttackLimb?.attack == null ||
_previousAttackLimb?.attack is Attack previousAttack && (previousAttack.AfterAttack != AIBehaviorAfterAttack.FallBack || previousAttack.CoolDownTimer <= 0)))
{
// Keep heading to the last known position of the target
var memory = GetTargetMemory(target, false);
@@ -1126,31 +1130,42 @@ namespace Barotrauma
return;
}
}
attackLimbSelectionTimer -= deltaTime;
if (AttackLimb == null || attackLimbSelectionTimer <= 0)
{
attackLimbSelectionTimer = attackLimbSelectionInterval * Rand.Range(0.9f, 1.1f);
if (!IsAttackRunning && !IsCoolDownRunning)
{
AttackLimb = GetAttackLimb(attackWorldPos);
}
}
bool canAttack = true;
bool pursue = false;
if (IsCoolDownRunning)
if (IsCoolDownRunning && (_previousAttackLimb == null || AttackLimb == null || AttackLimb.attack.CoolDownTimer > 0))
{
var currentAttackLimb = AttackingLimb ?? _previousAttackingLimb;
var currentAttackLimb = AttackLimb ?? _previousAttackLimb;
if (currentAttackLimb.attack.CoolDownTimer >= currentAttackLimb.attack.CoolDown + currentAttackLimb.attack.CurrentRandomCoolDown - currentAttackLimb.attack.AfterAttackDelay)
{
return;
}
switch (currentAttackLimb.attack.AfterAttack)
AIBehaviorAfterAttack activeBehavior = currentAttackLimb.attack.AfterAttack;
switch (activeBehavior)
{
case AIBehaviorAfterAttack.Pursue:
case AIBehaviorAfterAttack.PursueIfCanAttack:
if (currentAttackLimb.attack.SecondaryCoolDown <= 0)
{
// No (valid) secondary cooldown defined.
if (currentAttackLimb.attack.AfterAttack == AIBehaviorAfterAttack.Pursue)
if (activeBehavior == AIBehaviorAfterAttack.Pursue)
{
canAttack = false;
pursue = true;
}
else
{
UpdateFallBack(attackWorldPos, deltaTime, true);
UpdateFallBack(attackWorldPos, deltaTime, followThrough: true);
return;
}
}
@@ -1162,13 +1177,13 @@ namespace Barotrauma
if (_previousAiTarget != null && SelectedAiTarget != _previousAiTarget)
{
canAttack = false;
if (currentAttackLimb.attack.AfterAttack == AIBehaviorAfterAttack.PursueIfCanAttack)
if (activeBehavior == AIBehaviorAfterAttack.PursueIfCanAttack)
{
// Fall back if cannot attack.
UpdateFallBack(attackWorldPos, deltaTime, true);
UpdateFallBack(attackWorldPos, deltaTime, followThrough: true);
return;
}
AttackingLimb = null;
AttackLimb = null;
}
else
{
@@ -1177,19 +1192,19 @@ namespace Barotrauma
if (newLimb != null)
{
// Attack with the new limb
AttackingLimb = newLimb;
AttackLimb = newLimb;
}
else
{
// No new limb was found.
if (currentAttackLimb.attack.AfterAttack == AIBehaviorAfterAttack.Pursue)
if (activeBehavior == AIBehaviorAfterAttack.Pursue)
{
canAttack = false;
pursue = true;
}
else
{
UpdateFallBack(attackWorldPos, deltaTime, true);
UpdateFallBack(attackWorldPos, deltaTime, followThrough: true);
return;
}
}
@@ -1204,10 +1219,15 @@ namespace Barotrauma
break;
case AIBehaviorAfterAttack.FallBackUntilCanAttack:
case AIBehaviorAfterAttack.FollowThroughUntilCanAttack:
case AIBehaviorAfterAttack.ReverseUntilCanAttack:
if (activeBehavior == AIBehaviorAfterAttack.ReverseUntilCanAttack)
{
Reverse = true;
}
if (currentAttackLimb.attack.SecondaryCoolDown <= 0)
{
// No (valid) secondary cooldown defined.
UpdateFallBack(attackWorldPos, deltaTime, currentAttackLimb.attack.AfterAttack == AIBehaviorAfterAttack.FollowThroughUntilCanAttack);
UpdateFallBack(attackWorldPos, deltaTime, activeBehavior == AIBehaviorAfterAttack.FollowThroughUntilCanAttack);
return;
}
else
@@ -1217,7 +1237,7 @@ namespace Barotrauma
// Don't allow attacking when the attack target has just changed.
if (_previousAiTarget != null && SelectedAiTarget != _previousAiTarget)
{
UpdateFallBack(attackWorldPos, deltaTime, currentAttackLimb.attack.AfterAttack == AIBehaviorAfterAttack.FollowThroughUntilCanAttack);
UpdateFallBack(attackWorldPos, deltaTime, activeBehavior == AIBehaviorAfterAttack.FollowThroughUntilCanAttack);
return;
}
else
@@ -1227,12 +1247,12 @@ namespace Barotrauma
if (newLimb != null)
{
// Attack with the new limb
AttackingLimb = newLimb;
AttackLimb = newLimb;
}
else
{
// No new limb was found.
UpdateFallBack(attackWorldPos, deltaTime, currentAttackLimb.attack.AfterAttack == AIBehaviorAfterAttack.FollowThroughUntilCanAttack);
UpdateFallBack(attackWorldPos, deltaTime, activeBehavior == AIBehaviorAfterAttack.FollowThroughUntilCanAttack);
return;
}
}
@@ -1240,7 +1260,7 @@ namespace Barotrauma
else
{
// Cooldown not yet expired -> steer away from the target
UpdateFallBack(attackWorldPos, deltaTime, currentAttackLimb.attack.AfterAttack == AIBehaviorAfterAttack.FollowThroughUntilCanAttack);
UpdateFallBack(attackWorldPos, deltaTime, activeBehavior == AIBehaviorAfterAttack.FollowThroughUntilCanAttack);
return;
}
}
@@ -1269,7 +1289,7 @@ namespace Barotrauma
if (newLimb != null)
{
// Attack with the new limb
AttackingLimb = newLimb;
AttackLimb = newLimb;
}
else
{
@@ -1291,7 +1311,12 @@ namespace Barotrauma
UpdateFallBack(attackWorldPos, deltaTime, followThrough: true);
return;
case AIBehaviorAfterAttack.FallBack:
case AIBehaviorAfterAttack.Reverse:
default:
if (activeBehavior == AIBehaviorAfterAttack.Reverse)
{
Reverse = true;
}
UpdateFallBack(attackWorldPos, deltaTime, followThrough: false);
return;
}
@@ -1303,12 +1328,13 @@ namespace Barotrauma
if (canAttack)
{
if (AttackingLimb == null || !IsValidAttack(AttackingLimb, Character.GetAttackContexts(), SelectedAiTarget?.Entity as IDamageable))
if (AttackLimb == null || !IsValidAttack(AttackLimb, Character.GetAttackContexts(), SelectedAiTarget?.Entity))
{
AttackingLimb = GetAttackLimb(attackWorldPos);
AttackLimb = GetAttackLimb(attackWorldPos);
}
canAttack = AttackingLimb != null && AttackingLimb.attack.CoolDownTimer <= 0;
canAttack = AttackLimb != null && AttackLimb.attack.CoolDownTimer <= 0;
}
if (!AIParams.CanOpenDoors)
{
if (!Character.AnimController.SimplePhysicsEnabled && SelectedAiTarget.Entity.Submarine != null && Character.Submarine == null && (!canAttackDoors || !canAttackWalls || !AIParams.TargetOuterWalls))
@@ -1347,8 +1373,8 @@ namespace Barotrauma
// Target a specific limb instead of the target center position
if (wallTarget == null && targetCharacter != null)
{
var targetLimbType = AttackingLimb.Params.Attack.Attack.TargetLimbType;
attackTargetLimb = GetTargetLimb(AttackingLimb, targetCharacter, targetLimbType);
var targetLimbType = AttackLimb.Params.Attack.Attack.TargetLimbType;
attackTargetLimb = GetTargetLimb(AttackLimb, targetCharacter, targetLimbType);
if (attackTargetLimb == null)
{
State = AIState.Idle;
@@ -1361,7 +1387,7 @@ namespace Barotrauma
}
}
Vector2 attackLimbPos = Character.AnimController.SimplePhysicsEnabled ? Character.WorldPosition : AttackingLimb.WorldPosition;
Vector2 attackLimbPos = Character.AnimController.SimplePhysicsEnabled ? Character.WorldPosition : AttackLimb.WorldPosition;
Vector2 toTarget = attackWorldPos - attackLimbPos;
// Add a margin when the target is moving away, because otherwise it might be difficult to reach it if the attack takes some time to execute
if (wallTarget != null && Character.Submarine == null)
@@ -1389,23 +1415,23 @@ namespace Barotrauma
Vector2 CalculateMargin(Vector2 targetVelocity)
{
if (targetVelocity == Vector2.Zero) { return Vector2.Zero; }
float diff = AttackingLimb.attack.Range - AttackingLimb.attack.DamageRange;
if (diff <= 0 || toTarget.LengthSquared() <= MathUtils.Pow2(AttackingLimb.attack.DamageRange)) { return Vector2.Zero; }
float diff = AttackLimb.attack.Range - AttackLimb.attack.DamageRange;
if (diff <= 0 || toTarget.LengthSquared() <= MathUtils.Pow2(AttackLimb.attack.DamageRange)) { return Vector2.Zero; }
float dot = Vector2.Dot(Vector2.Normalize(targetVelocity), Vector2.Normalize(Character.AnimController.Collider.LinearVelocity));
if (dot <= 0 || !MathUtils.IsValid(dot)) { return Vector2.Zero; }
float distanceOffset = diff * AttackingLimb.attack.Duration;
float distanceOffset = diff * AttackLimb.attack.Duration;
// Intentionally omit the unit conversion because we use distanceOffset as a multiplier.
return targetVelocity * distanceOffset * dot;
}
// Check that we can reach the target
distance = toTarget.Length();
canAttack = distance < AttackingLimb.attack.Range;
canAttack = distance < AttackLimb.attack.Range;
// Crouch if the target is down (only humanoids), so that we can reach it.
if (Character.AnimController is HumanoidAnimController humanoidAnimController && distance < AttackingLimb.attack.Range * 2)
if (Character.AnimController is HumanoidAnimController humanoidAnimController && distance < AttackLimb.attack.Range * 2)
{
if (Math.Abs(toTarget.Y) > AttackingLimb.attack.Range / 2 && Math.Abs(toTarget.X) <= AttackingLimb.attack.Range)
if (Math.Abs(toTarget.Y) > AttackLimb.attack.Range / 2 && Math.Abs(toTarget.X) <= AttackLimb.attack.Range)
{
humanoidAnimController.Crouching = true;
}
@@ -1413,14 +1439,14 @@ namespace Barotrauma
if (canAttack)
{
if (AttackingLimb.attack.Ranged)
if (AttackLimb.attack.Ranged)
{
// Check that is facing the target
float offset = AttackingLimb.Params.GetSpriteOrientation() - MathHelper.PiOver2;
Vector2 forward = VectorExtensions.Forward(AttackingLimb.body.TransformedRotation - offset * Character.AnimController.Dir);
float offset = AttackLimb.Params.GetSpriteOrientation() - MathHelper.PiOver2;
Vector2 forward = VectorExtensions.Forward(AttackLimb.body.TransformedRotation - offset * Character.AnimController.Dir);
float angle = VectorExtensions.Angle(forward, toTarget);
canAttack = angle < MathHelper.ToRadians(AttackingLimb.attack.RequiredAngle);
if (canAttack && AttackingLimb.attack.AvoidFriendlyFire)
canAttack = angle < MathHelper.ToRadians(AttackLimb.attack.RequiredAngle);
if (canAttack && AttackLimb.attack.AvoidFriendlyFire)
{
float minDistance = MathUtils.Pow(ConvertUnits.ToDisplayUnits(Character.AnimController.Collider.GetMaxExtent() * 3), 2);
bool IsFarEnough(Character other) => Vector2.DistanceSquared(Character.WorldPosition, other.WorldPosition) > minDistance;
@@ -1434,11 +1460,11 @@ namespace Barotrauma
}
if (canAttack)
{
canAttack = !IsBlocked(attackSimPos) && !IsBlocked(AttackingLimb.SimPosition + forward * ConvertUnits.ToSimUnits(AttackingLimb.attack.Range));
canAttack = !IsBlocked(attackSimPos) && !IsBlocked(AttackLimb.SimPosition + forward * ConvertUnits.ToSimUnits(AttackLimb.attack.Range));
bool IsBlocked(Vector2 targetPosition)
{
foreach (var body in Submarine.PickBodies(AttackingLimb.SimPosition, targetPosition, myBodies, Physics.CollisionCharacter))
foreach (var body in Submarine.PickBodies(AttackLimb.SimPosition, targetPosition, myBodies, Physics.CollisionCharacter))
{
Character hitTarget = null;
if (body.UserData is Character c)
@@ -1460,22 +1486,8 @@ namespace Barotrauma
}
}
}
else if (!IsAttackRunning && !IsCoolDownRunning)
{
// If not, reset the attacking limb, if the cooldown is not running
// Don't use the property, because we don't want cancel reversing, if we are reversing.
if (attackLimbResetTimer > attackLimbResetInterval)
{
_attackingLimb = null;
attackLimbResetTimer = 0;
}
else
{
attackLimbResetTimer += deltaTime;
}
}
}
Limb steeringLimb = canAttack && !AttackingLimb.attack.Ranged ? AttackingLimb : null;
Limb steeringLimb = canAttack && !AttackLimb.attack.Ranged ? AttackLimb : null;
if (steeringLimb == null)
{
// If the attacking limb is a hand or claw, for example, using it as the steering limb can end in the result where the character circles around the target.
@@ -1490,9 +1502,9 @@ namespace Barotrauma
var pathSteering = SteeringManager as IndoorsSteeringManager;
if (AttackingLimb != null && AttackingLimb.attack.Retreat)
if (AttackLimb != null && AttackLimb.attack.Retreat)
{
UpdateFallBack(attackWorldPos, deltaTime, false);
UpdateFallBack(attackWorldPos, deltaTime, followThrough: false);
}
else
{
@@ -1527,7 +1539,7 @@ namespace Barotrauma
}
// When pursuing, we don't want to pursue too close
float max = 300;
float margin = AttackingLimb != null ? Math.Min(AttackingLimb.attack.Range * 0.9f, max) : max;
float margin = AttackLimb != null ? Math.Min(AttackLimb.attack.Range * 0.9f, max) : max;
if (!canAttack || distance > margin)
{
// Steer towards the target if in the same room and swimming
@@ -1558,10 +1570,10 @@ namespace Barotrauma
}
else
{
if (AttackingLimb.attack.Ranged)
if (AttackLimb.attack.Ranged)
{
float dir = Character.AnimController.Dir;
if (dir > 0 && attackWorldPos.X > AttackingLimb.WorldPosition.X + margin || dir < 0 && attackWorldPos.X < AttackingLimb.WorldPosition.X - margin)
if (dir > 0 && attackWorldPos.X > AttackLimb.WorldPosition.X + margin || dir < 0 && attackWorldPos.X < AttackLimb.WorldPosition.X - margin)
{
SteeringManager.Reset();
}
@@ -1658,9 +1670,9 @@ namespace Barotrauma
}
break;
case CirclePhase.CloseIn:
if (AttackingLimb != null && distance > 0 && distance < AttackingLimb.attack.Range * GetStrikeDistanceMultiplier(targetSub.Velocity))
if (AttackLimb != null && distance > 0 && distance < AttackLimb.attack.Range * GetStrikeDistanceMultiplier(targetSub.Velocity))
{
strikeTimer = AttackingLimb.attack.CoolDown;
strikeTimer = AttackLimb.attack.CoolDown;
CirclePhase = CirclePhase.Strike;
}
else if (!breakCircling && sqrDistToSub <= MathUtils.Pow2(subSize + selectedTargetingParams.CircleStartDistance / 2) && targetSub.Velocity.LengthSquared() <= MathUtils.Pow2(GetTargetMaxSpeed()))
@@ -1703,10 +1715,10 @@ namespace Barotrauma
// When the offset position is outside of the sub it happens that the creature sometimes reaches the target point,
// which makes it continue circling around the point (as supposed)
// But when there is some offset and the offset is too near, this is not what we want.
if (AttackingLimb != null && sqrDistToSub < MathUtils.Pow2(subSize + circleFallbackDistance))
if (AttackLimb != null && sqrDistToSub < MathUtils.Pow2(subSize + circleFallbackDistance))
{
CirclePhase = CirclePhase.Strike;
strikeTimer = AttackingLimb.attack.CoolDown;
strikeTimer = AttackLimb.attack.CoolDown;
}
else
{
@@ -1741,9 +1753,9 @@ namespace Barotrauma
}
}
}
if (AttackingLimb != null && distance > 0 && distance < AttackingLimb.attack.Range * requiredDistMultiplier && IsFacing(margin: MathHelper.Lerp(0.5f, 0.9f, currentAttackIntensity)))
if (AttackLimb != null && distance > 0 && distance < AttackLimb.attack.Range * requiredDistMultiplier && IsFacing(margin: MathHelper.Lerp(0.5f, 0.9f, currentAttackIntensity)))
{
strikeTimer = AttackingLimb.attack.CoolDown;
strikeTimer = AttackLimb.attack.CoolDown;
CirclePhase = CirclePhase.Strike;
}
canAttack = false;
@@ -1800,7 +1812,7 @@ namespace Barotrauma
}
}
if (!canAttack || distance > Math.Min(AttackingLimb.attack.Range * 0.9f, 100))
if (!canAttack || distance > Math.Min(AttackLimb.attack.Range * 0.9f, 100))
{
if (pathSteering != null)
{
@@ -1811,7 +1823,7 @@ namespace Barotrauma
SteeringManager.SteeringSeek(steerPos, 10);
}
}
else if (AttackingLimb.attack.Ranged)
else if (AttackLimb.attack.Ranged)
{
// Too close
UpdateFallBack(attackWorldPos, deltaTime, followThrough: false);
@@ -1824,18 +1836,18 @@ namespace Barotrauma
}
if (canAttack)
{
if (!UpdateLimbAttack(deltaTime, AttackingLimb, attackSimPos, distance, attackTargetLimb))
if (!UpdateLimbAttack(deltaTime, AttackLimb, attackSimPos, distance, attackTargetLimb))
{
IgnoreTarget(SelectedAiTarget);
}
}
else if (IsAttackRunning)
{
AttackingLimb.attack.ResetAttackTimer();
AttackLimb.attack.ResetAttackTimer();
}
}
private bool IsValidAttack(Limb attackingLimb, IEnumerable<AttackContext> currentContexts, IDamageable target)
private bool IsValidAttack(Limb attackingLimb, IEnumerable<AttackContext> currentContexts, Entity target)
{
if (attackingLimb == null) { return false; }
if (target == null) { return false; }
@@ -1854,10 +1866,11 @@ namespace Barotrauma
// Check that is approximately facing the target
Vector2 attackLimbPos = Character.AnimController.SimplePhysicsEnabled ? Character.WorldPosition : attackingLimb.WorldPosition;
Vector2 toTarget = attackWorldPos - attackLimbPos;
if (attack.MinRange > 0 && toTarget.LengthSquared() < MathUtils.Pow2(attack.MinRange)) { return false; }
float offset = attackingLimb.Params.GetSpriteOrientation() - MathHelper.PiOver2;
Vector2 forward = VectorExtensions.Forward(attackingLimb.body.TransformedRotation - offset * Character.AnimController.Dir);
float angle = VectorExtensions.Angle(forward, toTarget);
if (angle > MathHelper.ToRadians(attack.RequiredAngle)) { return false; }
float angle = MathHelper.ToDegrees(VectorExtensions.Angle(forward, toTarget));
if (angle > attack.RequiredAngle) { return false; }
}
return true;
}
@@ -1867,7 +1880,7 @@ namespace Barotrauma
private Limb GetAttackLimb(Vector2 attackWorldPos, Limb ignoredLimb = null)
{
var currentContexts = Character.GetAttackContexts();
IDamageable target = wallTarget != null ? wallTarget.Structure : SelectedAiTarget?.Entity as IDamageable;
Entity target = wallTarget != null ? wallTarget.Structure : SelectedAiTarget?.Entity;
if (target == null) { return null; }
Limb selectedLimb = null;
float currentPriority = -1;
@@ -1901,12 +1914,13 @@ namespace Barotrauma
float CalculatePriority(Limb limb, Vector2 attackPos)
{
if (Character.AnimController.SimplePhysicsEnabled) { return 1 + limb.attack.Priority; }
float prio = 1 + limb.attack.Priority;
if (Character.AnimController.SimplePhysicsEnabled) { return prio; }
float dist = Vector2.Distance(limb.WorldPosition, attackPos);
// The limb is ignored if the target is not close. Prevents character going in reverse if very far away from it.
// We also need a max value that is more than the actual range.
float distanceFactor = MathHelper.Lerp(1, 0, MathUtils.InverseLerp(0, limb.attack.Range * 3, dist));
return (1 + limb.attack.Priority) * distanceFactor;
return prio * distanceFactor;
}
}
@@ -1919,7 +1933,7 @@ namespace Barotrauma
Character.AnimController.ReleaseStuckLimbs();
LatchOntoAI?.DeattachFromBody(reset: true, cooldown: 1);
if (attacker == null || attacker.AiTarget == null || attacker.Removed || attacker.IsDead) { return; }
if (Character.Params.CanInteract && attackResult.Damage > 10)
if (attackResult.Damage >= AIParams.DamageThreshold)
{
ReleaseDragTargets();
}
@@ -2007,9 +2021,11 @@ namespace Barotrauma
if (State == AIState.Attack && (IsAttackRunning || IsCoolDownRunning))
{
// Don't retaliate or escape while performing an attack/under cooldown
retaliate = false;
avoidGunFire = false;
if (IsAttackRunning)
{
avoidGunFire = false;
}
}
if (retaliate)
{
@@ -2022,7 +2038,7 @@ namespace Barotrauma
}
}
}
else if (avoidGunFire)
else if (avoidGunFire && attackResult.Damage >= AIParams.DamageThreshold)
{
State = AIState.Escape;
avoidTimer = AIParams.AvoidTime * Rand.Range(0.75f, 1.25f);
@@ -2114,7 +2130,7 @@ namespace Barotrauma
{
if (attackingLimb.attack.CoolDownTimer > 0)
{
SetAimTimer();
SetAimTimer(Math.Min(attackingLimb.attack.CoolDown, 1.5f));
// Managed to hit a living/non-destroyed target. Increase the priority more if the target is low in health -> dies easily/soon
float greed = AIParams.AggressionGreed;
if (!(damageTarget is Character))
@@ -2245,19 +2261,19 @@ namespace Barotrauma
// TODO: test adding some random variance here?
attackVector = attackWorldPos - WorldPosition;
}
Vector2 attackDir = Vector2.Normalize(followThrough ? attackVector.Value : -attackVector.Value);
if (!MathUtils.IsValid(attackDir))
Vector2 dir = Vector2.Normalize(followThrough ? attackVector.Value : -attackVector.Value);
if (!MathUtils.IsValid(dir))
{
attackDir = Vector2.UnitY;
dir = Vector2.UnitY;
}
steeringManager.SteeringManual(deltaTime, attackDir);
if (Character.AnimController.InWater)
steeringManager.SteeringManual(deltaTime, dir);
if (Character.AnimController.InWater && !Reverse)
{
SteeringManager.SteeringAvoid(deltaTime, lookAheadDistance: avoidLookAheadDistance, weight: 15);
}
if (checkBlocking)
{
return !IsBlocked(deltaTime, SimPosition + attackDir * (avoidLookAheadDistance / 2));
return !IsBlocked(deltaTime, SimPosition + dir * (avoidLookAheadDistance / 2));
}
return true;
}
@@ -2990,7 +3006,7 @@ namespace Barotrauma
if (HasValidPath(requireNonDirty: true)) { return; }
wallHits.Clear();
Structure wall = null;
Vector2 rayStart = AttackingLimb != null ? AttackingLimb.SimPosition : SimPosition;
Vector2 rayStart = AttackLimb != null ? AttackLimb.SimPosition : SimPosition;
if (AIParams.WallTargetingMethod.HasFlag(WallTargetingMethod.Target))
{
Vector2 rayEnd = SelectedAiTarget.SimPosition;
@@ -3303,6 +3319,7 @@ namespace Barotrauma
foreach (var triggerObject in activeTriggers)
{
AITrigger trigger = triggerObject.Key;
if (trigger.IsPermanent) { continue; }
trigger.UpdateTimer(deltaTime);
if (!trigger.IsActive)
{
@@ -3471,7 +3488,7 @@ namespace Barotrauma
disableTailCoroutine = null;
}
Character.AnimController.ReleaseStuckLimbs();
AttackingLimb = null;
AttackLimb = null;
movementMargin = 0;
ResetEscape();
if (isStateChanged && to == AIState.Idle && from != to)
@@ -148,14 +148,14 @@ namespace Barotrauma
}
if (TargetCharacter != null)
{
if (enemyAI.AttackingLimb?.attack == null)
if (enemyAI.AttackLimb?.attack == null)
{
DeattachFromBody(reset: true, cooldown: 1);
}
else
{
float range = enemyAI.AttackingLimb.attack.DamageRange * 2f;
if (Vector2.DistanceSquared(TargetCharacter.WorldPosition, enemyAI.AttackingLimb.WorldPosition) > range * range)
float range = enemyAI.AttackLimb.attack.DamageRange * 2f;
if (Vector2.DistanceSquared(TargetCharacter.WorldPosition, enemyAI.AttackLimb.WorldPosition) > range * range)
{
DeattachFromBody(reset: true, cooldown: 1);
}
@@ -265,11 +265,11 @@ namespace Barotrauma
if (enemyAI.IsSteeringThroughGap) { break; }
if (_attachPos == Vector2.Zero) { break; }
if (!AttachToSub && !AttachToCharacters) { break; }
if (enemyAI.AttackingLimb == null) { break; }
if (enemyAI.AttackLimb == null) { break; }
if (targetBody == null) { break; }
if (IsAttached && AttachJoints[0].BodyB == targetBody) { break; }
Vector2 referencePos = TargetCharacter != null ? TargetCharacter.WorldPosition : ConvertUnits.ToDisplayUnits(transformedAttachPos);
if (Vector2.DistanceSquared(referencePos, enemyAI.AttackingLimb.WorldPosition) < enemyAI.AttackingLimb.attack.DamageRange * enemyAI.AttackingLimb.attack.DamageRange)
if (Vector2.DistanceSquared(referencePos, enemyAI.AttackLimb.WorldPosition) < enemyAI.AttackLimb.attack.DamageRange * enemyAI.AttackLimb.attack.DamageRange)
{
AttachToBody(transformedAttachPos);
}