(a97ef2847) AI improvements: - Fix steering issues when attacking (yet again): We still have to offset the sim positions. - Fix the steering vector when escaping from the enemy that is inside the sub. - Enemies can now navigate out from the sub, when escaping. - Fix door tag not being used when targeting items. Change the priority boosts considering doors. Should now entirely ignore the outer doors when inside and inner doors when outside. - Change the way enemies react to characters that are not in the same sub. Halve the priority when the character is not in the same hull. Add some boost to the priority when escaping.
This commit is contained in:
@@ -26,7 +26,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class TargetingPriority
|
||||
{
|
||||
public string TargetTag;
|
||||
@@ -100,6 +99,9 @@ namespace Barotrauma
|
||||
|
||||
private readonly float aggressiongreed;
|
||||
private readonly float aggressionhurt;
|
||||
// TODO: expose?
|
||||
private readonly float priorityFearIncreasement = 2;
|
||||
private readonly float memoryFadeTime = 0.5f;
|
||||
|
||||
public bool AttackHumans
|
||||
{
|
||||
@@ -385,7 +387,7 @@ namespace Barotrauma
|
||||
#endregion
|
||||
|
||||
#region Escape
|
||||
|
||||
private Vector2 escapePoint;
|
||||
private void UpdateEscape(float deltaTime)
|
||||
{
|
||||
if (SelectedAiTarget == null || SelectedAiTarget.Entity == null || SelectedAiTarget.Entity.Removed)
|
||||
@@ -393,13 +395,44 @@ namespace Barotrauma
|
||||
State = AIState.Idle;
|
||||
return;
|
||||
}
|
||||
|
||||
Vector2 escapeDir = Vector2.Normalize(SimPosition - SelectedAiTarget.SimPosition);
|
||||
if (!MathUtils.IsValid(escapeDir)) escapeDir = Vector2.UnitY;
|
||||
SteeringManager.SteeringManual(deltaTime, escapeDir);
|
||||
SteeringManager.SteeringWander();
|
||||
if (Character.CurrentHull == null)
|
||||
else if (selectedTargetMemory != null)
|
||||
{
|
||||
selectedTargetMemory.Priority += deltaTime * priorityFearIncreasement;
|
||||
}
|
||||
if (Character.CurrentHull != null)
|
||||
{
|
||||
// Seek exit, if inside
|
||||
if (SteeringManager is IndoorsSteeringManager indoorSteering && escapePoint == Vector2.Zero)
|
||||
{
|
||||
foreach (Gap gap in Gap.GapList)
|
||||
{
|
||||
if (gap.Submarine != Character.Submarine) { continue; }
|
||||
if (gap.Open < 1 || gap.IsRoomToRoom) { continue; }
|
||||
var path = indoorSteering.PathFinder.FindPath(Character.SimPosition, gap.SimPosition);
|
||||
if (!path.Unreachable)
|
||||
{
|
||||
if (escapePoint != Vector2.Zero)
|
||||
{
|
||||
// Ignore the gap if it's further away than the previously assigned escape point
|
||||
if (Vector2.DistanceSquared(Character.SimPosition, gap.SimPosition) > Vector2.DistanceSquared(Character.SimPosition, escapePoint)) { continue; }
|
||||
}
|
||||
escapePoint = gap.SimPosition;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (escapePoint != Vector2.Zero && Vector2.DistanceSquared(Character.SimPosition, escapePoint) > 1)
|
||||
{
|
||||
SteeringManager.SteeringSeek(escapePoint);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If outside or near enough the escapePoint, steer away
|
||||
escapePoint = Vector2.Zero;
|
||||
Vector2 escapeDir = Vector2.Normalize(WorldPosition - SelectedAiTarget.WorldPosition);
|
||||
if (!MathUtils.IsValid(escapeDir)) escapeDir = Vector2.UnitY;
|
||||
SteeringManager.SteeringManual(deltaTime, escapeDir);
|
||||
SteeringManager.SteeringWander();
|
||||
SteeringManager.SteeringAvoid(deltaTime, colliderSize * 3.0f);
|
||||
}
|
||||
}
|
||||
@@ -443,16 +476,7 @@ namespace Barotrauma
|
||||
raycastTimer = RaycastInterval;
|
||||
}
|
||||
|
||||
if (wallTarget != null)
|
||||
{
|
||||
attackWorldPos = wallTarget.Position;
|
||||
attackSimPos = ConvertUnits.ToSimUnits(attackWorldPos);
|
||||
if (Character.Submarine == null && wallTarget.Structure.Submarine != null)
|
||||
{
|
||||
attackWorldPos += wallTarget.Structure.Submarine.Position;
|
||||
}
|
||||
}
|
||||
else if (SelectedAiTarget.Entity is Character c)
|
||||
if (SelectedAiTarget.Entity is Character c)
|
||||
{
|
||||
//target the closest limb if the target is a character
|
||||
float closestDist = Vector2.DistanceSquared(SelectedAiTarget.WorldPosition, WorldPosition) * 10.0f;
|
||||
@@ -469,14 +493,22 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
// Take the sub position into account in the sim pos
|
||||
if (Character.Submarine == null && SelectedAiTarget.Entity.Submarine != null)
|
||||
if (wallTarget != null)
|
||||
{
|
||||
attackSimPos += SelectedAiTarget.Entity.Submarine.SimPosition;
|
||||
attackWorldPos = wallTarget.Position;
|
||||
attackSimPos = ConvertUnits.ToSimUnits(attackWorldPos);
|
||||
}
|
||||
else if (Character.Submarine != null && SelectedAiTarget.Entity.Submarine == null)
|
||||
else
|
||||
{
|
||||
attackSimPos -= Character.Submarine.SimPosition;
|
||||
// Take the sub position into account in the sim pos
|
||||
if (Character.Submarine == null && SelectedAiTarget.Entity.Submarine != null)
|
||||
{
|
||||
attackSimPos += SelectedAiTarget.Entity.Submarine.SimPosition;
|
||||
}
|
||||
else if (Character.Submarine != null && SelectedAiTarget.Entity.Submarine == null)
|
||||
{
|
||||
attackSimPos -= Character.Submarine.SimPosition;
|
||||
}
|
||||
}
|
||||
|
||||
if (Math.Abs(Character.AnimController.movement.X) > 0.1f && !Character.AnimController.InWater)
|
||||
@@ -490,32 +522,26 @@ namespace Barotrauma
|
||||
if (wallTarget != null && wallTarget.SectionIndex > -1 && CanPassThroughHole(wallTarget.Structure, wallTarget.SectionIndex))
|
||||
{
|
||||
WallSection section = wallTarget.Structure.GetSection(wallTarget.SectionIndex);
|
||||
Hull targetHull = section.gap?.FlowTargetHull;
|
||||
if (targetHull != null && !section.gap.IsRoomToRoom)
|
||||
Vector2 targetPos = wallTarget.Structure.SectionPosition(wallTarget.SectionIndex, true);
|
||||
if (section?.gap != null && section.gap.IsRoomToRoom && SteerThroughGap(wallTarget.Structure, section, targetPos, deltaTime))
|
||||
{
|
||||
Vector2 targetPos = wallTarget.Structure.SectionPosition(wallTarget.SectionIndex, true);
|
||||
if (wallTarget.Structure.IsHorizontal)
|
||||
{
|
||||
targetPos.Y = targetHull.WorldRect.Y - targetHull.Rect.Height / 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
targetPos.X = targetHull.WorldRect.Center.X;
|
||||
}
|
||||
|
||||
latchOntoAI?.DeattachFromBody();
|
||||
Character.AnimController.ReleaseStuckLimbs();
|
||||
if (steeringManager is IndoorsSteeringManager)
|
||||
{
|
||||
steeringManager.SteeringManual(deltaTime, Vector2.Normalize(targetPos - Character.WorldPosition));
|
||||
}
|
||||
else
|
||||
{
|
||||
steeringManager.SteeringSeek(ConvertUnits.ToSimUnits(targetPos));
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (SelectedAiTarget.Entity is Structure wall)
|
||||
{
|
||||
for (int i = 0; i < wall.Sections.Length; i++)
|
||||
{
|
||||
WallSection section = wall.Sections[i];
|
||||
if (CanPassThroughHole(wall, i) && section?.gap != null)
|
||||
{
|
||||
if (SteerThroughGap(wall, section, section.gap.WorldPosition, deltaTime))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (SelectedAiTarget.Entity is Item i)
|
||||
{
|
||||
var door = i.GetComponent<Door>();
|
||||
@@ -528,6 +554,8 @@ namespace Barotrauma
|
||||
if (Character.WorldPosition.Y < door.Item.WorldRect.Y && Character.WorldPosition.Y > door.Item.WorldRect.Y - door.Item.Rect.Height)
|
||||
{
|
||||
velocity.Y = 0;
|
||||
latchOntoAI?.DeattachFromBody();
|
||||
Character.AnimController.ReleaseStuckLimbs();
|
||||
steeringManager.SteeringManual(deltaTime, velocity);
|
||||
return;
|
||||
}
|
||||
@@ -537,6 +565,8 @@ namespace Barotrauma
|
||||
if (Character.WorldPosition.X < door.Item.WorldRect.X && Character.WorldPosition.X > door.Item.WorldRect.Right)
|
||||
{
|
||||
velocity.X = 0;
|
||||
latchOntoAI?.DeattachFromBody();
|
||||
Character.AnimController.ReleaseStuckLimbs();
|
||||
steeringManager.SteeringManual(deltaTime, velocity);
|
||||
return;
|
||||
}
|
||||
@@ -670,8 +700,11 @@ namespace Barotrauma
|
||||
{
|
||||
if (indoorsSteering.CurrentPath.Unreachable)
|
||||
{
|
||||
//wander around randomly and decrease the priority faster if no path is found
|
||||
if (selectedTargetMemory != null) selectedTargetMemory.Priority -= deltaTime * 10.0f;
|
||||
if (selectedTargetMemory != null)
|
||||
{
|
||||
//wander around randomly and decrease the priority faster if no path is found
|
||||
selectedTargetMemory.Priority -= deltaTime * memoryFadeTime * 10;
|
||||
}
|
||||
SteeringManager.SteeringWander();
|
||||
}
|
||||
else if (indoorsSteering.CurrentPath.Finished)
|
||||
@@ -702,6 +735,34 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private bool SteerThroughGap(Structure wall, WallSection section, Vector2 targetWorldPos, float deltaTime)
|
||||
{
|
||||
Hull targetHull = section.gap?.FlowTargetHull;
|
||||
if (targetHull != null)
|
||||
{
|
||||
if (wall.IsHorizontal)
|
||||
{
|
||||
targetWorldPos.Y = targetHull.WorldRect.Y - targetHull.Rect.Height / 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
targetWorldPos.X = targetHull.WorldRect.Center.X;
|
||||
}
|
||||
latchOntoAI?.DeattachFromBody();
|
||||
Character.AnimController.ReleaseStuckLimbs();
|
||||
if (steeringManager is IndoorsSteeringManager)
|
||||
{
|
||||
steeringManager.SteeringManual(deltaTime, Vector2.Normalize(targetWorldPos - Character.WorldPosition));
|
||||
}
|
||||
else
|
||||
{
|
||||
steeringManager.SteeringSeek(ConvertUnits.ToSimUnits(targetWorldPos));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private Limb GetAttackLimb(Vector2 attackWorldPos, Limb ignoredLimb = null)
|
||||
{
|
||||
AttackContext currentContext = Character.GetAttackContext();
|
||||
@@ -725,11 +786,6 @@ namespace Barotrauma
|
||||
{
|
||||
wallTarget = null;
|
||||
|
||||
if (Character.AnimController.CurrentHull != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//check if there's a wall between the target and the Character
|
||||
Vector2 rayStart = SimPosition;
|
||||
Vector2 rayEnd = SelectedAiTarget.SimPosition;
|
||||
@@ -737,7 +793,7 @@ namespace Barotrauma
|
||||
|
||||
if (offset)
|
||||
{
|
||||
rayStart -= ConvertUnits.ToSimUnits(SelectedAiTarget.Entity.Submarine.Position);
|
||||
rayStart -= SelectedAiTarget.Entity.Submarine.SimPosition;
|
||||
}
|
||||
|
||||
Body closestBody = Submarine.CheckVisibility(rayStart, rayEnd, ignoreSubs: true);
|
||||
@@ -785,8 +841,12 @@ namespace Barotrauma
|
||||
sectionPos.X += (wall.BodyWidth <= 0.0f ? wall.Rect.Width : wall.BodyWidth) / 2 * attachTargetNormal.X;
|
||||
}
|
||||
|
||||
latchOntoAI?.SetAttachTarget(wall.Submarine.PhysicsBody.FarseerBody, wall.Submarine, sectionPos, attachTargetNormal);
|
||||
if (wall.Submarine != null)
|
||||
{
|
||||
sectionPos += wall.Submarine.Position;
|
||||
}
|
||||
wallTarget = new WallTarget(sectionPos, wall, sectionIndex);
|
||||
latchOntoAI?.SetAttachTarget(wall.Submarine.PhysicsBody.FarseerBody, wall.Submarine, ConvertUnits.ToSimUnits(sectionPos), attachTargetNormal);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -945,8 +1005,13 @@ namespace Barotrauma
|
||||
targetingTag = "dead";
|
||||
if (targetCharacter.Submarine != Character.Submarine)
|
||||
{
|
||||
// In a different sub or the target is outside when we are inside or vice versa -> Significantly reduce the value
|
||||
valueModifier = 0.1f;
|
||||
// In a different sub or the target is outside when we are inside or vice versa -> Ignore the target
|
||||
continue;
|
||||
}
|
||||
else if (targetCharacter.CurrentHull != Character.CurrentHull)
|
||||
{
|
||||
// In the same sub, halve the priority, if not in the same hull.
|
||||
valueModifier = 0.5f;
|
||||
}
|
||||
}
|
||||
else if (targetCharacter.AIController is EnemyAIController enemy)
|
||||
@@ -959,10 +1024,23 @@ namespace Barotrauma
|
||||
{
|
||||
targetingTag = "weaker";
|
||||
}
|
||||
if (targetCharacter.Submarine != Character.Submarine)
|
||||
if (State == AIState.Escape && targetingTag == "stronger")
|
||||
{
|
||||
// In a different sub or the target is outside when we are inside or vice versa -> Significantly reduce the value
|
||||
valueModifier = 0.1f;
|
||||
// Frightened
|
||||
valueModifier = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (targetCharacter.Submarine != Character.Submarine)
|
||||
{
|
||||
// In a different sub or the target is outside when we are inside or vice versa -> Ignore the target
|
||||
continue;
|
||||
}
|
||||
else if (targetCharacter.CurrentHull != Character.CurrentHull)
|
||||
{
|
||||
// In the same sub, halve the priority, if not in the same hull.
|
||||
valueModifier = 0.5f;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (targetCharacter.Submarine != null && Character.Submarine == null)
|
||||
@@ -1002,14 +1080,27 @@ namespace Barotrauma
|
||||
else if (target.Entity is Structure s)
|
||||
{
|
||||
targetingTag = "wall";
|
||||
if (character.CurrentHull == null && aggressiveBoarding)
|
||||
if (aggressiveBoarding)
|
||||
{
|
||||
valueModifier = s.HasBody ? 2 : 0;
|
||||
foreach (var section in s.Sections)
|
||||
// Ignore walls when inside.
|
||||
valueModifier = character.CurrentHull == null ? 2 : 0;
|
||||
if (valueModifier > 0)
|
||||
{
|
||||
if (section.gap != null)
|
||||
// Ignore structures that doesn't have a body (not walls)
|
||||
valueModifier *= s.HasBody ? 1 : 0;
|
||||
}
|
||||
for (int i = 0; i < s.Sections.Length; i++)
|
||||
{
|
||||
var section = s.Sections[i];
|
||||
if (CanPassThroughHole(s, i))
|
||||
{
|
||||
// up to 100% more priority for every gap in the wall
|
||||
// Ignore walls that can be passed through
|
||||
valueModifier = 0;
|
||||
break;
|
||||
}
|
||||
else if (section.gap != null)
|
||||
{
|
||||
// up to 100% priority increase for every gap in the wall
|
||||
valueModifier *= 1 + section.gap.Open;
|
||||
}
|
||||
}
|
||||
@@ -1021,12 +1112,36 @@ namespace Barotrauma
|
||||
}
|
||||
if (door != null)
|
||||
{
|
||||
//increase priority if the character is outside and an aggressive boarder, and the door is from outside to inside
|
||||
if (character.CurrentHull == null && aggressiveBoarding && !door.LinkedGap.IsRoomToRoom)
|
||||
// If there's not a more specific tag for the door
|
||||
if (string.IsNullOrEmpty(targetingTag) || targetingTag == "room")
|
||||
{
|
||||
valueModifier = door.IsOpen ? 5 : 3;
|
||||
targetingTag = "door";
|
||||
}
|
||||
else if (door.IsOpen || door.Item.Condition <= 0.0f) //ignore broken and open doors
|
||||
bool isOutdoor = door.LinkedGap?.FlowTargetHull != null && !door.LinkedGap.IsRoomToRoom;
|
||||
bool isOpen = door.IsOpen || door.Item.Condition <= 0.0f;
|
||||
//increase priority if the character is outside and an aggressive boarder, and the door is from outside to inside
|
||||
if (aggressiveBoarding)
|
||||
{
|
||||
if (character.CurrentHull == null)
|
||||
{
|
||||
valueModifier = isOutdoor ? 1 : 0;
|
||||
valueModifier *= isOpen ? 5 : 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (target.Entity is Structure s)
|
||||
{
|
||||
targetingTag = "wall";
|
||||
if (character.CurrentHull == null && aggressiveBoarding)
|
||||
{
|
||||
valueModifier = s.HasBody ? 2 : 0;
|
||||
foreach (var section in s.Sections)
|
||||
{
|
||||
valueModifier = isOutdoor ? 0 : 1;
|
||||
valueModifier *= isOpen ? 0 : 1;
|
||||
}
|
||||
}
|
||||
else if (isOpen) //ignore broken and open doors
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -1094,7 +1209,6 @@ namespace Barotrauma
|
||||
return memory;
|
||||
}
|
||||
|
||||
private float memoryFadeTime = 0.5f;
|
||||
private List<AITarget> removals = new List<AITarget>();
|
||||
private void UpdateTargetMemories(float deltaTime)
|
||||
{
|
||||
@@ -1118,6 +1232,8 @@ namespace Barotrauma
|
||||
{
|
||||
latchOntoAI?.DeattachFromBody();
|
||||
Character.AnimController.ReleaseStuckLimbs();
|
||||
escapePoint = Vector2.Zero;
|
||||
wallTarget = null;
|
||||
}
|
||||
|
||||
private int GetMinimumPassableHoleCount()
|
||||
|
||||
@@ -520,9 +520,10 @@ namespace Barotrauma
|
||||
// Ignore blocking on items, because it causes cases where a Mudraptor cannot hit the hatch, for example.
|
||||
wasHit = true;
|
||||
}
|
||||
else if (damageTarget is Structure && structureBody?.UserData is Structure)
|
||||
else if (damageTarget is Structure wall && structureBody != null &&
|
||||
(structureBody.UserData is Structure || (structureBody.UserData is Submarine sub && sub == wall.Submarine)))
|
||||
{
|
||||
// If the attack is aimed to a structure and hits a structure, it's successful
|
||||
// If the attack is aimed to a structure (wall) and hits a structure or the sub, it's successful
|
||||
wasHit = true;
|
||||
}
|
||||
else
|
||||
|
||||
Reference in New Issue
Block a user