(65debb5e8) Remove HasAccessToPath method. Add CanAccessThroughDoor method and use it in GetNodePenalty method. Shouldn't anymore find paths to places where doesn't have access to. TODO: CheckDoorsInPath should ideally use the same code for checking the doors, but since it works ok, I didn't touch it (much). Merely reduced the interact distance.

This commit is contained in:
Joonas Rikkonen
2019-05-16 05:38:17 +03:00
parent 4695e6f92b
commit 674f5a656f
12 changed files with 462 additions and 146 deletions
@@ -313,46 +313,61 @@ namespace Barotrauma
return currentPath.CurrentNode.SimPosition - pos;
}
/// <summary>
/// This method generates some garbage and is a bit expensive since it uses the recursive Item.GetConnectedComponents() method.
/// </summary>
public bool HasAccessToPath(SteeringPath path)
private bool CanAccessThroughDoor(WayPoint currentNode, WayPoint nextNode, out Controller closestButton)
{
foreach (var node in path.Nodes)
closestButton = null;
if (currentNode == null) { return false; }
if (nextNode == null) { return false; }
var door = nextNode.ConnectedDoor;
if (door == null) { return true; }
if (door.IsOpen) { return true; }
if (canBreakDoors) { return true; }
if (door.IsStuck) { return false; }
if (!canOpenDoors || character.LockHands) { return false; }
if (door.HasIntegratedButtons)
{
var door = node.ConnectedDoor;
if (door != null)
{
if (door.IsOpen) { return true; }
if (door.IsStuck) { return false; }
if (door.HasIntegratedButtons)
{
if (!door.HasRequiredItems(character, false))
{
return false;
}
else
{
foreach (var button in door.Item.GetConnectedComponents<Controller>(true))
{
if (!button.HasRequiredItems(character, false))
{
return false;
}
else if (Vector2.DistanceSquared(button.Item.WorldPosition, door.Item.WorldPosition) > button.Item.InteractDistance * button.Item.InteractDistance)
{
return false;
}
}
}
}
}
return door.HasRequiredItems(character, false);
}
else
{
bool canUseButton = false;
float closestDistance = 0;
foreach (var button in door.Item.GetConnectedComponents<Controller>(true))
{
if (!button.HasRequiredItems(character, false))
{
continue;
}
// Ignore buttons that are on the wrong side of the door
if (door.IsHorizontal)
{
if (Math.Sign(button.Item.Position.Y - nextNode.Position.Y) != Math.Sign(currentNode.Position.Y - nextNode.Position.Y)) { continue; }
}
else
{
if (Math.Sign(button.Item.Position.X - nextNode.Position.X) != Math.Sign(currentNode.Position.X - nextNode.Position.X)) { continue; }
}
float distance = Vector2.DistanceSquared(button.Item.WorldPosition, currentNode.WorldPosition);
// Too far from the current node (can't reach)
if (distance > button.Item.InteractDistance * button.Item.InteractDistance)
{
continue;
}
else if (closestButton == null || distance < closestDistance)
{
closestButton = button;
closestDistance = distance;
}
canUseButton = true;
}
return canUseButton;
}
return true;
}
// TODO: use the CanAccessThroughDoor method.
private void CheckDoorsInPath()
{
// TODO: if no doors was found, seek more nodes?
for (int i = 0; i < 2; i++)
{
Door door = null;
@@ -419,7 +434,7 @@ namespace Barotrauma
foreach (Controller controller in buttons)
{
float dist = Vector2.DistanceSquared(controller.Item.WorldPosition, character.WorldPosition);
if (dist > controller.Item.InteractDistance * controller.Item.InteractDistance * 2.0f) continue;
if (dist > controller.Item.InteractDistance * controller.Item.InteractDistance) { continue; }
if (dist < closestDist || closestButton == null)
{
@@ -467,24 +482,9 @@ namespace Barotrauma
{
penalty = 100.0f;
}
else if (!canBreakDoors)
if (!CanAccessThroughDoor(node.Waypoint, nextNode.Waypoint, out _))
{
//door closed and the character can't open doors -> node can't be traversed
if (!canOpenDoors || character.LockHands) { return null; }
var doorButtons = nextNode.Waypoint.ConnectedDoor.Item.GetConnectedComponents<Controller>();
if (!doorButtons.Any())
{
if (!nextNode.Waypoint.ConnectedDoor.HasRequiredItems(character, false)) { return null; }
}
foreach (Controller button in doorButtons)
{
if (Math.Sign(button.Item.Position.X - nextNode.Waypoint.Position.X) !=
Math.Sign(node.Position.X - nextNode.Position.X)) { continue; }
if (!button.HasRequiredItems(character, false)) { return null; }
}
return null;
}
}
@@ -644,6 +644,49 @@ namespace Barotrauma
{
character.AIController.SteeringManager.Reset();
}
if (goToObjective != null) { return; }
if (currentHull == null) { return; }
//goto objective doesn't exist (a safe hull not found, or a path to a safe hull not found)
// -> attempt to manually steer away from hazards
Vector2 escapeVel = Vector2.Zero;
foreach (FireSource fireSource in currentHull.FireSources)
{
Vector2 dir = character.Position - fireSource.Position;
float distMultiplier = MathHelper.Clamp(100.0f / Vector2.Distance(fireSource.Position, character.Position), 0.1f, 10.0f);
escapeVel += new Vector2(Math.Sign(dir.X) * distMultiplier, !character.IsClimbing ? 0 : Math.Sign(dir.Y) * distMultiplier);
}
foreach (Character enemy in Character.CharacterList)
{
//don't run from friendly NPCs
if (enemy.TeamID == Character.TeamType.FriendlyNPC) { continue; }
//friendly NPCs don't run away from anything but characters controlled by EnemyAIController (= monsters)
if (character.TeamID == Character.TeamType.FriendlyNPC && !(enemy.AIController is EnemyAIController)) { continue; }
if (enemy.CurrentHull == currentHull && !enemy.IsDead && !enemy.IsUnconscious &&
(enemy.AIController is EnemyAIController || enemy.TeamID != character.TeamID))
{
Vector2 dir = character.Position - enemy.Position;
float distMultiplier = MathHelper.Clamp(100.0f / Vector2.Distance(enemy.Position, character.Position), 0.1f, 10.0f);
escapeVel += new Vector2(Math.Sign(dir.X) * distMultiplier, !character.IsClimbing ? 0 : Math.Sign(dir.Y) * distMultiplier);
}
}
if (escapeVel != Vector2.Zero)
{
//only move if we haven't reached the edge of the room
if ((escapeVel.X < 0 && character.Position.X > currentHull.Rect.X + 50) ||
(escapeVel.X > 0 && character.Position.X < currentHull.Rect.Right - 50))
{
character.AIController.SteeringManager.SteeringManual(deltaTime, escapeVel);
}
else
{
character.AnimController.TargetDir = escapeVel.X < 0.0f ? Direction.Right : Direction.Left;
character.AIController.SteeringManager.Reset();
}
}
else
{
character.AIController.SteeringManager.Reset();
}
}
}
@@ -371,6 +371,31 @@ namespace Barotrauma
{
#if DEBUG
DebugConsole.ThrowError("AIObjectiveFixLeak failed - the item \"" + weldingTool + "\" has no RepairTool component but is tagged as a welding tool");
#endif
abandon = true;
return;
}
Vector2 gapDiff = Leak.WorldPosition - character.WorldPosition;
// TODO: use the collider size/reach?
if (!character.AnimController.InWater && Math.Abs(gapDiff.X) < 100 && gapDiff.Y < 0.0f && gapDiff.Y > -150)
{
HumanAIController.AnimController.Crouching = true;
}
float reach = ConvertUnits.ToSimUnits(repairTool.Range);
bool canOperate = ConvertUnits.ToSimUnits(gapDiff.Length()) < reach;
if (canOperate)
{
TryAddSubObjective(ref operateObjective, () => new AIObjectiveOperateItem(repairTool, character, objectiveManager, option: "", requireEquip: true, operateTarget: Leak));
}
else
{
TryAddSubObjective(ref gotoObjective, () => new AIObjectiveGoTo(ConvertUnits.ToSimUnits(GetStandPosition()), character, objectiveManager) { CloseEnough = reach * 0.75f });
}
var repairTool = weldingTool.GetComponent<RepairTool>();
if (repairTool == null)
{
#if DEBUG
DebugConsole.ThrowError("AIObjectiveFixLeak failed - the item \"" + weldingTool + "\" has no RepairTool component but is tagged as a welding tool");
#endif
abandon = true;
return;
@@ -74,21 +74,6 @@ namespace Barotrauma
}
}
public override void Update(float deltaTime)
{
if (objectiveManager.CurrentObjective == this)
{
if (randomTimer > 0)
{
randomTimer -= deltaTime;
}
else
{
SetRandom();
}
}
}
public override bool IsCompleted() => false;
public override bool CanBeCompleted => true;
@@ -143,6 +143,10 @@ namespace Barotrauma
{
isCompleted = true;
}
if (component.AIOperate(deltaTime, character, this))
{
isCompleted = true;
}
}
else
{
@@ -639,6 +639,25 @@ namespace Barotrauma.Items.Components
}
}
if (targetItem.Prefab.DeconstructItems.Any())
{
inputContainer.Inventory.RemoveItem(targetItem);
Entity.Spawner.AddToRemoveQueue(targetItem);
MoveInputQueue();
PutItemsToLinkedContainer();
}
else
{
if (outputContainer.Inventory.Items.All(i => i != null))
{
targetItem.Drop(dropper: null);
}
else
{
outputContainer.Inventory.TryPutItem(targetItem, user: null, createNetworkEvent: true);
}
}
if (targetItem.Prefab.DeconstructItems.Any())
{
inputContainer.Inventory.RemoveItem(targetItem);
@@ -212,33 +212,6 @@ namespace Barotrauma.Items.Components
}
}
public Vector2? PosToMaintain
{
get { return posToMaintain; }
set { posToMaintain = value; }
}
struct ObstacleDebugInfo
{
public Vector2 Point1;
public Vector2 Point2;
public Vector2? Intersection;
public float Dot;
public Vector2 AvoidStrength;
public ObstacleDebugInfo(GraphEdge edge, Vector2? intersection, float dot, Vector2 avoidStrength)
{
Point1 = edge.Point1;
Point2 = edge.Point2;
Intersection = intersection;
Dot = dot;
AvoidStrength = avoidStrength;
}
}
//edge point 1, edge point 2, avoid strength
private List<ObstacleDebugInfo> debugDrawObstacles = new List<ObstacleDebugInfo>();
@@ -1536,6 +1536,10 @@ namespace Barotrauma
{
ApplyStatusEffects(!waterProof && inWater ? ActionType.InWater : ActionType.NotInWater, deltaTime);
}
if (!broken)
{
ApplyStatusEffects(!waterProof && inWater ? ActionType.InWater : ActionType.NotInWater, deltaTime);
}
ApplyStatusEffects(!waterProof && inWater ? ActionType.InWater : ActionType.NotInWater, deltaTime);
if (body == null || !body.Enabled || !inWater || ParentInventory != null || Removed) { return; }
@@ -360,6 +360,25 @@ namespace Barotrauma
}
}
public string DisplayName
{
get;
private set;
}
private string roomName;
[Editable, Serialize("", true, translationTextTag: "RoomName.")]
public string RoomName
{
get { return roomName; }
set
{
if (roomName == value) { return; }
roomName = value;
DisplayName = TextManager.Get(roomName, returnNull: true) ?? roomName;
}
}
public override Rectangle Rect
{
get