Release v0.15.12.0

This commit is contained in:
Joonas Rikkonen
2021-10-27 18:50:57 +03:00
parent bf95e82d80
commit 234fb6bc06
450 changed files with 26042 additions and 10457 deletions
@@ -1,8 +1,8 @@
using Barotrauma.Items.Components;
using Barotrauma.Extensions;
using Barotrauma.Items.Components;
using Microsoft.Xna.Framework;
using System;
using System.Linq;
using Barotrauma.Extensions;
using FarseerPhysics;
namespace Barotrauma
@@ -15,6 +15,11 @@ namespace Barotrauma
private bool canOpenDoors;
public bool CanBreakDoors { get; set; }
private bool ShouldBreakDoor(Door door) =>
CanBreakDoors &&
!door.Item.Indestructible && !door.Item.InvulnerableToDamage &&
(door.Item.Submarine == null || door.Item.Submarine.TeamID != character.TeamID);
private Character character;
private Vector2 currentTarget;
@@ -23,7 +28,7 @@ namespace Barotrauma
private float buttonPressCooldown;
const float ButtonPressInterval = 0.5f;
const float ButtonPressInterval = 0.25f;
public SteeringPath CurrentPath
{
@@ -78,7 +83,7 @@ namespace Barotrauma
public IndoorsSteeringManager(ISteerable host, bool canOpenDoors, bool canBreakDoors) : base(host)
{
pathFinder = new PathFinder(WayPoint.WayPointList.FindAll(wp => wp.SpawnType == SpawnType.Path), indoorsSteering: true);
pathFinder = new PathFinder(WayPoint.WayPointList.FindAll(wp => wp.SpawnType == SpawnType.Path), true);
pathFinder.GetNodePenalty = GetNodePenalty;
this.canOpenDoors = canOpenDoors;
@@ -111,9 +116,14 @@ namespace Barotrauma
IsPathDirty = true;
}
public void SteeringSeek(Vector2 target, float weight, Func<PathNode, bool> startNodeFilter = null, Func<PathNode, bool> endNodeFilter = null, Func<PathNode, bool> nodeFilter = null, bool checkVisiblity = true)
public void SteeringSeekSimple(Vector2 targetSimPos, float weight = 1)
{
steering += CalculateSteeringSeek(target, weight, startNodeFilter, endNodeFilter, nodeFilter, checkVisiblity);
steering += base.DoSteeringSeek(targetSimPos, weight);
}
public void SteeringSeek(Vector2 target, float weight, float minGapWidth = 0, Func<PathNode, bool> startNodeFilter = null, Func<PathNode, bool> endNodeFilter = null, Func<PathNode, bool> nodeFilter = null, bool checkVisiblity = true)
{
steering += CalculateSteeringSeek(target, weight, minGapWidth, startNodeFilter, endNodeFilter, nodeFilter, checkVisiblity);
}
/// <summary>
@@ -158,42 +168,47 @@ namespace Barotrauma
}
}
private Vector2 CalculateSteeringSeek(Vector2 target, float weight, Func<PathNode, bool> startNodeFilter = null, Func<PathNode, bool> endNodeFilter = null, Func<PathNode, bool> nodeFilter = null, bool checkVisibility = true)
private Vector2 CalculateSteeringSeek(Vector2 target, float weight, float minGapSize = 0, Func<PathNode, bool> startNodeFilter = null, Func<PathNode, bool> endNodeFilter = null, Func<PathNode, bool> nodeFilter = null, bool checkVisibility = true)
{
Vector2 targetDiff = target - currentTarget;
if (currentPath != null && currentPath.Nodes.Any())
bool needsNewPath = currentPath == null || currentPath.Unreachable || currentPath.Finished;
if (!needsNewPath && character.Submarine != null && character.Params.PathFinderPriority > 0.5f)
{
//current path calculated relative to a different sub than where the character is now
//take that into account when calculating if the target has moved
Submarine currentPathSub = currentPath?.Nodes.First().Submarine;
if (currentPathSub != character.Submarine && character.Submarine != null)
Vector2 targetDiff = target - currentTarget;
if (currentPath != null && currentPath.Nodes.Any() && character.Submarine != null)
{
Vector2 subDiff = character.Submarine.SimPosition - currentPathSub.SimPosition;
targetDiff += subDiff;
//target in a different sub than where the character is now
//take that into account when calculating if the target has moved
Submarine currentPathSub = currentPath?.CurrentNode?.Submarine;
if (currentPathSub == character.Submarine) { currentPathSub = currentPath?.Nodes.LastOrDefault()?.Submarine; }
if (currentPathSub != character.Submarine && targetDiff.LengthSquared() > 1 && currentPathSub != null)
{
Vector2 subDiff = character.Submarine.SimPosition - currentPathSub.SimPosition;
targetDiff += subDiff;
}
}
if (targetDiff.LengthSquared() > 1)
{
needsNewPath = true;
}
}
bool needsNewPath = character.Params.PathFinderPriority > 0.5f && (currentPath == null || currentPath.Unreachable || targetDiff.LengthSquared() > 1);
//find a new path if one hasn't been found yet or the target is different from the current target
if (needsNewPath || findPathTimer < -1.0f)
{
IsPathDirty = true;
if (findPathTimer < 0)
{
SkipCurrentPathNodes();
currentTarget = target;
Vector2 currentPos = host.SimPosition;
if (character != null && character.Submarine == null)
pathFinder.InsideSubmarine = character.Submarine != null && !character.Submarine.Info.IsRuin;
pathFinder.ApplyPenaltyToOutsideNodes = character.Submarine != null && character.PressureProtection <= 0;
var newPath = pathFinder.FindPath(currentPos, target, character.Submarine, "(Character: " + character.Name + ")", minGapSize, startNodeFilter, endNodeFilter, nodeFilter, checkVisibility: checkVisibility);
bool useNewPath = needsNewPath || currentPath == null || currentPath.CurrentNode == null || character.Submarine != null && findPathTimer < -1 && Math.Abs(character.AnimController.TargetMovement.X) <= 0;
if (newPath.Unreachable || newPath.Nodes.None())
{
var targetHull = Hull.FindHull(ConvertUnits.ToDisplayUnits(target), null, false);
if (targetHull != null && targetHull.Submarine != null)
{
currentPos -= targetHull.Submarine.SimPosition;
}
useNewPath = false;
}
pathFinder.InsideSubmarine = character.Submarine != null;
pathFinder.ApplyPenaltyToOutsideNodes = character.PressureProtection <= 0;
var newPath = pathFinder.FindPath(currentPos, target, character.Submarine, "(Character: " + character.Name + ")", startNodeFilter, endNodeFilter, nodeFilter, checkVisibility: checkVisibility);
bool useNewPath = needsNewPath || currentPath == null || currentPath.CurrentNode == null || findPathTimer < -1 && Math.Abs(character.AnimController.TargetMovement.X) <= 0;
if (!useNewPath && currentPath != null && currentPath.CurrentNode != null && newPath.Nodes.Any() && !newPath.Unreachable)
else if (!useNewPath && currentPath != null && currentPath.CurrentNode != null)
{
// Check if the new path is the same as the old, in which case we just ignore it and continue using the old path (or the progress would reset).
if (IsIdenticalPath())
@@ -205,7 +220,7 @@ namespace Barotrauma
// Use the new path if it has significantly lower cost (don't change the path if it has marginally smaller cost. This reduces navigating backwards due to new path that is calculated from the node just behind us).
float t = (float)currentPath.CurrentIndex / (currentPath.Nodes.Count - 1);
useNewPath = newPath.Cost < currentPath.Cost * MathHelper.Lerp(0.95f, 0, t);
if (!useNewPath)
if (!useNewPath && character.Submarine != null)
{
// It's possible that the current path was calculated from a start point that is no longer valid.
// Therefore, let's accept also paths with a greater cost than the current, if the current node is much farther than the new start node.
@@ -232,12 +247,42 @@ namespace Barotrauma
}
if (useNewPath)
{
if (currentPath != null)
{
CheckDoorsInPath();
}
currentPath = newPath;
}
float priority = MathHelper.Lerp(3, 1, character.Params.PathFinderPriority);
findPathTimer = priority * Rand.Range(1.0f, 1.2f);
IsPathDirty = false;
return DiffToCurrentNode();
void SkipCurrentPathNodes()
{
if (!character.AnimController.InWater || character.Submarine != null) { return; }
if (CurrentPath == null || CurrentPath.Unreachable || CurrentPath.Finished) { return; }
if (CurrentPath.CurrentIndex < 0 || CurrentPath.CurrentIndex >= CurrentPath.Nodes.Count - 1) { return; }
// Check if we could skip ahead to NextNode when the character is swimming and using waypoints outside.
// Do this to optimize the old path before creating and evaluating a new path.
// In general, this is to avoid behavior where:
// a) the character goes back to first reach CurrentNode when the second node would be closer; or
// b) the character moves along the path when they could cut through open space to reduce the total distance.
float pathDistance = Vector2.Distance(character.WorldPosition, CurrentPath.CurrentNode.WorldPosition);
pathDistance += CurrentPath.GetLength(startIndex: CurrentPath.CurrentIndex);
for (int i = CurrentPath.Nodes.Count - 1; i > CurrentPath.CurrentIndex + 1; i--)
{
var waypoint = CurrentPath.Nodes[i];
float directDistance = Vector2.DistanceSquared(character.WorldPosition, waypoint.WorldPosition);
if (directDistance > (pathDistance * pathDistance) || Submarine.PickBody(host.SimPosition, waypoint.SimPosition, collisionCategory: Physics.CollisionLevel | Physics.CollisionWall) != null)
{
pathDistance -= CurrentPath.GetLength(startIndex: i - 1, endIndex: i);
continue;
}
CurrentPath.SkipToNode(i);
break;
}
}
}
}
@@ -271,25 +316,35 @@ namespace Barotrauma
pos2 -= CurrentPath.Nodes.Last().Submarine.SimPosition;
}
return currentTarget - pos2;
}
if (canOpenDoors && !character.LockHands && buttonPressCooldown <= 0.0f)
}
bool doorsChecked = false;
if (!character.LockHands && buttonPressCooldown <= 0.0f)
{
CheckDoorsInPath();
doorsChecked = true;
}
Vector2 pos = host.SimPosition;
if (character != null && currentPath.CurrentNode != null)
if (character != null && CurrentPath.CurrentNode != null)
{
if (CurrentPath.CurrentNode.Submarine != null)
var nodeSub = CurrentPath.CurrentNode.Submarine;
if (nodeSub != null)
{
if (character.Submarine == null)
{
pos -= CurrentPath.CurrentNode.Submarine.SimPosition;
// Going inside
pos -= ConvertUnits.ToSimUnits(nodeSub.Position);
}
else if (character.Submarine != currentPath.CurrentNode.Submarine)
else if (character.Submarine != nodeSub)
{
pos -= ConvertUnits.ToSimUnits(currentPath.CurrentNode.Submarine.Position - character.Submarine.Position);
// Different subs
pos -= ConvertUnits.ToSimUnits(nodeSub.Position - character.Submarine.Position);
}
}
else if (character.Submarine != null)
{
// Going outside
pos += ConvertUnits.ToSimUnits(character.Submarine.Position);
}
}
bool isDiving = character.AnimController.InWater && character.AnimController.HeadInWater;
// Only humanoids can climb ladders
@@ -362,7 +417,7 @@ namespace Barotrauma
}
if (isAboveFloor || nextLadderSameAsCurrent)
{
currentPath.SkipToNextNode();
NextNode(!doorsChecked);
}
}
else if (nextLadder != null)
@@ -372,7 +427,7 @@ namespace Barotrauma
//e.g. no point in going down to reach the starting point of a path when we could go directly to the one above
if (Math.Sign(currentPath.CurrentNode.WorldPosition.Y - character.WorldPosition.Y) != Math.Sign(currentPath.NextNode.WorldPosition.Y - character.WorldPosition.Y))
{
currentPath.SkipToNextNode();
NextNode(!doorsChecked);
}
}
return diff;
@@ -394,7 +449,7 @@ namespace Barotrauma
float distance = horizontalDistance + verticalDistance;
if (ConvertUnits.ToSimUnits(distance) < targetDistance)
{
currentPath.SkipToNextNode();
NextNode(!doorsChecked);
}
}
}
@@ -419,7 +474,7 @@ namespace Barotrauma
float targetDistance = Math.Max(colliderSize.X / 2 * margin, minWidth / 2);
if (horizontalDistance < targetDistance && isAboveFeet && isNotTooHigh && (door == null || door.CanBeTraversed))
{
currentPath.SkipToNextNode();
NextNode(!doorsChecked);
}
}
if (currentPath.CurrentNode == null)
@@ -429,28 +484,51 @@ namespace Barotrauma
return currentPath.CurrentNode.SimPosition - pos;
}
private void NextNode(bool checkDoors)
{
if (checkDoors)
{
CheckDoorsInPath();
}
currentPath.SkipToNextNode();
}
private bool CanAccessDoor(Door door, Func<Controller, bool> buttonFilter = null)
{
if (door.IsOpen || door.IsBroken) { return true; }
if (!door.Item.IsInteractable(character)) { return false; }
if (!CanBreakDoors)
if (door.IsBroken) { return true; }
if (!door.IsOpen)
{
if (door.IsStuck || door.IsJammed) { return false; }
if (!canOpenDoors || character.LockHands) { return false; }
if (!door.Item.IsInteractable(character)) { return false; }
if (!ShouldBreakDoor(door))
{
if (door.IsStuck || door.IsJammed) { return false; }
if (!canOpenDoors || character.LockHands) { return false; }
}
}
if (door.HasIntegratedButtons)
{
return door.HasAccess(character) || CanBreakDoors;
return door.IsOpen || door.HasAccess(character) || ShouldBreakDoor(door);
}
else
{
return door.Item.GetConnectedComponents<Controller>(true).Any(b => b.HasAccess(character) && (buttonFilter == null || buttonFilter(b))) || CanBreakDoors;
// We'll want this to run each time, because the delegate is used to find a valid button component.
bool canAccessButtons = door.Item.GetConnectedComponents<Controller>(true).Any(b => b.HasAccess(character) && (buttonFilter == null || buttonFilter(b)));
return canAccessButtons || door.IsOpen || ShouldBreakDoor(door);
}
}
private Vector2 GetColliderSize() => ConvertUnits.ToDisplayUnits(character.AnimController.Collider.GetSize());
private float GetColliderLength()
{
Vector2 colliderSize = character.AnimController.Collider.GetSize();
return ConvertUnits.ToDisplayUnits(Math.Max(colliderSize.X, colliderSize.Y));
}
private void CheckDoorsInPath()
{
for (int i = 0; i < 2; i++)
if (!canOpenDoors) { return; }
for (int i = 0; i < 5; i++)
{
WayPoint currentWaypoint = null;
WayPoint nextWaypoint = null;
@@ -461,17 +539,21 @@ namespace Barotrauma
{
door = currentPath.Nodes.First().ConnectedDoor;
shouldBeOpen = door != null;
if (i > 0) { break; }
}
else
{
if (i == 0)
bool closeDoors = character.IsBot && character.IsInFriendlySub || character.Params.AI != null && character.Params.AI.KeepDoorsClosed;
if (i == 0 || !closeDoors)
{
currentWaypoint = currentPath.CurrentNode;
nextWaypoint = currentPath.NextNode;
}
else
{
currentWaypoint = currentPath.PrevNode;
int previousIndex = currentPath.CurrentIndex - i;
if (previousIndex < 0) { break; }
currentWaypoint = currentPath.Nodes[previousIndex];
nextWaypoint = currentPath.CurrentNode;
}
if (currentWaypoint?.ConnectedDoor == null) { continue; }
@@ -480,24 +562,30 @@ namespace Barotrauma
{
//the node we're heading towards is the last one in the path, and at a door
//the door needs to be open for the character to reach the node
if (currentWaypoint.ConnectedDoor.LinkedGap != null && currentWaypoint.ConnectedDoor.LinkedGap.IsRoomToRoom)
if (currentWaypoint.ConnectedDoor.LinkedGap != null)
{
shouldBeOpen = true;
door = currentWaypoint.ConnectedDoor;
// Keep the airlock doors closed, but not in ruins/wrecks
if (currentWaypoint.ConnectedDoor.LinkedGap.IsRoomToRoom || currentWaypoint.Submarine?.Info.IsRuin != null || currentWaypoint.Submarine?.Info.IsWreck != null)
{
shouldBeOpen = true;
door = currentWaypoint.ConnectedDoor;
}
}
}
else
{
float colliderLength = GetColliderLength();
door = currentWaypoint.ConnectedDoor;
if (door.LinkedGap.IsHorizontal)
{
int dir = Math.Sign(nextWaypoint.WorldPosition.X - door.Item.WorldPosition.X);
shouldBeOpen = (door.Item.WorldPosition.X - character.WorldPosition.X) * dir > -50.0f;
float size = character.AnimController.InWater ? colliderLength : GetColliderSize().X;
shouldBeOpen = (door.Item.WorldPosition.X - character.WorldPosition.X) * dir > -size;
}
else
{
int dir = Math.Sign(nextWaypoint.WorldPosition.Y - door.Item.WorldPosition.Y);
shouldBeOpen = (door.Item.WorldPosition.Y - character.WorldPosition.Y) * dir > -80.0f;
shouldBeOpen = (door.Item.WorldPosition.Y - character.WorldPosition.Y) * dir > -colliderLength;
}
}
}
@@ -541,7 +629,7 @@ namespace Barotrauma
}
else if (closestButton != null)
{
if (Vector2.DistanceSquared(closestButton.Item.WorldPosition, character.WorldPosition) < MathUtils.Pow(closestButton.Item.InteractDistance * 2, 2))
if (Vector2.DistanceSquared(closestButton.Item.WorldPosition, character.WorldPosition) < MathUtils.Pow(closestButton.Item.InteractDistance + GetColliderLength(), 2))
{
closestButton.Item.TryInteract(character, false, true);
buttonPressCooldown = ButtonPressInterval;