Unstable 0.16.3.0
This commit is contained in:
@@ -207,8 +207,10 @@ namespace Barotrauma
|
||||
} = new HashSet<Submarine>();
|
||||
|
||||
public bool IsTargetingPlayerTeam => IsTargetInPlayerTeam(SelectedAiTarget);
|
||||
public bool IsBeingChasedBy(Character c) => c.AIController is EnemyAIController enemyAI && enemyAI.SelectedAiTarget?.Entity is Character && (enemyAI.State == AIState.Aggressive || enemyAI.State == AIState.Attack);
|
||||
private bool IsBeingChased => SelectedAiTarget?.Entity is Character targetCharacter && IsBeingChasedBy(targetCharacter);
|
||||
public static bool IsTargetBeingChasedBy(Character target, Character character)
|
||||
=> character?.AIController is EnemyAIController enemyAI && enemyAI.SelectedAiTarget?.Entity == target && (enemyAI.State == AIState.Attack || enemyAI.State == AIState.Aggressive);
|
||||
public bool IsBeingChasedBy(Character c) => IsTargetBeingChasedBy(Character, c);
|
||||
private bool IsBeingChased => IsBeingChasedBy(SelectedAiTarget?.Entity as Character);
|
||||
|
||||
private bool IsTargetInPlayerTeam(AITarget target) => target?.Entity?.Submarine != null && target.Entity.Submarine.Info.IsPlayer || target?.Entity is Character targetCharacter && targetCharacter.IsOnPlayerTeam;
|
||||
|
||||
@@ -1322,7 +1324,7 @@ namespace Barotrauma
|
||||
Vector2 rayEnd = rayStart + dir.ClampLength(Character.AnimController.Collider.GetLocalFront().Length() * 2);
|
||||
Body closestBody = Submarine.CheckVisibility(rayStart, rayEnd, ignoreSubs: true);
|
||||
if (Submarine.LastPickedFraction != 1.0f && closestBody != null &&
|
||||
(!AIParams.TargetOuterWalls || !canAttackWalls && closestBody.UserData is Structure s && s.Submarine != null || !canAttackDoors && closestBody.UserData is Item i && i.Submarine != null && i.GetComponent<Door>() != null))
|
||||
((!AIParams.TargetOuterWalls || !canAttackWalls) && closestBody.UserData is Structure s && s.Submarine != null || !canAttackDoors && closestBody.UserData is Item i && i.Submarine != null && i.GetComponent<Door>() != null))
|
||||
{
|
||||
// Target is unreachable, there's a door or wall ahead
|
||||
State = AIState.Idle;
|
||||
@@ -2385,6 +2387,24 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
#region Targeting
|
||||
public static bool IsLatchedTo(Character target, Character character)
|
||||
{
|
||||
if (target.AIController is EnemyAIController enemyAI && enemyAI.LatchOntoAI != null)
|
||||
{
|
||||
return enemyAI.LatchOntoAI.IsAttached && enemyAI.LatchOntoAI.TargetCharacter == character;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool IsLatchedToSomeoneElse(Character target, Character character)
|
||||
{
|
||||
if (target.AIController is EnemyAIController enemyAI && enemyAI.LatchOntoAI != null)
|
||||
{
|
||||
return enemyAI.LatchOntoAI.IsAttached && enemyAI.LatchOntoAI.TargetCharacter != null && enemyAI.LatchOntoAI.TargetCharacter != character;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool IsLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub;
|
||||
|
||||
//goes through all the AItargets, evaluates how preferable it is to attack the target,
|
||||
@@ -2398,6 +2418,7 @@ namespace Barotrauma
|
||||
targetingParams = null;
|
||||
bool isAnyTargetClose = false;
|
||||
bool isBeingChased = IsBeingChased;
|
||||
float maxModifier = 5;
|
||||
foreach (AITarget aiTarget in AITarget.List)
|
||||
{
|
||||
if (aiTarget.InDetectable) { continue; }
|
||||
@@ -2537,12 +2558,12 @@ namespace Barotrauma
|
||||
{
|
||||
if (CanPassThroughHole(s, i))
|
||||
{
|
||||
valueModifier *= leadsInside ? (IsAggressiveBoarder ? 3 : 1) : 0;
|
||||
valueModifier *= leadsInside ? (IsAggressiveBoarder ? maxModifier : 1) : 0;
|
||||
}
|
||||
else if (IsAggressiveBoarder && leadsInside && canAttackWalls && AIParams.TargetOuterWalls)
|
||||
else if (IsAggressiveBoarder && leadsInside && canAttackWalls)
|
||||
{
|
||||
// Up to 25% priority increase for every gap in the wall when an aggressive boarder is outside
|
||||
valueModifier *= 1 + section.gap.Open * 0.25f;
|
||||
// Up to 100% priority increase for every gap in the wall when an aggressive boarder is outside
|
||||
valueModifier *= 1 + section.gap.Open;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -2580,6 +2601,7 @@ namespace Barotrauma
|
||||
// We are actually interested in breaking things -> reduce the priority when the wall is already broken
|
||||
// (Terminalcells)
|
||||
valueModifier *= 1 - section.gap.Open * 0.25f;
|
||||
valueModifier = Math.Max(valueModifier, 0.1f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2599,6 +2621,7 @@ namespace Barotrauma
|
||||
valueModifier *= 1 + section.gap.Open;
|
||||
}
|
||||
}
|
||||
valueModifier = Math.Clamp(valueModifier, 0, maxModifier);
|
||||
}
|
||||
}
|
||||
if (door != null)
|
||||
@@ -2610,7 +2633,7 @@ namespace Barotrauma
|
||||
bool isOpen = door.CanBeTraversed;
|
||||
if (!isOpen)
|
||||
{
|
||||
if (!canAttackDoors || isOutdoor && !AIParams.TargetOuterWalls) { continue; }
|
||||
if (!canAttackDoors) { continue; }
|
||||
}
|
||||
else if (!Character.AnimController.CanEnterSubmarine)
|
||||
{
|
||||
@@ -2624,11 +2647,11 @@ namespace Barotrauma
|
||||
// Increase the priority if the character is outside and the door is from outside to inside
|
||||
if (door.CanBeTraversed)
|
||||
{
|
||||
valueModifier = 3;
|
||||
valueModifier = maxModifier;
|
||||
}
|
||||
else if (door.LinkedGap != null)
|
||||
{
|
||||
valueModifier = 1 + door.LinkedGap.Open;
|
||||
valueModifier = 1 + door.LinkedGap.Open * (maxModifier - 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -2727,6 +2750,37 @@ namespace Barotrauma
|
||||
|
||||
if (SelectedAiTarget == aiTarget)
|
||||
{
|
||||
if (Character.Submarine == null && aiTarget.Entity is ISpatialEntity spatialEntity && spatialEntity.Submarine != null)
|
||||
{
|
||||
if (targetingTag == "door" || targetingTag == "wall")
|
||||
{
|
||||
Vector2 rayStart = Character.SimPosition;
|
||||
Vector2 rayEnd = aiTarget.SimPosition + spatialEntity.Submarine.SimPosition;
|
||||
Body closestBody = Submarine.PickBody(rayStart, rayEnd, collisionCategory: Physics.CollisionWall | Physics.CollisionLevel, allowInsideFixture: true);
|
||||
if (closestBody != null && closestBody.UserData is ISpatialEntity hit)
|
||||
{
|
||||
Vector2 hitPos = hit.SimPosition;
|
||||
if (closestBody.UserData is Submarine)
|
||||
{
|
||||
hitPos = Submarine.LastPickedPosition;
|
||||
}
|
||||
else if (hit.Submarine != null)
|
||||
{
|
||||
hitPos += hit.Submarine.SimPosition;
|
||||
}
|
||||
float subHalfWidth = spatialEntity.Submarine.Borders.Width / 2;
|
||||
float subHalfHeight = spatialEntity.Submarine.Borders.Height / 2;
|
||||
Vector2 diff = ConvertUnits.ToDisplayUnits(rayEnd - hitPos);
|
||||
bool isOtherSideOfTheSub = Math.Abs(diff.X) > subHalfWidth || Math.Abs(diff.Y) > subHalfHeight;
|
||||
if (isOtherSideOfTheSub)
|
||||
{
|
||||
IgnoreTarget(aiTarget);
|
||||
ResetAITarget();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Stick to the current target
|
||||
valueModifier *= 1.1f;
|
||||
}
|
||||
@@ -2757,19 +2811,22 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if (targetParams.AttackPattern == AttackPattern.Circle)
|
||||
if (Character.Submarine == null && aiTarget.Entity?.Submarine != null && targetCharacter == null)
|
||||
{
|
||||
if (Character.Submarine == null && aiTarget.Entity?.Submarine != null && !isAnyTargetClose)
|
||||
if (targetParams.AttackPattern == AttackPattern.Circle || targetParams.AttackPattern == AttackPattern.Sweep)
|
||||
{
|
||||
if (Submarine.MainSubs.Contains(aiTarget.Entity.Submarine))
|
||||
if (!isAnyTargetClose)
|
||||
{
|
||||
// Prioritize targets that are near the horizontal center of the sub, but only when none of the targets is reachable.
|
||||
float horizontalDistanceToSubCenter = Math.Abs(aiTarget.WorldPosition.X - aiTarget.Entity.Submarine.WorldPosition.X);
|
||||
dist *= MathHelper.Lerp(1f, 5f, MathUtils.InverseLerp(0, 10000, horizontalDistanceToSubCenter));
|
||||
}
|
||||
else
|
||||
{
|
||||
dist *= 5;
|
||||
if (Submarine.MainSubs.Contains(aiTarget.Entity.Submarine))
|
||||
{
|
||||
// Prioritize targets that are near the horizontal center of the sub, but only when none of the targets is reachable.
|
||||
float horizontalDistanceToSubCenter = Math.Abs(aiTarget.WorldPosition.X - aiTarget.Entity.Submarine.WorldPosition.X);
|
||||
dist *= MathHelper.Lerp(1f, 5f, MathUtils.InverseLerp(0, 10000, horizontalDistanceToSubCenter));
|
||||
}
|
||||
else if (targetParams.AttackPattern == AttackPattern.Circle)
|
||||
{
|
||||
dist *= 5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2936,9 +2993,9 @@ namespace Barotrauma
|
||||
if (HasValidPath(requireNonDirty: true)) { return; }
|
||||
wallHits.Clear();
|
||||
Structure wall = null;
|
||||
Vector2 rayStart = AttackingLimb != null ? AttackingLimb.SimPosition : SimPosition;
|
||||
if (AIParams.WallTargetingMethod.HasFlag(WallTargetingMethod.Target))
|
||||
{
|
||||
Vector2 rayStart = SimPosition;
|
||||
Vector2 rayEnd = SelectedAiTarget.SimPosition;
|
||||
if (SelectedAiTarget.Entity.Submarine != null && Character.Submarine == null)
|
||||
{
|
||||
@@ -2952,7 +3009,6 @@ namespace Barotrauma
|
||||
}
|
||||
if (AIParams.WallTargetingMethod.HasFlag(WallTargetingMethod.Heading))
|
||||
{
|
||||
Vector2 rayStart = SimPosition;
|
||||
Vector2 rayEnd = rayStart + VectorExtensions.Forward(Character.AnimController.Collider.Rotation + MathHelper.PiOver2, avoidLookAheadDistance * 5);
|
||||
if (SelectedAiTarget.Entity.Submarine != null && Character.Submarine == null)
|
||||
{
|
||||
@@ -2968,7 +3024,6 @@ namespace Barotrauma
|
||||
}
|
||||
if (AIParams.WallTargetingMethod.HasFlag(WallTargetingMethod.Steering))
|
||||
{
|
||||
Vector2 rayStart = SimPosition;
|
||||
Vector2 rayEnd = rayStart + Steering * 5;
|
||||
if (SelectedAiTarget.Entity.Submarine != null && Character.Submarine == null)
|
||||
{
|
||||
@@ -3021,6 +3076,7 @@ namespace Barotrauma
|
||||
// Blocked by a wall that shouldn't be targeted. The main intention here is to prevent monsters from entering the the tail and the nose pieces.
|
||||
if (!isTargetingDoor)
|
||||
{
|
||||
IgnoreTarget(SelectedAiTarget);
|
||||
ResetAITarget();
|
||||
}
|
||||
}
|
||||
@@ -3032,6 +3088,7 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
// Blocked by a disabled wall.
|
||||
IgnoreTarget(SelectedAiTarget);
|
||||
ResetAITarget();
|
||||
}
|
||||
}
|
||||
@@ -3082,8 +3139,17 @@ namespace Barotrauma
|
||||
if (!(hit.UserData is Structure w)) { return false; }
|
||||
if (w.Submarine == null) { return false; }
|
||||
if (w.Submarine != SelectedAiTarget.Entity.Submarine) { return false; }
|
||||
if (Character.Submarine == null && w.prefab.Tags.Contains("inner")) { return false; }
|
||||
if (!AIParams.TargetOuterWalls && !w.prefab.Tags.Contains("inner")) { return false; }
|
||||
if (Character.Submarine == null)
|
||||
{
|
||||
if (w.prefab.Tags.Contains("inner"))
|
||||
{
|
||||
if (!Character.AnimController.CanEnterSubmarine) { return false; }
|
||||
}
|
||||
else if (!AIParams.TargetOuterWalls)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
wall = w;
|
||||
return true;
|
||||
}
|
||||
@@ -3120,7 +3186,8 @@ namespace Barotrauma
|
||||
{
|
||||
if (door.LinkedGap.Size > ConvertUnits.ToDisplayUnits(colliderWidth))
|
||||
{
|
||||
return SteerThroughGap(door.LinkedGap, door.LinkedGap.FlowTargetHull.WorldPosition, deltaTime, maxDistance: 100);
|
||||
float maxDistance = Math.Max(ConvertUnits.ToDisplayUnits(colliderLength), 100);
|
||||
return SteerThroughGap(door.LinkedGap, door.LinkedGap.FlowTargetHull.WorldPosition, deltaTime, maxDistance: maxDistance);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3584,12 +3651,12 @@ namespace Barotrauma
|
||||
|
||||
public override bool SteerThroughGap(Gap gap, Vector2 targetWorldPos, float deltaTime, float maxDistance = -1)
|
||||
{
|
||||
wallTarget = null;
|
||||
LatchOntoAI?.DeattachFromBody(reset: true, cooldown: 2);
|
||||
Character.AnimController.ReleaseStuckLimbs();
|
||||
bool success = base.SteerThroughGap(gap, targetWorldPos, deltaTime, maxDistance);
|
||||
if (success)
|
||||
{
|
||||
wallTarget = null;
|
||||
LatchOntoAI?.DeattachFromBody(reset: true, cooldown: 2);
|
||||
Character.AnimController.ReleaseStuckLimbs();
|
||||
SteeringManager.SteeringAvoid(deltaTime, avoidLookAheadDistance, weight: 1);
|
||||
}
|
||||
IsSteeringThroughGap = success;
|
||||
|
||||
@@ -20,7 +20,6 @@ namespace Barotrauma
|
||||
private float reactTimer;
|
||||
private float unreachableClearTimer;
|
||||
private bool shouldCrouch;
|
||||
public bool IsInsideCave { get; private set; }
|
||||
/// <summary>
|
||||
/// Resets each frame
|
||||
/// </summary>
|
||||
@@ -58,14 +57,14 @@ namespace Barotrauma
|
||||
private float obstacleRaycastTimer;
|
||||
|
||||
private readonly float enemyCheckInterval = 0.2f;
|
||||
private readonly float enemySpotDistanceOutside = 1500;
|
||||
private readonly float enemySpotDistanceOutside = 800;
|
||||
private readonly float enemySpotDistanceInside = 1000;
|
||||
private float enemycheckTimer;
|
||||
|
||||
/// <summary>
|
||||
/// How far other characters can hear reports done by this character (e.g. reports for fires, intruders). Defaults to infinity.
|
||||
/// How far other characters can hear reports done by this character (e.g. reports for fires, intruders).
|
||||
/// </summary>
|
||||
public float ReportRange { get; set; } = float.PositiveInfinity;
|
||||
public float ReportRange { get; set; }
|
||||
|
||||
private float _aimSpeed = 1;
|
||||
public float AimSpeed
|
||||
@@ -167,6 +166,7 @@ namespace Barotrauma
|
||||
objectiveManager = new AIObjectiveManager(c);
|
||||
reactTimer = GetReactionTime();
|
||||
SortTimer = Rand.Range(0f, sortObjectiveInterval);
|
||||
ReportRange = Character.IsOnPlayerTeam ? float.PositiveInfinity : 1000;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
@@ -306,7 +306,7 @@ namespace Barotrauma
|
||||
UseIndoorSteeringOutside = false;
|
||||
}
|
||||
|
||||
if (Character.Submarine == null || !IsOnFriendlyTeam(Character.TeamID, Character.Submarine.TeamID) && !Character.IsEscorted)
|
||||
if (Character.Submarine == null || Character.IsOnPlayerTeam && !Character.IsEscorted && !IsOnFriendlyTeam(Character.TeamID, Character.Submarine.TeamID))
|
||||
{
|
||||
// Spot enemies while staying outside or inside an enemy ship.
|
||||
// does not apply for escorted characters, such as prisoners or terrorists who have their own behavior
|
||||
@@ -327,9 +327,13 @@ namespace Barotrauma
|
||||
float dist = toTarget.LengthSquared();
|
||||
float maxDistance = Character.Submarine == null ? enemySpotDistanceOutside : enemySpotDistanceInside;
|
||||
if (dist > maxDistance * maxDistance) { continue; }
|
||||
Vector2 forward = VectorExtensions.Forward(Character.AnimController.Collider.Rotation);
|
||||
forward.X *= Character.AnimController.Dir;
|
||||
if (Vector2.Dot(toTarget, forward) < 0.2f) { continue; }
|
||||
if (EnemyAIController.IsLatchedToSomeoneElse(c, Character)) { continue; }
|
||||
var head = Character.AnimController.GetLimb(LimbType.Head);
|
||||
if (head == null) { continue; }
|
||||
float rotation = head.body.TransformedRotation;
|
||||
Vector2 forward = VectorExtensions.Forward(rotation);
|
||||
float angle = MathHelper.ToDegrees(VectorExtensions.Angle(toTarget, forward));
|
||||
if (angle > 70) { continue; }
|
||||
if (!Character.CanSeeCharacter(c)) { continue; }
|
||||
if (dist < closestDistance || closestEnemy == null)
|
||||
{
|
||||
@@ -344,8 +348,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IsInsideCave = Character.CurrentHull == null && Level.Loaded?.Caves.FirstOrDefault(c => c.Area.Contains(Character.WorldPosition)) is Level.Cave;
|
||||
|
||||
if (UseIndoorSteeringOutside || Character.CurrentHull?.Submarine != null || hasValidPath || IsCloseEnoughToTarget(steeringBuffer))
|
||||
{
|
||||
@@ -1242,7 +1244,7 @@ namespace Barotrauma
|
||||
{
|
||||
//if the other character did not witness the attack, and the character is not within report range (or capable of reporting)
|
||||
//don't react to the attack
|
||||
if (Character.IsDead || Character.IsUnconscious || !CheckReportRange(Character, otherCharacter, ReportRange))
|
||||
if (Character.IsDead || Character.IsUnconscious || otherCharacter.TeamID != Character.TeamID || !CheckReportRange(Character, otherCharacter, ReportRange))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -1259,8 +1261,8 @@ namespace Barotrauma
|
||||
{
|
||||
if (Character.Submarine == null)
|
||||
{
|
||||
// Outside -> don't react.
|
||||
return AIObjectiveCombat.CombatMode.None;
|
||||
// Outside
|
||||
return attacker.Submarine == null ? AIObjectiveCombat.CombatMode.Defensive : AIObjectiveCombat.CombatMode.Retreat;
|
||||
}
|
||||
if (!Character.Submarine.GetConnectedSubs().Contains(attacker.Submarine))
|
||||
{
|
||||
@@ -1852,7 +1854,7 @@ namespace Barotrauma
|
||||
bool ignoreFire = objectiveManager.CurrentOrder is AIObjectiveExtinguishFires extinguishOrder && extinguishOrder.Priority > 0 || objectiveManager.HasActiveObjective<AIObjectiveExtinguishFire>();
|
||||
bool ignoreWater = HasDivingSuit(character);
|
||||
bool ignoreOxygen = ignoreWater || HasDivingMask(character);
|
||||
bool ignoreEnemies = ObjectiveManager.IsCurrentOrder<AIObjectiveFightIntruders>() || ObjectiveManager.GetActiveObjectives<AIObjectiveFightIntruders>().Any();
|
||||
bool ignoreEnemies = ObjectiveManager.IsCurrentOrder<AIObjectiveFightIntruders>() || ObjectiveManager.IsCurrentObjective<AIObjectiveFightIntruders>();
|
||||
float safety = CalculateHullSafety(hull, visibleHulls, character, ignoreWater, ignoreOxygen, ignoreFire, ignoreEnemies);
|
||||
if (isCurrentHull)
|
||||
{
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ namespace Barotrauma
|
||||
// The validity changes when a character picks the item up.
|
||||
if (!IsValidTarget(target, character, checkInventory: true)) { return Objectives.ContainsKey(target) && IsItemInsideValidSubmarine(target, character); }
|
||||
if (target.CurrentHull.FireSources.Count > 0) { return false; }
|
||||
// Don't repair items in rooms that have enemies inside.
|
||||
// Don't clean up items in rooms that have enemies inside.
|
||||
if (Character.CharacterList.Any(c => c.CurrentHull == target.CurrentHull && !HumanAIController.IsFriendly(c) && HumanAIController.IsActive(c))) { return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
+48
-10
@@ -117,7 +117,10 @@ namespace Barotrauma
|
||||
private float AimSpeed => HumanAIController.AimSpeed;
|
||||
private float AimAccuracy => HumanAIController.AimAccuracy;
|
||||
|
||||
private bool EnemyIsClose() => Enemy != null && Enemy.CurrentHull != null && HumanAIController.VisibleHulls.Contains(Enemy.CurrentHull) && Math.Abs(character.WorldPosition.X - Enemy.WorldPosition.X) < 300;
|
||||
private bool IsEnemyCloserThan(float margin) =>
|
||||
Enemy != null && Enemy.CurrentHull != null &&
|
||||
character.InWater && Vector2.DistanceSquared(character.WorldPosition, Enemy.WorldPosition) < margin * margin ||
|
||||
HumanAIController.VisibleHulls.Contains(Enemy.CurrentHull) && Math.Abs(character.WorldPosition.X - Enemy.WorldPosition.X) < margin;
|
||||
|
||||
public AIObjectiveCombat(Character character, Character enemy, CombatMode mode, AIObjectiveManager objectiveManager, float priorityModifier = 1, float coolDown = 10.0f)
|
||||
: base(character, objectiveManager, priorityModifier)
|
||||
@@ -144,12 +147,19 @@ namespace Barotrauma
|
||||
Mode = CombatMode.Retreat;
|
||||
}
|
||||
spreadTimer = Rand.Range(-10f, 10f);
|
||||
SetAimTimer(Rand.Range(1f, 1.5f) / AimSpeed);
|
||||
HumanAIController.SortTimer = 0;
|
||||
}
|
||||
|
||||
protected override float GetPriority()
|
||||
{
|
||||
if (character.TeamID == CharacterTeamType.FriendlyNPC && Enemy != null)
|
||||
if (Enemy == null)
|
||||
{
|
||||
Priority = 0;
|
||||
Abandon = true;
|
||||
return Priority;
|
||||
}
|
||||
if (character.TeamID == CharacterTeamType.FriendlyNPC)
|
||||
{
|
||||
if (Enemy.Submarine == null || (Enemy.Submarine.TeamID != character.TeamID && Enemy.Submarine != character.Submarine))
|
||||
{
|
||||
@@ -160,6 +170,13 @@ namespace Barotrauma
|
||||
}
|
||||
float damageFactor = MathUtils.InverseLerp(0.0f, 5.0f, character.GetDamageDoneByAttacker(Enemy) / 100.0f);
|
||||
Priority = TargetEliminated ? 0 : Math.Min((95 + damageFactor) * PriorityModifier, 100);
|
||||
if (Priority > 0)
|
||||
{
|
||||
if (EnemyAIController.IsLatchedToSomeoneElse(Enemy, character))
|
||||
{
|
||||
Priority = 0;
|
||||
}
|
||||
}
|
||||
return Priority;
|
||||
}
|
||||
|
||||
@@ -366,7 +383,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
bool isAllowedToSeekWeapons = character.CurrentHull != null && !EnemyIsClose() && character.TeamID != CharacterTeamType.FriendlyNPC && IsOffensiveOrArrest;
|
||||
bool isAllowedToSeekWeapons = character.CurrentHull != null && !IsEnemyCloserThan(300) && character.IsOnPlayerTeam && IsOffensiveOrArrest;
|
||||
if (!isAllowedToSeekWeapons)
|
||||
{
|
||||
if (WeaponComponent == null)
|
||||
@@ -418,9 +435,16 @@ namespace Barotrauma
|
||||
onCompleted: () => RemoveSubObjective(ref seekWeaponObjective),
|
||||
onAbandon: () =>
|
||||
{
|
||||
SpeakNoWeapons();
|
||||
RemoveSubObjective(ref seekWeaponObjective);
|
||||
Mode = CombatMode.Retreat;
|
||||
if (Weapon == null)
|
||||
{
|
||||
SpeakNoWeapons();
|
||||
Mode = CombatMode.Retreat;
|
||||
}
|
||||
else
|
||||
{
|
||||
Mode = CombatMode.Defensive;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -478,13 +502,25 @@ namespace Barotrauma
|
||||
weaponComponent = null;
|
||||
float bestPriority = 0;
|
||||
float lethalDmg = -1;
|
||||
bool enemyIsClose = EnemyIsClose();
|
||||
bool isAllowedToSeekWeapons = !IsEnemyCloserThan(300);
|
||||
bool prioritizeMelee = IsEnemyCloserThan(50) || EnemyAIController.IsLatchedTo(Enemy, character);
|
||||
foreach (var weapon in weaponList)
|
||||
{
|
||||
float priority = weapon.CombatPriority;
|
||||
if (prioritizeMelee)
|
||||
{
|
||||
if (weapon is MeleeWeapon)
|
||||
{
|
||||
priority *= 5;
|
||||
}
|
||||
else
|
||||
{
|
||||
priority /= 2;
|
||||
}
|
||||
}
|
||||
if (!weapon.IsLoaded(character))
|
||||
{
|
||||
if (weapon is RangedWeapon && enemyIsClose)
|
||||
if (weapon is RangedWeapon && !isAllowedToSeekWeapons)
|
||||
{
|
||||
// Close to the enemy. Ignore weapons that don't have any ammunition (-> Don't seek ammo).
|
||||
continue;
|
||||
@@ -693,7 +729,7 @@ namespace Barotrauma
|
||||
var slots = Weapon.AllowedSlots.Where(s => IsHandSlotType(s));
|
||||
if (character.Inventory.TryPutItem(Weapon, character, slots))
|
||||
{
|
||||
aimTimer = Rand.Range(0.2f, 0.4f) / AimSpeed;
|
||||
SetAimTimer(Rand.Range(0.2f, 0.4f) / AimSpeed);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1014,7 +1050,7 @@ namespace Barotrauma
|
||||
}
|
||||
if (!canSeeTarget)
|
||||
{
|
||||
aimTimer = Rand.Range(0.2f, 0.4f) / AimSpeed;
|
||||
SetAimTimer(Rand.Range(0.2f, 0.4f) / AimSpeed);
|
||||
return;
|
||||
}
|
||||
if (Weapon.RequireAimToUse)
|
||||
@@ -1074,7 +1110,7 @@ namespace Barotrauma
|
||||
else if (!character.IsFacing(Enemy.WorldPosition))
|
||||
{
|
||||
// Don't do the facing check if we are close to the target, because it easily causes the character to get stuck here when it flips around.
|
||||
aimTimer = Rand.Range(1f, 1.5f) / AimSpeed;
|
||||
SetAimTimer(Rand.Range(1f, 1.5f) / AimSpeed);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1190,5 +1226,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SetAimTimer(float newTimer) => aimTimer = Math.Max(aimTimer, newTimer);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -120,7 +120,7 @@ namespace Barotrauma
|
||||
}
|
||||
if (character.CanInteractWith(container.Item, checkLinked: false))
|
||||
{
|
||||
if (RemoveExisting || (RemoveExistingWhenNecessary && !container.Inventory.CanBePut(item)))
|
||||
if (RemoveExisting || (RemoveExistingWhenNecessary && !container.Inventory.CanBePut(ItemToContain)))
|
||||
{
|
||||
HumanAIController.UnequipContainedItems(container.Item, predicate: RemoveExistingPredicate, unequipMax: RemoveMax);
|
||||
}
|
||||
|
||||
+1
@@ -70,6 +70,7 @@ namespace Barotrauma
|
||||
if (!targetCharactersInOtherSubs && character.Submarine.TeamID != target.Submarine.TeamID) { return false; }
|
||||
if (target.HasAbilityFlag(AbilityFlags.IgnoredByEnemyAI)) { return false; }
|
||||
if (target.IsArrested) { return false; }
|
||||
if (EnemyAIController.IsLatchedToSomeoneElse(target, character)) { return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -108,7 +108,7 @@ namespace Barotrauma
|
||||
AllowToFindDivingGear = false,
|
||||
AllowDangerousPressure = true,
|
||||
ConditionLevel = MIN_OXYGEN,
|
||||
RemoveExisting = true
|
||||
RemoveExistingWhenNecessary = true
|
||||
};
|
||||
},
|
||||
onAbandon: () =>
|
||||
|
||||
+4
@@ -76,6 +76,10 @@ namespace Barotrauma
|
||||
// -> ignore find safety unless we need to find a diving gear
|
||||
Priority = 0;
|
||||
}
|
||||
else if (objectiveManager.Objectives.Any(o => o is AIObjectiveCombat && o.Priority > 0))
|
||||
{
|
||||
Priority = 0;
|
||||
}
|
||||
Priority = MathHelper.Clamp(Priority, 0, 100);
|
||||
if (divingGearObjective != null && !divingGearObjective.IsCompleted && divingGearObjective.CanBeCompleted)
|
||||
{
|
||||
|
||||
+5
@@ -401,6 +401,11 @@ namespace Barotrauma
|
||||
{
|
||||
if (!ownerItem.IsInteractable(character)) { continue; }
|
||||
if (!(ownerItem.GetComponent<ItemContainer>()?.HasRequiredItems(character, addMessage: false) ?? true)) { continue; }
|
||||
//the item is inside an item inside an item (e.g. fuel tank in a welding tool in a cabinet -> reduce priority to prefer items that aren't inside a tool)
|
||||
if (ownerItem != item.Container)
|
||||
{
|
||||
itemPriority *= 0.1f;
|
||||
}
|
||||
}
|
||||
Vector2 itemPos = (rootInventoryOwner ?? item).WorldPosition;
|
||||
float yDist = Math.Abs(character.WorldPosition.Y - itemPos.Y);
|
||||
|
||||
@@ -77,6 +77,9 @@ namespace Barotrauma
|
||||
// TODO: Currently we never check the visibility (to the end node), which is actually unintentional.
|
||||
// I don't think it has caused any issues so far, so let's keep defaulting to false for now, because the less we do raycasts the better.
|
||||
// However, if there are cases where the bots attempt to go through walls (select the end node that is behind an obstacle), we should set this true.
|
||||
|
||||
// NOTE: This seemes to have caused an issue now Regalis11/Barotrauma#8067: namely, the bot was trying to use a waypoint that was obstructed by a shuttle
|
||||
// because obstruction was only checked when checking visibility in PathFinder. Changed that so that obstructed nodes are no longer used.
|
||||
public bool CheckVisibility { get; set; }
|
||||
public bool IgnoreIfTargetDead { get; set; }
|
||||
public bool AllowGoingOutside { get; set; }
|
||||
|
||||
+27
-79
@@ -7,11 +7,11 @@ namespace Barotrauma
|
||||
class AIObjectiveReturn : AIObjective
|
||||
{
|
||||
public override string Identifier { get; set; } = "return";
|
||||
private AIObjectiveGoTo moveInsideObjective, moveInCaveObjective, moveOutsideObjective;
|
||||
private bool usingEscapeBehavior;
|
||||
private bool isSteeringThroughGap;
|
||||
public Submarine ReturnTarget { get; }
|
||||
|
||||
private AIObjectiveGoTo moveInsideObjective, moveOutsideObjective;
|
||||
private bool usingEscapeBehavior, isSteeringThroughGap;
|
||||
|
||||
public AIObjectiveReturn(Character character, Character orderGiver, AIObjectiveManager objectiveManager, float priorityModifier = 1.0f) : base(character, objectiveManager, priorityModifier)
|
||||
{
|
||||
ReturnTarget = GetReturnTarget(Submarine.MainSubs) ?? GetReturnTarget(Submarine.Loaded);
|
||||
@@ -112,7 +112,6 @@ namespace Barotrauma
|
||||
}
|
||||
if (targetHull != null)
|
||||
{
|
||||
RemoveSubObjective(ref moveInCaveObjective);
|
||||
RemoveSubObjective(ref moveOutsideObjective);
|
||||
TryAddSubObjective(ref moveInsideObjective,
|
||||
constructor: () => new AIObjectiveGoTo(targetHull, character, objectiveManager)
|
||||
@@ -137,91 +136,41 @@ namespace Barotrauma
|
||||
IsCompleted = true;
|
||||
}
|
||||
}
|
||||
else if (!isSteeringThroughGap && moveInCaveObjective == null && moveOutsideObjective == null)
|
||||
else if (!isSteeringThroughGap && moveOutsideObjective == null)
|
||||
{
|
||||
if (HumanAIController.IsInsideCave)
|
||||
Hull targetHull = null;
|
||||
float targetDistanceSquared = float.MaxValue;
|
||||
bool targetIsAirlock = false;
|
||||
foreach (var hull in ReturnTarget.GetHulls(false))
|
||||
{
|
||||
WayPoint closestOutsideWaypoint = null;
|
||||
float closestDistance = float.MaxValue;
|
||||
foreach (var w in WayPoint.WayPointList)
|
||||
bool hullIsAirlock = hull.IsTaggedAirlock();
|
||||
if(hullIsAirlock || (!targetIsAirlock && hull.LeadsOutside(character)))
|
||||
{
|
||||
if (w.Tunnel != null && w.Tunnel.Type == Level.TunnelType.Cave) { continue; }
|
||||
if (w.linkedTo.None(l => l is WayPoint linkedWaypoint && linkedWaypoint.Tunnel?.Type == Level.TunnelType.Cave)) { continue; }
|
||||
float distance = Vector2.DistanceSquared(character.WorldPosition, w.WorldPosition);
|
||||
if (closestOutsideWaypoint == null || distance < closestDistance)
|
||||
float distanceSquared = Vector2.DistanceSquared(character.WorldPosition, hull.WorldPosition);
|
||||
if (targetHull == null || distanceSquared < targetDistanceSquared)
|
||||
{
|
||||
closestOutsideWaypoint = w;
|
||||
closestDistance = distance;
|
||||
targetHull = hull;
|
||||
targetDistanceSquared = distanceSquared;
|
||||
targetIsAirlock = hullIsAirlock;
|
||||
}
|
||||
}
|
||||
if (closestOutsideWaypoint != null)
|
||||
{
|
||||
RemoveSubObjective(ref moveInsideObjective);
|
||||
RemoveSubObjective(ref moveOutsideObjective);
|
||||
TryAddSubObjective(ref moveInCaveObjective,
|
||||
constructor: () => new AIObjectiveGoTo(closestOutsideWaypoint, character, objectiveManager)
|
||||
{
|
||||
endNodeFilter = n => n.Waypoint == closestOutsideWaypoint,
|
||||
AllowGoingOutside = true
|
||||
},
|
||||
onCompleted: () => RemoveSubObjective(ref moveInCaveObjective),
|
||||
onAbandon: () => Abandon = true);
|
||||
}
|
||||
else
|
||||
{
|
||||
#if DEBUG
|
||||
DebugConsole.ThrowError("Error with a Return objective: no suitable main or side path node target found for 'moveOutsideObjective'");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
if (targetHull != null)
|
||||
{
|
||||
RemoveSubObjective(ref moveInsideObjective);
|
||||
TryAddSubObjective(ref moveOutsideObjective,
|
||||
constructor: () => new AIObjectiveGoTo(targetHull, character, objectiveManager)
|
||||
{
|
||||
AllowGoingOutside = true
|
||||
},
|
||||
onCompleted: () => RemoveSubObjective(ref moveOutsideObjective),
|
||||
onAbandon: () => Abandon = true);
|
||||
}
|
||||
else
|
||||
{
|
||||
Hull targetHull = null;
|
||||
float targetDistanceSquared = float.MaxValue;
|
||||
bool targetIsAirlock = false;
|
||||
foreach (var hull in ReturnTarget.GetHulls(false))
|
||||
{
|
||||
bool hullIsAirlock = hull.IsTaggedAirlock();
|
||||
if(hullIsAirlock || (!targetIsAirlock && hull.LeadsOutside(character)))
|
||||
{
|
||||
float distanceSquared = Vector2.DistanceSquared(character.WorldPosition, hull.WorldPosition);
|
||||
if (targetHull == null || distanceSquared < targetDistanceSquared)
|
||||
{
|
||||
targetHull = hull;
|
||||
targetDistanceSquared = distanceSquared;
|
||||
targetIsAirlock = hullIsAirlock;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (targetHull != null)
|
||||
{
|
||||
RemoveSubObjective(ref moveInsideObjective);
|
||||
RemoveSubObjective(ref moveInCaveObjective);
|
||||
TryAddSubObjective(ref moveOutsideObjective,
|
||||
constructor: () => new AIObjectiveGoTo(targetHull, character, objectiveManager)
|
||||
{
|
||||
AllowGoingOutside = true
|
||||
},
|
||||
onCompleted: () => RemoveSubObjective(ref moveOutsideObjective),
|
||||
onAbandon: () => Abandon = true);
|
||||
}
|
||||
else
|
||||
{
|
||||
#if DEBUG
|
||||
DebugConsole.ThrowError("Error with a Return objective: no suitable target for 'moveOutsideObjective'");
|
||||
DebugConsole.ThrowError("Error with a Return objective: no suitable target for 'moveOutsideObjective'");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (HumanAIController.IsInsideCave)
|
||||
{
|
||||
RemoveSubObjective(ref moveOutsideObjective);
|
||||
}
|
||||
else
|
||||
{
|
||||
RemoveSubObjective(ref moveInCaveObjective);
|
||||
}
|
||||
}
|
||||
usingEscapeBehavior = shouldUseEscapeBehavior;
|
||||
@@ -249,7 +198,6 @@ namespace Barotrauma
|
||||
{
|
||||
base.Reset();
|
||||
moveInsideObjective = null;
|
||||
moveInCaveObjective = null;
|
||||
moveOutsideObjective = null;
|
||||
usingEscapeBehavior = false;
|
||||
isSteeringThroughGap = false;
|
||||
|
||||
@@ -333,7 +333,6 @@ namespace Barotrauma
|
||||
//if searching for a path inside the sub, make sure the waypoint is visible
|
||||
if (checkVisibility && isCharacter)
|
||||
{
|
||||
if (node.Waypoint.isObstructed) { return false; }
|
||||
var body = Submarine.PickBody(rayStart, node.TempPosition,
|
||||
collisionCategory: Physics.CollisionWall | Physics.CollisionLevel | Physics.CollisionStairs);
|
||||
if (body != null)
|
||||
@@ -350,6 +349,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (nodeFilter != null && !nodeFilter(node)) { return false; }
|
||||
if (startNodeFilter != null && !startNodeFilter(node)) { return false; }
|
||||
if (node.Waypoint.isObstructed) { return false; }
|
||||
// Always check the visibility for the start node
|
||||
if (!IsWaypointVisible(node, start)) { return false; }
|
||||
if (node.IsBlocked()) { return false; }
|
||||
@@ -364,6 +364,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (nodeFilter != null && !nodeFilter(node)) { return false; }
|
||||
if (endNodeFilter != null && !endNodeFilter(node)) { return false; }
|
||||
if (node.Waypoint.isObstructed) { return false; }
|
||||
// Only check the visibility for the end node when allowed (fix leaks)
|
||||
if (!IsWaypointVisible(node, end, checkVisibility: checkVisibility)) { return false; }
|
||||
if (node.IsBlocked()) { return false; }
|
||||
|
||||
Reference in New Issue
Block a user